Activités pratiques : passage à l'échelle - Citadelles

Durée2h30

Présentation & objectifs

Cette séance complète la séance précédente en utilisant les mêmes techniques, cette fois pour concevoir une application complète, en l’occurrence un jeu de société de bonne facture, Citadelles. En effet, ces techniques passent à l’échelle et permettent de concevoir des applications complexes.

Important

De même que pour la séance précédente vous n’allez pas écrire du code, vous allez réfléchir à comment structurer une solution à un problème donné et vous donnerez le pseudo-code correspondant. Vous n’avez donc pas besoin d’utiliser votre ordinateur (du moins dans un premier temps), un simple papier/crayon suffit.

Contenu de l’activité

Le jeu Citadelles, créé en 2000, par Bruno Faidutti combine hasard, réflexion et surprise (chaque joueur/joueuse n’a qu’une vision partielle de la situation).

Il s’agit d’arriver le plus loin dans la construction d’une citadelle aboutie avec ses “quartiers” relevant de la religion, de la noblesse, du commerce, de la soldatesque et du merveilleux. Pour cela chaque joueur.euse dispose d’un trésor et va incarner, à chaque tour, un rôle (assassin, voleur, magicien, roi, évêque, marchand, architecte, condottiere), avec ses pouvoirs spécifiques. Ce rôle est masqué (du moins initialement) à ses adversaires.

Il existe trois éditions du jeu. Nous allons nous baser sur la seconde édition en considérant :

  • les règles pour jouer à deux et
  • en nous limitant aux personnages introduits dans la première édition.

Les règles du jeu sont disponibles sur Internet (voir jeuxavolonte.asso.fr). Les annexes vous donnent différentes indications disponibles sur les cartes du jeu, qui ne sont pas toujours disponibles sur Internet et varient d’une édition à l’autre.

Nous allons nous intéresser à une une version informatisée de ce jeu permettant à deux joueur.euse.s humains de se rencontrer ou, plus précisément, à une seule personne de reproduire une partie à deux joueur.euse.s (l’application propose pour chaque les choix légaux possibles).

Une partie importante de la conception consiste à préciser les contours de l’application à partir d’une description informelle de ce que l’on souhaite réaliser, qui prend ici la forme des règles du jeu. Comme on se focalise ici sur un jeu à deux et un nombre limité de personnages, vous pouvez survoler les règles qui régissent le choix des personnage, hors jeu à deux, ainsi que la description des “nouveaux” personnages.

Les méthodologies de conception associées à la programmation orientée-objet (on dit aussi programmation par objets, cf. FranceTerme) ont la bonne propriété de conduire naturellement à une approche incrémentale qui découpe le problème en sous-problèmes et raffine les solutions associés aux sous-problèmes.

En pratique, au niveau de granularité de notre application, le découpage va correspondre à un découpage en classes (pour des applications de plus grande taille, on peut être amené à considérer d’autres niveaux de granularité qui prendront le nom de composants, modules, couche, systèmes…). On va donc dans un premier temps essayer de faire émerger les classes qui pourraient avoir du sens par rapport à l’application (voir le cours sur l’identification des classes).

On peut ensuite essayer de remplir un tableau Classes, objets, attributs… Ce remplissage va aussi se faire de manière incrémentale. La définition des responsabilités associées à chaque classe et des relations/associations entre classes notamment peut remettre en question le découpage initial, c’est-à-dire faire apparaître ou disparaître des classes, ou encore conduire à modifier le nom des classes afin de mieux capturer leur rôle dans l’application.

Ici, on va commencer par s’intéresser aux classes qui permettent de gérer les cartes. Intuitivement, ces classes constituent une forme de socle (ou couche basse) sur lequel les autres classes vont s’appuyer, c’est-à-dire que ces autres classes vont utiliser les classes du socle mais l’inverse ne va pas être vrai. C’est bien sûr une forme d’architecture logicielle qui n’apparait pas systématiquement mais qui permet un découpage utile : on construit des fondations stables sur lesquelles les couches supérieures vont pouvoir s’appuyer.

Une fois le tableau Classes, objets, atributs… disponible, il est possible de préciser le contenu des méthodes.

Quel que soit l’étape de conception, il est difficile d’apprécier la solidité de l’édifice, même si en avançant dans le processus on a plus d’éléments pour le faire. Si des éléments de conception s’avèrent ne pas tenir la route, il ne faut pas hésiter à revenir en arrière (sans détruire ce qui a déjà été fait parce qu’il arrive aussi qu’on remette en question des points qui finalement tenaient la route).

Il y a aussi des choix à faire. Certains peuvent être meilleurs que d’autres, dans l’absolu (l’implémentation de la conception sera problématique sinon) ou du fait de connaissances additionnelles sur l’application (environnement technique, propriétés dites “non fonctionnelles” de performance, sécurité, maintenabilité, extensibilité…"). Il est en général impossible de tous les explorer. À défaut d’expérience, la simplicité peut initialement être un bon critère de choix.

Question 1

À la simple lecture de la règle du jeu, quelles sont les classes qui vous semblent utiles ? Essayez de distinguer celles qui le sont sûrement et celles qui le sont peut-être.

Les cartes

On va tout d’abord s’intéresser aux cartes du jeu, individuellement, puis dans leur utilisation.

1 - Les cartes, inviduellement

Question 1.1

Listez, en leur donnant un nom, les différents types de cartes qui apparaissent dans le jeu. Organisez-les en une hiérarchie.

Question 1.2

Déterminez les attributs nécessaires à la gestion d’une carte.

2 - Les cartes, dans leur utilisation

On s’intéresse à la fois au type des cartes regroupées et à la manière dont les cartes sont utilisées. Par exemple, si on considère une pioche, une fois la pioche créée, on peut la mélanger, piocher une carte et se défausser d’une carte (la question peut alors être de savoir où mettre la carte, sur ou sous la pioche ?). Quand on a plusieurs types de cartes, la pioche contient des cartes d’un type donné.

Une autre propriété qu’il est utile de repérer est si le regroupement est utilisé par un joueur.euse unique ou partagé entre plusieurs. Par exemple, une pioche est typiquement partagée alors qu’une main est sous le contrôle d’un joueur.euse, qu’elle soit visible ou pas des autres.

Question 2.1

À partir de la règle du jeu, déterminez les différents types de regroupements de cartes utilisés au cours de la partie en leur donnant un nom, en y associant un type de cartes et en indiquant s’ils sont partagés ou pas.

Question 2.2

Il s’agit maintenant d’être un peu plus spécifique en considérant chacun des types de la question précédente comme une classe et de remplir le tableau Classes, objets, attributs… correspondant.

Vous avez précédemment utilisé des listes. Elles sont utiles ici pour représenter des séquences de cartes, par exemple le type d’une séquence de personnages sera [Personnage]. De même, on peut convenir de représenter le type d’un ensemble de personnages par {Personnage}.

Mise en place d’une partie simple

Dans un premier temps, toujours dans l’optique de travailler incrémentalement, on va se focaliser sur le déroulement d’un tour de jeu, sans prendre en compte les pouvoirs des personnages.

Question 3

Maintenant que vous avez une meilleure compréhension des cartes utilisées, commencez à compléter le tableau Classes, objets, attributs… pour les autres classes identifiées initialement, en vous focalisant sur leurs attributs. Pour ce qui est des méthodes, quelle méthode va lancer la partie, et, à très gros grain, comment vont se passer les interactions entre les principaux objets impliqués ?

La gestion de la partie est complexe, d’après la règle, elle peut être naturellement découpée en trois phases : le choix des personnages, les tours des joueur.euse.s et le décomptage des points. On pourrait créer une classe Phase puis créer des sous-classes pour chacune des sous-phases, spécialisée avec le comportement spécifique de la sous-phase. Mais on peut aussi découper la méthode de jeu en trois sous-méthodes qui vont prendre en charge chacune des phases.

Mais, au fait, n’y a-t-il pas quelque chose à faire avant de lancer le choix des personnages ?

3 - Le choix des personnages

Question 3.1

Analyser la manière dont se passe le choix des joueur.euse.s dans le cas d’une partie à 2 en déterminant notamment les actions élémentaires qui reviennent et la manière dont elles s’enchaînent, ce qui pourra donner lieu à une première définition textuelle de la phase, qu’il sera ensuite aisée de transformer en pseudo-code. Cette analyse devrait aussi contribuer à enrichir le tableau Classe, objets, attributs…

4 - Les tours des joueurs

Question 4.1

Commençons par nous intéresser à l’organisation d’un tour. Il faut d’abord faire jouer les joueur.euse.s dans l’ordre prédéfini des personnages. Voici une suggestion pour ce faire. Si on dispose de la séquence des personnages, par exemple dans l’attribut personnages d’une partie (une donnée utile pour sa réinitialisation entre chaque tour), il est possible de traverser cette séquence et demander à chaque joueur.euse.s de jouer son tour s’il a le personnage (s’il ne l’a pas, rien ne va se passer).

Proposez un pseudo-code pour un tour de jeu, sans oublier de tenir compte de la possibilité que ce tour de jeu termine la partie et complétez le tableau des classes.

  • Gestion des choix : on suppose qu’on dispose d’une méthode qu’il est possible de directement appliquer à la classe IHM sans fabriquer d’instance choisit(message: [Character], [T]) -> Tmessage est le message à afficher, [T] la séquence, aussi à afficher, des choix possibles, avec T le type des choix. La méthode retourne le choix sélectionné. Concrètement, cette méthode affiche la séquence des choix en les numérotant et requiert l’entrée d’un nombre correspondant à son choix (qui doit être dans le bon intervalle sinon la méthode boucle) et retourne le choix correspondant.

    Par exemple, pour demander un choix de personnages dans une séquence de personnages personnages et récupérer le résultat, il suffit d’écrire personnage = IHM.choisit("Choisissez un personnage", personnages).

  • Opérations sur les séquences : on suppose qu’on peut accèder à l’élément de rang n d’une séquence de type [T] via la méthode élément(n: Entier) -> T. Plutôt qu’écrire seq.élément(n), on pourra écrire seq[n].

Question 4.2

Il s’agit maintenant d’enchaîner les tours et de clore la partie, concrètement de finaliser le pseudo-code qui gère une partie.

Prise en compte des rôles

Premier survol de la question

Pour commencer, on va (à nouveau) simplifier le problème en considérant que l’action associée au rôle du personnage a lieu à un moment déterminé du tour, par exemple au début. Une première idée consiste donc à définir dans la classe Joueur une méthode auxiliaire agit(Personnage) -> Aucun qui va dérouler les actions associées au rôle.

La structure de la méthode est la suivante :

METHOD agit(personnage: Personnage) -> Aucun:
  rôle = personnage.rôle
  if rôle == "Assassin":
    ...
  else if rôle == "Voleur"
    ...
  else ...

Ce qu’on peut aussi noter, en utilisant une construction dite de pattern matching :

METHOD agit(personnage: Personnage) -> Aucun:
  personnage.rôle match
    case "Assassin":
      ...
    case "Voleur"
      ...
    case ...
Question 5

Passez en revue les différents rôles et essayez de déterminer s’il y a des ajouts à faire à la conception initiale (attributs, méthodes auxiliaires).

Approfondissement

Question 6

Prenez en compte le fait que certaines actions liées à un rôle peuvent s’exécuter en début, fin ou à n’importe quel moment du tour et compléter le pseudo-code des méthodes correspondantes.

Notez qu’il faut s’entendre sur ce que veut dire à n’importe quel moment du tour. La lecture de la règle semble dire qu’il ne s’agit pas d’agir hors du tour du personnage, mais d’avoir le choix d’agir, pendant son tour, au début, à la fin ou avant la construction, avec la contrainte qu’il ne peut y avoir qu’une seule collecte des revenus.

Question 7 (avancée)

On peut se demander si le comportement d’un.e joueur.euse.s, qui dépend de la personification en cours, ne pourrait pas plutôt être associé au personnage lui-même. Un joueur (en tant qu’objet) pourrait alors déléguer son comportement au personnage (en tant qu’objet) dont il endosse le rôle.

Esquissez les modifications à faire pour mettre en place cette idée.

Pour aller plus loin

Il est notamment possible de :

  • terminer la conception, initiale ou avancée ;
  • l’étendre pour prendre en compte :
    • les cartes merveilles ;
    • de nouveaux personnages ;
  • passer du pseudo-code au code pour vérifier la cohérence de la conception et obtenir une application exécutable ;
  • (avancé) faire évoluer la conception et le code pour créer un joueur virtuel autonome dans ses décisions ;
  • (avancé) faire évoluer la conception et le code pour supporter une interface graphique et donner la possibilité à 2 utilisateurs (ou plus) d’interagir chacun sur sa machine.

Annexes - Distribution des cartes de Quartier

Important

Notez que ces informations et ces textes, relatifs à la seconde édition du jeu, éditée par Ubik en 2003, sont protégés. J’ai rajouté l’émoji.

  • 12 cartes Évêque

    • 3 temples (coût 1 pièce d’or)
    • 3 églises (2)
    • 4 monastères (3)
    • 2 cathédrales (5)
  • 12 cartes Condottiere

    • 3 tours de guet (1)
    • 3 prisons (2)
    • 3 casernes (3)
    • 3 forteresses (5)
  • 21 carte Commerce

    • 5 tavernes (1)
    • 4 échoppes (2)
    • 4 marchés (2)
    • 3 comptoirs (3)
    • 3 ports (4)
    • 2 hôtels de ville (5)
  • 12 cartes Noblesse

    • 5 manoirs (3)
    • 2 palais (5)
    • 5 châteaux (4)
  • 14 cartes Merveille

    • 1 carte coûtant 2 pièces d’or
      • Cour des miracles : Pour le décompte final des points, la Cour des miracles est considérée comme un quartier de la couleur de votre choix. Vous ne pouvez pas utiliser cette capacité si vous avez construit la Cour des miracles au dernier tour de jeu.
    • 1 carte coûtant 3 pièces d’or
      • Donjon : Le donjon ne peut pas être détruit par le Condottiere.
    • 1 carte coûtant 4 pièces d’or
      • Trésor impérial : Le trésor impérial coûte 4 pièces d’or à bâtir, mais il rapporte autant de points que vous aurez de pièces d’or en votre possession en fin de partie.
    • 6 cartes coûtant 5 pièces d’or
      • Carrière : Vous pouvez bâtir un bâtiment de même nom que l’un de vos bâtiments déjà en jeu. Vous ne perdez pas les bâtiments ainsi construits si la carrière est détruite.
      • Cimetière : Lorsque le Condottiere détruit un quartier, vous pouvez payer une pièce d’or pour le prendre dans votre main. Vous ne pouvez pas faire cela si vous êtes vous-même Condottiere.
      • Fontaine aux souhaits : En fin de partie, la Fontaine outre les 5 points de victoire correspondant à son coût, rapport 1 point de victoire par autre bâtiment {\color{magenta} violet} en votre possession.
      • Laboratoire : Une fois par tour, vous pouvez vous défausser d’une carte quartier de votre main et recevoir une pièce d’or en contrepartie.
      • Manufacture : Une fois par tour, vous pouvez payer deux pièces d’or pour piocher trois cartes.
      • Observatoire : Si vous choisissez de piocher des cartes au début de votre tour, vous en piochez trois, en choisissez une et défaussez les deux autres.
    • 5 cartes coûtant 6 pièces d’or
      • Bibliothèque : Si vous choisissez de piocher des cartes au début de votre tour, vous en piochez deux et les conservez toutes les deux.
      • Dracoport : Cette réalisation de prestige - on n’a pas vu de dragon dans le royaume depuis bientôt mille ans - coûte six pièces d’or à bâtir mais vaut huit points dans le décompte de fin de partie.
      • École de Magie : Pour la perception des revenus, l’École de Magie est considérée comme un quartier de la couleur de votre choix. Elle vous rapporte donc si vous êtes {\color{yellow} Roi}, {\color{blue} Évêque}, {\color{green} Marchand} ou {\color{red}Condottiere}.
      • Grande Muraille : Le prix à payer par le Condottiere pour détruire vos autres quartiers est augmenté de 1.
      • Université : Cette réalisation de prestige - nul n’a jamais bien compris à quoi pouvait servir une université 😉 - coûte six pièces d’or à bâtir mais vaut huit points dans le décompte de fin de partie.