Files
Tetris/app/src/main/java/org/Models/Grille.java
2025-05-22 11:07:37 +02:00

569 lines
15 KiB
Java

package org.Models;
/**
* Uniquement afin de se servir d'une paire de coordonées.
*/
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
/**
* Représente la grille de jeu du Tetris, gère l'état des cases, la pièce courante,
* le score, la suppression des lignes et les déplacements/rotations des pièces.
*/
@SuppressWarnings("deprecation")
public class Grille extends Observable {
private boolean[][] grille;
public int nbLignes;
public int nbColonnes;
private PieceCourante pieceCourante;
private int pieceCouranteX;
private int pieceCouranteY;
private boolean enPause = false;
private int score = 0;
private int nbLignesSupprimees = 0;
/**
* Crée une nouvelle grille de jeu avec le nombre de lignes et de colonnes spécifié.
*
* @param nbLignes nombre de lignes de la grille
* @param nbColonnes nombre de colonnes de la grille
*/
public Grille(int nbLignes, int nbColonnes) {
this.nbLignes = nbLignes;
this.nbColonnes = nbColonnes;
this.grille = new boolean[nbLignes][nbColonnes];
initGrille();
}
/**
* Initialise toutes les cases de la grille à vide (false).
*/
public void initGrille() {
for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) {
this.grille[i][j] = false;
}
}
}
/**
* Modifie l'état d'une case de la grille.
*
* @param i indice de ligne
* @param j indice de colonne
* @param value true si la case est occupée, false sinon
*/
public void setCase(int i, int j, boolean value) {
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
this.grille[i][j] = value;
setChanged();
notifyObservers();
}
}
/**
* Retourne l'état d'une case de la grille.
*
* @param i indice de ligne
* @param j indice de colonne
* @return true si la case est occupée, false sinon
*/
public boolean getCase(int i, int j) {
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
return this.grille[i][j];
}
return false;
}
/**
* Retourne la pièce courante.
*
* @return la pièce courante
*/
public PieceCourante getPieceCourante() {
return pieceCourante;
}
/**
* Définit la pièce courante et la place en haut de la grille.
*
* @param pieceCourante la nouvelle pièce courante
*/
public void setPieceCourante(PieceCourante pieceCourante) {
this.pieceCourante = pieceCourante;
this.pieceCouranteX = 3;
this.pieceCouranteY = 0;
setChanged();
notifyObservers();
}
/**
* Retourne la position X de la pièce courante.
*
* @return position X
*/
public int getPieceCouranteX() {
return pieceCouranteX;
}
/**
* Retourne la position X de la pièce courante.
*
* @return position X
*/
public int getPieceCouranteY() {
return pieceCouranteY;
}
/**
* Déplace la pièce courante dans la direction spécifiée si possible.
*
* @param direction direction du déplacement
*/
public void deplacerPiece(Direction direction) {
switch (direction) {
case BAS:
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceBas();
}
break;
case GAUCHE:
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceGauche();
}
break;
case DROITE:
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceDroite();
}
break;
case TOUTENBAS:
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceToutEnBas();
}
default:
break;
}
}
/**
* Déplace la pièce courante d'une case vers le bas.
*/
private void deplacerPieceBas() {
pieceCouranteY++;
setChanged();
notifyObservers();
}
/**
* Déplace la pièce courante d'une case vers la gauche.
*/
private void deplacerPieceGauche() {
pieceCouranteX--;
setChanged();
notifyObservers();
}
/**
* Déplace la pièce courante d'une case vers la droite.
*/
private void deplacerPieceDroite() {
pieceCouranteX++;
setChanged();
notifyObservers();
}
/**
* Fait descendre la pièce courante jusqu'à ce qu'elle ne puisse plus descendre.
*/
private void deplacerPieceToutEnBas() {
while (peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) {
deplacerPieceBas();
}
}
/**
* Retourne le nombre de lignes de la grille.
*
* @return nombre de lignes
*/
public int getNbLignes() {
return nbLignes;
}
/**
* Retourne le nombre de colonnes de la grille.
*
* @return nombre de colonnes
*/
public int getNbColonnes() {
return nbColonnes;
}
/**
* Retourne la liste des points occupés par la pièce courante dans la grille.
*
* @return liste des points colorés
*/
public List<Point> motifPieceCouranteColoriee() {
List<Point> casesColores = new ArrayList<>();
boolean[][] motif = pieceCourante.getMotif();
for (int i = 0; i < motif.length; i++) {
for (int j = 0; j < motif[i].length; j++) {
if (motif[i][j]) {
casesColores.add(new Point(pieceCouranteX + j, pieceCouranteY + i));
}
}
}
return casesColores;
}
/**
* Vérifie si la pièce peut bouger dans la direction donnée.
*
* @param direction direction du déplacement
* @param motif motif de la pièce
* @return true si le déplacement est possible, false sinon
*/
public boolean peuxBouger(Direction direction, List<Point> motif) {
int deltaX = 0;
int deltaY = 0;
switch (direction) {
case GAUCHE:
deltaX = -1;
break;
case DROITE:
deltaX = 1;
break;
case BAS:
deltaY = 1;
break;
case TOUTENBAS:
break;
default:
break;
}
List<Point> nouvellePosition = calculerNouvellePosition(motif, deltaX, deltaY);
return positionValide(nouvellePosition);
}
/**
* Retourne la matrice représentant la grille.
*
* @return matrice de booleans
*/
public boolean[][] getGrille() {
return grille;
}
/**
* Fixe la pièce courante sur la grille et vérifie la suppression de lignes.
*/
public void fixerPiece() {
for (Point caseColoree : motifPieceCouranteColoriee()) {
setCase(caseColoree.y, caseColoree.x, true);
}
verifierEtSupprimerLignesSiBesoin();
}
/**
* Vérifie si la pièce courante doit être fixée (ne peut plus descendre).
*
* @return true si la pièce doit être fixée, false sinon
*/
public boolean doitFixerPiece() {
if (!peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) {
return true;
}
return false;
}
/**
* Met la grille en pause ou la reprend.
*
* @param enPause true pour mettre en pause, false pour reprendre
*/
public void setEnPause(boolean enPause) {
this.enPause = enPause;
}
/**
* Indique si la grille est en pause.
*
* @return true si en pause, false sinon
*/
public boolean estEnPause() {
return enPause;
}
/**
* Vérifie et supprime les lignes complètes si besoin, met à jour le score.
*/
public void verifierEtSupprimerLignesSiBesoin() {
int tmpNbLignesSupprimees = 0;
System.out.println("Debut uppression d'une ligne......");
for (int i = nbLignes - 1; i >= 0; i--) {
boolean ligneSupprimable = true;
for (int j = 0; j < nbColonnes; j++) {
if (!this.grille[i][j]) {
ligneSupprimable = false;
break;
}
}
if (ligneSupprimable) {
supprimerLigne(i);
nbLignesSupprimees += 1;
i++;
tmpNbLignesSupprimees++;
}
}
System.out.println(tmpNbLignesSupprimees + " Lignes supprimées");
switch (tmpNbLignesSupprimees) {
case 1:
score += 100;
break;
case 2:
score += 300;
break;
case 3:
score += 500;
break;
case 4:
score += 800;
break;
default:
break;
}
}
/**
* Retourne le score actuel.
*
* @return score
*/
public int getScore() {
return score;
}
/**
* Définit le score.
*
* @param score nouveau score
*/
public void setScore(int score) {
this.score = score;
}
/**
* Retourne le nombre total de lignes supprimées.
*
* @return nombre de lignes supprimées
*/
public int getNbLignesSupprimees() {
return nbLignesSupprimees;
}
/**
* Définit le nombre de lignes supprimées.
*
* @param nbLignesSupprimees nouveau nombre de lignes supprimées
*/
public void setNbLignesSupprimees(int nbLignesSupprimees) {
this.nbLignesSupprimees = nbLignesSupprimees;
}
/**
* Supprime la ligne d'indice i et fait descendre les lignes au-dessus.
*
* @param i indice de la ligne à supprimer
*/
public void supprimerLigne(int i) {
for (int ligne = i; ligne > 0; ligne--) {
for (int j = 0; j < nbColonnes; j++) {
this.setCase(ligne, j, this.getCase(ligne - 1, j));
}
}
for (int j = 0; j < nbColonnes; j++) {
this.setCase(0, j, false);
}
setChanged();
notifyObservers();
}
/**
* Retourne la liste des points d'une ligne donnée.
*
* @param i indice de la ligne
* @return liste des points de la ligne
*/
public List<Point> getLignePoints(int i) {
List<Point> coordonnesCase = new ArrayList<>();
for (int j = 0; j < nbColonnes; j++) {
coordonnesCase.add(new Point(j, i));
}
return coordonnesCase;
}
/**
* Fait descendre une ligne d'une position.
*
* @param i indice de la ligne à descendre
*/
public void descendreLigne(int i) {
for (int j = 0; j < nbColonnes; j++) {
List<Point> coordonnesCase = new ArrayList<>();
coordonnesCase.add(new Point(j, i));
if (this.grille[i][j]) {
this.setCase(i + 1, j, true);
}
this.setCase(i, j, false);
}
}
/**
* Tente de tourner la pièce courante dans l'orientation donnée.
*
* @param orientation sens de rotation
*/
public void tournerPiece(Orientation orientation) {
if (peuxTournerPiece(orientation)) {
boolean[][] motif = pieceCourante.getMotif();
boolean[][] nouveauMotif;
if (orientation == Orientation.SENSHORAIRE) {
nouveauMotif = PieceCourante.rotationHoraire(motif);
} else {
nouveauMotif = PieceCourante.rotationAntiHoraire(motif);
}
pieceCourante.setMotif(nouveauMotif);
setChanged();
notifyObservers();
}
}
/**
* Vérifie si la pièce courante peut tourner dans l'orientation donnée.
*
* @param orientation sens de rotation
* @return true si la rotation est possible, false sinon
*/
public boolean peuxTournerPiece(Orientation orientation) {
List<Point> positionApresRotation = calculerPositionApresRotation(orientation);
return positionValide(positionApresRotation);
}
/**
* Calcule la position de la pièce après rotation.
*
* @param orientation sens de rotation
* @return liste des points après rotation
*/
private List<Point> calculerPositionApresRotation(Orientation orientation) {
List<Point> nouvellePosition = new ArrayList<>();
boolean[][] motifActuel = pieceCourante.getMotif();
boolean[][] motifTourne;
boolean[][] motifCopie = new boolean[motifActuel.length][motifActuel[0].length];
for (int i = 0; i < motifActuel.length; i++) {
for (int j = 0; j < motifActuel[i].length; j++) {
motifCopie[i][j] = motifActuel[i][j];
}
}
if (orientation == Orientation.SENSHORAIRE) {
motifTourne = PieceCourante.rotationHoraire(motifCopie);
} else {
motifTourne = PieceCourante.rotationAntiHoraire(motifCopie);
}
for (int i = 0; i < motifTourne.length; i++) {
for (int j = 0; j < motifTourne[i].length; j++) {
if (motifTourne[i][j]) {
nouvellePosition.add(new Point(pieceCouranteX + j, pieceCouranteY + i));
}
}
}
return nouvellePosition;
}
/**
* Vérifie si la position donnée est valide (dans la grille et non occupée).
*
* @param points liste des points à vérifier
* @return true si la position est valide, false sinon
*/
public boolean positionValide(List<Point> points) {
for (Point point : points) {
if (point.x < 0 || point.x >= nbColonnes || point.y < 0 || point.y >= nbLignes) {
return false;
}
if (grille[point.y][point.x]) {
return false;
}
}
return true;
}
/**
* Calcule la nouvelle position d'une liste de points après déplacement.
*
* @param points liste de points d'origine
* @param deltaX déplacement en X
* @param deltaY déplacement en Y
* @return nouvelle liste de points déplacés
*/
private List<Point> calculerNouvellePosition(List<Point> points, int deltaX, int deltaY) {
List<Point> nouvellePosPoints = new ArrayList<>();
for (Point point : points) {
nouvellePosPoints.add(new Point(point.x + deltaX, point.y + deltaY));
}
return nouvellePosPoints;
}
/**
* Réinitialise la grille, le score, le nombre de lignes supprimées et la pièce courante.
*/
public void reinitialiserGrille() {
initGrille();
score = 0;
nbLignesSupprimees = 0;
pieceCourante = null;
pieceCouranteX = 3;
pieceCouranteY = 0;
setChanged();
notifyObservers();
}
}