feat: add of an MVC base architecture for Tetris
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
*/
|
||||
package org;
|
||||
|
||||
import org.Controllers.IO;
|
||||
import org.Models.Grille;
|
||||
import org.Models.Jeu;
|
||||
import org.Views.VueGrille;
|
||||
|
||||
public class App {
|
||||
@@ -13,7 +15,16 @@ public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(new App().getGreeting());
|
||||
Grille grille = new Grille(10, 10);
|
||||
VueGrille vueGrille = new VueGrille(grille);
|
||||
|
||||
// Models
|
||||
Grille grille = new Grille(20, 10);
|
||||
Jeu jeu = new Jeu(grille);
|
||||
|
||||
// Views
|
||||
VueGrille vueGrille = new VueGrille(grille, jeu);
|
||||
|
||||
// Controllers
|
||||
IO io = new IO(jeu);
|
||||
vueGrille.addKeyListener(io);
|
||||
}
|
||||
}
|
||||
47
app/src/main/java/org/Controllers/IO.java
Normal file
47
app/src/main/java/org/Controllers/IO.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package org.Controllers;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
|
||||
import org.Models.Direction;
|
||||
import org.Models.Jeu;
|
||||
|
||||
public class IO implements KeyListener {
|
||||
|
||||
private Jeu jeu;
|
||||
|
||||
public IO(Jeu jeu) {
|
||||
this.jeu = jeu;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
case KeyEvent.VK_DOWN:
|
||||
jeu.getGrille().deplacerPiece(Direction.BAS);
|
||||
break;
|
||||
|
||||
case KeyEvent.VK_LEFT:
|
||||
jeu.getGrille().deplacerPiece(Direction.GAUCHE);
|
||||
break;
|
||||
|
||||
case KeyEvent.VK_RIGHT:
|
||||
jeu.getGrille().deplacerPiece(Direction.DROITE);
|
||||
break;
|
||||
|
||||
case KeyEvent.VK_SPACE:
|
||||
jeu.getGrille().deplacerPiece(Direction.TOUTENBAS);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
8
app/src/main/java/org/Models/Direction.java
Normal file
8
app/src/main/java/org/Models/Direction.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package org.Models;
|
||||
|
||||
public enum Direction {
|
||||
BAS,
|
||||
GAUCHE,
|
||||
DROITE,
|
||||
TOUTENBAS,
|
||||
}
|
||||
@@ -1,15 +1,143 @@
|
||||
package org.Models;
|
||||
|
||||
public class Grille {
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Observable;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Grille extends Observable { // TODO: ?? implements Runnable {
|
||||
private boolean[][] grille;
|
||||
public int nbLignes;
|
||||
public int nbColonnes;
|
||||
|
||||
private PieceCourante pieceCourante;
|
||||
private int pieceCouranteX;
|
||||
private int pieceCouranteY;
|
||||
|
||||
public Grille(int nbLignes, int nbColonnes) {
|
||||
this.nbLignes = nbLignes;
|
||||
this.nbColonnes = nbColonnes;
|
||||
this.grille = new boolean[nbLignes][nbColonnes];
|
||||
|
||||
for (int i = 0; i < nbLignes; i++) {
|
||||
for (int j = 0; j < nbColonnes; j++) {
|
||||
this.grille[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setCase(int i, int j, boolean value) {
|
||||
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
|
||||
this.grille[i][j] = value;
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getCase(int i, int j) {
|
||||
if (i >= 0 && i < nbLignes && j >= 0 && j < nbColonnes) {
|
||||
return this.grille[i][j];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public PieceCourante getPieceCourante() {
|
||||
return pieceCourante;
|
||||
}
|
||||
|
||||
public void setPieceCourante(PieceCourante pieceCourante) {
|
||||
this.pieceCourante = pieceCourante;
|
||||
this.pieceCouranteX = 3;
|
||||
this.pieceCouranteY = 0;
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
public int getPieceCouranteX() {
|
||||
return pieceCouranteX;
|
||||
}
|
||||
|
||||
public int getPieceCouranteY() {
|
||||
return pieceCouranteY;
|
||||
}
|
||||
|
||||
public void deplacerPiece(Direction direction) {
|
||||
|
||||
switch (direction) {
|
||||
case BAS:
|
||||
if (peuxBouger(direction)) {
|
||||
deplacerPieceBas();
|
||||
}
|
||||
break;
|
||||
case GAUCHE:
|
||||
if (peuxBouger(direction)) {
|
||||
deplacerPieceGauche();
|
||||
}
|
||||
break;
|
||||
case DROITE:
|
||||
if (peuxBouger(direction)) {
|
||||
deplacerPieceDroite();
|
||||
}
|
||||
break;
|
||||
case TOUTENBAS:
|
||||
if (peuxBouger(direction)) {
|
||||
deplacerPieceToutEnBas();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void deplacerPieceBas() {
|
||||
pieceCouranteY++;
|
||||
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
private void deplacerPieceGauche() {
|
||||
pieceCouranteX--;
|
||||
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
private void deplacerPieceDroite() {
|
||||
pieceCouranteX++;
|
||||
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
private void deplacerPieceToutEnBas() {
|
||||
while (peuxBouger(Direction.BAS)) {
|
||||
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;
|
||||
// }
|
||||
|
||||
public int getNbLignes() {
|
||||
return nbLignes;
|
||||
@@ -18,4 +146,74 @@ public class Grille {
|
||||
public int getNbColonnes() {
|
||||
return nbColonnes;
|
||||
}
|
||||
|
||||
public List<Point> motifPieceCouranteColoriee() {
|
||||
List<Point> casesColores = new ArrayList<>();
|
||||
|
||||
boolean[][] motif = pieceCourante.getMotif();
|
||||
for (int i = 0; i < motif.length; i++) {
|
||||
for (int j = 0; j < motif[i].length; j++) {
|
||||
if (motif[i][j]) {
|
||||
casesColores.add(new Point(pieceCouranteX + j, pieceCouranteY + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return casesColores;
|
||||
}
|
||||
|
||||
public boolean peuxBouger(Direction direction) {
|
||||
int deltaX = 0;
|
||||
int deltaY = 0;
|
||||
|
||||
switch (direction) {
|
||||
case GAUCHE:
|
||||
deltaX = -1;
|
||||
break;
|
||||
case DROITE:
|
||||
deltaX = 1;
|
||||
break;
|
||||
case BAS:
|
||||
deltaY = 1;
|
||||
break;
|
||||
case TOUTENBAS:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
List<Point> 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;
|
||||
}
|
||||
|
||||
public boolean[][] getGrille() {
|
||||
return grille;
|
||||
}
|
||||
|
||||
public void fixerPiece() {
|
||||
for (Point caseColoree : motifPieceCouranteColoriee()) {
|
||||
setCase(caseColoree.y, caseColoree.x, true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean doitFixerPiece() {
|
||||
if (!peuxBouger(Direction.BAS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,92 @@
|
||||
package org.Models;
|
||||
|
||||
public class Jeu {
|
||||
import java.awt.Point;
|
||||
import java.util.Observable;
|
||||
|
||||
import org.Models.Pieces.PieceCarre;
|
||||
import org.Models.Pieces.PieceL;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Jeu extends Observable implements Runnable {
|
||||
private Grille grille;
|
||||
private Ordonnanceur ordonnanceur;
|
||||
|
||||
private PieceCourante pieceSuivante;
|
||||
private int pieceSuivanteX;
|
||||
private int pieceSuivanteY;
|
||||
|
||||
// TODO : remove test variable
|
||||
public int test = 0;
|
||||
|
||||
public boolean jeuEnCours = true;
|
||||
|
||||
public Jeu(Grille grille) {
|
||||
this.grille = grille;
|
||||
|
||||
this.grille.setPieceCourante(getNouvellePiece());
|
||||
this.pieceSuivante = getNouvellePiece();
|
||||
|
||||
this.ordonnanceur = new Ordonnanceur(this, 1000);
|
||||
this.ordonnanceur.start();
|
||||
}
|
||||
|
||||
private PieceCourante getNouvellePiece() {
|
||||
this.pieceSuivante = new PieceL();
|
||||
this.pieceSuivanteX = 3;
|
||||
this.pieceSuivanteY = 0;
|
||||
return pieceSuivante;
|
||||
}
|
||||
|
||||
public PieceCourante getPieceSuivante() {
|
||||
return pieceSuivante;
|
||||
}
|
||||
|
||||
public int getPieceSuivanteX() {
|
||||
return pieceSuivanteX;
|
||||
}
|
||||
|
||||
public int getPieceSuivanteY() {
|
||||
return pieceSuivanteY;
|
||||
}
|
||||
|
||||
public Grille getGrille() {
|
||||
return grille;
|
||||
}
|
||||
|
||||
public boolean estFinPartie() {
|
||||
for (Point caseColoree : this.grille.motifPieceCouranteColoriee()) {
|
||||
if (this.grille.getCase(caseColoree.y, caseColoree.x)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void finPartie() {
|
||||
this.jeuEnCours = false;
|
||||
ordonnanceur.interrupt();
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// TODO: game logic here
|
||||
|
||||
if (estFinPartie()) {
|
||||
finPartie();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.grille.doitFixerPiece()) {
|
||||
this.grille.deplacerPiece(Direction.BAS);
|
||||
} else {
|
||||
this.grille.fixerPiece();
|
||||
this.grille.setPieceCourante(getNouvellePiece());
|
||||
}
|
||||
|
||||
setChanged();
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
32
app/src/main/java/org/Models/Ordonnanceur.java
Normal file
32
app/src/main/java/org/Models/Ordonnanceur.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package org.Models;
|
||||
|
||||
public class Ordonnanceur extends Thread {
|
||||
|
||||
Runnable runnable;
|
||||
long pause;
|
||||
private boolean estActif = true;
|
||||
|
||||
public Ordonnanceur(Runnable runnable, long pause) {
|
||||
this.runnable = runnable;
|
||||
this.pause = pause;
|
||||
}
|
||||
|
||||
public void stopOrdonnanceur() {
|
||||
estActif = false;
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (estActif) {
|
||||
try {
|
||||
Thread.sleep(pause);
|
||||
runnable.run();
|
||||
} catch (InterruptedException e) {
|
||||
if (!estActif) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,6 @@
|
||||
package org.Models;
|
||||
|
||||
public class PieceCourante {
|
||||
protected boolean[][] motif = new boolean[4][4];
|
||||
|
||||
PieceCourante() {
|
||||
initialiserPieceCourante();
|
||||
}
|
||||
|
||||
public void initialiserPieceCourante() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
this.motif[i][j] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
public interface PieceCourante {
|
||||
boolean[][] motif = new boolean[4][4];
|
||||
abstract public boolean[][] getMotif();
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package org.Models;
|
||||
|
||||
public class PieceL extends PieceCourante {
|
||||
|
||||
PieceL(){
|
||||
super();
|
||||
this.motif[1][0] = true;
|
||||
this.motif[2][0] = true;
|
||||
this.motif[3][0] = true;
|
||||
this.motif[3][1] = true;
|
||||
this.motif[3][2] = true;
|
||||
}
|
||||
}
|
||||
17
app/src/main/java/org/Models/Pieces/PieceCarre.java
Normal file
17
app/src/main/java/org/Models/Pieces/PieceCarre.java
Normal file
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean[][] getMotif() {
|
||||
return motif;
|
||||
}
|
||||
}
|
||||
19
app/src/main/java/org/Models/Pieces/PieceL.java
Normal file
19
app/src/main/java/org/Models/Pieces/PieceL.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.Models.Pieces;
|
||||
|
||||
import org.Models.PieceCourante;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[][] getMotif() {
|
||||
return motif;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,58 @@
|
||||
package org.Views;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.Border;
|
||||
|
||||
import org.Models.Grille;
|
||||
import org.Models.Jeu;
|
||||
import org.Models.Ordonnanceur;
|
||||
import org.Models.PieceCourante;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
|
||||
public class VueGrille extends JFrame {
|
||||
@SuppressWarnings("deprecation")
|
||||
public class VueGrille extends JFrame implements Observer, Runnable {
|
||||
private JPanel grillePanel;
|
||||
private int tailleJPanel = 1000;
|
||||
private Grille grille;
|
||||
private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
private double tailleJPanelX = screenSize.getHeight() / 4;
|
||||
private double tailleJPanelY = screenSize.getHeight() / 2;
|
||||
|
||||
public VueGrille(Grille grille) {
|
||||
private Grille grille;
|
||||
private Jeu jeu;
|
||||
private Ordonnanceur ordonnanceur;
|
||||
|
||||
private boolean afficherFenetreFinPartie = false;
|
||||
|
||||
public VueGrille(Grille grille, Jeu jeu) {
|
||||
this.grille = grille;
|
||||
this.jeu = jeu;
|
||||
grillePanel = new JPanel(new GridLayout(grille.getNbLignes(), grille.getNbColonnes()));
|
||||
setSize(tailleJPanel, tailleJPanel);
|
||||
setSize((int) tailleJPanelX, (int) tailleJPanelY);
|
||||
setContentPane(grillePanel);
|
||||
initialiserVueGrille();
|
||||
|
||||
grille.addObserver(this);
|
||||
jeu.addObserver(this);
|
||||
|
||||
this.ordonnanceur = new Ordonnanceur(this, 1000);
|
||||
ordonnanceur.start();
|
||||
}
|
||||
|
||||
private void initialiserVueGrille() {
|
||||
Border border = BorderFactory.createLineBorder(Color.BLACK);
|
||||
for (int i = 0; i < grille.getNbLignes(); i++) {
|
||||
for (int j = 0; j < grille.getNbColonnes(); j++) {
|
||||
JPanel caseG = new JPanel();
|
||||
caseG.setBorder(border);
|
||||
caseG.setBackground(Color.WHITE);
|
||||
grillePanel.add(caseG);
|
||||
}
|
||||
@@ -38,4 +62,98 @@ public class VueGrille extends JFrame {
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
setTitle("TETRIS");
|
||||
}
|
||||
|
||||
public synchronized void updateGrille() {
|
||||
for (int i = 0; i < grille.getNbLignes(); i++) {
|
||||
for (int j = 0; j < grille.getNbColonnes(); j++) {
|
||||
JPanel caseG = (JPanel) grillePanel.getComponent(i * grille.getNbColonnes() + j);
|
||||
if (grille.getGrille()[i][j]) {
|
||||
caseG.setBackground(Color.BLUE);
|
||||
} else {
|
||||
caseG.setBackground(Color.WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dessiner la pièce courante
|
||||
if (jeu != null) {
|
||||
PieceCourante piece = this.grille.getPieceCourante();
|
||||
int posX = this.grille.getPieceCouranteX();
|
||||
int posY = this.grille.getPieceCouranteY();
|
||||
|
||||
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]) {
|
||||
|
||||
int grilleX = posX + j;
|
||||
int grilleY = posY + i;
|
||||
|
||||
if (grilleY >= 0 && grilleY < grille.getNbLignes() &&
|
||||
grilleX >= 0 && grilleX < grille.getNbColonnes()) {
|
||||
JPanel caseG = (JPanel) grillePanel
|
||||
.getComponent(grilleY * grille.getNbColonnes() + grilleX);
|
||||
caseG.setBackground(Color.RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void afficherFinPartie() {
|
||||
JDialog gameOverDialog = new JDialog(this, "Partie terminée", true);
|
||||
gameOverDialog.setLayout(new BorderLayout());
|
||||
|
||||
gameOverDialog.setUndecorated(true);
|
||||
|
||||
gameOverDialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
|
||||
|
||||
JLabel gameOverLabel = new JLabel("GAME OVER", JLabel.CENTER);
|
||||
gameOverLabel.setFont(new Font("Arial", Font.BOLD, 50));
|
||||
gameOverLabel.setForeground(Color.RED);
|
||||
gameOverLabel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
gameOverDialog.add(gameOverLabel, BorderLayout.CENTER);
|
||||
|
||||
JButton quitButton = new JButton("Quitter");
|
||||
quitButton.addActionListener(e -> System.exit(0));
|
||||
|
||||
JPanel buttonPanel = new JPanel();
|
||||
buttonPanel.add(quitButton);
|
||||
gameOverDialog.add(buttonPanel, BorderLayout.SOUTH);
|
||||
|
||||
gameOverDialog.pack();
|
||||
gameOverDialog.setLocationRelativeTo(this);
|
||||
gameOverDialog.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 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();
|
||||
}
|
||||
|
||||
if (o instanceof Jeu && !afficherFenetreFinPartie) {
|
||||
if (!this.jeu.jeuEnCours) {
|
||||
afficherFenetreFinPartie = true;
|
||||
afficherFinPartie();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* * Met à jour la vue de la grille périodiquement selon l'ordonnaceur.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
updateGrille();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user