diff --git a/app/src/main/java/org/App.java b/app/src/main/java/org/App.java index b4c4a7a..b881973 100644 --- a/app/src/main/java/org/App.java +++ b/app/src/main/java/org/App.java @@ -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); } } \ No newline at end of file diff --git a/app/src/main/java/org/Controllers/IO.java b/app/src/main/java/org/Controllers/IO.java new file mode 100644 index 0000000..8d12872 --- /dev/null +++ b/app/src/main/java/org/Controllers/IO.java @@ -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) { + } +} diff --git a/app/src/main/java/org/Models/Direction.java b/app/src/main/java/org/Models/Direction.java new file mode 100644 index 0000000..21587c6 --- /dev/null +++ b/app/src/main/java/org/Models/Direction.java @@ -0,0 +1,8 @@ +package org.Models; + +public enum Direction { + BAS, + GAUCHE, + DROITE, + TOUTENBAS, +} diff --git a/app/src/main/java/org/Models/Grille.java b/app/src/main/java/org/Models/Grille.java index 0d5767f..c007f3d 100644 --- a/app/src/main/java/org/Models/Grille.java +++ b/app/src/main/java/org/Models/Grille.java @@ -1,16 +1,144 @@ 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 motifPieceColoree) { + // int maxY = 0; + // for (Point caseColoree : motifPieceColoree) { + // if (caseColoree.y > maxY) { + // maxY = caseColoree.y; + // } + // } + + // return maxY; + // } + + // public int getMaxXPieceCouranteColoree(List 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 motifPieceCouranteColoriee() { + List casesColores = new ArrayList<>(); + + boolean[][] motif = pieceCourante.getMotif(); + for (int i = 0; i < motif.length; i++) { + for (int j = 0; j < motif[i].length; j++) { + if (motif[i][j]) { + casesColores.add(new Point(pieceCouranteX + j, pieceCouranteY + i)); + } + } + } + + return casesColores; + } + + 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 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; + } +} \ No newline at end of file diff --git a/app/src/main/java/org/Models/Jeu.java b/app/src/main/java/org/Models/Jeu.java index 25c029e..5bbe0bb 100644 --- a/app/src/main/java/org/Models/Jeu.java +++ b/app/src/main/java/org/Models/Jeu.java @@ -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(); + } } diff --git a/app/src/main/java/org/Models/Ordonnanceur.java b/app/src/main/java/org/Models/Ordonnanceur.java new file mode 100644 index 0000000..8a12876 --- /dev/null +++ b/app/src/main/java/org/Models/Ordonnanceur.java @@ -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; + } + } + } + } +} diff --git a/app/src/main/java/org/Models/PieceCourante.java b/app/src/main/java/org/Models/PieceCourante.java index 86d1530..185ed61 100644 --- a/app/src/main/java/org/Models/PieceCourante.java +++ b/app/src/main/java/org/Models/PieceCourante.java @@ -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(); } diff --git a/app/src/main/java/org/Models/PieceL.java b/app/src/main/java/org/Models/PieceL.java deleted file mode 100644 index 226a9b0..0000000 --- a/app/src/main/java/org/Models/PieceL.java +++ /dev/null @@ -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; - } -} diff --git a/app/src/main/java/org/Models/Pieces/PieceCarre.java b/app/src/main/java/org/Models/Pieces/PieceCarre.java new file mode 100644 index 0000000..49cb427 --- /dev/null +++ b/app/src/main/java/org/Models/Pieces/PieceCarre.java @@ -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; + } +} diff --git a/app/src/main/java/org/Models/Pieces/PieceL.java b/app/src/main/java/org/Models/Pieces/PieceL.java new file mode 100644 index 0000000..d5009fe --- /dev/null +++ b/app/src/main/java/org/Models/Pieces/PieceL.java @@ -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; + } +} diff --git a/app/src/main/java/org/Views/VueGrille.java b/app/src/main/java/org/Views/VueGrille.java index 7358e68..fedee60 100644 --- a/app/src/main/java/org/Views/VueGrille.java +++ b/app/src/main/java/org/Views/VueGrille.java @@ -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(); + } }