PT21_APL2020/Grid.java

395 lines
13 KiB
Java
Raw Permalink Normal View History

2021-12-06 22:07:53 +01:00
/**
* 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);
}
}
}