TP 3
Implémentation du contrôleur Halma.
Métadonnées
- Date limite : Lundi 7 avril, 23h59
- Soumission : GitLab
- Équipes : Triplets d'étudiants, comme annoncé en classe
- Soumission tardive : Non possible. Vous serez noté selon le contenu présent dans votre branche principale à la date limite
Il est de votre responsabilité de soumettre le code dans les délais. N'attendez pas la dernière minute, GitLab peut échouer.
Objectifs
-
Pour le troisième TP, vous compléterez votre implémentation du
Controller
Halma et intégrerez des concepts avancés pour garantir la qualité du code dans le processus de compilation :- Vous devez implémenter vos propres tests et assurer une couverture de test avancée avec une configuration de build.
- Votre code doit respecter les règles de formatage de code de niveau industriel.
- Votre code doit être entièrement documenté avec des commentaires JavaDoc pour toutes les méthodes et classes publiques.
- Votre code ne doit pas être trop complexe.
-
Vous commencerez avec une configuration fournie de maven qui impose certaines normes. Vous étendrez ensuite cette configuration pour imposer des normes de qualité de code supplémentaires.
- Votre configuration de build produira un livrable autonome, qui est une première version jouable de votre jeu.
Contexte
Pour les étapes TP-1 et TP-2, vous avez déjà implémenté le modèle Halma et une partie du contrôleur Halma. Votre soumission a été notée selon un ensemble de tests d'interface prédéfinis, ainsi que des normes de code.
Lisez attentivement le rapport de test
Les tests de soumission utilisés pour la notation contenaient des scénarios supplémentaires de tests unitaires. Lisez attentivement le rapport de test et assurez-vous de corriger tout problème potentiel dans votre modèle. En fonction de la qualité de votre implémentation du modèle, vous devrez peut-être investir un peu plus de temps pour l'étape 3.
Instructions
Plusieurs changements sont nécessaires pour réussir cette troisième étape. Voici une répartition détaillée de ce qui est attendu.
Fichiers de configuration du projet
Mettez à jour votre lanceur et vos fichiers xml
Mettez à jour votre configuration, sinon votre jeu ne sera PAS jouable. Vous perdrez ÉNORMÉMENT de points si vous ignorez cela.
- Allez dans la branche
tp3-files
de votre dépôt et fusionnez les deuxgoogle_checks.xml
etpom.xml
dans votre code. - Par la suite, il vous sera également demandé d'ajouter trois plugins supplémentaires aux fichiers de
configuration
pom.xml
. Notez que ce sont les seules modifications autorisées, et toute autre modification dupom.xml
entraînera un rejet.
Il est particulièrement interdit de :- Intégrer d'autres versions de JUnit.
- Intégrer des bibliothèques supplémentaires.
Arguments d'exécution
Au moment de la soumission, votre implémentation du jeu en forme d'étoile doit être jouable en utilisant le lanceur fourni et l'interface utilisateur textuelle / client en ligne de commande. Il en va de même pour le fichier JAR produit par votre configuration de build.
Au démarrage, votre jeu doit être capable de traiter les arguments d'exécution suivants :
- Taille de base du plateau, par exemple
1
, pour produire une étoile avec une taille de base de1
:
y\x | 00 01 02 03 04
----+--------------------
00 | [ ] [ ]
01 | ( )
02 | ( ) ( )
03 | [ ] ( ) [ ]
04 | ( ) ( )
05 | ( )
06 | [ ] [ ]
-
Noms des joueurs impliqués. Ceux-ci doivent être soit 3, soit 6. Les autres entrées doivent être rejetées.
-
Exemple : Votre programme doit être lançable avec les commandes suivantes :
mvn exec:java "-Dexec.args=1 Max Quentin Hafedh"
-> Crée une grille de cellules en forme d'étoile 5*7 avec 3 joueurs.java -jar target/halma.jar 1 Max Quentin Hafedh
-> Même chose, mais à partir du JARmvn exec:java "-Dexec.args=2 Max Quentin Ryan Hafedh Maram Roman"
-> Crée une grille de cellules en forme d'étoile 9*13 avec 6 joueurs.java -jar target/halma.jar 2 Max Quentin Ryan Hafedh Maram Roman
-> Même chose, mais à partir du JAR
Lanceur exemple
Votre jeu sera contrôlé par une boucle principale, qui (jusqu'à la fin du jeu) effectue les actions suivantes :
- Déterminer les coups valides pour le joueur actuel. (Modèle Blackboard, voir explications en classe.)
- Présenter les coups valides sur la console et demander au joueur de faire un choix.
- Retourner le choix au contrôleur et avancer l'état du jeu, basé sur la sélection du joueur.
Vous n'êtes pas obligé d'implémenter la fonctionnalité UI / de sollicitation, vous pouvez donc vous concentrer sur les composants de logique du jeu. Les classes pour l'impression et la sollicitation d'options sont fournies, ci-dessous un exemple de code pour illustrer comment utiliser la fonctionnalité fournie.
Modèle Blackboard pour la boucle de contrôle
Voici une implémentation exemple pour faire avancer le jeu jusqu'à la fin. N'hésitez pas à utiliser les classes UI fournies pour imprimer l'état du jeu et récupérer les choix de l'utilisateur via la ligne de commande.
private static void runTp03(String[] args) {
// Parse runtime parameters
int baseSize = Integer.parseInt(args[0]);
String[] playerNames = // ... trouvez une façon d'interpréter les arguments.
// Appellez votre implementation "StarModelFactory" ici.
ModelFactory modelFactory = new StarModelFactory();
// Set move selectors // ... plus tard on va utiliser des IA pour choisier des actions.
// Pour l'instant le choix des options est interactif (et l'implémentation est fournie)
MoveSelector[] moveSelectors = playerNamesToMoveSelectors(playerNames);
// Initialize controller
Controller controller = new ControllerImpl(modelFactory, baseSize, playerNames);
// Initialize visualizer
boolean useColours = true; // Set to false if you're on windows and textual output looks weird.
TextualVisualizer visualizer = new TextualVisualizer(useColours);
// Proceed until game end
while (!controller.isGameOver()) { // Voila la boucle prinicipale... continuer le jeu jusqu'à une personne a gagné
printAndRequestAndPerformAction(controller, visualizer, moveSelectors);
}
System.out.println(visualizer.stringifyModel(controller.getModel()));
System.out.println("GAME OVER!");
}
// Plus tard (TP4), quand on utilise des IAs, il y aura des autres "MoveSelectors" qui ne sont pas interactifs mais font leur propre choix.
private static MoveSelector[] playerNamesToMoveSelectors(String[] playerNames) {
MoveSelector[] moveSelectors = new MoveSelector[playerNames.length];
for (int i = 0; i < moveSelectors.length; i++) {
moveSelectors[i] = new InteractiveMoveSelector();
}
return moveSelectors;
}
/**
* Prints mode, possible moves and requests player interaction.
*/
private static void printAndRequestAndPerformAction(Controller controller, TextualVisualizer visualizer,
MoveSelector[] moveSelectors) {
// Clear the screen (Works only on native ANSI terminals, not in IDE / windows)
visualizer.clearScreen(); // Comment out this line if you're on windows.
// Retrieve and visualize model
System.out.println(visualizer.stringifyModel(controller.getModel()));
// Print all possible actions:
System.out.println(visualizer.getCurrentPlayerAnnouncement(controller.getModel()));
System.out.println(visualizer.announcePossibleMoves(controller.getPlayerMoves()));
// Request action and visualize choice (for current player)
int currentPlayer = controller.getModel().getCurrentPlayer();
List<Move> availableMoves = controller.getPlayerMoves();
// if more than one move, ask selector
Move selectedMove = null;
if (availableMoves.size() > 1) {
selectedMove = moveSelectors[currentPlayer].selectMove(availableMoves);
} else {
// If only one move available, directly apply it.
selectedMove = availableMoves.getFirst();
}
System.out.println(visualizer.getChoseMoveAnnouncement(selectedMove, currentPlayer));
// Perform selected action:
controller.performMove(selectedMove);
System.out.println("\n\n");
}
Code
Votre soumission doit fournir des implémentations pour toutes les méthodes d'interface non implémentées du paquet modèle et contrôleur.
getPlayerMoves
: Détermine où un joueur peut déplacer ses figurines. (Voir illustrations à la fin du document.)isGameOver
: Indique si un joueur a toutes ses figurines dans sa zone cible.performMove
: Demande au contrôleur de modifier le modèle, en exécutant un mouvement fourni.
Ne réinventez pas la roue
Il n'est pas nécessaire d'implémenter la classe Move
ou la classe Field
. Celles-ci sont fournies, dans le cadre des dépendances pré-configurées.
Tests
Votre code de test
Il n'est pas nécessaire d'=> Vous devez ajouter une nouvelle classe, quiextends
plus de classes abstraites qu'avant.extends
la classe abstraite existanteAbstractStarControllerTest
afin d'accéder aux tests fournis et de permettre à votre soumission d'être testable.- Pour ce faire, créez une nouvelle classe Java dans
src/test/java/ca/uqam/info/solanum/students/halma/controller/StarControllerTest.java
(Vérifiez bien qu'elle se trouve dans votre dossier de tests) - Votre classe doit
extends
la classe abstraite existante et fournir une implémentation pour la méthode abstraitegetController
:
- Pour ce faire, créez une nouvelle classe Java dans
- Cependant, vous devrez écrire plus de tests unitaires pour votre nouveau code de contrôleur, afin d'atteindre le ratio de couverture de tests demandé.
Tests
Comme pour le TP précédent, tous les tests fournis doivent réussir, mais je testerai votre soumission au-delà des tests fournis.
Couverture
- Votre code de contrôleur et de modèle doit atteindre une couverture d'au moins
90%
des lignes de code. - Pour le TP3, vous êtes censé intégrer le plugin
jacoco
dans la configuration de votrepom.xml
afin d'atteindre la couverture demandée. Votre build doit être configuré pour échouer si la couverture n'est pas atteinte. - Si vous n'êtes pas familier avec
jacoco
, révisez la session du laboratoire CI. Une configuration d'exemple est fournie.
Artéfacts de build
- Votre configuration de build doit être configurée pour fournir un JAR autonome, c'est-à-dire un fichier JAR qui peut être utilisé tel quel, sans dépendances supplémentaires.
- Ce jar doit s'appeler
halma.jar
ouHalma.jar
. - Vous pouvez tester le fichier JAR produit par la phase
package
, en utilisant les instructions de lancement ci-dessus. - Pour produire un JAR autonome, vous devez modifier le
pom.xml
pour utiliser deux plugins maven : (des exemples ont été démontrés en classe et sont disponibles sur GitLab)- Le plugin
maven-assembly
- Le plugin
copy-rename
- Le plugin
Documentation existante
- Pour toute question concernant la fonctionnalité demandée par votre implémentation, consultez
la
JavaDoc
des interfaces, des records et exceptions personnalisées existants
Votre documentation JavaDoc
Votre code doit être entièrement documenté à 100 % :
- Toutes les classes non privées doivent avoir une description JavaDoc.
- Toutes les méthodes non privées doivent avoir une description JavaDoc.
- Si une méthode implémente une interface existante, aucune documentation supplémentaire ne doit être fournie, car
l'interface est déjà documentée. Il suffit de placer une annotation
@Override
avant la signature de la méthode.
Utilisez maven
Utilisez mvn clean package
pour vérifier l'état de vos commentaires JavaDoc. Il vous indiquera si des documentations sont manquantes ou incorrectes.
Encombrement
- Gardez votre dépôt sans encombrement. (Voir TP0 + TP1 + TP2)
- À moins que ce ne soit déjà fait, vous pouvez utiliser un
fichier
.gitignore
qui exclut les fichiers.class
, les fichiers cachés du système d'exploitation et le dossiertarget
de maven. - Ne commitez pas de fichiers binaires ou générés (
.class
, dossiertarget
, oujavadoc
généré).
- À moins que ce ne soit déjà fait, vous pouvez utiliser un
fichier
- Ne rajoutez pas de déclarations
System.out.println
dans votre code à vous, cela perturbera la fonctionnalité lors des étapes suivantes.
Plagiat et ChatGPT
- À moins que cela ne soit explicitement autorisé, n'utilisez pas ChatGPT ou d'autres outils génératifs pour générer du code ou de la documentation.
- Vous pouvez consulter internet pour trouver de l'inspiration ou aider votre compréhension, mais ne copiez jamais
de code sans citer la source. Chaque fois que vous copiez-collez, vous DEVEZ fournir une référence / un
commentaire. Sinon, cela est légalement considéré comme du plagiat.
- Utilisez un commentaire Java
// ...
pour fournir la source, par exemple, le post exact de stackoverflow.
- Utilisez un commentaire Java
- Ne partagez pas de code avec d'autres équipes
- Vous pouvez discuter, dessiner des concepts sur un morceau de papier, mais ne transmettez jamais de code.
- Je ferai des vérifications automatisées de plagiat sur toutes les soumissions.
- Je suis contractuellement obligé de signaler les équipes qui ont copié à l'administration universitaire. Cela peut avoir de graves conséquences pour vos études, y compris l'expulsion à vie des études au Canada.
Plagiat
Le plagiat, c'est-à-dire soumettre un travail qui n'est pas le vôtre (c'est-à-dire de votre équipe), est une infraction académique grave qui peut entraîner une expulsion des études. Ne prenez aucun risque.
Déclaration de contribution
Vous devez inclure une déclaration de contribution, que nous pourrons prendre en compte en cas de déséquilibre important entre les contributions des membres de l'équipe. Utilisez le modèle ci-dessous pour inclure un fichier " contributions.md" à la racine de votre dépôt.
Les autres noms ou formats de fichiers ne sont pas acceptés (PDF, pages, html, ...). Assurez-vous de nommer votre
fichier contributions.md
Pour le contenu de votre fichier contributions.md
, utilisez exactement le modèle ci-dessous. Ne recréez pas la
grille dans un autre
logiciel (excel, numbers, paint, ...). Utilisez votre code-permanent comme ID
.
| ID | First | Last | Contribution (%) |
|-----|-------|------|--------------|
| ... | ... | ... | ... |
| ... | ... | ... | ... |
| ... | ... | ... | ... |
Les contributions additives de tous les membres de l'équipe doivent se rendre à 100 %.
Schéma de notation
Ce TP vaut 15 % de votre note totale.
Critère | Pourcentage maximal |
---|---|
Limite de complexité cyclomatique respectée, pas d'avertissements checkstyle | 25 % |
Tests supplémentaires non publiés réussis | 20 % |
JAR fonctionnel produit par la phase package |
15 % |
Couverture de tests > 90 %, testé avec jacoco | 15 % |
Les tests fournis réussissent | 10 % |
Javadoc appliquée dans le pom, sans avertissements | 5 % |
Dépôt sans encombrement | 5 % |
Déclaration de contribution | 5 % |
Recommandation
Simulez le processus de notation avant la soumission : Re-clonez votre propre projet et exécutez mvn clean package
et assurez-vous que votre projet peut être construit sans avertissements ni erreurs, en utilisant le pom.xml
fourni pour ce TP3
.
Rejet administratif
Voici une liste des éléments à éviter à tout prix. Si au moins un des éléments de cette liste s'applique, votre soumission perdra tous les points relatifs au code. D'autres critères peuvent également conduire à un rejet administratif.
- Solution non fournie dans le répertoire GitLab dédié, ou non sur la branche
main
. - Solution répartie sur plusieurs branches non fusionnées.
- Le dépôt contient un fichier zip.
- Le code ne compile pas avec la commande maven fournie.
- Le
pom.xml
a été modifié de manière sémantique au-delà de l'intégration des pluginsjacoco
/assembly
/copy-rename
. (Par rapport aupom.xml
spécifique et fourni pour le TP3) - Les classes fournies n'implémentent pas les interfaces fournies.
- L'implémentation des classes de test abstraites n'est pas fournie dans le package de tests.
- La structure du projet n'est pas respectée.
- Le programme tente une communication réseau à l'exécution.
- Le programme se bloque à l'exécution.
- Code copié depuis ChatGPT / utilisation de Cursor / copié d'autres IA génératives.
- Copié d'autres équipes.
- Code copié depuis des forums / bases de connaissances en ligne sans citer la source (citation).
Illustrations supplémentaires
Cette section récapitule les règles du jeu et illustre la représentation UI par défaut des mouvements.
Illustrations des règles du jeu
Lors de son tour, un joueur ne peut déplacer qu'une seule figurine. Cependant, si la figurine a été déplacée en utilisant un "saut", des sauts répétés avec la même figurine sont autorisés.
Certaines règles doivent être respectées lors du déplacement d'une figurine :
- Le champ cible doit être vacant.
- Le champ cible doit être sur le plateau en forme d'étoile (c'est-à-dire qu'il n'est pas permis de déplacer les figurines hors du terrain).
- Les sauts ne sont autorisés que vers des voisins étendus (le champ derrière un champ, c'est-à-dire une distance de 2). De plus, les sauts ne sont autorisés que si le champ intermédiaire n'est pas vacant. Cependant, si ce champ n'est pas vacant, peu importe à quel joueur appartient la figurine intermédiaire.
Mouvements du contrôleur
Cette section fournit des illustrations supplémentaires sur le fonctionnement attendu de la méthode getMoves
.
Si un joueur a effectué un saut, c'est-à-dire a déplacé une figurine en sautant par-dessus une autre figurine, le jeu ne passe pas au joueur suivant. Cependant...
- Pour le reste du tour du joueur, tous les mouvements suivants (tels que proposés par la méthode
getMoves
) doivent uniquement lister la figurine précédemment déplacée. - Le joueur n'est pas autorisé à effectuer des contre-sauts, c'est-à-dire que le résultat de
getMoves
ne doit pas contenir un mouvement ramenant une figurine au champ d'où elle venait. Cependant, des cercles plus larges qui ramènent à la case d'origine sont autorisés. - Le joueur n'est pas obligé de continuer à sauter sa figurine, même si cela est possible. Pour couvrir cette
option,
getMoves
doit inclure un "mouvement nul" spécial, qui a un champ source et un champ cible identiques (la position actuelle de la figurine). - Pour terminer leur tour après avoir effectué au moins un saut, le joueur doit utiliser impérativement un "mouvement nul" final.
Ne pas trop optimiser
Il est possible qu'un seul mouvement soit possible (par exemple, terminer une série de sauts avec un "mouvement nul"). Le contrôleur ne doit pas appliquer automatiquement les options sans alternatives, mais doit tout de même retourner les options possibles. Le code d'exemple de tableau noir fourni comprend toutefois un contrôle pour sélectionner automatiquement les mouvements s'ils sont sans alternatives.
Exemple 1
Sélection implicite de la seule option offerte par la méthode getMoves
du contrôleur :
- Après le saut initial, la méthode
getMoves
du contrôleur n'a retourné qu'une seule option : le "mouvement nul". - Cependant, le contrôleur n'a pas appliqué implicitement le seul mouvement disponible.
- C'est la boucle de tableau noir de l'UI qui n'a pas demandé au joueur de confirmer (sa seule) option, car elle est sans alternatives.
$ java -jar halma.jar 2 Max Ryan Roman
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [1]
01 | [ ] [1]
02 | [ ] ( ) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [0] ( ) ( ) [ ]
06 | [0] ( ) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | ( ) ( ) ( )
09 | ( ) ( )
10 | [ ] ( ) [2]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (01,05) -> (02,04) 01: (01,05) -> (02,06) 02: (00,06) => (02,04)
03: (00,06) => (02,08) 04: (01,07) -> (02,06) 05: (01,07) -> (02,08)
Enter move ID:
3
Chosen move: (00,06) => (02,08)
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [1]
01 | [ ] [1]
02 | [ ] ( ) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [0] ( ) ( ) [ ]
06 | [ ] ( ) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | (0) ( ) ( )
09 | ( ) ( )
10 | [ ] ( ) [2]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (02,08) == (02,08)
Chosen move: (02,08) == (02,08)
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [1]
01 | [ ] [1]
02 | [ ] ( ) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [0] ( ) ( ) [ ]
06 | [ ] ( ) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | (0) ( ) ( )
09 | ( ) ( )
10 | [ ] ( ) [2]
11 | [ ] [2]
12 | [ ] [2]
Exemple 2
Omission d'un contre-saut :
- Après un mouvement de saut
(00,06) => (02,08)
, le joueur n'a que deux options :- continuer avec un autre saut (vers le haut) :
(02,08) => (02,04)
- terminer son tour, par un mouvement nul explicite :
(02,08) == (02,08)
- Le contre-saut, c'est-à-dire revenir à
(00,06)
, n'est pas autorisé. - Déplacer une autre figurine que celle qui a précédemment sauté n'est pas autorisé.
- continuer avec un autre saut (vers le haut) :
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [ ]
01 | [ ] [1]
02 | [ ] (1) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [ ] ( ) ( ) [ ]
06 | [0] (0) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | ( ) ( ) (2)
09 | ( ) ( )
10 | [ ] ( ) [ ]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (02,06) -> (02,04) 01: (02,06) -> (03,05) 02: (02,06) -> (01,05)
03: (02,06) -> (03,07) 04: (02,06) -> (02,08) 05: (00,06) -> (01,05)
06: (00,06) => (02,08) 07: (01,07) -> (01,05) 08: (01,07) => (03,05)
09: (01,07) -> (02,08)
Enter move ID:
6
Chosen move: (00,06) => (02,08)
y\x | 00 01 02 03 04 05 06 07 08
----+------------------------------------
00 | [ ] [ ]
01 | [ ] [1]
02 | [ ] (1) [1]
03 | ( ) ( )
04 | ( ) ( ) ( )
05 | [ ] ( ) ( ) [ ]
06 | [ ] (0) ( ) ( ) [ ]
07 | [0] ( ) ( ) [ ]
08 | (0) ( ) (2)
09 | ( ) ( )
10 | [ ] ( ) [ ]
11 | [ ] [2]
12 | [ ] [2]
It's Max's turn. Max, your options are:
Available moves:
00: (02,08) == (02,08) 01: (02,08) => (02,04)
Enter move ID:
Exemples de syntaxe de mouvement
Voici un bref rappel de la manière de lire les objets Move
sérialisés :
(00,00) -> (01,01)
: Mouvement standard, déplaçant une figurine sur un champ vers un voisin adjacent.(00,00) => (02,02)
: Mouvement de saut, déplaçant une figurine sur un champ étendu vers un voisin adjacent. Ne peut être effectué que si le champ intermédiaire n'est pas vacant.(00,00) == (00,00)
: Mouvement nul : Utilisé pour indiquer la fin d'une série (ou d'un seul) saut. La figurine reste à sa position actuelle.
Note : Les contre-sauts ne sont autorisés en aucun cas, c'est-à-dire qu'un mouvement de saut ne doit pas être suivi par un saut retour à la position d'origine. C'est une décision de conception pour limiter les tours de joueur "sans fin" où une figurine saute en avant et en arrière.