/** * La classe <code>Grid</code> est utilisée pour générer la grille de blocs * * @version 0.1 * @author Adil HAMMERSCHMIDT & Lucas GRANDJEAN */ import java.io.*; import java.awt.*; import javax.swing.*; import java.util.Random; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; public class Grid extends JPanel { private Random random = new Random(); private int col = 15; private int row = 10; private char charTab[][] = new char[10][15]; private Block blockTab[][] = new Block[10][15]; private String[] connectedCellList = null; private final ArrayList<String> cellNeigbors = new ArrayList<>(); private HUD gameHUD; private int score = 0; /** *Constructeur de la Grid : Génère une grille aléatoire. *@param HUD Nom de l'HUD */ public Grid(HUD gameHUD) { this.gameHUD = gameHUD; this.setLayout(new GridLayout(row,col,0,0)); this.setBackground(Color.WHITE); this.initGrid(); } /** *TODO : Surchage du constructeur avec le fichier * *@param s Nom du fichier *@param HUD Nom de l'HUD */ public Grid(String s, HUD gameHUD) { this.gameHUD = gameHUD; this.setLayout(new GridLayout(row,col,0,0)); try { File grid_file = new File(s); FileInputStream flux = new FileInputStream(grid_file); this.initGrid(flux); try { flux.close(); } catch (IOException e) { System.err.println("Impossible de fermer le fichier " + s + " !"); } } catch (FileNotFoundException e) { System.err.println("Impossible d'ouvrir le fichier " + s + " !"); } } /** * Met en place la grille * */ public void initGrid() { GridMouseHandler gridMouseHandler = new GridMouseHandler(); int i,j; for (i=0; i<row; i++) { for (j=0; j<col; j++) { int randomBlock=this.random.nextInt(3); try { switch(randomBlock) { case 0: charTab[i][j]='R'; break; case 1: charTab[i][j]='V'; break; case 2: charTab[i][j]='B'; break; default: throw new OutOfRangeBlock(); } this.blockTab[i][j] = new Block(j, i, charTab[i][j]); this.blockTab[i][j].addMouseListener(gridMouseHandler); this.blockTab[i][j].setBackground(Color.WHITE); this.add(this.blockTab[i][j]); } catch(OutOfRangeBlock e) { System.out.println(e); } } } } /** * Surchage de la fonction initGrid pour fonctionner avec les fichiers * *@param f Flux du fichier */ public void initGrid(FileInputStream f) { GridMouseHandler gridMouseHandler = new GridMouseHandler(); int i,j; char c; try { for (i=0; i<row; i++) { for (j=0; j<col; j++) { c = (char)f.read(); if (c == '\r') { c = (char)f.read(); c = (char)f.read(); } charTab[i][j]=c; this.blockTab[i][j] = new Block(j, i, charTab[i][j]); this.blockTab[i][j].addMouseListener(gridMouseHandler); this.blockTab[i][j].setBackground(Color.WHITE); this.add(this.blockTab[i][j]); } } } catch(IOException e) { System.err.println("Impossible de lire le fichier !"); } } /** * Cette méthode explore avec récursivité une matrice, partant * de la ligne et colonne spécifiée en argument, processe ses valeurs * puis regarde s'il possède un voisin horizontalement, verticalement et diagonalement (selon les options) * qui possède les mêmes valeurs. Chaques celulles voisines détectées rappellent la fonction, * jusqu'à ce qu'il n'y ai plus de voisins disponibles. * Toutes les celulles voisines ont les valeurs des lignes/colonnes * storés dans une Array String (délimité par un ;). * * @param matrix (2D Array Int) La matrice pour check les voisins * * @param matrix_X (int) Index de la ligne de base * * @param matrix_Y (int) Index de la colonne de base * * @return (String[] Array) Une Array de String contenant des sets délimités par un ";" * avec les valeurs des index de la Ligne;Colonne pour les cellules voisines détectées. * (Cellule de base inclue au début) */ public String[] getMatrixNeighCells(char[][] matrix, int matrix_X, int matrix_Y) { /* Dans une matrice on peut avoir jusqu'à 8 voisins qui contiennent la même valeur. On va exclure les diagonales. */ int[][] directions = { {0, -1}, // Haut {-1, 0}, // Gauche {1, 0}, // Droite {0, 1}, // Bas }; // Cellule de base String baseCell = String.valueOf((matrix_X)) + ";" + String.valueOf((matrix_Y)); if (!this.cellNeigbors.contains(baseCell)) { this.cellNeigbors.add(baseCell); } for (int[] d : directions) { int dx = d[0]; // Ligne int dy = d[1]; // Colonne if ((matrix_X + dx) >= 0 && (matrix_X + dx) < (matrix.length) && (matrix_Y + dy) >= 0 && (matrix_Y + dy) < (matrix[0].length) && matrix[matrix_X + dx][matrix_Y + dy] == matrix[matrix_X][matrix_Y] && matrix[matrix_X][matrix_Y] != '1') { // Pour les celulles avec des voisins à la même valeur String neighbor = String.valueOf((matrix_X + dx)) + ";" + String.valueOf((matrix_Y + dy)); if (!this.cellNeigbors.contains(neighbor)) { this.cellNeigbors.add(neighbor); // Récursivité pour les voisins String[] tmp = getMatrixNeighCells(matrix, matrix_X + dx, matrix_Y + dy); if (tmp != null) { for (String str : tmp) { if (!this.cellNeigbors.contains(str)) { this.cellNeigbors.add(str); } } } } } } // On return la liste s'il y'a au moins une cellule voisine aux valeurs adjacente à celle de base if (this.cellNeigbors.size() >= 2) { return this.cellNeigbors.toArray(new String[this.cellNeigbors.size()]); } return null; } /** * Permet de vérifier s'il y'a des lignes vides en dessous de chaques boules. * S'il y'en a, on abaisse d'un niveau la boule et on fait une récursion. *@param j Numéro de colonne */ public void reorganizeCol(int j) { for(int i = 0; i < row-1; i++) { if(blockTab[i][j].getStatus() == 1 && blockTab[i+1][j].getStatus() == 0) { blockTab[i+1][j].setColor(blockTab[i][j].getColor()); blockTab[i+1][j].setStatus(1); blockTab[i][j].setStatus(0); blockTab[i+1][j].setBackground(Color.WHITE); blockTab[i][j].setBackground(Color.WHITE); charTab[i][j]='1'; charTab[i+1][j]=blockTab[i+1][j].getColor(); reorganizeCol(j); } } this.validate(); this.repaint(); } /** * Permet de vérifier si une des colonnes est vide. Si oui, on décale et on relance. *@param i Numéro de col */ public void reorganizeRow(int i) { int nbCount = 0; for(int j = 0; j < row; j++) { if(blockTab[j][i].getStatus() == 0) { nbCount++; } if(nbCount == row) { for(int k = 0; k < row; k++) { blockTab[k][i].setColor(blockTab[k][i+1].getColor()); if(blockTab[k][i+1].getStatus() == 1) { blockTab[k][i].setStatus(1); blockTab[k][i+1].setStatus(0); charTab[k][i]=blockTab[k][i+1].getColor(); } blockTab[k][i].setBackground(Color.WHITE); blockTab[k][i+1].setBackground(Color.WHITE); charTab[k][i+1]='1'; } if(i != col-2) { reorganizeRow(i+1); } } } this.validate(); this.repaint(); } /** * Getter du tableau de la grille * *@return tableau de la grille */ public char[][] getGrid() { return this.charTab; } /** * Un setter avec incrémentation du score * On change le label du score. */ public void addScore(int score) { this.score += score; gameHUD.setScoreLabel(this.score); } /** * Getter du score * @return le score */ public int getScore() { return this.score; } /** * On itère chaque blocs pour savoir s'il leur reste des voisins * Si non, on fait gagner le joueur * @return un bool, true marquant la victoire du joueur. */ public void checkWin() { int moveLeft = 0; for (int i=0; i< blockTab.length; i++) { for (int j=0; j< blockTab[i].length; j++) { cellNeigbors.clear(); connectedCellList = getMatrixNeighCells(getGrid(), i,j); if (connectedCellList != null && connectedCellList.length > 1) moveLeft++; } } if(moveLeft <= 0) endGame(); } /** * Créé l'écran de fin de partie */ public void endGame() { FinalScreen fs = new FinalScreen(score); JFrame topFrame = (JFrame) SwingUtilities.getWindowAncestor(this); topFrame.dispose(); } /** * Mouse Event */ public class GridMouseHandler extends MouseAdapter { /** * Si le joueur passe la souris sur un des Blocks, * on vérifie les cellules voisines du block ponté * puis on change le backGround des cellules voisines */ @Override public void mouseEntered(MouseEvent evt) { Block source = (Block) evt.getSource(); for (int i = 0; i < blockTab.length; i++) { for (int j = 0; j < blockTab[i].length; j++) { if (blockTab[i][j] == source) { cellNeigbors.clear(); connectedCellList = getMatrixNeighCells(getGrid(), i, j); if (connectedCellList != null && connectedCellList.length > 1) { for (String cells : connectedCellList) { int x = Integer.valueOf(cells.split(";")[0]); int y = Integer.valueOf(cells.split(";")[1]); blockTab[x][y].setBackground(Color.YELLOW); } } } } } } /** * Si le joueur quitte le block avec le pointeur de souris, * on remet le background à sa couleur de base */ @Override public void mouseExited(MouseEvent evt) { if (connectedCellList != null) { for (String cells : connectedCellList) { int x = Integer.valueOf(cells.split(";")[0]); int y = Integer.valueOf(cells.split(";")[1]); blockTab[x][y].setBackground(Color.WHITE); } } } /** * Si le joueur clique et relâche la souris, * on désactive le block (en changeant le status), * on remet le fond, on lui assigne une couleur hors du champs * (afin de désactiver le surlignage), et on réorganise les * lignes et colonnes du grid. * On cherche aussi si le joueur a gagné en appelant CheckWin() */ @Override public void mouseReleased(MouseEvent evt) { if (connectedCellList != null) { int ccListSize = connectedCellList.length; for (String cells : connectedCellList) { int x = Integer.valueOf(cells.split(";")[0]); int y = Integer.valueOf(cells.split(";")[1]); charTab[x][y]='1'; blockTab[x][y].setStatus(0); blockTab[x][y].setBackground(Color.WHITE); } addScore((ccListSize-2) * (ccListSize-2)); for(int j = 0; j < col; j++) { reorganizeCol(j); } for(int i = (col-2) ; i >= 0; i--) { reorganizeRow(i); } } checkWin(); mouseEntered(evt); } } }