395 lines
13 KiB
Java
395 lines
13 KiB
Java
/**
|
|
* 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);
|
|
}
|
|
}
|
|
|
|
} |