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 motifPieceCouranteColoriee() { List 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 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 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 getLignePoints(int i) { List 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 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 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 calculerPositionApresRotation(Orientation orientation) { List 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 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 calculerNouvellePosition(List points, int deltaX, int deltaY) { List 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(); } }