Jeu

tCHu – étape 6

1 Introduction

Cette étape a pour but d'écrire la classe gérant la totalité du déroulement d'une partie de tCHu.

Notez que cette étape devra être rendue deux fois :

  1. pour le rendu testé habituel (délai : 9 avril, 17h00),
  2. pour le rendu intermédiaire (délai : 16 avril, 17h00).

Le deuxième de ces rendus sera corrigé par lecture de votre code pour les étapes 1 à 6, et il vous faudra donc soigner sa qualité et sa documentation. Il est fortement conseillé de lire notre guide à ce sujet.

2 Concepts

2.1 Déroulement d'une partie

La plupart des règles de tCHu ont été décrites dans les étapes précédentes mais sont résumées ici afin de vous en donner une vue d'ensemble.

2.1.1 Début de partie

Au début de la partie, le joueur qui jouera en premier est tiré au sort.

Cela fait, chaque joueur reçoit 40 wagons de sa couleur, 4 cartes wagon/locomotives tirées du sommet de la pioche de cartes, et 5 billets tirés du sommet de la pioche de billets.

Afin que la partie puisse effectivement débuter, chaque joueur doit choisir au moins 3 des 5 billets reçu, qu'il garde en mains. Les billets non retenus ne sont plus remis en jeu.

2.1.2 Milieu de partie

Tant et aussi longtemps que tous les joueurs possèdent au moins encore 2 wagons, ils jouent à tour de rôle, en commençant par le joueur tiré au sort précédemment.

Lorsque c'est à son tour, un joueur doit effectuer exactement l'une des trois actions suivantes :

  1. tirer les trois billets du sommet de la pioche, et en garder au moins 1, les billets non retenus n'étant plus remis en jeu,
  2. tirer deux cartes, chacune d'entre elles pouvant être soit celle du sommet de la pioche, soit l'une des 5 cartes disposées face visible ; dans ce dernier cas, la carte du sommet de la pioche est retournée afin de remplacer celle prise par le joueur,
  3. (tenter de) s'emparer d'une route, selon les règles décrites à la §2.2 de l'étape 2.

Dès que le joueur a effectué son action, son tour est terminé, et c'est au prochain joueur de jouer.

2.1.3 Fin de partie

Dès qu'un joueur termine son tour avec 2 wagons ou moins, le dernier tour commence. Chaque joueur, y compris celui qui vient de terminer son tour avec 2 wagons ou moins, jouent alors encore un tour chacun. Ensuite, le total des points de chaque joueur est déterminé en additionnant :

  • les points « de construction », acquis en s'emparant de routes,
  • les points gagnés ou perdus grâce aux billets,
  • l'éventuel bonus de 10 points dû au chemin le plus long.

Le joueur ayant le plus grand nombre de points est déclaré vainqueur. En cas d'égalité, les deux joueurs sont ex æquo.

3 Mise en œuvre Java

3.1 Classe Game

La classe Game du paquetage ch.epfl.tchu.game, publique, finale et non instanciable, représente une partie de tCHu. Elle n'offre qu'une seule méthode publique et bien entendu statique :

  • void play(Map<PlayerId, Player> players, Map<PlayerId, String> playerNames, SortedBag<Ticket> tickets, Random rng), qui fait jouer une partie de tCHu aux joueurs donnés, dont les noms figurent dans la table playerNames ; les billets disponibles pour cette partie sont ceux de tickets, et le générateur aléatoire rng est utilisé pour créer l'état initial du jeu et pour mélanger les cartes de la défausse pour en faire une nouvelle pioche quand cela est nécessaire ; lève IllegalArgumentException si l'une des deux tables associatives a une taille différente de 2.

Pour faire jouer une partie aux joueurs, la méthode play doit appeler les différentes méthodes de chacun des deux joueurs afin de savoir comment ils désirent jouer. Ces méthodes sont bien entendu celles de l'interface Player décrite à l'étape précédente.

Pour le début de la partie, les méthodes suivantes doivent être appelées, dans l'ordre donné :

  • avant le véritable début de la partie, la méthode initPlayers de chaque joueur doit être appelée afin de lui communiquer sa propre identité, et le nom de chaque joueur — le sien inclus,
  • une fois que le joueur qui jouera en premier a été tiré au hasard, la méthode receiveInfo doit être appelée afin de communiquer cette information à tous les joueurs, en lui passant la chaîne produite par la méthode willPlayFirst de la classe Info,
  • la méthode setInitialTicketChoice de chaque joueur doit être appelée pour lui communiquer les billets qu'il reçoit initialement,
  • la méthode chooseInitialTickets de chaque joueur doit être appelée pour savoir quels billets chaque joueur a décidé de garder,
  • la méthode receiveInfo de chaque joueur doit être appelée pour communiquer à chacun le nombre de billets gardés par chaque joueur ; cette chaîne est produite par la méthode keptTickets de Info.

La partie commence ensuite. Jusqu'à sa fin, chaque joueur doit jouer à tour de rôle, et pour ce faire, la méthode play appelle les méthodes suivantes du joueur courant, dans l'ordre donné :

  • la méthode nextTurn est appelée pour savoir quelle action le joueur courant désire effectuer parmi les trois possibles,
  • si le joueur courant désire tirer des billets, alors :
    • la méthode chooseTickets est appelée afin de déterminer quels billets il garde parmi ceux tirés,
  • si le joueur courant désire tirer des cartes, alors :
    • la méthode drawSlot est appelée une première fois pour savoir quelle carte il désire tirer en premier,
    • la méthode drawSlot est appelée une seconde fois pour savoir quelle carte il désire tirer en second,
  • si le joueur courant désire (tenter de) s'emparer d'une route, alors :
    • la méthode claimedRoute est appelée pour déterminer de quelle route il s'agit,
    • la méthode initialClaimCards est appelée pour déterminer les cartes (initiales) à utiliser,
    • si la route est en tunnel, que les trois cartes du sommet de la pioche impliquent l'utilisation d'au moins une carte additionnelle, et que le joueur courant a dans sa main les cartes additionnelles nécessaires, alors :
      • la méthode chooseAdditionalCards est appelée pour déterminer s'il désire jouer des cartes additionnelles, et si oui, lesquelles.

De plus, la méthode receiveInfo de l'interface Player doit être appelée régulièrement pour informer les deux joueurs du déroulement de la partie. L'argument à passer à cette méthode est toujours obtenu au moyen d'une des méthodes de la classe Info, qui devrait être claire en fonction du contexte. À noter qu'il est nécessaire de créer une instance de cette classe par joueur, puisque son constructeur prend en argument le nom du joueur pour lequel les informations doivent être produites.

La méthode receiveInfo doit être appelée :

  • (comme dit plus haut) dès que l'identité du premier joueur est connue — et avant de dire aux joueurs quels sont leurs cinq billets initiaux,
  • (comme dit plus haut) dès que les joueurs ont choisi le nombre de billets qu'ils gardent initialement — mais seulement lorsque tous les joueurs ont fait leur choix, afin de ne pas donner un avantage au joueur choisissant en dernier,
  • lorsque le tour d'un joueur commence,
  • lorsqu'un joueur tire des billets,
  • lorsqu'un joueur a choisi de garder un certain nombre de billets,
  • lorsqu'un joueur tire une carte de la pioche ou une carte face visible,
  • lorsqu'un joueur s'est emparé d'une route, en communiquant toutes les cartes utilisées — initiales et additionnelles dans le cas d'un tunnel,
  • lorsqu'un joueur tente de s'emparer d'un tunnel,
  • lorsqu'un joueur tente de s'emparer d'un tunnel et que les trois cartes du sommet de la pioche déterminant le nombre de cartes additionnelles à jouer ont été tirées,
  • lorsqu'un joueur ne peut ou ne veut jouer les cartes additionnelles nécessaires pour s'emparer d'un tunnel,
  • lorsque le dernier tour commence,
  • lorsqu'un joueur obtient le bonus pour le plus long chemin,
  • lorsqu'un joueur remporte la victoire, ou lorsque les deux joueurs sont ex æquo.

Finalement, la méthode updateState de chacun des deux joueurs doit être appelée régulièrement afin de leur communiquer les changements d'état de la partie. Cette méthode pourrait être appelée à chaque changement d'état, mais l'appeler dans les cas suivants suffit :

  • juste avant d'appeler la méthode chooseInitialTickets pour demander aux joueurs de choisir leurs billets initiaux, afin qu'ils puissent faire ce choix en connaissant l'état actuel de la partie — en particulier les cartes initiales qui leur ont été distribuées,
  • juste avant d'appeler la méthode nextTurn pour demander au joueur courant de choisir l'action qu'il désire effectuer, afin qu'il puisse faire ce choix en connaissant l'état actuel de la partie,
  • juste avant d'appeler drawSlot pour la seconde fois lorsqu'un joueur tire des cartes, afin qu'il sache p.ex. quelle carte a remplacé la carte face visible qu'il a éventuellement tirée en premier,
  • juste avant d'informer les joueurs du résultat final de la partie, afin qu'ils connaissent l'état dans lequel la partie s'est effectivement terminé.

Ces appels à updateState provoqueront la mise à jour de l'interface graphique lorsque celle-ci sera écrite, et il est donc important qu'ils soient faits au bon moment.

3.1.1 Conseils de programmation

  1. Recréation de la pioche

    Prêtez attention au fait qu'au cours du jeu, la pioche de cartes va progressivement se vider, et la défausse se remplir. Lorsque la pioche est totalement vide, elle doit être recréée à partir de la défausse. Il faut donc penser à appeler la méthode withCardsDeckRecreatedIfNeeded au bon moment !

    Notre conseil est de l'appeler systématiquement avant chaque appel à une méthode qui doit tirer une carte de la pioche, et de ne jamais tirer plus d'une carte à la fois en cours de partie. En particulier, les trois cartes à tirer lorsqu'un joueur tente de s'emparer d'un tunnel doivent absolument être tirées l'une après l'autre.

  2. Méthodes auxiliaires

    Il peut être utile d'ajouter des méthodes privées à votre classe Game afin de simplifier votre code, par exemple :

    • une méthode permettant d'envoyer une information à tous les joueurs, en appelant la méthode receiveInfo de chacun d'eux,
    • une méthode permettant d'informer tous les joueurs d'un changement d'état, en appelant la méthode updateState de chacun d'eux.

3.2 Tests

Comme d'habitude, nous ne vous fournissons plus de tests mais un fichier de vérification de signatures contenu dans une archive Zip à importer dans votre projet.

4 Résumé

Pour cette étape, vous devez :

  • écrire la classe Game selon les indications données ci-dessus,
  • tester votre code,
  • documenter la totalité des entités publiques que vous avez définies,
  • rendre votre code au plus tard le 9 avril 2021 à 17h00, via le système de rendu.

Ce rendu est un rendu testé, auquel 20 points sont attribués, au prorata des tests unitaires passés avec succès.

N'attendez surtout pas le dernier moment pour effectuer votre rendu, car vous n'êtes pas à l'abri d'imprévus. Souvenez-vous qu'aucun retard, aussi insignifiant soit-il, ne sera toléré !