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
etboard.css
) qui corrigent des erreurs des versions distribuées précédemment, - la classe
Tiles
du paquetage principal1, qui définit la liste des tuilesTILES
.
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 :
- annuler, dans le pré adjacent, les éventuels cerfs dévorés par des smilodons,
- calculer les points rapportés par les animaux restants dans le pré adjacent,
- 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.
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
- Gestion des arguments
JavaFX facilite la gestion des arguments passés au programme puisque la classe
Application
possède une méthodegetParameters
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éthodegetUnnamed
, et les arguments « nommés » — la graine dans notre cas — au moyen degetNamed
.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.
- 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éthodeparseUnsignedLong
, - obtenir une « fabrique de générateur aléatoire » au moyen de la méthode
getDefault
deRandomGeneratorFactory
, - 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
deCollections
pour lui faire mélanger (une copie de) la liste des tuiles obtenue de la classeTiles
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
. - transformer la version textuelle de la graine, passée en argument au programme, en une valeur de type
- 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éthodescreate
é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éthodeinitial
deGameState
. Une fois le graphe de scène construit, son contenu est modifié pour contenir le résultat de l'application de la méthodewithStartingTilePlaced
à 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
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.