Programme principal

ChaCuN – étape 11

1. Introduction

Le but de cette dernière étape est de terminer le projet en écrivant le programme principal.

2. Concepts

2.1. Création d'une partie

Pour pouvoir jouer une partie de ChaCuN, il faut pouvoir communiquer au programme :

  • le nombre de joueurs et leurs noms,
  • la manière dont les tuiles doivent être mélangées — en cas de jeu distant.

La manière dont les tuiles doivent être mélangées peut être spécifiée au moyen d'un simple entier de 64 bits nommé graine (seed), utilisé pour initialiser le générateur aléatoire (random generator) qui sert à mélanger les tuiles. À chaque graine correspond donc un ordre de mélange des tuiles, ce qui est très utile pour le jeu distant. En effet, il suffit que tous les joueurs lancent le jeu sur leur ordinateur avec la même graine pour avoir la garantie que les tuiles apparaîtront dans le même ordre.

La graine est bien entendu optionnelle, et si elle n'est pas spécifiée, elle est choisie au hasard.

Les joueurs et l'éventuelle graine sont passés en arguments au programme lors de son lancement. Tous les arguments qui ne commencent pas par un tiret (-) sont les noms des joueurs, dans l'ordre, tandis que la graine peut être spécifiée au moyen d'un argument commençant par --seed= suivi de la graine elle-même, en base 10.

Par exemple, pour lancer une partie entre deux joueurs nommés Dalia et Claude, en utilisant la graine 2024, on passe les arguments suivants au programme :

Dalia Claude --seed=2024

La graine n'a pas besoin d'apparaître en dernière position, mais cela est probablement plus clair. L'ordre des joueurs est par contre important, puisqu'il s'agit de l'ordre de jeu, qui détermine également les couleurs. Dans cet exemple, Dalia serait la joueuse de couleur rouge, Claude le joueur de couleur bleue.

La manière de passer des arguments à un programme lancé depuis IntelliJ est décrite dans notre guide à ce sujet.

2.2. Interface finale

La vidéo ci-dessous montre le comportement de l'interface finale du programme lorsqu'on le lance avec les arguments donnés à la section précédente.

Les actions effectuées au cours de cette vidéo sont :

 1:AA   2:C   3:AL   4:D   5:A2   6:B   7:AV   8:7
 9:AE  10:Y  11:AA  12:D  13:AZ  14:7  15:BD  16:A
17:BI  18:A  19:AO  20:B  21:AW  22:A  23:AE  24:7

3. Mise en œuvre Java

Avant de commencer cette étape, il vous faut télécharger l'archive Zip que nous mettons à votre disposition, et qui contient trois fichiers à importer dans votre projet, à savoir :

  • une version mise à jour de deux feuilles de style (message-board.css et board.css) qui corrigent des erreurs des versions distribuées précédemment,
  • la classe Tiles du paquetage principal1, qui définit la liste des tuiles TILES.

La classe Tiles a été générée automatiquement au moyen des données d'une feuille de calcul décrivant les tuiles. La feuille de calcul utilisée n'est toutefois pas celle qu'il vous avait été demandé de remplir, qui contient malheureusement encore des erreurs.

3.1. Correction d'erreur

Comme expliqué à l'étape 6, lors de la pose de la fosse à pieux, il faut :

  1. annuler, dans le pré adjacent, les éventuels cerfs dévorés par des smilodons,
  2. calculer les points rapportés par les animaux restants dans le pré adjacent,
  3. annuler la totalité des animaux présents dans le pré adjacent.

Malheureusement, la méthode withScoredHuntingTrap de MessageBoard ne permet pour l'instant pas de faire cela, car elle fait l'hypothèse qu'aucun animal du pré adjacent n'est annulé au moment du calcul des points.

Pour corriger le problème, il faut donc lui ajouter un argument représentant les cerfs annulés, et utiliser correctement cet argument dans sa mise en œuvre.

3.2. Classe Main

La classe Main, du sous-paquetage gui, instanciable, est la classe principale du projet. Comme toute classe principale d'une application utilisant JavaFX, elle hérite de Application et fournit une mise en œuvre de sa méthode start. Elle possède de plus une méthode main qui ne fait rien d'autre qu'appeler launch en lui passant les arguments reçus.

La méthode start se charge de créer l'état de jeu initial en fonction des arguments passés au programme, puis de construire l'interface graphique. Les sections suivantes donnent des indications quant à ces différents aspects.

3.2.1. Graphe de scène

Le graphe de scène construit par la méthode start, et qui constitue le graphe de scène complet de la fenêtre du programme, est présenté plus bas.

Bien entendu, la plus grosse partie de ce graphe de scène est construite par les méthodes create des classes écrites lors des étapes précédentes. Dans la figure ci-dessous, ces parties du graphe de scène sont représentées par des boîtes pointues en haut, dont le nom est celui de la classe chargée de leur construction. Par exemple, la boîte la plus à gauche, intitulée Board, représente la partie du graphe de scène construite par la méthode create de la classe BoardUI.

Ce graphe de scène est le premier a utilisé des nœuds de type BorderPane, qui ont la particularité d'avoir au maximum 5 enfants, qui se trouvent chacun dans une zone particulière de l'espace occupé par leur parent. Le nom de la zone dans laquelle placer chacun des enfants est donnée sur les flèches allant du parent aux enfants dans la figure ci-dessous.

main-sg;32.png
Figure 1 : Graphe de scène de la fenêtre principale

La fenêtre principale — c.-à-d. la valeur de type Stage passée à start — doit être configurée de manière à ce qu'elle porte le titre « ChaCuN », et que ses dimensions initiales soient de 1440×1080 unités.

3.2.2. Conseils de programmation

  1. Gestion des arguments

    JavaFX facilite la gestion des arguments passés au programme puisque la classe Application possède une méthode getParameters qui permet de les obtenir sous une forme déjà pré-analysée.

    La valeur retournée par getParameters représente les arguments passés au programme, et il est possible d'obtenir les arguments « anonymes » — les noms des joueurs dans notre cas — au moyen de la méthode getUnnamed, et les arguments « nommés » — la graine dans notre cas — au moyen de getNamed.

    Par exemple, en lançant le programme avec les arguments suivants :

    Dalia Claude --seed=2024
    

    la méthode getUnnamed retourne une valeur égale à :

    List.of("Dalia", "Claude")
    

    tandis que getNamed en retourne une égale à :

    Map.of("seed", "2024")
    

    Notez que dans un souci de simplicité, les arguments invalides — nombre de joueur inférieur à 2 ou supérieur à 5, graine qui ne correspond pas à un entier dans la plage donnée plus haut, etc. — peuvent simplement faire planter le programme avec une exception.

  2. Mélange des tuiles

    Le mélange des tuiles doit être fait de manière déterministe, et identique dans tous les projets, afin de permettre le jeu distant. La technique exacte à utiliser pour les mélanger consiste à :

    • transformer la version textuelle de la graine, passée en argument au programme, en une valeur de type long au moyen de la méthode parseUnsignedLong,
    • obtenir une « fabrique de générateur aléatoire » au moyen de la méthode getDefault de RandomGeneratorFactory,
    • passer la graine à la méthode create de la fabrique pour obtenir un générateur aléatoire,
    • passer ce générateur aléatoire à la méthode shuffle de Collections pour lui faire mélanger (une copie de) la liste des tuiles obtenue de la classe Tiles fournie.

    Une fois le mélange de toute les tuiles effectué, il est facile d'en extraire trois listes contenant les différents types de tuiles, par exemple en utilisant le collecteur groupingBy.

    Si le mélange est fait comme décrit ci-dessus, avec la graine 2024 les cinq premières tuiles du tas des normales sont les tuiles 69, 26, 35, 30 et 6, tandis que les cinq premières du tas des menhir sont les tuiles 83, 87, 79, 81 et 91.

    Dans le cas où aucune graine n'est passée en argument au programme, les tuiles doivent être mélangées de manière totalement aléatoire, en créant le générateur aléatoire avec la variante sans arguments de la méthode create.

  3. Propriétés et gestionnaires d'événements

    Afin de pouvoir construire le graphe de scène, la méthode start doit créer un certain nombre de propriétés JavaFX, et passer différents gestionnaires d'événements aux méthodes create écrites précédemment.

    Par exemple, l'une des propriétés les plus importantes est celle contenant l'état de la partie, c.-à-d. une valeur de type GameState. Initialement, cette propriété contient l'état du jeu retourné par la méthode initial de GameState. Une fois le graphe de scène construit, son contenu est modifié pour contenir le résultat de l'application de la méthode withStartingTilePlaced à cet état initial. Ensuite, le contenu de cette propriété évolue au cours de la partie, en fonction des actions effectuées par les joueurs.

    (Attention à ne mettre à jour l'état de cette propriété avec le résultat de withStartingTilePlaced que lorsque le graphe de scène a été construit ! En effet, comme nous utilisons parfois des auditeurs pour mettre à jour l'interface, elle ne sera réellement à jour qu'une fois que les auditeurs auront été installés et que le premier changement d'état aura eu lieu.)

    En plus de la propriété contenant l'état du jeu, la méthode start en définit également d'autres contenant la rotation à appliquer à la tuile à placer, la liste des actions et l'ensemble des identifiants des tuiles à mettre en évidence, ainsi que des gestionnaires d'événements qui changent leur valeur en fonction des actions effectuées par l'utilisateur.

3.3. Tests

Étant donné que la classe représentant le programme principal doit impérativement avoir le bon nom et posséder une méthode main ayant la bonne signature, nous vous fournissons un fichier de vérification de signature pour cette étape. Il doit être intégré à votre projet comme ceux des étapes 1 à 6. Notez toutefois que les anciens fichiers de vérification de signature ne sont plus nécessaires, et peuvent être supprimés.

Pour tester votre programme, le plus simple est de jouer des parties de ChaCuN, entre autres à distance avec d'autres groupes, afin de vous assurer que les actions sont interprétées de la même manière par les différentes versions du programme.

4. Résumé

Pour cette étape, vous devez :

  • écrire la classe Main selon les indications données ci-dessus,
  • tester votre code,
  • rendre votre projet entre le 27/5 et le 31/5 à 18h00, sauf si vous désirez faire une étape 12, auquel cas vous devrez le rendre avant le 24/5 à 18h00 ; les instructions concernant ce rendu final seront publiées prochainement.

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é !

Notes de bas de page

1

Notez que cette nouvelle version de la classe Tiles appartient bien au paquetage principal, et pas au sous-paquetage tile comme certaines versions distribuées précédemment.