Puissance4: créer application jeu en Java

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:

CCF26092009_00000-1024x738.jpg

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
 * 8) 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.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks

Reply

  Loading...
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
esc
cancel