TP de synthèse de CVDA - P4 Magic

Le projet que vous allez découvrir a été entièrement réalisé par un de vos prédécesseurs, Antoine Georges, de la promo 2013. Qu'il soit vivement remercié pour tout le travail qu'il a accompli.

  • Pratique de Java
  • Prise en main d'un code existant
  • Maîtrise des tests unitaires
  • Maîtrise des outils d'un environnement de développement
  • Utilisation de Git

À la fin du TP, vous devez rendre un rapport au format PDF et faire un push sur Github (voir phase 6).

<note warning>Attention ! Si vous ne faites pas le push sur Github correctement, vous ne pourrez pas avoir plus de 4/20 au TP. En conséquence, il vous est vivement recommandé de vous assurer auprès de l'enseignant, avant de quitter la salle, que vous avez correctement rendu votre TP ! </note>

Si vous avez un doute, n'hésitez pas à poser des questions.

Barème :

  • Respect de consignes : 1 point
  • Qualité du code : 1 point
  • Présentation, orthographe : 1 point
  • Q1 : 1 point
  • Q2 : 1 point
  • Q3 : 2 points
  • Q4 : 1 point
  • Q5 : 4 points
  • Q6 : 1 point
  • Q7 : 2 points ou plus
  • Q8 : 2 points
  • Q9 : 1 point
  • Q10 : 1 point
  • Q11 : 1 point

Dans ce TP, vous allez contribuer à l'amélioration du code d'un jeu de Puissance 4 déjà fonctionnel, nommé P4Magic.

P4Magic est un puissance 4 un peu spécial : les règles traditionnelles s'appliquent, mais en plus, certaines cases sont porteuses de “pouvoirs magiques”, appelés “effets” par la suite, qui s'activent quand une pièce est jouée dans cette case et qui changent l'état du jeu.

Par exemple, si une case est porteuse de l'effet ChangeColor, le pion qui sera joué dans cette case changera immédiatement de couleur (et donc, deviendra un pion de l'adversaire). Certains effets ont un impact uniquement sur la case sur laquelle ils se trouvent, d'autres ont un impact sur tout le plateau.

Bien sûr, du point de vue des joueurs, on ne sait pas quelles cases sont porteuses de quels effets tant qu'on ne les a pas essayées.

Votre rôle dans ce TP va être de développer et tester de nouveaux effets pour augmenter la complexité du jeu.

Phase 1. Prise en main du projet

1. Avec votre navigateur préféré, connectez-vous à Github puis faites un fork du projet P4Magic qui se trouve à l'adresse suivante : https://github.com/ameliecordier/P4Magic-G4.git. Si vous n'avez pas de compte GitHub, vous pouvez cloner directement le projet, mais vous ne pourrez pas faire la dernière question du TP.

2. Depuis votre IDE préféré, clonez le projet que vous venez de forker.

3. Au besoin, fixez les imports. Vous pourriez être amenés à ajouter JUnit 4.12 et Harmcrest 1.3 dans Test Libraries. Si vous rencontrez des difficultés dans cette phase, n'hésitez pas à demander.

4. Exécutez le programme pour le tester en mode graphique. La première fenêtre vous permet de choisir le pourcentage de chances qu'une case soit porteuse d'un effet. Ensuite, vous lancez la partie. Vous observerez que vous êtes en mode “debug” : les cases aux bordures vertes sont les cases sur lesquelles un effet (on ne sait pas lequel) s'applique. Les autres cases sont normales. Si vous arrivez à une configuration gagnante, le jeu vous le dira. Vous pouvez jouer un peu avec l'interface, mais ne perdez pas trop de temps !

Phase 2. Découverte du code et prise en main des tests

À partir de maintenant, vous n'aurez plus besoin d'utiliser l'interface graphique. Vous pouvez le faire si vous le souhaitez, mais le reste du TP de requiert pas son utilisation.

Avant d'aller plus loin, vous devez comprendre comment fonctionne le code. Rassurez-vous, vous n'avez pas besoin de maîtriser tous les détails pour réaliser le TP.

Pour mieux comprendre les explications qui suivent, n'hésitez pas à consulter le diagramme de classes disponible ici : diagramme UML.

Le projet est implémenté en respectant l'architecture MVC. Il contient 4 packages : model, view, controller (pour MVC) et un package contenant juste la classe principale qui permet de lancer le jeu. L'interface est réalisée en Swing. Le projet est accompagné de tests unitaires, stockés dans un répertorie séparé.

Dans le package controller, le GameController assure la communication entre le modèle (qui contient la logique applicative du jeu, et la vue (qui contient la partie interface).

Dans le package view, le GameView se charge de la gestion de l'interface graphique.

Dans le package puissance4, le seul rôle du main est de lancer le jeu, ce qui se fait de la façon suivante :

  • création d'un objet Game
  • création d'une vue pour cet objet
  • gestion des contrôleurs

Enfin, tous les objets du jeu se trouvent dans le package model. C'est sur ce package de doit porter votre attention.

  • Board est le plateau de jeu. Il propose quelques méthodes intéressantes permettant de l'afficher en mode texte (utile pour les tests), de compter le nombre de tuiles différentes sur le plateau, etc. Un plateau de jeu appartient à un objet Game qui fait le lien entre le plateau et les joueurs.
  • Player représente le joueur. Un joueur a une couleur (la couleur de ses pions) et un id. HumanPlayer étend Player.
  • Tile représente une case du puissance 4. Une case a deux propriétés principales : effect et status. effect indique quel effet s'applique actuellement sur la case (une case peut comporter un et un seul effet), status indique à qui appartient la case. status a pour valeur '-1' si la case est libre, '1' ou '2' si elle appartient respectivement au joueur 1 ou au joueur 2.
  • Game est l'objet principal qui permet de gérer une partie. Il dispose d'un board et propose les méthodes qui permettent de jouer un coup sur le board, de vérifier qu'il y a un gagnant, etc.
  • Enfin, le package model prend en charge la gestion des effets, de la façon suivante :
    • EffectFactory implémente le design pattern factory. La factory permet de produire des effets. Lorsque vous allez créer vos nouveaux effets, vous devrez les ajouter à la liste des effets qui peuvent être produits par la factory si vous voulez qu'ils soient utilisés dans le jeu. Le détail du fonctionnement de la factory est expliqué dans les commentaires du code.
    • Effect est une classe abstraite que tous les effets du jeu se devront d'implémenter.
    • Par convention, tous les noms des classes implémentant un effet devront se terminer par le mot Effect.

Phase 3. Implémentation de votre premier effet

Dans la version initiale du projet cloné, un seul effet est implémenté et testé. Il s'agit de l'effet ChangeColorEffect. Dans cette partie, vous allez implémenter un second effet.

1. Commencez par exécuter les tests contenus dans la classe ChangeColorEffectTest de sorte à vous assurer que la mécanique de vos tests fonctionne bien.

Q1 : dans votre compte-rendu, écrivez Q1 puis faites une capture d'écran (lisible) de la sortie de JUnit qui s'affiche dans l'onglet “Test Results”. (1 point)

2. Récupérez le code de test pour la classe DisappearEffect ci-dessous.

DisappearEffectTest.java
package model;
/**
 * MagicP4
 * IUT Lyon 1 - 2016
 */
 
 
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
 
/**
 * Classe de tests de l'effet Disappear 
 * Principe de l'effet : un pion joué sur
 * une case portant l'effet Disappear disparaît immédiatement. Conséquences :
 * l'état du jeu n'est pas modifié, le pion joué n'apparaît pas sur la grille,
 * et le tour de jeu change
 *
 * @author acordier
 */
public class DisappearEffectTest {
 
    static Game aGame;
 
    public DisappearEffectTest() {
    }
 
    @BeforeClass
    public static void setUpClass() {
 
    }
 
    @AfterClass
    public static void tearDownClass() {
    }
 
    @Before
    public void setUp() {
 
        // Création d'un jeu vide
        aGame = new Game();
        Board b = new Board(10, 10);
        aGame.setBoard(b);
 
    }
 
    @After
    public void tearDown() {
    }
 
    /**
     * Test du bon fonctionnement du jeu, en dehors de l'effet Résultats
     * attendus après le coup : - un pion de plus sur le plateau - le tour de
     * jeu est passé - l'effet a bien été appliqué
     */
    @Test
    public void testDisappearEffectNormalGame() {
 
        // On pré-remplit le plateau pour les besoins de la simulation 
        Utils.simulateAGame(aGame);
 
        // Effet fixé sur une case (qui n'est pas encore remplie)
        int height = aGame.getBoard().getHeight();
        // height-3 correspond à la première case vide dans la colonne O, vu que l'on a déjà joué deux coups dans cette colonne
        aGame.getBoard().getTileIJ(height - 3, 0).setEffect(new DisappearEffect());
 
        // Récupération de l'ID du joueur avant que le coup soit joué 
        int id_player = aGame.getCurrentPlayer().getId();
 
        // Récupération du nombre de pions présents 
        int nb_tokens_before = aGame.getBoard().getTotalTilesCount();
 
        // Coup joué sur une case ne contenant pas l'effet 
        aGame.playMove(1);
 
        // Récupération du nombre de pions après le coup 
        int nb_tokens_after = aGame.getBoard().getTotalTilesCount();
 
        // Vérifications :
        // - l'effet est bien appliqué sur la case 
        // - le tour de jeu a bien changé
        // - il y a bien un pion de plus sur le plateau
        assertTrue("Doit être d'effet disappear", aGame.getBoard().getTileIJ(height - 3, 0).getEffect() instanceof DisappearEffect);
        assertTrue(aGame.getCurrentPlayer().getId() != id_player);
        assertEquals(nb_tokens_before + 1, nb_tokens_after);
    }
 
    /**
     * Test de DisappearEffect sur grille vide 
     * Vérification de l'état de la
     * tuile après application de l'effet 
     * Résultats attendus : la case doit être
     * vide, le tour de jeu doit être passé
     */
    @Test
    public void testDisappearEffectEmptyGame() {
 
        // Effet fixé sur une case 
        int height = aGame.getBoard().getHeight();
        aGame.getBoard().getTileIJ(height - 1, 0).setEffect(new DisappearEffect());
 
        // Récupération de l'ID du joueur courant 
        int id_player = aGame.getCurrentPlayer().getId();
 
        // Coup joué sur la case de l'effet 
        aGame.playMove(0);
 
        // Vérifications :
        // - la case est bien vide après
        // - l'effet est bien appliqué sur la case 
        // - le tour de jeu a bien changé
        assertEquals(-1, aGame.getBoard().getTileIJ(height - 1, 0).getStatus());
        assertTrue("Doit être d'effet disappear", aGame.getBoard().getTileIJ(height - 1, 0).getEffect() instanceof DisappearEffect);
        assertTrue(aGame.getCurrentPlayer().getId() != id_player);
 
    }
 
    /**
     * Test de DisappearEffect sur grille vide 
     * Vérification du nombre de jetons
     * après jeu 
     * Résultat attendu : le nombre doit être égal à 0
     */
    @Test
    public void testDisappearEffectEmptyGameWithTilesNumber() {
 
        // Effet fixé sur une case 
        int height = aGame.getBoard().getHeight();
        aGame.getBoard().getTileIJ(height - 1, 0).setEffect(new DisappearEffect());
 
        // Coup joué sur cette case 
        aGame.playMove(0);
 
        // Vérification que le nombre de jetons au total est égal à 0  
        assertEquals(0, aGame.getBoard().getTotalTilesCount());
 
    }
 
    /**
     * Test de DisappearEffect sur grille pré-remplie 
     * Vérification de l'état de
     * la tuile après application de l'effet 
     * Résultat attendu : la case doit
     * être vide, l'effet doit être sur la case 
     * et le tour doit être passé
     */
    @Test
    public void testDisappearEffectFilledGame() {
 
        // On pré-remplit le plateau pour les besoins de la simulation 
        Utils.simulateAGame(aGame);
 
        // Effet fixé sur une case (qui n'est pas encore remplie)
        int height = aGame.getBoard().getHeight();
        // height-3 correspond à la première case vide dans la colonne O, vu que l'on a déjà joué deux coups dans cette colonne
        aGame.getBoard().getTileIJ(height - 3, 0).setEffect(new DisappearEffect());
 
        // Récupération de l'ID du joueur avant que le coup soit joué 
        int id_player = aGame.getCurrentPlayer().getId();
 
        // Coup joué sur cette case 
        aGame.playMove(0);
 
        // Vérifications :
        // - la case est bien vide après
        // - l'effet est bien appliqué sur la case 
        // - le tour de jeu a bien changé
        assertEquals(-1, aGame.getBoard().getTileIJ(height - 3, 0).getStatus());
        assertTrue("Doit être d'effet disappear", aGame.getBoard().getTileIJ(height - 3, 0).getEffect() instanceof DisappearEffect);
        assertTrue(aGame.getCurrentPlayer().getId() != id_player);
 
    }
 
}

3. Exécutez ce code de test.

Q2 : dans votre compte-rendu, écrivez Q2 puis faites une capture d'écran lisible de la sortie de JUnit qui s'affiche dans l'onglet “Test Results”. (1 point)

4. Implémentez le code nécessaire dans la classe DisappearEffect de sorte à ce que tous les tests passent.

Q3 : dans votre compte-rendu, écrivez Q3 puis faites une capture d'écran lisible de la sortie de JUnit qui s'affiche dans l'onglet “Test Results”. (1 point pour le rapport, un point pour le code)

Q4 : faites un commit de votre projet après avoir bien veillé à sauvegarder tous vos fichiers. (1 point)

<note important>Attention : si vous voulez que vos effets soient actifs dans le jeu en mode graphique, il ne faut pas oublier de mettre à jour la classe EffectFactory à chaque création d'un nouvel effet ! </note>

Phase 4. Implémentation de votre second effet

Vous trouverez ci-dessous une liste d'effets possibles. Dans cette partie, vous devez en choisir un, implémenter la classe de test, implémenter le code correspondant, et vérifier que tout fonctionne.

Liste des effets :

  • Rajouter un pion de la couleur courante dans chaque colonne
  • Fait disparaître un certain nombre de pions de façon aléatoire (effet à paramétrer par nombre de pions, nombre de couleurs)
  • Changer la couleur de tout le voisinage (pion joué + les 7 pions autour, s'il y en a)
  • Fait disparaître la colonne dans laquelle le pion est joué
  • Fait disparaître la ligne dans laquelle le pion est joué
  • Fait exploser le voisinage du pion joué (et gérer les effets en cascade)
  • Fait apparaître de manière aléatoire un autre pion sur le jeu

Q5 : effectuez un commit de votre code de test et de votre code d'effet (1 point pour le commit + 1 point pour le rapport + 2 points pour le code)

Q6 : exécutez en une seule fois l'ensemble des tests présents dans le projet. Dans le rapport, indiquez Q6 et faites une capture d'écran lisible de la sortie de JUnit. (1 point)

Phase 5. À votre tour d'améliorer le puissance 4

Il reste beaucoup de choses à faire pour améliorer 'Magic P4'. Dans cette partie, vous pouvez choisir de traiter les questions libres (QL) que vous voulez. Plus vous traitez de questions, plus vous aurez de points.

Par ailleurs, si votre code est suffisamment bien fait, il sera intégré au projet principal et sera donc disponible pour les futurs étudiants qui feront ce TP. Vous deviendrez donc contributeurs officiels de P4Magic.

Q7 : Dans votre rapport, écrivez Q7 et inscrivez la liste des questions que vous avez traité

QL1 : implémenter un autre effet, et écrire la classe de tests correspondante. Vous pouvez choisir d'implémenter n'importe quel autre effet de la liste précédente, ou bien créer votre propre effet. Dans le second cas, veillez à bien expliquer le comportement attendu de votre effet. (1 point)

QL2 : écrire les tests de la méthode win. La classe Game contient une méthode win dont le rôle est de vérifier sur le plateau s'il existe une suite gagnante. Dans cette question, il s'agit d'écrire les tests qui permettent de s'assurer que cette méthode est correcte. (3 points)

QL3 : couverture de code, couverture des tests. Ici, il s'agit de faire une analyse du code existant et de déterminer d'une part s'il y a du code mort, et d'autre part s'il y a du code non testé. Ensuite, il faut faire des recommandations pour améliorer la qualité du code, c'est-à-dire préparer un plan de test dans lequel les priorités seront définies. Vous répondrez à cette question dans votre rapport. (2 points)

QL4 : réfléchir à une ré-organisation du code qui permettrait de faire un P4Magic à plus de deux joueurs. Ici, il s'agit de réfléchir à tout ce qu'il faudrait modifier pour que l'on puisse jouer au P4Magic à plus de deux joueurs. Vous rapporterez le résultat de vos réflexions dans votre rapport, à moins que vous ne préfériez implémenter directement votre solution ? (2 points)

QL5 : améliorer le paramétrage du jeu. Dans cette question, il s'agit d'implémenter toutes les méthodes pour pouvoir paramétrer le jeu au lancement. Les paramètres attendus sont :

  • Taille de la grille
  • Couleurs des joueurs
  • Pourcentage d'effets sur les cases
  • Liste des effets qui s'appliquent
  • Activation / désactivation du mode debug.

Vous pouvez ajouter d'autres paramètres à votre convenance. (3 points)

QL6 : réaliser la partie IHM pour la QL5.

QL7 : durée de vie des effets. Dans la version actuelle, les effets sont actifs en permanence, ce qui peut poser problème dans certaines configuration. Votre mission : modifier l'implémentation de sorte à ce que l'on puisse choisir, pour chaque effet, s'il est actif en permanence, ou bien s'il disparaît de la case après avoir été activé un certain nombre de fois. (3 points)

QL8 : tester les méthodes de comptage de tuiles. La classe Board contient trois méthodes de comptage de tuiles (par couleur, et total). Il s'agit d'écrire les tests de ces méthodes puis d'imaginer une façon de les refactorer plus astucieusement. (2 points)

QL9 : gérer les cases mortes. Imaginez une façon de gérer des cases mortes, c'est-à-dire des cases dans lesquelles il ne peut pas y avoir de jeton. (1 point)

Phase 6. Rendu du TP

Q8 : vérifiez que votre code est bien commenté, bien documenté et bien formaté, puis générez la javadoc totale. Ajoutez à votre rapport un screenshot qui prouve que la javadoc a bien été générée. (2 points)

Q9 : générez le diagramme UML du projet global, mettez-le en forme, et intégrez-le à votre rapport. (1 point)

Q10 : Effectuez un dernier commit de votre projet. Ensuite, n'oubliez pas de pousser votre travail sur Github pour qu'il puisse être corrigé. Copiez l'URL de votre projet Github dans votre rapport. Déposez votre rapport sur Tomuss. (1 point)

Q11 : Depuis Github, effectuez un pull request sur le projet original. (1 point)