Puissance 4: Classes Java qui permettent de réaliser le jeu puissance 4 avec interface graphique.
Avant de commencer à programmer, une bonne pratique consiste à conceptualiser son interface graphique sur une simple feuille de papier. Voici une image qui peut servir de modèle pour le concept de l’interface graphique du jeu puissance 4:
Tout d’abord la classe Main qui présente l’énoncé de l’application puissance4.
/** * Ce mini-projet consiste à réaliser un Puissance 4 (graphique). * * Les règles sont définies ici : * http://fr.wikipedia.org/wiki/Puissance_4 * */ /** * Raisonnement : * * 1) récupérer les règles quelque part (pour définir exactement le problème) * 2) créer (ou récupérer) les pièces graphiques du jeu : le plateau, les pions (rouge/jaune/blanc) * 3) réfléchir sur la structure du jeu (mais ici, c'est direct, tout est déjà défini) * 4) choix : soit faire l'interface graphique puis les fonctions, soit le contraire * le bon choix : commencer par l'architecture de l'application (donc les fonctions) * nous utiliserons le modèle MVC : * http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur * (à partir d'ici, vous êtes supposé savoir ce que signifie MVC durant tout le projet) * 5) créer les objets MVC dans le main() (et les classes qui les représentent) * 6) dessiner sur une feuille de papier l'interface graphique voulue * 7) concevoir le modèle pour représenter les données : * ici, il y a un tableau à deux dimensions [6 lignes, 7 colonnes] qui va contenir des int * 0 = blanc (par défaut), 1 = rouge, 2 = jaune * on peut déjà deviner qu'il faudra une méthode pour vérifier si la grille est valide *le controleur, en général, ne doit pas être conçu spécifiquement : * on le complète au fur et à mesure où on a besoin de relier la Vue et le Modele * 9) la conception de la Vue a déjà été faite (sur la feuille de papier !) * 10) c'est parti ! */ public class Main { public static void main(String[] args) { // on crée les trois objets qui représentent MVC Controleur monControleur = new Controleur(); Vue maVue = new Vue(monControleur); Modele monModele = new Modele(monControleur); // on associé la vue et le modèle dans le controleur monControleur.setVue(maVue); monControleur.setModele(monModele); // on affiche l'interface graphique maVue.pack(); maVue.setVisible(true); // on lance une nouvelle partie monControleur.nouvellePartie(); monControleur.printPlateau(); } }
Ensuite la classe modèle qui represente le modèle du jeu puissance 4
/** * Cette classe représente un modele. * Définition : * http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur#Le_mod.C3.A8le * */ public class Modele { public Controleur monControleur; private int[][] plateau; public final int CONTINUE = 0; public final int GAGNANT_ROUGE = 1; public final int GAGNANT_JAUNE = 2; public final int EGALITE = 3; public Modele(Controleur c) { monControleur = c; } // méthode de debug public void printPlateau() { for(int i = 0; i < plateau.length; i ++) { for(int j = 0; j < plateau[i].length; j ++) { System.out.print(plateau[i][j] + "\t"); } System.out.println(); } } // méthode qui permet de réinitialiser le Modele public void reinitialiser() { plateau = new int[6][7]; // initialisé par défaut à 0 partout } // méthode qui dit si oui ou non une colonne est remplie public boolean colonnePleine(int numeroColonne) { for(int i = 0; i < plateau.length; i ++) { if(plateau[i][numeroColonne] == 0) // si on détecte un 0 dans la colonne numeroColonne, // c'est qu'il y au moins une place disponible dans la colonne return false; } return true; } /** * Méthode qui ajoute un jeton dans une colonne; le but est d'ajouter le jeton le plus * bas possible dans la colonne... Donc nous allons commencer par le bas de la colonne * et remonter jusqu'à trouver un 0 (qui signifie case vide). * * @pre : la colonne n'est pas pleine * @post : le jeton est ajouté */ public void ajouterJeton(int numeroColonne, int joueur) { for(int i = 5; i >= 0; i--) { if(plateau[i][numeroColonne] == 0) { // on a trouvé la première case vide plateau[i][numeroColonne] = joueur; // on place le jeton break; // on sort de la boucle puisqu'on vient d'ajouter notre jeton } } } // fait une copie du tableau du modèle et le renvoie public int[][] getTableauDuModele() { int[][] res = new int[6][7]; for(int i = 0; i < plateau.length; i ++) for(int j = 0; j < plateau[i].length; j ++) res[i][j] = plateau[i][j]; return res; } /** * Méthode qui sert à vérifier la grille. La vérification consiste à voir * si il y a un gagnant ou une égalité (ou si le jeu peut continuer). * * @return : GAGNANT_ROUGE si Rouge gagne, GAGNANT_JAUNE si Jaune gagne, EGALITE si égalité * sinon, CONTINUE si le jeu continue */ public int verifierGrille(int joueur) { int res = CONTINUE; // par défaut, il n'y a rien à faire // ici débute l'algorithme de vérification de la grille ! // ------------------------------------------------------ /* * C'est précisément ici que se situe la partie la plus compliquée de ce mini-projet. * En effet, il faut déterminer un algorithme qui va permettre de trouver qui gagne. * Ce qui est simple, c'est que la vérification est effectuée à chaque coup. Donc, * il suffit de vérifier si il y a 4 fois la même valeur 'joueur' soit sur une ligne, * soit sur une colonne, soit sur une diagonale (dans les deux sens). * * Nous allons donc effectuer les vérifications dans cet ordre. */ // lignes for(int i = 0; i < plateau.length; i++) for(int j = 0; j < plateau[i].length - 3; j++) if(plateau[i][j] == joueur && plateau[i][j+1] == joueur && plateau[i][j+2] == joueur && plateau[i][j+3] == joueur) res = joueur; // colonnes for(int i = 0; i < plateau.length - 3; i++) for(int j = 0; j < plateau[i].length; j++) if(plateau[i][j] == joueur && plateau[i+1][j] == joueur && plateau[i+2][j] == joueur && plateau[i+3][j] == joueur) res = joueur; /* * Jusque là, très facile... on vérifie (sans dépasser les bords du tableau) si il y a * quatre valeurs identiques qui se suivent mais quand on arrive aux diagonales, c'est * tout de suite moins évident. Conseil : prendre une feuille de papier et essayer de * réfléchir et répondre à la question suivante : "comment vais-je pouvoir parcourir * le tableau sur toutes ses diagonales sans en oublier aucune et sans dépasser les * bords du tableau ?". */ // diagonales -45° for(int i = 0; i < 3; i ++) for(int j = 0; j < plateau[i].length - 3; j++) if(plateau[i][j] == joueur && plateau[i+1][j+1] == joueur && plateau[i+2][j+2] == joueur && plateau[i+3][j+3] == joueur) res = joueur; // diagonales 45° for(int i = 0; i < 3; i ++) for(int j = 3; j < plateau[i].length; j++) if(plateau[i][j] == joueur && plateau[i+1][j-1] == joueur && plateau[i+2][j-2] == joueur && plateau[i+3][j-3] == joueur) res = joueur; // égalité ? if(res != joueur) { res = EGALITE; // on suppose que c'est une égalité (même si ce n'est pas forcément le cas) for(int j = 0; j < plateau[0].length; j++) { if(plateau[0][j] == 0) { res = CONTINUE; // on nie l'égalité si il y a un 0 sur la ligne supérieure ! break; } } } // ------------- // fin de l'algo return res; } }
La classe contrôleur représente le contrôleur de l’application jeu puissance 4
import javax.swing.JOptionPane; /** * Cette classe représente un controleur. * Définition : * http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur#Le_contr.C3.B4leur * */ public class Controleur { public Vue maVue; public Modele monModele; public int joueur = 0; // 0 = personne ne joue, 1 = Rouge, 2 = Jaune public void setVue(Vue v) { maVue = v; } public void setModele(Modele m) { monModele = m; } public void setJoueur(int j) { joueur = j; } public int getJoueur() { return joueur; } public void printPlateau() { monModele.printPlateau(); } // lorsque appellée, le Controleur va réinitialiser tout le jeu, ce qui permet // de démarrer une nouvelle partie ! public void nouvellePartie() { monModele.reinitialiser(); maVue.reinitialiser(); setJoueur(1); // joueur Rouge joue ! System.out.println("Nouvelle partie lancée !"); } // méthode appellée par la Vue lorsqu'un joueur essaie de jouer ! public void leJoueurJoue(int numeroColonne) { System.out.println("Joueur:"+joueur+"/Colonne:"+numeroColonne); /* * C'est ici que l'on est supposé effectuer tout le traitement logique du jeu. * Quand un joueur joue, on sait qui joue (grâce à la variable 'joueur') et on * sait aussi quelle colonne a été jouée (grâce au paramètre 'numeroColonne'). * * Cela est donc suffisant pour effectuer les traitements nécessaire. Ensuite, * on repasse la main à l'autre joueur sauf si le joueur actuel a gagné ou que * nous tombons sur une égalité. Nous laisserons le soin de tester ces cas * de figure à une autre fonction. */ monModele.ajouterJeton(numeroColonne, joueur); maVue.repaint(); // on repeint la Vue // on print la grille sur la console pour debug printPlateau(); // on vérifie l'état de la grille (gagnant ? égalité ?) int etatGrille = monModele.verifierGrille(joueur); if(etatGrille == monModele.GAGNANT_ROUGE) { JOptionPane.showMessageDialog(maVue, "Le joueur Rouge a gagné. Bravo !"); } else if(etatGrille == monModele.GAGNANT_JAUNE) { JOptionPane.showMessageDialog(maVue, "Le joueur Jaune a gagné. Bravo !"); } else if(etatGrille == monModele.EGALITE) { JOptionPane.showMessageDialog(maVue, "Egalité !"); } if(etatGrille == monModele.GAGNANT_ROUGE || etatGrille == monModele.GAGNANT_JAUNE || etatGrille == monModele.EGALITE) { nouvellePartie(); return; // on sort de la méthode, sinon la suite s'exécute et ce n'est pas ce que l'on veut ! } // on inverse les rôles et on le notifie à la Vue if(joueur == 1) joueur = 2; else joueur = 1; maVue.definirJoueur(joueur); // si la colonne est remplie, on désactive le bouton de la Vue if(monModele.colonnePleine(numeroColonne)) maVue.desactiverBouton(numeroColonne); } // fait une copie du tableau du modèle et le renvoie // méthode peu importante... sert uniquement pour la GUI public int[][] getTableauDuModele() { return monModele.getTableauDuModele(); } }
La classe vue est la classe qui s’occupe de construire l’interface graphique du jeu puissance 4
/** * Cette classe représente une vue. * Définition : * http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur#La_vue * */ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; @SuppressWarnings("serial") // pour retirer un warning public class Vue extends JFrame { public final String[] STATUS = {"Joueur Rouge joue", "Joueur Jaune joue"}; public Controleur monControleur; public JPanel superPanel; public JPanel plateau; public JPanel commandes; public JPanel boutonsPoussoirs; public JButton[] bColTab; public JLabel lStatus; public Vue(Controleur c) { monControleur = c; creerInterface(); } /** * Cette fonction s'occupe de créer l'interface en créant chaque élément et le * positionnant au bon endroit ! */ public void creerInterface() { setDefaultCloseOperation(EXIT_ON_CLOSE); setTitle("Mini-projet 1 : Puissance 4 !"); superPanel = new JPanel(); BoxLayout bL1 = new BoxLayout(superPanel, BoxLayout.Y_AXIS); superPanel.setLayout(bL1); plateau = new JPanel() { // on va surcharger ici la méthode paint pour retracer l'image de fond public void paint(Graphics g) { try { BufferedImage image = ImageIO.read(new File("images/plateau.jpg")); g.drawImage(image, 0, 0, null); /* * Ici, on doit ajouter le dessin de chaque jeton qui devrait être là. * Ces jetons sont récupérés par le controleur qui va chercher ce qui * se trouve dans le modèle à cet instant. */ int[][] tabJetons = monControleur.getTableauDuModele(); // on doit imprimer chaque ligne, et il faut l'aligner correctement... // ça ne va donc pas être très joli à regarder comme code ! for(int i = 0; i < tabJetons.length; i++) { for(int j = 0; j < tabJetons[i].length; j++) { int joueur = tabJetons[i][j]; int X_start = 20 + j*104; int Y_start = 13 + i*104; if(joueur == 0) g.setColor(Color.white); else if(joueur == 1) g.setColor(Color.red); else if(joueur == 2) g.setColor(Color.yellow); g.fillOval(X_start, Y_start, 85, 85); } } } catch (IOException e) { JOptionPane.showMessageDialog(null, "Erreur: image plateau non trouvée."); System.exit(-1); } } }; plateau.setPreferredSize(new Dimension(744, 636)); JButton bNouveau = new JButton("Nouvelle partie !"); bNouveau.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { monControleur.nouvellePartie(); } }); lStatus = new JLabel("Pas de partie en cours."); commandes = new JPanel(); BoxLayout bL2 = new BoxLayout(commandes, BoxLayout.X_AXIS); commandes.setLayout(bL2); commandes.add(bNouveau); commandes.add(Box.createHorizontalStrut(50)); // pour faire un espace de 50px commandes.add(lStatus); bColTab = new JButton[7]; for(int i = 0; i < bColTab.length; i++) { // pour chaque JButton du tableau bColTab[i] = new JButton("v"); // on lui met le texte "v" bColTab[i].addActionListener(new ActionListener() { // et on implémente un ActionListener @Override public void actionPerformed(ActionEvent e) { JButton jB = (JButton) e.getSource(); int i; // pour le garder après la boucle for(i = 0; i < 7; i++) { if(jB == bColTab[i]) break; } monControleur.leJoueurJoue(i); } }); } boutonsPoussoirs = new JPanel(); BoxLayout bL3 = new BoxLayout(boutonsPoussoirs, BoxLayout.X_AXIS); boutonsPoussoirs.setLayout(bL3); for(int i = 0; i < bColTab.length; i++) { boutonsPoussoirs.add(bColTab[i]); if(i != bColTab.length-1) boutonsPoussoirs.add(Box.createHorizontalStrut(63)); } superPanel.add(Box.createVerticalStrut(15)); superPanel.add(commandes); superPanel.add(Box.createVerticalStrut(15)); superPanel.add(boutonsPoussoirs); superPanel.add(Box.createVerticalStrut(15)); superPanel.add(plateau); add(superPanel); } // cette méthode réinitialiser la Vue public void reinitialiser() { lStatus.setText(STATUS[0]); for(int i = 0; i < bColTab.length; i++) bColTab[i].setEnabled(true); plateau.repaint(); } // cette méthode définit le joueur qui joue public void definirJoueur(int numeroJoueur) { if(numeroJoueur == 1) lStatus.setText(STATUS[0]); else if(numeroJoueur == 2) lStatus.setText(STATUS[1]); else System.out.println("Bug qui ne devrait jamais arriver !!"); } // désactive un bouton de jeu public void desactiverBouton(int numeroColonne) { bColTab[numeroColonne].setEnabled(false); } }
Tout y est l’application de jeu puissance 4 est prête à être exécutée.