Compare commits

...

16 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
e93b1cfb64 fix: some fixes and actual L piece 2025-05-20 11:34:18 +02:00
8eafa03d77 refactor: split in init vueGrille initialization of vueGrille rather than in constructor 2025-05-20 11:29:49 +02:00
Morph01
a3854362c8 refactor: better achitecture and next piece preview fixed 2025-05-20 11:29:20 +02:00
Morph01
aa6ab95728 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
2025-05-20 11:28:35 +02:00
Morph01
85aafa8240 fix: piece generation 2025-05-20 11:25:34 +02:00
33 changed files with 1284 additions and 241 deletions

View File

@@ -0,0 +1,13 @@
arguments=--init-script /home/morph/.config/Code/User/globalStorage/redhat.java/1.42.0/config_linux/org.eclipse.osgi/58/0/.cp/gradle/init/init.gradle --init-script /home/morph/.config/Code/User/globalStorage/redhat.java/1.42.0/config_linux/org.eclipse.osgi/58/0/.cp/gradle/protobuf/init.gradle
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=/home/morph/.sdkman/candidates/java/23-tem
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true

View File

@@ -1,11 +1,15 @@
# Tetris
## 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:
- Slides de cours: https://partage.liris.cnrs.fr/index.php/s/7ZwqDcXbFrwsLPB
- Sujet du Tetris: https://partage.liris.cnrs.fr/index.php/s/wsyyzFDrRrjzCHw
![Diagramme activité](readme_files/activiteTetris.svg)
- Sujet du Tetris: https://partage.liris.cnrs.fr/index.php/s/wsyyzFDrRrjzCHw

View File

@@ -0,0 +1,13 @@
arguments=--init-script /home/morph/.cache/jdtls/config/org.eclipse.osgi/248/0/.cp/gradle/init/init.gradle
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(LOCAL_INSTALLATION(/home/morph/.sdkman/candidates/gradle/current))
connection.project.dir=..
eclipse.preferences.version=1
gradle.user.home=
java.home=/usr/lib/jvm/java-21-openjdk
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true

View File

@@ -0,0 +1,4 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
org.eclipse.jdt.core.compiler.compliance=21
org.eclipse.jdt.core.compiler.source=21

Binary file not shown.

Binary file not shown.

View File

@@ -23,9 +23,6 @@ dependencies {
// This dependency is used by the application.
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.

View File

@@ -1,6 +1,3 @@
/*
* This source file was generated by the Gradle 'init' task
*/
package org;
import org.Controllers.IO;
@@ -12,25 +9,29 @@ import org.Views.VueTetris;
public class App {
public String getGreeting() {
return "Hello World!";
}
public String getGreeting() {
return "Hello World!";
}
public static void main(String[] args) {
public static void main(String[] args) {
// Models
Grille grille = new Grille(20, 10);
Musique musique = new Musique();
musique.start();
Jeu jeu = new Jeu(grille,musique);
// Models
Grille grille = new Grille(20, 10);
Musique musique = new Musique();
musique.start();
Jeu jeu = new Jeu(grille, musique);
// Views
VueTetris vueTetris = new VueTetris(grille, jeu);
// Views
VueTetris vueTetris = new VueTetris(grille, jeu);
// Controllers
IO io = new IO(jeu);
vueTetris.addKeyListener(io);
// Controllers
IO io = new IO(jeu);
vueTetris.addKeyListener(io);
new TetrisBandeauControleur(vueTetris.getVueBandeauControle(), musique, grille);//Création d'un controleur de bandeau avec la musique instanciée
}
new TetrisBandeauControleur(vueTetris.getVueBandeauControle(), musique, grille, jeu);// Création d'un
// controleur de
// bandeau avec la
// musique
// instanciée
}
}

View File

@@ -5,19 +5,34 @@ import java.awt.event.KeyListener;
import org.Models.Direction;
import org.Models.Jeu;
import org.Models.Orientation;
/**
* Gère les entrées clavier pour contrôler le jeu.
*/
public class IO implements KeyListener {
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) {
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
public void keyPressed(KeyEvent e) {
if (jeu.getGrille().estEnPause()){
System.err.println("Grille est en pause");
if (jeu.getGrille().estEnPause()) {
return;
}
switch (e.getKeyCode()) {
@@ -35,16 +50,35 @@ public class IO implements KeyListener {
case KeyEvent.VK_SPACE:
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:
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
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
public void keyReleased(KeyEvent e) {
}

View File

@@ -1,48 +1,91 @@
package org.Controllers;
import org.Models.Grille;
import org.Models.Jeu;
import org.Models.Musique;
import org.Views.VueBandeauControle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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 {
private boolean partieEnPause = false;
private boolean partieTerminee = false;
private Musique musique;
private VueBandeauControle vueControle;
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.musique = musique;
this.grille = grille;
this.jeu = jeu;
// action play/pause
// Listener pour le bouton play/pause
this.vueControle.getPauseButton().addActionListener(e -> switchPlayPause());
// Listener pour le bouton quitter
this.vueControle.getQuitterButton().addActionListener(e -> {
System.out.println("Fermeture de l'application...");
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() {
if (partieTerminee) {
return;
}
partieEnPause = !partieEnPause;
grille.setEnPause(partieEnPause);
grille.setEnPause(partieEnPause);
musique.basculePlayPause();
vueControle.getPauseButton().setText(partieEnPause ? "PLAY" : "PAUSE");
System.out.println(partieEnPause ? "Partie en pause" : "Partie en cours");
}
public void setPartieTerminee() {
partieTerminee = true;
musique.arreterMusique();
vueControle.getPauseButton().setEnabled(false);
System.out.println("Partie terminée");
/**
* Affiche une page d'aide avec les commandes du jeu.
* Met la partie en pause pendant l'affichage de l'aide.
*/
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;
/**
* Enumère les directions possibles pour le déplacement d'une pièce dans le jeu Tetris.
*/
public enum Direction {
BAS,
GAUCHE,

View File

@@ -1,12 +1,20 @@
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 { // TODO: ?? implements Runnable {
public class Grille extends Observable {
private boolean[][] grille;
public int nbLignes;
public int nbColonnes;
@@ -16,6 +24,15 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
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;
@@ -23,6 +40,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
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++) {
@@ -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) {
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
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) {
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
return this.grille[i][j];
@@ -46,10 +80,20 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
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;
@@ -58,34 +102,49 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
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)) {
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceBas();
}
break;
case GAUCHE:
if (peuxBouger(direction)) {
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceGauche();
}
break;
case DROITE:
if (peuxBouger(direction)) {
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceDroite();
}
break;
case TOUTENBAS:
if (peuxBouger(direction)) {
if (peuxBouger(direction, this.motifPieceCouranteColoriee())) {
deplacerPieceToutEnBas();
}
default:
@@ -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() {
pieceCouranteY++;
@@ -100,6 +162,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers();
}
/**
* Déplace la pièce courante d'une case vers la gauche.
*/
private void deplacerPieceGauche() {
pieceCouranteX--;
@@ -107,6 +172,9 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers();
}
/**
* Déplace la pièce courante d'une case vers la droite.
*/
private void deplacerPieceDroite() {
pieceCouranteX++;
@@ -114,43 +182,38 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
notifyObservers();
}
/**
* Fait descendre la pièce courante jusqu'à ce qu'elle ne puisse plus descendre.
*/
private void deplacerPieceToutEnBas() {
while (peuxBouger(Direction.BAS)) {
while (peuxBouger(Direction.BAS, this.motifPieceCouranteColoriee())) {
deplacerPieceBas();
}
}
// TODO : ENLEVER ?
// public int getMaxYPieceCouranteColoree(List<Point> motifPieceColoree) {
// int maxY = 0;
// for (Point caseColoree : motifPieceColoree) {
// 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;
// }
/**
* 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<>();
@@ -166,7 +229,14 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
return casesColores;
}
public boolean peuxBouger(Direction direction) {
/**
* 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;
@@ -186,48 +256,314 @@ public class Grille extends Observable { // TODO: ?? implements Runnable {
break;
}
List<Point> motifPieceColoree = motifPieceCouranteColoriee();
for (Point caseColoree : motifPieceColoree) {
int newX = caseColoree.x + deltaX;
int newY = caseColoree.y + deltaY;
if (newX < 0 || newX >= nbColonnes || newY < 0 || newY >= nbLignes) {
return false;
}
if (grille[newY][newX]) {
return false;
}
}
return true;
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)) {
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();
}
}

View File

@@ -1,10 +1,25 @@
package org.Models;
/**
* Uniquement afin de se servir d'une paire de coordonées.
*/
import java.awt.Point;
import java.util.Observable;
import java.util.Random;
import org.Models.Pieces.PieceI;
import org.Models.Pieces.PieceJ;
import org.Models.Pieces.PieceL;
import org.Models.Pieces.PieceO;
import org.Models.Pieces.PieceS;
import org.Models.Pieces.PieceT;
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")
public class Jeu extends Observable implements Runnable {
private Grille grille;
@@ -14,30 +29,79 @@ public class Jeu extends Observable implements Runnable {
private PieceCourante pieceSuivante;
private int pieceSuivanteX;
private int pieceSuivanteY;
// TODO : remove test variable
public int test = 0;
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) {
this.grille = grille;
this.musique = musique;
this.musique = musique;
this.grille.setPieceCourante(getNouvellePiece());
this.pieceSuivante = getNouvellePiece();
this.ordonnanceur = new Ordonnanceur(this, 1000);
this.ordonnanceur.start();
}
/**
* Génère et retourne une nouvelle pièce aléatoire.
*
* @return une nouvelle pièce courante
*/
private PieceCourante getNouvellePiece() {
this.pieceSuivante = new PieceL();
this.pieceSuivanteX = 3;
this.pieceSuivanteY = 0;
return pieceSuivante;
Random random = new Random();
int randomiiii = random.nextInt(7);
PieceCourante nouvellePiece;
switch (randomiiii) {
case 0:
nouvellePiece = new PieceI();
break;
case 1:
nouvellePiece = new PieceL();
break;
case 2:
nouvellePiece = new PieceJ();
break;
case 3:
nouvellePiece = new PieceO();
break;
case 4:
nouvellePiece = new PieceS();
break;
case 5:
nouvellePiece = new PieceT();
break;
case 6:
nouvellePiece = new PieceZ();
break;
default:
nouvellePiece = new PieceL();
break;
}
return nouvellePiece;
}
/**
* Retourne la pièce suivante.
*
* @return la pièce suivante
*/
public PieceCourante getPieceSuivante() {
return pieceSuivante;
}
@@ -50,13 +114,24 @@ public class Jeu extends Observable implements Runnable {
return pieceSuivanteY;
}
/**
* Retourne la grille de jeu.
*
* @return la grille
*/
public Grille getGrille() {
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() {
for (Point caseColoree : this.grille.motifPieceCouranteColoriee()) {
if (this.grille.getCase(caseColoree.y, caseColoree.x)) {
finPartie();
return true;
}
}
@@ -64,6 +139,9 @@ public class Jeu extends Observable implements Runnable {
return false;
}
/**
* Termine la partie, arrête l'ordonnanceur et la musique.
*/
public void finPartie() {
this.jeuEnCours = false;
ordonnanceur.interrupt();
@@ -75,9 +153,50 @@ public class Jeu extends Observable implements Runnable {
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
public void run() {
// TODO: game logic here
if (!jeuEnCours || grille.estEnPause()) {
return;
}
@@ -91,7 +210,8 @@ public class Jeu extends Observable implements Runnable {
this.grille.deplacerPiece(Direction.BAS);
} else {
this.grille.fixerPiece();
this.grille.setPieceCourante(getNouvellePiece());
this.grille.setPieceCourante(pieceSuivante);
this.pieceSuivante = getNouvellePiece();
}
setChanged();

View File

@@ -1,53 +1,106 @@
package org.Models;
import javazoom.jl.player.Player;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Musique extends Thread {
private Player player;
private volatile boolean stop = false;
private volatile boolean enPause = false;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
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 {
while (!stop) {
if (!enPause) {
try (InputStream musique = getClass().getResourceAsStream("/TetrisMusic.mp3")) {
if (musique == null) {
System.err.println("Erreur : le fichier musique.mp3 est introuvable.");
return;
}
BufferedInputStream buffer = new BufferedInputStream(musique);
this.player = new Player(buffer);
System.out.println("Lecture de la musique...");
player.play();
} catch (Exception e) {
System.err.println("Erreur lors de la lecture de la musique : " + e.getMessage());
}
} else {
Thread.sleep(1000); // Attendre 1 seconde avant de vérifier à nouveau
}
InputStream audioSrc = getClass().getResourceAsStream("/TetrisMusic.wav");
if (audioSrc == null) {
return;
}
} catch (InterruptedException e) {
System.err.println("Musique interrompue : " + e.getMessage());
}
System.out.println("Fin du thread de musique.");
}
public void basculePlayPause() {
enPause = !enPause;
if (enPause && player != null) {
System.out.println("Musique en pause.");
player.close();
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioSrc);
clip = AudioSystem.getClip();
clip.open(audioStream);
estInitialise = true;
clip.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP && !estEnPause && !estArrete) {
clip.setFramePosition(0);
clip.start();
}
});
System.out.println("Clip audio initialisé avec succès.");
} catch (UnsupportedAudioFileException e) {
} catch (LineUnavailableException e) {
} catch (IOException e) {
}
}
public void arreterMusique() {
System.out.println("Arrêt de la musique.");
stop = true;
if (player != null) {
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() {
if (estInitialise) {
estArrete = true;
clip.stop();
clip.close();
System.out.println("Arrêt de la musique.");
}
}
}

View File

@@ -1,21 +1,33 @@
package org.Models;
/**
* Ordonnanceur exécute périodiquement une tâche {@link Runnable} dans un thread séparé.
*/
public class Ordonnanceur extends Thread {
Runnable runnable;
long pause;
private boolean estActif = true;
/**
* 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) {
this.runnable = runnable;
this.pause = pause;
}
/**
* Arrête l'ordonnanceur en interrompant le thread.
*/
public void stopOrdonnanceur() {
estActif = false;
interrupt();
}
/**
* Exécute la tâche périodiquement tant que le thread n'est pas interrompu.
*/
@Override
public void run() {
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,6 +1,37 @@
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 {
boolean[][] motif = new boolean[4][4];
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

@@ -1,18 +0,0 @@
package org.Models.Pieces;
import org.Models.PieceCourante;
public class PieceCarre implements PieceCourante {
public PieceCarre() {
motif[1][1] = true;
motif[1][2] = true;
motif[2][1] = true;
motif[2][2] = true;
}
@Override
public boolean[][] getMotif() {
return motif;
}
}

View File

@@ -2,8 +2,17 @@ package org.Models.Pieces;
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 {
private boolean[][] motif = new boolean[4][4];
/**
* Crée une nouvelle pièce I avec son motif initial.
*/
public PieceI() {
motif[0][1] = true;
motif[1][1] = true;
@@ -11,8 +20,23 @@ public class PieceI implements PieceCourante {
motif[3][1] = true;
}
/**
* Retourne le motif actuel de la pièce I.
*
* @return le motif sous forme de matrice de booleans
*/
@Override
public boolean[][] getMotif() {
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,16 +2,41 @@ package org.Models.Pieces;
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 {
private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce J avec son motif initial.
*/
public PieceJ() {
motif[0][2] = true;
motif[1][2] = true;
motif[2][2] = true;
motif[0][1] = true;
motif[1][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() {
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,18 +2,41 @@ package org.Models.Pieces;
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 PieceL(){
motif[1][0] = true;
motif[2][0] = true;
motif[3][0] = true;
motif[3][1] = true;
motif[3][2] = true;
private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce L avec son motif initial.
*/
public PieceL() {
motif[0][1] = true;
motif[1][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
public boolean[][] getMotif() {
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,16 +2,41 @@ package org.Models.Pieces;
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 {
private boolean[][] motif = new boolean[4][4];
/**
* Crée une nouvelle pièce O avec son motif initial.
*/
public PieceO() {
motif[0][1] = true;
motif[0][2] = true;
motif[1][1] = true;
motif[1][2] = true;
motif[2][1] = true;
motif[2][2] = true;
}
/**
* Retourne le motif actuel de la pièce O.
*
* @return le motif sous forme de matrice de booleans
*/
@Override
public boolean[][] getMotif() {
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,9 +1,17 @@
package org.Models.Pieces;
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 {
private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce S avec son motif initial.
*/
public PieceS() {
motif[0][1] = true;
motif[1][1] = true;
@@ -11,8 +19,23 @@ public class PieceS implements PieceCourante {
motif[2][2] = true;
}
/**
* Retourne le motif actuel de la pièce S.
*
* @return le motif sous forme de matrice de booleans
*/
@Override
public boolean[][] getMotif() {
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,8 +2,17 @@ package org.Models.Pieces;
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 {
private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce T avec son motif initial.
*/
public PieceT() {
motif[0][1] = true;
motif[1][1] = true;
@@ -11,8 +20,23 @@ public class PieceT implements PieceCourante {
motif[1][2] = true;
}
/**
* Retourne le motif actuel de la pièce T.
*
* @return le motif sous forme de matrice de booleans
*/
@Override
public boolean[][] getMotif() {
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,9 +1,16 @@
package org.Models.Pieces;
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 {
private boolean[][] motif = new boolean[3][3];
/**
* Crée une nouvelle pièce Z avec son motif initial.
*/
public PieceZ() {
motif[0][2] = true;
motif[1][2] = true;
@@ -11,8 +18,23 @@ public class PieceZ implements PieceCourante {
motif[2][1] = true;
}
/**
* Retourne le motif actuel de la pièce Z.
*
* @return le motif sous forme de matrice de booleans
*/
@Override
public boolean[][] getMotif() {
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;
import java.awt.*;
package org.Views;
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 {
private int lignes;
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) {
this.lignes = lignes;
this.colonnes = colonnes;
@@ -29,11 +42,17 @@ public class GridLayoutCarre implements LayoutManager {
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
public void layoutContainer(Container parent) {
int largeur = parent.getWidth();
int hauteur = parent.getHeight();
int tailleCase= Math.min(largeur / colonnes, hauteur / lignes);
int tailleCase = Math.min(largeur / colonnes, hauteur / lignes);
int offSetX = (largeur - tailleCase * colonnes) / 2;
int offSetY = (hauteur - tailleCase * lignes) / 2;
int index = 0;
@@ -51,4 +70,4 @@ public class GridLayoutCarre implements LayoutManager {
}
}
}
}

View File

@@ -1,32 +1,54 @@
package org.Views;
import org.Models.GridLayoutCarre;
import org.Models.Jeu;
import org.Models.PieceCourante;
import javax.swing.*;
import java.awt.*;
import java.awt.BorderLayout;
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.Observer;
public class VueBandeauControle extends JPanel {
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")
public class VueBandeauControle extends JPanel implements Observer {
private JLabel scoreLabel;
private JPanel nextPiecePanel;
private JButton pauseButton;
private JButton aideButton;
private JPanel[][] caseNextPiece = new JPanel[4][4];
private JLabel nbLigneLabel;
private JButton quitterButton;
private Jeu jeu;
private JButton relancerButton;
public VueBandeauControle() {
/**
* Crée une nouvelle instance de VueBandeauControle.
* @param jeu
*/
public VueBandeauControle(Jeu jeu) {
this.jeu = jeu;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBackground(Color.gray);
// setPreferredSize();
// SCORE
scoreLabel = new JLabel("SCORE : 0");
scoreLabel = new JLabel("SCORE : " + jeu.getGrille().getScore());
scoreLabel.setForeground(Color.white);
scoreLabel.setFont(new Font("Arial", Font.PLAIN, 16));
scoreLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
// NB LIGNE
nbLigneLabel = new JLabel("LIGNES : 0");
nbLigneLabel = new JLabel("LIGNES : " + jeu.getGrille().getNbLignesSupprimees());
nbLigneLabel.setForeground(Color.white);
nbLigneLabel.setFont(new Font("Arial", Font.PLAIN, 16));
nbLigneLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
@@ -46,14 +68,33 @@ public class VueBandeauControle extends JPanel {
pauseButton.setMargin(margeBoutton);
quitterButton.setPreferredSize(buttonSize);
quitterButton.setMargin(margeBoutton);
JPanel boutonsPanel = new JPanel();
boutonsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 0));
boutonsPanel.setOpaque(false);
boutonsPanel.add(pauseButton);
boutonsPanel.add(quitterButton);
JPanel hautBoutonsPanel = new JPanel();
// boutonsPanel.setLayout(new BoxLayout(boutonsPanel, BoxLayout.X_AXIS));
hautBoutonsPanel.setOpaque(false);
hautBoutonsPanel.add(pauseButton);
hautBoutonsPanel.add(Box.createRigidArea(new Dimension(10, 0)));
hautBoutonsPanel.add(quitterButton);
pauseButton.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(scoreLabel);
add(Box.createVerticalStrut(20));
@@ -61,27 +102,50 @@ public class VueBandeauControle extends JPanel {
add(Box.createVerticalStrut(20));
add(nextPiecePanel);
add(Box.createVerticalStrut(20));
add(boutonsPanel);
add(hautBoutonsPanel);
add(Box.createVerticalGlue()); // force le JPanel à prendre toute la hauteur
add(footerPanel);
// setVisible(true);
jeu.addObserver(this);
}
/**
* Met à jour le score affiché dans le bandeau de contrôle.
* @param score
*/
public void setScore(int score) {
scoreLabel.setText("Score: " + score);
}
/**
* Récupère le bouton de pause.
* @return
*/
public JButton getPauseButton() {
return pauseButton;
}
/**
* Récupère le bouton de quitter.
* @return
*/
public JButton getQuitterButton() {
return quitterButton;
}
/**
* Récupère le panneau de la prochaine pièce.
* @return
*/
public JPanel getNextPiecePanel() {
return nextPiecePanel;
}
/**
* Récupère le bouton de relancer.
* @return
*/
private void initierNextPiecePanel() {
nextPiecePanel.removeAll();
nextPiecePanel.setLayout(new GridLayoutCarre(4, 4));
@@ -102,20 +166,57 @@ public class VueBandeauControle extends JPanel {
nextPiecePanel.repaint();
}
/**
* Affiche la prochaine pièce dans le panneau de la prochaine pièce.
* @param piece
*/
public void afficherPieceSuivante(PieceCourante piece) {
boolean[][] motif = piece.getMotif();
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
caseNextPiece[i][j].setBackground(Color.WHITE);
}
}
boolean[][] motif = piece.getMotif();
for (int i = 0; i < motif.length; i++) {
for (int j = 0; j < motif[i].length; j++) {
if (motif[i][j]) {
caseNextPiece[i][j].setBackground(Color.RED);
} else {
caseNextPiece[i][j].setBackground(Color.WHITE);
}
}
}
nextPiecePanel.revalidate();
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
public void update(Observable o, Object arg) {
if (o instanceof Jeu) {
afficherPieceSuivante(jeu.getPieceSuivante());
setScore(jeu.getGrille().getScore());
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,17 +1,22 @@
package org.Views;
import javax.swing.*;
import org.Models.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.util.Observable;
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")
public class VueGrille extends JPanel implements Observer, Runnable {
private JPanel grillePanel;
@@ -26,29 +31,21 @@ public class VueGrille extends JPanel implements Observer, Runnable {
private int nbColonnes;
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) {
this.grille = grille;
this.jeu = jeu;
this.nbLignes = grille.getNbLignes();
this.nbColonnes = grille.getNbColonnes();
this.nbLignes = grille.getNbLignes();
this.nbColonnes = grille.getNbColonnes();
setLayout(new BorderLayout());
grillePanel = new JPanel(new GridLayoutCarre(nbLignes,nbColonnes)); //pour que les cases soient carrés
add(grillePanel, BorderLayout.CENTER);
casesGrille = new JPanel[nbLignes][nbColonnes];
for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) {
JPanel caseG = new JPanel(){
@Override
public Dimension getPreferredSize(){
return super.getPreferredSize();
}
};
caseG.setBackground(Color.WHITE);
caseG.setBorder(BorderFactory.createLineBorder(Color.GRAY));
casesGrille[i][j] = caseG;
grillePanel.add(caseG);
}
}
initialiserVueGrille();
grille.addObserver(this);
jeu.addObserver(this);
@@ -57,10 +54,14 @@ public class VueGrille extends JPanel implements Observer, Runnable {
ordonnanceur.start();
}
/**
* Redimensionne les cases de la grille pour qu'elles restent carrées
* lors du redimensionnement du panneau.
*/
public void resizeCases() {
int largeurPanel = grillePanel.getWidth();
int hauteurPanel = grillePanel.getHeight();
if (largeurPanel==0||hauteurPanel==0) {
if (largeurPanel == 0 || hauteurPanel == 0) {
return;
}
int taille = Math.min(largeurPanel, hauteurPanel);
@@ -71,7 +72,7 @@ public class VueGrille extends JPanel implements Observer, Runnable {
int tailleCaseX = largeurPanel / nbColonnes;
int tailleCaseY = hauteurPanel / nbLignes;
int tailleCase = Math.min(tailleCaseX, tailleCaseY);
Dimension taillePreferee=new Dimension(tailleCase, tailleCase);
Dimension taillePreferee = new Dimension(tailleCase, tailleCase);
for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) {
casesGrille[i][j].setPreferredSize(taillePreferee);
@@ -81,16 +82,32 @@ public class VueGrille extends JPanel implements Observer, Runnable {
grillePanel.repaint();
}
/**
* Initialise l'affichage de la grille et crée les cases graphiques.
*/
private void initialiserVueGrille() {
for (int i = 0; i < grille.getNbLignes(); i++) {
for (int j = 0; j < grille.getNbColonnes(); j++) {
JPanel caseG = new JPanel();
grillePanel = new JPanel(new GridLayoutCarre(nbLignes, nbColonnes)); // pour que les cases soient carrés
add(grillePanel, BorderLayout.CENTER);
casesGrille = new JPanel[nbLignes][nbColonnes];
for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) {
JPanel caseG = new JPanel() {
@Override
public Dimension getPreferredSize() {
return super.getPreferredSize();
}
};
caseG.setBackground(Color.WHITE);
caseG.setBorder(BorderFactory.createLineBorder(Color.GRAY));
casesGrille[i][j] = caseG;
grillePanel.add(caseG);
}
}
}
/**
* Met à jour l'affichage de la grille et de la pièce courante.
*/
public synchronized void updateGrille() {
for (int i = 0; i < nbLignes; i++) {
for (int j = 0; j < nbColonnes; j++) {
@@ -130,26 +147,27 @@ public class VueGrille extends JPanel implements Observer, Runnable {
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.
*/
@Override
public void update(Observable o, Object arg) {
if (o instanceof Grille) {
updateGrille();
}
public void update(Observable o, Object arg) {
if (o instanceof Grille) {
updateGrille();
}
if (o instanceof Jeu && !afficherFenetreFinPartie) {
if (!this.jeu.jeuEnCours) {
afficherFenetreFinPartie = true;
afficherFinPartie();
}
if (o instanceof Jeu) {
Jeu jeu = (Jeu) o;
if (!jeu.jeuEnCours && !afficherFenetreFinPartie) {
afficherFenetreFinPartie = true;
new VueGameOver(
jeu.getGrille().getScore(),
e -> System.exit(0),
() -> jeu.reinitialiserPartie()
);
}
}
}
/**
* * Met à jour la vue de la grille périodiquement selon l'ordonnaceur.

View File

@@ -1,13 +1,19 @@
package org.Views;
import org.Controllers.TetrisBandeauControleur;
import org.Models.*;
import javax.swing.*;
import java.awt.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
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 {
private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public static double tailleJFrameX = screenSize.getHeight() / 2;
@@ -15,12 +21,23 @@ public class VueTetris extends JFrame {
private VueGrille vueGrille;
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) {
super("Tetris");
this.vueGrille = new VueGrille(grille, jeu);
this.vueControle = new VueBandeauControle();
//TetrisBandeauControleur controleur = new TetrisBandeauControleur(vueControle);
this.vueControle = new VueBandeauControle(jeu);
// TetrisBandeauControleur controleur = new
// TetrisBandeauControleur(vueControle);
this.vueGrille = new VueGrille(grille, jeu);
this.vueControle = new VueBandeauControle(jeu);
// TetrisBandeauControleur controleur = new
// TetrisBandeauControleur(vueControle);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
@@ -31,7 +48,15 @@ public class VueTetris extends JFrame {
setSize((int) tailleJFrameX, (int) tailleJFrameY);
setLocationRelativeTo(null);
//listener permettanbt de redimensionner les cases de la grille
// listener permettanbt de redimensionner les cases de la grille
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
vueGrille.resizeCases();
}
});
// listener permettanbt de redimensionner les cases de la grille
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
@@ -41,11 +66,16 @@ public class VueTetris extends JFrame {
setVisible(true);
vueGrille.resizeCases();
vueGrille.resizeCases();
// Utilisation de la pièce L
vueControle.afficherPieceSuivante(jeu.getPieceSuivante());
}
/**
* Retourne la vue du bandeau de contrôle.
*
* @return la vue du bandeau de contrôle
*/
public VueBandeauControle getVueBandeauControle() {
return this.vueControle;
}

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 214 KiB