Compare commits

...

11 Commits

Author SHA1 Message Date
7dffb0fca8 refactor: wrong println 2025-05-22 11:08:41 +02:00
ROGER
f6c6c5044a Ajout de la javadoc 2025-05-22 11:07:37 +02:00
c50210db3f refactor: remove view import from model 2025-05-22 10:28:17 +02:00
e817b064e1 refactor: remove git tree error during merges 2025-05-22 10:12:58 +02:00
33b5683a59 fix: restart game with ordonnanceur removal 2025-05-22 09:51:38 +02:00
8888b6dfb3 feat: add classDiagram 2025-05-21 18:12:36 +02:00
94ebb17d19 refactor: clean up unused imports and improve reinitialization call 2025-05-20 16:27:24 +02:00
3232cd136f Squashed commit of the following:
commit ab2fbeb8be52dc2886f39b72c158483793185b5c
Author: Morph01 <thibaut6969delastreet@gmail.com>
Date:   Tue May 20 15:18:20 2025 +0200

    feat: wav music file rather than mp3

commit e4ea00b4dfbe32a9a03d078a8b77c35bd934f2e6
Author: Morph01 <thibaut6969delastreet@gmail.com>
Date:   Tue May 20 14:34:34 2025 +0200

    refactor: refactor imports

commit 68ee1318f8f450f6c9358de8484c17978428949e
Author: Morph01 <thibaut6969delastreet@gmail.com>
Date:   Tue May 20 13:56:35 2025 +0200

    feat: add rotation support

commit c106cc74e351f40afbf5f03395ea14627f10b29b
Author: Morph01 <thibaut6969delastreet@gmail.com>
Date:   Tue May 20 11:44:48 2025 +0200

    fix: upper bound of generated pieces

commit 3caa2a69a49424e46078749eab53827df160af6c
Author: Morph01 <thibaut6969delastreet@gmail.com>
Date:   Tue May 20 11:40:37 2025 +0200

    Merge branch 'Gwendal' into Thibaut

commit be1fd8ddd58df1a0ab02d005ba68da154ae199c7
Author: Morph01 <thibaut6969delastreet@gmail.com>
Date:   Mon May 19 23:01:43 2025 +0200

    refactor: split in init vueGrille initialization of vueGrille rather than in constructor

commit 5ec983979b363bf671945757234047ddcc9a333e
Author: Morph01 <145839520+Morph01@users.noreply.github.com>
Date:   Mon May 19 22:47:02 2025 +0200

    refactor: better achitecture and next piece preview fixed

commit 524d96ef493c6053cfff4958697cea8583927aca
Author: Morph01 <145839520+Morph01@users.noreply.github.com>
Date:   Mon May 19 22:16:42 2025 +0200

    Squashed commit of the following:

    commit a8f543114e
    Author: ROGER <gwendal.roger@etu.univ-lyon1.fr>
    Date:   Mon May 19 21:59:38 2025 +0200

        ajout d'une fonctionnalité permettant de mettre en pause le Jeu grace à un bouton. Ajout d'un vérouillage du clavier pendant lause

    commit 27862eb3cd
    Author: ROGER <gwendal.roger@etu.univ-lyon1.fr>
    Date:   Fri May 16 17:10:15 2025 +0200

        Ajoute de l'arret de la musique en fin de partie + arret propre de l'ordonanceur

    commit 2fec9fc96e
    Author: ROGER <gwendal.roger@etu.univ-lyon1.fr>
    Date:   Fri May 16 16:17:58 2025 +0200

        integration du responsive dans l'affichage de la grille

commit 671d12f418bb6c5a0ae623e5195d78eebef4e641
Author: Morph01 <145839520+Morph01@users.noreply.github.com>
Date:   Mon May 19 22:02:16 2025 +0200

    fix: piece generation
2025-05-20 16:10:52 +02:00
ROGER
4aab505eed ajout d'un bouton restart 2025-05-20 15:13:37 +02:00
ROGER
ec3e46cbe8 ajout d'un bouton d'aideà avec play/pause quand le menu aide est affiché 2025-05-20 14:34:08 +02:00
ROGER
abd6ae07c8 ajout du score et nombre de lignes supprimés dans la vue + vue gameover 2025-05-20 12:38:23 +02:00
29 changed files with 1083 additions and 214 deletions

View File

@@ -1,11 +1,15 @@
# Tetris # Tetris
## Projet Informatique Encadré ## Projet Informatique Encadré
### POO - objectif de modularité POO - objectif de modularité
# Diagramme activité proposé
![Diagramme activité proposé](readme_files/activiteTetris.svg)
# Diagramme des classes final
![Diagramme classes final](readme_files/classDiagram.svg)
--- ---
Liens: Liens:
- Slides de cours: https://partage.liris.cnrs.fr/index.php/s/7ZwqDcXbFrwsLPB - Slides de cours: https://partage.liris.cnrs.fr/index.php/s/7ZwqDcXbFrwsLPB
- Sujet du Tetris: https://partage.liris.cnrs.fr/index.php/s/wsyyzFDrRrjzCHw - Sujet du Tetris: https://partage.liris.cnrs.fr/index.php/s/wsyyzFDrRrjzCHw
![Diagramme activité](readme_files/activiteTetris.svg)

Binary file not shown.

Binary file not shown.

View File

@@ -23,9 +23,6 @@ dependencies {
// This dependency is used by the application. // This dependency is used by the application.
implementation(libs.guava) implementation(libs.guava)
// JLayer pour lire les fichiers MP3
implementation("javazoom:jlayer:1.0.1")
} }
// Apply a specific Java toolchain to ease working on different environments. // Apply a specific Java toolchain to ease working on different environments.

View File

@@ -1,6 +1,3 @@
/*
* This source file was generated by the Gradle 'init' task
*/
package org; package org;
import org.Controllers.IO; import org.Controllers.IO;
@@ -31,15 +28,10 @@ public class App {
IO io = new IO(jeu); IO io = new IO(jeu);
vueTetris.addKeyListener(io); vueTetris.addKeyListener(io);
new TetrisBandeauControleur(vueTetris.getVueBandeauControle(), musique, grille);// Création d'un controleur de new TetrisBandeauControleur(vueTetris.getVueBandeauControle(), musique, grille, jeu);// Création d'un
// bandeau avec la musique // controleur de
// bandeau avec la
// musique
// instanciée // instanciée
} }
// TODO: RESTE A IMPLEMENTER
// - rotation
// - score
// - lignes
// - menu pour fin de partie
// ext possible: conserver un historique de score dans un fichier txt ?
} }

View File

@@ -5,19 +5,34 @@ import java.awt.event.KeyListener;
import org.Models.Direction; import org.Models.Direction;
import org.Models.Jeu; import org.Models.Jeu;
import org.Models.Orientation;
/**
* Gère les entrées clavier pour contrôler le jeu.
*/
public class IO implements KeyListener { public class IO implements KeyListener {
private Jeu jeu; private Jeu jeu;
/**
* Crée un gestionnaire d'entrées clavier pour le jeu donné.
*
* @param jeu le jeu à contrôler
*/
public IO(Jeu jeu) { public IO(Jeu jeu) {
this.jeu = jeu; this.jeu = jeu;
} }
/**
* Gère les actions à effectuer lors de l'appui sur une touche.
*
* @param e l'événement de touche pressée
*/
@Override @Override
public void keyPressed(KeyEvent e) { public void keyPressed(KeyEvent e) {
if (jeu.getGrille().estEnPause()) { if (jeu.getGrille().estEnPause()) {
System.err.println("Grille est en pause");
return; return;
} }
switch (e.getKeyCode()) { switch (e.getKeyCode()) {
@@ -35,16 +50,35 @@ public class IO implements KeyListener {
case KeyEvent.VK_SPACE: case KeyEvent.VK_SPACE:
jeu.getGrille().deplacerPiece(Direction.TOUTENBAS); jeu.getGrille().deplacerPiece(Direction.TOUTENBAS);
break;
case KeyEvent.VK_R:
jeu.getGrille().tournerPiece(Orientation.SENSHORAIRE);
break;
case KeyEvent.VK_E:
jeu.getGrille().tournerPiece(Orientation.SENSANTIHORAIRE);
break;
default: default:
break; break;
} }
} }
/**
* Méthode appelée lors de la saisie d'un caractère (non utilisée).
*
* @param e l'événement de touche tapée
*/
@Override @Override
public void keyTyped(KeyEvent e) { public void keyTyped(KeyEvent e) {
} }
/**
* Méthode appelée lors du relâchement d'une touche (non utilisée).
*
* @param e l'événement de touche relâchée
*/
@Override @Override
public void keyReleased(KeyEvent e) { public void keyReleased(KeyEvent e) {
} }

View File

@@ -1,45 +1,91 @@
package org.Controllers; package org.Controllers;
import org.Models.Grille; import org.Models.Grille;
import org.Models.Jeu;
import org.Models.Musique; import org.Models.Musique;
import org.Views.VueBandeauControle; import org.Views.VueBandeauControle;
import javax.swing.*;
/**
* Contrôleur pour le bandeau de contrôle du jeu Tetris.
* Gère les actions des boutons (pause, quitter, aide, relancer) et l'état de la partie.
*/
public class TetrisBandeauControleur { public class TetrisBandeauControleur {
private boolean partieEnPause = false; private boolean partieEnPause = false;
private boolean partieTerminee = false; private boolean partieTerminee = false;
private Musique musique; private Musique musique;
private VueBandeauControle vueControle; private VueBandeauControle vueControle;
private Grille grille; private Grille grille;
private Jeu jeu;
public TetrisBandeauControleur(VueBandeauControle vueControle, Musique musique, Grille grille) { /**
* Crée un contrôleur pour le bandeau de contrôle.
*
* @param vueControle la vue du bandeau de contrôle
* @param musique le gestionnaire de musique
* @param grille la grille de jeu
* @param jeu l'instance du jeu
*/
public TetrisBandeauControleur(VueBandeauControle vueControle, Musique musique, Grille grille, Jeu jeu) {
this.vueControle = vueControle; this.vueControle = vueControle;
this.musique = musique; this.musique = musique;
this.grille = grille; this.grille = grille;
this.jeu = jeu;
// action play/pause // action play/pause
// Listener pour le bouton play/pause
this.vueControle.getPauseButton().addActionListener(e -> switchPlayPause()); this.vueControle.getPauseButton().addActionListener(e -> switchPlayPause());
// Listener pour le bouton quitter
this.vueControle.getQuitterButton().addActionListener(e -> { this.vueControle.getQuitterButton().addActionListener(e -> {
System.out.println("Fermeture de l'application..."); System.out.println("Fermeture de l'application...");
System.exit(0); System.exit(0);
}); });
// Listener pour le bouton aide
this.vueControle.getAideButton().addActionListener(e -> afficherAide());
// Listener pour le bouton recommencer
this.vueControle.getRelancerButton().addActionListener(e -> {
this.jeu.reinitialiserPartie();
vueControle.getPauseButton().setText("PAUSE");
partieEnPause = false;
partieTerminee = false;
});
} }
/**
* Bascule l'état de la partie entre pause et reprise.
* Met à jour la grille, la musique et le texte du bouton pause.
*/
public void switchPlayPause() { public void switchPlayPause() {
if (partieTerminee) { if (partieTerminee) {
return; return;
} }
partieEnPause = !partieEnPause; partieEnPause = !partieEnPause;
grille.setEnPause(partieEnPause); grille.setEnPause(partieEnPause);
grille.setEnPause(partieEnPause);
musique.basculePlayPause(); musique.basculePlayPause();
vueControle.getPauseButton().setText(partieEnPause ? "PLAY" : "PAUSE"); vueControle.getPauseButton().setText(partieEnPause ? "PLAY" : "PAUSE");
System.out.println(partieEnPause ? "Partie en pause" : "Partie en cours"); System.out.println(partieEnPause ? "Partie en pause" : "Partie en cours");
} }
public void setPartieTerminee() { /**
partieTerminee = true; * Affiche une page d'aide avec les commandes du jeu.
musique.arreterMusique(); * Met la partie en pause pendant l'affichage de l'aide.
vueControle.getPauseButton().setEnabled(false); */
System.out.println("Partie terminée"); public void afficherAide() {
String messageAide = "Utilisez les flèches du clavier pour déplacer la pièce courante :\n" +
"Flèche gauche : Déplacer à gauche\n" +
"Flèche droite : Déplacer à droite\n" +
"Flèche bas : Accélérer la chute\n" +
"R : Faire pivoter la pièce sens horaire\n" +
"E : Faire pivoter la pièce sens anti horaire\n" +
"Espace : Hard drop\n";
if (!grille.estEnPause()) {
grille.setEnPause(true);
musique.basculePlayPause();
JOptionPane.showMessageDialog(vueControle, messageAide, "Aide", JOptionPane.INFORMATION_MESSAGE);
grille.setEnPause(false);
musique.basculePlayPause();
}
} }
} }

View File

@@ -1,5 +1,7 @@
package org.Models; package org.Models;
/**
* Enumère les directions possibles pour le déplacement d'une pièce dans le jeu Tetris.
*/
public enum Direction { public enum Direction {
BAS, BAS,
GAUCHE, GAUCHE,

View File

@@ -1,12 +1,20 @@
package org.Models; package org.Models;
/**
* Uniquement afin de se servir d'une paire de coordonées.
*/
import java.awt.Point; import java.awt.Point;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Observable; 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") @SuppressWarnings("deprecation")
public class Grille extends Observable { // TODO: ?? implements Runnable { public class Grille extends Observable {
private boolean[][] grille; private boolean[][] grille;
public int nbLignes; public int nbLignes;
public int nbColonnes; public int nbColonnes;
@@ -16,6 +24,15 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
private int pieceCouranteY; private int pieceCouranteY;
private boolean enPause = false; 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) { public Grille(int nbLignes, int nbColonnes) {
this.nbLignes = nbLignes; this.nbLignes = nbLignes;
this.nbColonnes = nbColonnes; this.nbColonnes = nbColonnes;
@@ -23,6 +40,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
initGrille(); initGrille();
} }
/**
* Initialise toutes les cases de la grille à vide (false).
*/
public void initGrille() { public void initGrille() {
for (int i = 0; i < nbLignes; i++) { for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) { for (int j = 0; j < nbColonnes; j++) {
@@ -31,6 +51,13 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
} }
} }
/**
* 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) { public void setCase(int i, int j, boolean value) {
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) { if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
this.grille[i][j] = value; this.grille[i][j] = value;
@@ -39,6 +66,13 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
} }
} }
/**
* 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) { public boolean getCase(int i, int j) {
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) { if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
return this.grille[i][j]; return this.grille[i][j];
@@ -46,10 +80,20 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
return false; return false;
} }
/**
* Retourne la pièce courante.
*
* @return la pièce courante
*/
public PieceCourante getPieceCourante() { public PieceCourante getPieceCourante() {
return pieceCourante; 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) { public void setPieceCourante(PieceCourante pieceCourante) {
this.pieceCourante = pieceCourante; this.pieceCourante = pieceCourante;
this.pieceCouranteX = 3; this.pieceCouranteX = 3;
@@ -58,14 +102,29 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers(); notifyObservers();
} }
/**
* Retourne la position X de la pièce courante.
*
* @return position X
*/
public int getPieceCouranteX() { public int getPieceCouranteX() {
return pieceCouranteX; return pieceCouranteX;
} }
/**
* Retourne la position X de la pièce courante.
*
* @return position X
*/
public int getPieceCouranteY() { public int getPieceCouranteY() {
return pieceCouranteY; 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) { public void deplacerPiece(Direction direction) {
switch (direction) { switch (direction) {
@@ -93,6 +152,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
} }
} }
/**
* Déplace la pièce courante d'une case vers le bas.
*/
private void deplacerPieceBas() { private void deplacerPieceBas() {
pieceCouranteY++; pieceCouranteY++;
@@ -100,6 +162,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers(); notifyObservers();
} }
/**
* Déplace la pièce courante d'une case vers la gauche.
*/
private void deplacerPieceGauche() { private void deplacerPieceGauche() {
pieceCouranteX--; pieceCouranteX--;
@@ -107,6 +172,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers(); notifyObservers();
} }
/**
* Déplace la pièce courante d'une case vers la droite.
*/
private void deplacerPieceDroite() { private void deplacerPieceDroite() {
pieceCouranteX++; pieceCouranteX++;
@@ -114,43 +182,38 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers(); notifyObservers();
} }
/**
* Fait descendre la pièce courante jusqu'à ce qu'elle ne puisse plus descendre.
*/
private void deplacerPieceToutEnBas() { private void deplacerPieceToutEnBas() {
while (peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) { while (peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) {
deplacerPieceBas(); deplacerPieceBas();
} }
} }
// TODO : ENLEVER ? /**
// public int getMaxYPieceCouranteColoree(List<Point> motifPieceColoree) { * Retourne le nombre de lignes de la grille.
// int maxY = 0; *
// for (Point caseColoree : motifPieceColoree) { * @return nombre de lignes
// if (caseColoree.y > maxY) { */
// maxY = caseColoree.y;
// }
// }
// return maxY;
// }
// public int getMaxXPieceCouranteColoree(List<Point> motifPieceColoree) {
// int maxX = 0;
// for (Point caseColoree : motifPieceColoree) {
// if (caseColoree.x > maxX) {
// maxX = caseColoree.x;
// }
// }
// return maxX;
// }
public int getNbLignes() { public int getNbLignes() {
return nbLignes; return nbLignes;
} }
/**
* Retourne le nombre de colonnes de la grille.
*
* @return nombre de colonnes
*/
public int getNbColonnes() { public int getNbColonnes() {
return nbColonnes; 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() { public List<Point> motifPieceCouranteColoriee() {
List<Point> casesColores = new ArrayList<>(); List<Point> casesColores = new ArrayList<>();
@@ -166,6 +229,13 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
return casesColores; 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) { public boolean peuxBouger(Direction direction, List<Point> motif) {
int deltaX = 0; int deltaX = 0;
int deltaY = 0; int deltaY = 0;
@@ -186,35 +256,37 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
break; break;
} }
for (Point caseColoree : motif) { List<Point> nouvellePosition = calculerNouvellePosition(motif, deltaX, deltaY);
int newX = caseColoree.x + deltaX; return positionValide(nouvellePosition);
int newY = caseColoree.y + deltaY;
if (newX < 0 || newX >= nbColonnes || newY < 0 || newY >= nbLignes) {
return false;
}
if (grille[newY][newX]) {
return false;
}
}
return true;
} }
/**
* Retourne la matrice représentant la grille.
*
* @return matrice de booleans
*/
public boolean[][] getGrille() { public boolean[][] getGrille() {
return grille; return grille;
} }
/**
* Fixe la pièce courante sur la grille et vérifie la suppression de lignes.
*/
public void fixerPiece() { public void fixerPiece() {
for (Point caseColoree : motifPieceCouranteColoriee()) { for (Point caseColoree : motifPieceCouranteColoriee()) {
setCase(caseColoree.y, caseColoree.x, true); setCase(caseColoree.y, caseColoree.x, true);
} }
System.err.println("post fixage de piece");
verifierEtSupprimerLignesSiBesoin(); 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() { public boolean doitFixerPiece() {
if (!peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) { if (!peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) {
return true; return true;
@@ -223,32 +295,30 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
return false; 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) { public void setEnPause(boolean enPause) {
this.enPause = enPause; this.enPause = enPause;
} }
/**
* Indique si la grille est en pause.
*
* @return true si en pause, false sinon
*/
public boolean estEnPause() { public boolean estEnPause() {
return enPause; return enPause;
} }
// public void verifierEtSupprimerLignesSiBesoin() { /**
* Vérifie et supprime les lignes complètes si besoin, met à jour le score.
// 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;
// }
// }
// if (ligneSupprimable) {
// supprimerLigne(i);
// }
// }
// }
public void verifierEtSupprimerLignesSiBesoin() { public void verifierEtSupprimerLignesSiBesoin() {
int tmpNbLignesSupprimees = 0;
System.out.println("Debut uppression d'une ligne......");
for (int i = nbLignes - 1; i >= 0; i--) { for (int i = nbLignes - 1; i >= 0; i--) {
boolean ligneSupprimable = true; boolean ligneSupprimable = true;
@@ -261,30 +331,71 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
if (ligneSupprimable) { if (ligneSupprimable) {
supprimerLigne(i); supprimerLigne(i);
nbLignesSupprimees += 1;
i++; 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;
}
} }
// // TODO : EUHHHHHHHHHH JE CROIS PAS que ça marche comme ça c'est pas en mode /**
// gravité récursive, c'est juste tout descend de n lignes cassées * Retourne le score actuel.
// // dès qu'on a fixe la pièce courante on verifie le nombre de lignes a *
// supprimes * @return score
// // dans la grille */
public int getScore() {
return score;
}
// // -> /**
// // faire tomber JUSQU'EN bas la ligne X au dessus des n lines supprimes * Définit le score.
// // PAR CONTRE faire tomber de n lignes toutes les lignes au dessus de X. *
// while (peuxBouger(Direction.BAS, getLignePoints(i))) { * @param score nouveau score
// System.err.println("testtttttttttt descnete"); */
// descendreLigne(i - 1); public void setScore(int score) {
// } this.score = score;
}
// // for (int j = i - 2; j > 0; j--) { /**
// // descendreLigne(j); * 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) { public void supprimerLigne(int i) {
for (int ligne = i; ligne > 0; ligne--) { for (int ligne = i; ligne > 0; ligne--) {
for (int j = 0; j < nbColonnes; j++) { for (int j = 0; j < nbColonnes; j++) {
@@ -300,6 +411,12 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers(); 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) { public List<Point> getLignePoints(int i) {
List<Point> coordonnesCase = new ArrayList<>(); List<Point> coordonnesCase = new ArrayList<>();
@@ -310,6 +427,11 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
return coordonnesCase; return coordonnesCase;
} }
/**
* Fait descendre une ligne d'une position.
*
* @param i indice de la ligne à descendre
*/
public void descendreLigne(int i) { public void descendreLigne(int i) {
for (int j = 0; j < nbColonnes; j++) { for (int j = 0; j < nbColonnes; j++) {
List<Point> coordonnesCase = new ArrayList<>(); List<Point> coordonnesCase = new ArrayList<>();
@@ -322,4 +444,126 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
this.setCase(i, j, false); 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();
}
} }

View File

@@ -1,6 +1,10 @@
package org.Models; package org.Models;
/**
* Uniquement afin de se servir d'une paire de coordonées.
*/
import java.awt.Point; import java.awt.Point;
import java.util.Observable; import java.util.Observable;
import java.util.Random; import java.util.Random;
@@ -12,6 +16,10 @@ import org.Models.Pieces.PieceS;
import org.Models.Pieces.PieceT; import org.Models.Pieces.PieceT;
import org.Models.Pieces.PieceZ; import org.Models.Pieces.PieceZ;
/**
* Gère la logique principale du jeu Tetris, y compris la gestion de la grille,
* des pièces, de la musique, de l'ordonnancement et de l'état de la partie.
*/
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class Jeu extends Observable implements Runnable { public class Jeu extends Observable implements Runnable {
private Grille grille; private Grille grille;
@@ -21,58 +29,66 @@ public class Jeu extends Observable implements Runnable {
private PieceCourante pieceSuivante; private PieceCourante pieceSuivante;
private int pieceSuivanteX; private int pieceSuivanteX;
private int pieceSuivanteY; private int pieceSuivanteY;
public boolean jeuEnCours = true; public boolean jeuEnCours = true;
/**
* Crée une nouvelle instance du jeu avec la grille et la musique spécifiées.
*
* @param grille la grille de jeu
* @param musique le gestionnaire de musique
*/
public Jeu(Grille grille, Musique musique) { public Jeu(Grille grille, Musique musique) {
System.err.println("init jeu");
this.grille = grille; this.grille = grille;
this.musique = musique; this.musique = musique;
this.musique = musique;
System.err.println("init nouvelle piece courante");
this.grille.setPieceCourante(getNouvellePiece()); this.grille.setPieceCourante(getNouvellePiece());
System.err.println("init nouvelle piece suivante");
this.pieceSuivante = getNouvellePiece(); this.pieceSuivante = getNouvellePiece();
this.ordonnanceur = new Ordonnanceur(this, 1000); this.ordonnanceur = new Ordonnanceur(this, 1000);
this.ordonnanceur.start(); this.ordonnanceur.start();
} }
/**
* Génère et retourne une nouvelle pièce aléatoire.
*
* @return une nouvelle pièce courante
*/
private PieceCourante getNouvellePiece() { private PieceCourante getNouvellePiece() {
Random random = new Random(); Random random = new Random();
int randomiiii = random.nextInt(6); int randomiiii = random.nextInt(7);
System.err.println("randomiiiii : " + randomiiii);
PieceCourante nouvellePiece; PieceCourante nouvellePiece;
switch (randomiiii) { switch (randomiiii) {
case 0: case 0:
nouvellePiece = new PieceI(); nouvellePiece = new PieceI();
System.err.println("piece I");
break; break;
case 1: case 1:
nouvellePiece = new PieceL(); nouvellePiece = new PieceL();
System.err.println("piece L");
break; break;
case 2: case 2:
nouvellePiece = new PieceJ(); nouvellePiece = new PieceJ();
System.err.println("piece J");
break; break;
case 3: case 3:
nouvellePiece = new PieceO(); nouvellePiece = new PieceO();
System.err.println("piece O");
break; break;
case 4: case 4:
nouvellePiece = new PieceS(); nouvellePiece = new PieceS();
System.err.println("piece S");
break; break;
case 5: case 5:
nouvellePiece = new PieceT(); nouvellePiece = new PieceT();
System.err.println("piece T");
break; break;
case 6: case 6:
nouvellePiece = new PieceZ(); nouvellePiece = new PieceZ();
System.err.println("piece Z");
break; break;
default: default:
nouvellePiece = new PieceL(); nouvellePiece = new PieceL();
@@ -81,6 +97,11 @@ public class Jeu extends Observable implements Runnable {
return nouvellePiece; return nouvellePiece;
} }
/**
* Retourne la pièce suivante.
*
* @return la pièce suivante
*/
public PieceCourante getPieceSuivante() { public PieceCourante getPieceSuivante() {
return pieceSuivante; return pieceSuivante;
} }
@@ -93,13 +114,24 @@ public class Jeu extends Observable implements Runnable {
return pieceSuivanteY; return pieceSuivanteY;
} }
/**
* Retourne la grille de jeu.
*
* @return la grille
*/
public Grille getGrille() { public Grille getGrille() {
return grille; return grille;
} }
/**
* Vérifie si la partie est terminée (collision en haut de la grille).
*
* @return true si la partie est terminée, false sinon
*/
public boolean estFinPartie() { public boolean estFinPartie() {
for (Point caseColoree : this.grille.motifPieceCouranteColoriee()) { for (Point caseColoree : this.grille.motifPieceCouranteColoriee()) {
if (this.grille.getCase(caseColoree.y, caseColoree.x)) { if (this.grille.getCase(caseColoree.y, caseColoree.x)) {
finPartie();
return true; return true;
} }
} }
@@ -107,6 +139,9 @@ public class Jeu extends Observable implements Runnable {
return false; return false;
} }
/**
* Termine la partie, arrête l'ordonnanceur et la musique.
*/
public void finPartie() { public void finPartie() {
this.jeuEnCours = false; this.jeuEnCours = false;
ordonnanceur.interrupt(); ordonnanceur.interrupt();
@@ -118,9 +153,50 @@ public class Jeu extends Observable implements Runnable {
notifyObservers(); notifyObservers();
} }
/**
* Réinitialise tous les paramètres de la partie pour relancer le jeu.
*/
public void reinitialiserPartie() {
if (ordonnanceur != null) {
ordonnanceur.stopOrdonnanceur();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
this.grille.initGrille();
this.grille.setPieceCourante(getNouvellePiece());
this.grille.setScore(0);
this.grille.setNbLignesSupprimees(0);
this.pieceSuivante = getNouvellePiece();
this.jeuEnCours = true;
this.ordonnanceur = new Ordonnanceur(this, 1000);
this.ordonnanceur.start();
setChanged();
notifyObservers();
}
public void pauseJeu() {
grille.setEnPause(true);
if (musique != null) {
musique.basculePlayPause();
}
}
public void reprendreJeu() {
grille.setEnPause(false);
if (musique != null) {
musique.basculePlayPause();
}
}
/**
* Méthode exécutée périodiquement par l'ordonnanceur pour faire avancer le jeu.
*/
@Override @Override
public void run() { public void run() {
// TODO: game logic here
if (!jeuEnCours || grille.estEnPause()) { if (!jeuEnCours || grille.estEnPause()) {
return; return;
} }

View File

@@ -1,53 +1,106 @@
package org.Models; package org.Models;
import javazoom.jl.player.Player; import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.InputStream; import java.io.InputStream;
public class Musique extends Thread { import javax.sound.sampled.AudioInputStream;
private Player player; import javax.sound.sampled.AudioSystem;
private volatile boolean stop = false; import javax.sound.sampled.Clip;
private volatile boolean enPause = false; import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public void run() { /**
* Classe Musique n'étant pas purement données métier, donc n'aurait normalement
* sa place dans le package Models. Il faudrait l'externaliser. Par manque de
* temps, suite à notre discussion, nous avons décidé de laisser la musique ici.
*/
public class Musique {
private Clip clip;
private long clipPositionPause = 0;
private boolean estEnPause = false;
private boolean estArrete = false;
private boolean estInitialise = false;
public Musique() {
initialiserClip();
}
/**
* Initialise le clip audio à partir du fichier TetrisMusic.wav.
*/
private void initialiserClip() {
try { try {
while (!stop) { InputStream audioSrc = getClass().getResourceAsStream("/TetrisMusic.wav");
if (!enPause) { if (audioSrc == null) {
try (InputStream musique = getClass().getResourceAsStream("/TetrisMusic.mp3")) {
if (musique == null) {
System.err.println("Erreur : le fichier musique.mp3 est introuvable.");
return; return;
} }
BufferedInputStream buffer = new BufferedInputStream(musique);
this.player = new Player(buffer); AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioSrc);
System.out.println("Lecture de la musique..."); clip = AudioSystem.getClip();
player.play(); clip.open(audioStream);
} catch (Exception e) { estInitialise = true;
System.err.println("Erreur lors de la lecture de la musique : " + e.getMessage());
clip.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP && !estEnPause && !estArrete) {
clip.setFramePosition(0);
clip.start();
} }
} else { });
Thread.sleep(1000); // Attendre 1 seconde avant de vérifier à nouveau
} System.out.println("Clip audio initialisé avec succès.");
} } catch (UnsupportedAudioFileException e) {
} catch (InterruptedException e) {
System.err.println("Musique interrompue : " + e.getMessage()); } catch (LineUnavailableException e) {
}
System.out.println("Fin du thread de musique."); } catch (IOException e) {
}
public void basculePlayPause() {
enPause = !enPause;
if (enPause && player != null) {
System.out.println("Musique en pause.");
player.close();
} }
} }
/**
* Joue la musique à partir du début.
*/
public void start() {
if (estInitialise && !estArrete) {
clip.setFramePosition(0);
clip.start();
estEnPause = false;
System.out.println("Lecture de la musique...");
}
System.out.println("Fin de musique.");
}
/**
* met en pause la musique. rejoue la musique à partir de la position actuelle.
*/
public void basculePlayPause() {
if (!estInitialise || estArrete)
return;
estEnPause = !estEnPause;
if (estEnPause) {
clipPositionPause = clip.getMicrosecondPosition();
clip.stop();
System.out.println("Musique en pause à la position " + clipPositionPause + " µs");
} else {
clip.setMicrosecondPosition(clipPositionPause);
clip.start();
System.out.println("Musique reprise à la position " + clipPositionPause + " µs");
}
}
/**
* Arrête la musique et libère les ressources.
*/
public void arreterMusique() { public void arreterMusique() {
if (estInitialise) {
estArrete = true;
clip.stop();
clip.close();
System.out.println("Arrêt de la musique."); System.out.println("Arrêt de la musique.");
stop = true;
if (player != null) {
player.close();
} }
} }
} }

View File

@@ -1,19 +1,33 @@
package org.Models; package org.Models;
/**
* Ordonnanceur exécute périodiquement une tâche {@link Runnable} dans un thread séparé.
*/
public class Ordonnanceur extends Thread { public class Ordonnanceur extends Thread {
Runnable runnable; Runnable runnable;
long pause; long pause;
/**
* Crée un nouvel ordonnanceur.
*
* @param runnable la tâche à exécuter périodiquement
* @param pause la durée de pause entre chaque exécution (en millisecondes)
*/
public Ordonnanceur(Runnable runnable, long pause) { public Ordonnanceur(Runnable runnable, long pause) {
this.runnable = runnable; this.runnable = runnable;
this.pause = pause; this.pause = pause;
} }
/**
* Arrête l'ordonnanceur en interrompant le thread.
*/
public void stopOrdonnanceur() { public void stopOrdonnanceur() {
interrupt(); interrupt();
} }
/**
* Exécute la tâche périodiquement tant que le thread n'est pas interrompu.
*/
@Override @Override
public void run() { public void run() {
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {

View File

@@ -0,0 +1,8 @@
package org.Models;
/**
* Enumère les orientations possibles pour la rotation d'une pièce dans le jeu Tetris.
*/
public enum Orientation {
SENSHORAIRE,
SENSANTIHORAIRE,
}

View File

@@ -1,5 +1,37 @@
package org.Models; package org.Models;
/**
* Interface représentant une pièce courante du jeu Tetris.
* Définit les méthodes pour obtenir et définir le motif de la pièce.
*/
public interface PieceCourante { public interface PieceCourante {
abstract public boolean[][] getMotif(); abstract public boolean[][] getMotif();
abstract public void setMotif(boolean[][] motif);
static boolean[][] rotationHoraire(boolean[][] motif) {
int taille = motif.length;
boolean[][] resultat = new boolean[taille][taille];
for (int i = 0; i < taille; i++) {
for (int j = 0; j < taille; j++) {
resultat[j][taille - 1 - i] = motif[i][j];
}
}
return resultat;
}
static boolean[][] rotationAntiHoraire(boolean[][] motif) {
int taille = motif.length;
boolean[][] resultat = new boolean[taille][taille];
for (int i = 0; i < taille; i++) {
for (int j = 0; j < taille; j++) {
resultat[taille - 1 - j][i] = motif[i][j];
}
}
return resultat;
}
} }

View File

@@ -2,10 +2,17 @@ package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce I du Tetris.
* La pièce I est une ligne de 4 blocs disposés verticalement par défaut.
*/
public class PieceI implements PieceCourante { public class PieceI implements PieceCourante {
private boolean[][] motif = new boolean[4][4]; private boolean[][] motif = new boolean[4][4];
/**
* Crée une nouvelle pièce I avec son motif initial.
*/
public PieceI() { public PieceI() {
motif[0][1] = true; motif[0][1] = true;
motif[1][1] = true; motif[1][1] = true;
@@ -13,8 +20,23 @@ private boolean[][] motif = new boolean[4][4];
motif[3][1] = true; motif[3][1] = true;
} }
/**
* Retourne le motif actuel de la pièce I.
*
* @return le motif sous forme de matrice de booleans
*/
@Override @Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce I.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -2,18 +2,41 @@ package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce J du Tetris.
* La pièce J est une ligne de 3 blocs disposés verticalement par défaut.
*/
public class PieceJ implements PieceCourante { public class PieceJ implements PieceCourante {
private boolean[][] motif = new boolean[3][3]; private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce J avec son motif initial.
*/
public PieceJ() { public PieceJ() {
motif[0][2] = true; motif[0][1] = true;
motif[1][2] = true; motif[1][1] = true;
motif[2][2] = true;
motif[2][1] = true; motif[2][1] = true;
motif[2][0] = true;
} }
/**
* Retourne le motif actuel de la pièce J.
*
* @return le motif sous forme de matrice de booleans
*/
@Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce J.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -2,19 +2,41 @@ package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce L du Tetris.
* La pièce L est une ligne de 3 blocs disposés verticalement par défaut.
*/
public class PieceL implements PieceCourante { public class PieceL implements PieceCourante {
private boolean[][] motif = new boolean[3][3]; private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce L avec son motif initial.
*/
public PieceL() { public PieceL() {
motif[0][0] = true; motif[0][1] = true;
motif[1][0] = true; motif[1][1] = true;
motif[2][0] = true;
motif[2][1] = true; motif[2][1] = true;
motif[2][2] = true;
} }
/**
* Retourne le motif actuel de la pièce L.
*
* @return le motif sous forme de matrice de booleans
*/
@Override @Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce L.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -2,9 +2,17 @@ package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce O du Tetris.
* La pièce O est une ligne de 4 blocs disposés verticalement par défaut.
*/
public class PieceO implements PieceCourante { public class PieceO implements PieceCourante {
private boolean[][] motif = new boolean[4][4]; private boolean[][] motif = new boolean[4][4];
/**
* Crée une nouvelle pièce O avec son motif initial.
*/
public PieceO() { public PieceO() {
motif[1][1] = true; motif[1][1] = true;
motif[1][2] = true; motif[1][2] = true;
@@ -12,8 +20,23 @@ private boolean[][] motif = new boolean[4][4];
motif[2][2] = true; motif[2][2] = true;
} }
/**
* Retourne le motif actuel de la pièce O.
*
* @return le motif sous forme de matrice de booleans
*/
@Override @Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce O.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -1,11 +1,17 @@
package org.Models.Pieces; package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce S du Tetris.
* La pièce S est une ligne de 3 blocs disposés verticalement par défaut.
*/
public class PieceS implements PieceCourante { public class PieceS implements PieceCourante {
private boolean[][] motif = new boolean[3][3]; private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce S avec son motif initial.
*/
public PieceS() { public PieceS() {
motif[0][1] = true; motif[0][1] = true;
motif[1][1] = true; motif[1][1] = true;
@@ -13,8 +19,23 @@ private boolean[][] motif = new boolean[3][3];
motif[2][2] = true; motif[2][2] = true;
} }
/**
* Retourne le motif actuel de la pièce S.
*
* @return le motif sous forme de matrice de booleans
*/
@Override @Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce S.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -2,10 +2,17 @@ package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce T du Tetris.
* La pièce S est une ligne de 3 blocs disposés verticalement par défaut.
*/
public class PieceT implements PieceCourante { public class PieceT implements PieceCourante {
private boolean[][] motif = new boolean[3][3]; private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce T avec son motif initial.
*/
public PieceT() { public PieceT() {
motif[0][1] = true; motif[0][1] = true;
motif[1][1] = true; motif[1][1] = true;
@@ -13,8 +20,23 @@ private boolean[][] motif = new boolean[3][3];
motif[1][2] = true; motif[1][2] = true;
} }
/**
* Retourne le motif actuel de la pièce T.
*
* @return le motif sous forme de matrice de booleans
*/
@Override @Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce T.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -1,11 +1,16 @@
package org.Models.Pieces; package org.Models.Pieces;
import org.Models.PieceCourante; import org.Models.PieceCourante;
/**
* Représente la pièce Z du Tetris.
* La pièce S est une ligne de 3 blocs disposés verticalement par défaut.
*/
public class PieceZ implements PieceCourante { public class PieceZ implements PieceCourante {
private boolean[][] motif = new boolean[3][3]; private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce Z avec son motif initial.
*/
public PieceZ() { public PieceZ() {
motif[0][2] = true; motif[0][2] = true;
motif[1][2] = true; motif[1][2] = true;
@@ -13,8 +18,23 @@ private boolean[][] motif = new boolean[3][3];
motif[2][1] = true; motif[2][1] = true;
} }
/**
* Retourne le motif actuel de la pièce Z.
*
* @return le motif sous forme de matrice de booleans
*/
@Override @Override
public boolean[][] getMotif() { public boolean[][] getMotif() {
return motif; return motif;
} }
/**
* Définit le motif de la pièce Z.
*
* @param motif la nouvelle matrice de booleans représentant le motif
*/
@Override
public void setMotif(boolean[][] motif) {
this.motif = motif;
}
} }

View File

@@ -1,10 +1,23 @@
package org.Models; package org.Views;
import java.awt.*;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;
/**
* LayoutManager personnalisé pour disposer les composants dans une grille carrée,
* centrée dans le conteneur parent, avec des cases de taille égale.
*/
public class GridLayoutCarre implements LayoutManager { public class GridLayoutCarre implements LayoutManager {
private int lignes; private int lignes;
private int colonnes; private int colonnes;
/**
* Crée un nouveau GridLayoutCarre avec le nombre de lignes et de colonnes spécifié.
*
* @param lignes nombre de lignes
* @param colonnes nombre de colonnes
*/
public GridLayoutCarre(int lignes, int colonnes) { public GridLayoutCarre(int lignes, int colonnes) {
this.lignes = lignes; this.lignes = lignes;
this.colonnes = colonnes; this.colonnes = colonnes;
@@ -29,6 +42,12 @@ public class GridLayoutCarre implements LayoutManager {
return parent.getMinimumSize(); return parent.getMinimumSize();
} }
/**
* Dispose les composants enfants dans une grille carrée centrée,
* chaque case ayant la même taille.
*
* @param parent le conteneur parent
*/
@Override @Override
public void layoutContainer(Container parent) { public void layoutContainer(Container parent) {
int largeur = parent.getWidth(); int largeur = parent.getWidth();

View File

@@ -1,24 +1,40 @@
package org.Views; package org.Views;
import org.Models.GridLayoutCarre;
import org.Models.Jeu; import org.Models.Jeu;
import org.Models.PieceCourante; import org.Models.PieceCourante;
import javax.swing.*; import java.awt.BorderLayout;
import java.awt.*; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class VueBandeauControle extends JPanel implements Observer { public class VueBandeauControle extends JPanel implements Observer {
private JLabel scoreLabel; private JLabel scoreLabel;
private JPanel nextPiecePanel; private JPanel nextPiecePanel;
private JButton pauseButton; private JButton pauseButton;
private JButton aideButton;
private JPanel[][] caseNextPiece = new JPanel[4][4]; private JPanel[][] caseNextPiece = new JPanel[4][4];
private JLabel nbLigneLabel; private JLabel nbLigneLabel;
private JButton quitterButton; private JButton quitterButton;
private Jeu jeu; private Jeu jeu;
private JButton relancerButton;
/**
* Crée une nouvelle instance de VueBandeauControle.
* @param jeu
*/
public VueBandeauControle(Jeu jeu) { public VueBandeauControle(Jeu jeu) {
this.jeu = jeu; this.jeu = jeu;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
@@ -26,13 +42,13 @@ public class VueBandeauControle extends JPanel implements Observer {
// setPreferredSize(); // setPreferredSize();
// SCORE // SCORE
scoreLabel = new JLabel("SCORE : 0"); scoreLabel = new JLabel("SCORE : " + jeu.getGrille().getScore());
scoreLabel.setForeground(Color.white); scoreLabel.setForeground(Color.white);
scoreLabel.setFont(new Font("Arial", Font.PLAIN, 16)); scoreLabel.setFont(new Font("Arial", Font.PLAIN, 16));
scoreLabel.setAlignmentX(Component.CENTER_ALIGNMENT); scoreLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
// NB LIGNE // NB LIGNE
nbLigneLabel = new JLabel("LIGNES : 0"); nbLigneLabel = new JLabel("LIGNES : " + jeu.getGrille().getNbLignesSupprimees());
nbLigneLabel.setForeground(Color.white); nbLigneLabel.setForeground(Color.white);
nbLigneLabel.setFont(new Font("Arial", Font.PLAIN, 16)); nbLigneLabel.setFont(new Font("Arial", Font.PLAIN, 16));
nbLigneLabel.setAlignmentX(Component.CENTER_ALIGNMENT); nbLigneLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
@@ -52,14 +68,33 @@ public class VueBandeauControle extends JPanel implements Observer {
pauseButton.setMargin(margeBoutton); pauseButton.setMargin(margeBoutton);
quitterButton.setPreferredSize(buttonSize); quitterButton.setPreferredSize(buttonSize);
quitterButton.setMargin(margeBoutton); quitterButton.setMargin(margeBoutton);
JPanel boutonsPanel = new JPanel(); JPanel hautBoutonsPanel = new JPanel();
boutonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 0)); // boutonsPanel.setLayout(new BoxLayout(boutonsPanel, BoxLayout.X_AXIS));
boutonsPanel.setOpaque(false); hautBoutonsPanel.setOpaque(false);
boutonsPanel.add(pauseButton); hautBoutonsPanel.add(pauseButton);
boutonsPanel.add(quitterButton); hautBoutonsPanel.add(Box.createRigidArea(new Dimension(10, 0)));
hautBoutonsPanel.add(quitterButton);
pauseButton.setFocusable(false); pauseButton.setFocusable(false);
quitterButton.setFocusable(false); quitterButton.setFocusable(false);
// Relancer button
relancerButton = new JButton("RESTART");
relancerButton.setPreferredSize(buttonSize);
relancerButton.setMargin(margeBoutton);
relancerButton.setFocusable(false);
// AIDE BUTTON
aideButton = new JButton("?");
aideButton.setToolTipText("AFFICHER L'AIDE");
aideButton.setPreferredSize(new Dimension(45, 20));
aideButton.setFocusable(false);
JPanel footerPanel = new JPanel(new BorderLayout());
footerPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 40));
footerPanel.setOpaque(false);
footerPanel.add(relancerButton, BorderLayout.WEST);
footerPanel.add(aideButton, BorderLayout.EAST);
add(Box.createVerticalStrut(20)); add(Box.createVerticalStrut(20));
add(scoreLabel); add(scoreLabel);
add(Box.createVerticalStrut(20)); add(Box.createVerticalStrut(20));
@@ -67,28 +102,50 @@ public class VueBandeauControle extends JPanel implements Observer {
add(Box.createVerticalStrut(20)); add(Box.createVerticalStrut(20));
add(nextPiecePanel); add(nextPiecePanel);
add(Box.createVerticalStrut(20)); add(Box.createVerticalStrut(20));
add(boutonsPanel); add(hautBoutonsPanel);
add(Box.createVerticalGlue()); // force le JPanel à prendre toute la hauteur
add(footerPanel);
// setVisible(true); // setVisible(true);
jeu.addObserver(this); jeu.addObserver(this);
} }
/**
* Met à jour le score affiché dans le bandeau de contrôle.
* @param score
*/
public void setScore(int score) { public void setScore(int score) {
scoreLabel.setText("Score: " + score); scoreLabel.setText("Score: " + score);
} }
/**
* Récupère le bouton de pause.
* @return
*/
public JButton getPauseButton() { public JButton getPauseButton() {
return pauseButton; return pauseButton;
} }
/**
* Récupère le bouton de quitter.
* @return
*/
public JButton getQuitterButton() { public JButton getQuitterButton() {
return quitterButton; return quitterButton;
} }
/**
* Récupère le panneau de la prochaine pièce.
* @return
*/
public JPanel getNextPiecePanel() { public JPanel getNextPiecePanel() {
return nextPiecePanel; return nextPiecePanel;
} }
/**
* Récupère le bouton de relancer.
* @return
*/
private void initierNextPiecePanel() { private void initierNextPiecePanel() {
nextPiecePanel.removeAll(); nextPiecePanel.removeAll();
nextPiecePanel.setLayout(new GridLayoutCarre(4, 4)); nextPiecePanel.setLayout(new GridLayoutCarre(4, 4));
@@ -109,6 +166,10 @@ public class VueBandeauControle extends JPanel implements Observer {
nextPiecePanel.repaint(); nextPiecePanel.repaint();
} }
/**
* Affiche la prochaine pièce dans le panneau de la prochaine pièce.
* @param piece
*/
public void afficherPieceSuivante(PieceCourante piece) { public void afficherPieceSuivante(PieceCourante piece) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
@@ -129,12 +190,33 @@ public class VueBandeauControle extends JPanel implements Observer {
nextPiecePanel.repaint(); nextPiecePanel.repaint();
} }
/**
* Récupère le bouton d'aide.
* @return
*/
public JButton getAideButton() {
return aideButton;
}
/**
* Récupère le bouton de relancer.
* @return
*/
public JButton getRelancerButton() {
return relancerButton;
}
/**
* Récupère le label du nombre de lignes.
* @return
*/
@Override @Override
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
if (o instanceof Jeu) { if (o instanceof Jeu) {
afficherPieceSuivante(jeu.getPieceSuivante()); afficherPieceSuivante(jeu.getPieceSuivante());
setScore(jeu.getGrille().getScore());
// TODO : setScore ?? nbLigneLabel.setText("LIGNES : " + jeu.getGrille().getNbLignesSupprimees());
scoreLabel.setText("SCORE : " + jeu.getGrille().getScore());
} }
} }
} }

View File

@@ -0,0 +1,55 @@
package org.Views;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
/**
* Fenêtre affichée à la fin de la partie, indiquant le score et proposant
* de quitter ou de rejouer.
*/
public class VueGameOver extends JFrame {
private JButton quitterButton;
private JButton rejouerButton;
/**
* Crée une nouvelle fenêtre de fin de partie.
*
* @param score le score obtenu par le joueur
* @param quitterListener action à effectuer lors du clic sur "Quitter"
* @param rejouerListener action à effectuer lors du clic sur "Rejouer"
*/
public VueGameOver(int score, ActionListener quitterListener, Runnable rejouerListener) {
setTitle("FIN DE PARTIE");
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// Fenetre de fin de partie
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
// Texte de fin de partie
JLabel messageFin = new JLabel("GAME OVER !", SwingConstants.CENTER);
messageFin.setFont(new Font("Arial", Font.BOLD, 32));
panel.add(messageFin, BorderLayout.NORTH);
// Texte de score
JLabel messageScore = new JLabel("Votre score : " + score, SwingConstants.CENTER);
messageScore.setFont(new Font("Arial", Font.PLAIN, 24));
panel.add(messageScore, BorderLayout.CENTER);
// Boutons
JPanel buttonPanel = new JPanel();
quitterButton = new JButton("Quitter");
rejouerButton = new JButton("Rejouer");
// mise en forme boutons
quitterButton.addActionListener(quitterListener);
rejouerButton.addActionListener(e -> {
dispose();
rejouerListener.run();
});
buttonPanel.add(quitterButton);
buttonPanel.add(rejouerButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
setContentPane(panel);
setVisible(true);
}
}

View File

@@ -1,15 +1,22 @@
package org.Views; package org.Views;
import javax.swing.*;
import org.Models.*;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.util.Observable; import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import org.Models.Grille;
import org.Models.Jeu;
import org.Models.Ordonnanceur;
import org.Models.PieceCourante;
/**
* Vue graphique de la grille de jeu Tetris.
* Affiche l'état de la grille, la pièce courante et gère la mise à jour de l'affichage.
*/
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class VueGrille extends JPanel implements Observer, Runnable { public class VueGrille extends JPanel implements Observer, Runnable {
private JPanel grillePanel; private JPanel grillePanel;
@@ -24,11 +31,19 @@ public class VueGrille extends JPanel implements Observer, Runnable {
private int nbColonnes; private int nbColonnes;
private JPanel[][] casesGrille; private JPanel[][] casesGrille;
/**
* Construit la vue de la grille à partir du modèle de grille et du jeu.
*
* @param grille le modèle de la grille
* @param jeu le modèle du jeu
*/
public VueGrille(Grille grille, Jeu jeu) { public VueGrille(Grille grille, Jeu jeu) {
this.grille = grille; this.grille = grille;
this.jeu = jeu; this.jeu = jeu;
this.nbLignes = grille.getNbLignes(); this.nbLignes = grille.getNbLignes();
this.nbColonnes = grille.getNbColonnes(); this.nbColonnes = grille.getNbColonnes();
this.nbLignes = grille.getNbLignes();
this.nbColonnes = grille.getNbColonnes();
setLayout(new BorderLayout()); setLayout(new BorderLayout());
initialiserVueGrille(); initialiserVueGrille();
@@ -39,6 +54,10 @@ public class VueGrille extends JPanel implements Observer, Runnable {
ordonnanceur.start(); ordonnanceur.start();
} }
/**
* Redimensionne les cases de la grille pour qu'elles restent carrées
* lors du redimensionnement du panneau.
*/
public void resizeCases() { public void resizeCases() {
int largeurPanel = grillePanel.getWidth(); int largeurPanel = grillePanel.getWidth();
int hauteurPanel = grillePanel.getHeight(); int hauteurPanel = grillePanel.getHeight();
@@ -63,6 +82,9 @@ public class VueGrille extends JPanel implements Observer, Runnable {
grillePanel.repaint(); grillePanel.repaint();
} }
/**
* Initialise l'affichage de la grille et crée les cases graphiques.
*/
private void initialiserVueGrille() { private void initialiserVueGrille() {
grillePanel = new JPanel(new GridLayoutCarre(nbLignes, nbColonnes)); // pour que les cases soient carrés grillePanel = new JPanel(new GridLayoutCarre(nbLignes, nbColonnes)); // pour que les cases soient carrés
add(grillePanel, BorderLayout.CENTER); add(grillePanel, BorderLayout.CENTER);
@@ -83,6 +105,9 @@ public class VueGrille extends JPanel implements Observer, Runnable {
} }
} }
/**
* Met à jour l'affichage de la grille et de la pièce courante.
*/
public synchronized void updateGrille() { public synchronized void updateGrille() {
for (int i = 0; i < nbLignes; i++) { for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) { for (int j = 0; j < nbColonnes; j++) {
@@ -122,10 +147,6 @@ public class VueGrille extends JPanel implements Observer, Runnable {
repaint(); repaint();
} }
private void afficherFinPartie() {
System.err.println("FIN PARTIE"); // TODO : gerer affichage ?
}
/** /**
* * Met à jour la vue de la grille lorsque l'état de la grille change. * * Met à jour la vue de la grille lorsque l'état de la grille change.
*/ */
@@ -135,10 +156,15 @@ public class VueGrille extends JPanel implements Observer, Runnable {
updateGrille(); updateGrille();
} }
if (o instanceof Jeu && !afficherFenetreFinPartie) { if (o instanceof Jeu) {
if (!this.jeu.jeuEnCours) { Jeu jeu = (Jeu) o;
if (!jeu.jeuEnCours && !afficherFenetreFinPartie) {
afficherFenetreFinPartie = true; afficherFenetreFinPartie = true;
afficherFinPartie(); new VueGameOver(
jeu.getGrille().getScore(),
e -> System.exit(0),
() -> jeu.reinitialiserPartie()
);
} }
} }
} }

View File

@@ -1,12 +1,19 @@
package org.Views; package org.Views;
import org.Models.*; import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.*; import java.awt.Toolkit;
import java.awt.*;
import java.awt.event.ComponentAdapter; import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import org.Models.Grille;
import org.Models.Jeu;
/**
* Fenêtre principale du jeu Tetris.
* Affiche la grille de jeu et le bandeau de contrôle.
*/
public class VueTetris extends JFrame { public class VueTetris extends JFrame {
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public static double tailleJFrameX = screenSize.getHeight() / 2; public static double tailleJFrameX = screenSize.getHeight() / 2;
@@ -14,9 +21,19 @@ public class VueTetris extends JFrame {
private VueGrille vueGrille; private VueGrille vueGrille;
private VueBandeauControle vueControle; private VueBandeauControle vueControle;
/**
* Construit la fenêtre principale du jeu Tetris.
*
* @param grille le modèle de la grille
* @param jeu le modèle du jeu
*/
public VueTetris(Grille grille, Jeu jeu) { public VueTetris(Grille grille, Jeu jeu) {
super("Tetris"); super("Tetris");
this.vueGrille = new VueGrille(grille, jeu);
this.vueControle = new VueBandeauControle(jeu);
// TetrisBandeauControleur controleur = new
// TetrisBandeauControleur(vueControle);
this.vueGrille = new VueGrille(grille, jeu); this.vueGrille = new VueGrille(grille, jeu);
this.vueControle = new VueBandeauControle(jeu); this.vueControle = new VueBandeauControle(jeu);
// TetrisBandeauControleur controleur = new // TetrisBandeauControleur controleur = new
@@ -39,12 +56,26 @@ public class VueTetris extends JFrame {
} }
}); });
// listener permettanbt de redimensionner les cases de la grille
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
vueGrille.resizeCases();
}
});
setVisible(true); setVisible(true);
vueGrille.resizeCases(); vueGrille.resizeCases();
vueGrille.resizeCases();
vueControle.afficherPieceSuivante(jeu.getPieceSuivante()); vueControle.afficherPieceSuivante(jeu.getPieceSuivante());
} }
/**
* Retourne la vue du bandeau de contrôle.
*
* @return la vue du bandeau de contrôle
*/
public VueBandeauControle getVueBandeauControle() { public VueBandeauControle getVueBandeauControle() {
return this.vueControle; return this.vueControle;
} }

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 214 KiB