forked from menault/TD3_DEV51_Qualite_Algo
Compare commits
13 Commits
7206dca3ef
...
dick
| Author | SHA1 | Date | |
|---|---|---|---|
| e649d30b33 | |||
| 584e8dc3c6 | |||
| 82b4e0ffd1 | |||
| 9cb9088b81 | |||
| 77b3158262 | |||
| 0f7c1f6b71 | |||
| 87909bbe35 | |||
| 9d7c681ca3 | |||
| e6e0d4cf71 | |||
| ffbe1e232e | |||
| a3627d9023 | |||
| 2a8eac0720 | |||
| 0fa7ec527f |
32
Makefile
32
Makefile
@@ -15,21 +15,45 @@ JCFLAGS = -encoding UTF-8 -implicit:none -cp $(OUT) -d $(OUT)
|
||||
CLASSFILES = Pendu.class \
|
||||
Partie.class \
|
||||
Fenetre.class \
|
||||
Dessin.class
|
||||
Dessin.class \
|
||||
Mots.class \
|
||||
Event.class \
|
||||
LetterInputFilter.class \
|
||||
MenuDifficulte.class \
|
||||
Chronometre.class \
|
||||
Score.class
|
||||
|
||||
# Dépendances
|
||||
$(OUT)Pendu.class : $(IN)Pendu.java $(OUT)Partie.class $(OUT)Fenetre.class
|
||||
$(OUT)Pendu.class : $(IN)Pendu.java $(OUT)Partie.class $(OUT)Fenetre.class $(OUT)Event.class $(OUT)MenuDifficulte.class $(OUT)Score.class
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Partie.class : $(IN)Partie.java
|
||||
$(OUT)Partie.class : $(IN)Partie.java $(OUT)Mots.class
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Fenetre.class : $(IN)Fenetre.java $(OUT)Partie.class $(OUT)Dessin.class
|
||||
$(OUT)Fenetre.class : $(IN)Fenetre.java $(OUT)Partie.class $(OUT)Dessin.class $(OUT)Chronometre.class $(OUT)Score.class
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Dessin.class : $(IN)Dessin.java
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Mots.class : $(IN)Mots.java
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Event.class : $(IN)Event.java $(OUT)Fenetre.class $(OUT)LetterInputFilter.class
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)LetterInputFilter.class : $(IN)LetterInputFilter.java $(OUT)Fenetre.class
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)MenuDifficulte.class : $(IN)MenuDifficulte.java
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Chronometre.class : $(IN)Chronometre.java
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
$(OUT)Score.class : $(IN)Score.java
|
||||
$(JC) $(JCFLAGS) $<
|
||||
|
||||
# Commandes
|
||||
Pendu : $(OUT)Pendu.class
|
||||
|
||||
|
||||
83
src/Chronometre.java
Normal file
83
src/Chronometre.java
Normal file
@@ -0,0 +1,83 @@
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
* Composant chronomètre (mm:ss) pour le jeu du pendu.
|
||||
* - Démarre/stoppe/réinitialise le temps
|
||||
* - S'affiche comme une barre en haut de la fenêtre
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Adrien
|
||||
* Date : 08-10-2025
|
||||
* Licence :
|
||||
*/
|
||||
public class Chronometre extends JPanel implements ActionListener {
|
||||
|
||||
private final JLabel timeLabel = new JLabel("00:00", SwingConstants.CENTER);
|
||||
private final Timer timer; // tick chaque seconde
|
||||
private boolean running = false;
|
||||
private long startMillis = 0L;
|
||||
private long accumulatedMillis = 0L;
|
||||
|
||||
public Chronometre() {
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
timeLabel.setFont(new Font("Segoe UI", Font.BOLD, 20));
|
||||
add(timeLabel, BorderLayout.CENTER);
|
||||
|
||||
// Timer Swing (1s)
|
||||
timer = new Timer(1000, this);
|
||||
}
|
||||
|
||||
/** Démarre le chronomètre (ou reprend si en pause). */
|
||||
public void start() {
|
||||
if (!running) {
|
||||
running = true;
|
||||
startMillis = System.currentTimeMillis();
|
||||
timer.start();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/** Met en pause le chronomètre. */
|
||||
public void stop() {
|
||||
if (running) {
|
||||
running = false;
|
||||
accumulatedMillis += System.currentTimeMillis() - startMillis;
|
||||
timer.stop();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/** Remet le chronomètre à 00:00 (sans le relancer). */
|
||||
public void reset() {
|
||||
running = false;
|
||||
startMillis = 0L;
|
||||
accumulatedMillis = 0L;
|
||||
timer.stop();
|
||||
updateLabel(0L);
|
||||
}
|
||||
|
||||
/** Temps écoulé total (en millisecondes). */
|
||||
public long getElapsedMillis() {
|
||||
long now = System.currentTimeMillis();
|
||||
long runningMillis = running ? (now - startMillis) : 0L;
|
||||
return accumulatedMillis + runningMillis;
|
||||
}
|
||||
|
||||
/** Tick du timer : met à jour l'affichage. */
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
updateLabel(getElapsedMillis());
|
||||
}
|
||||
|
||||
// --- interne ---
|
||||
private void updateLabel(long elapsedMillis) {
|
||||
long totalSeconds = elapsedMillis / 1000;
|
||||
long minutes = totalSeconds / 60;
|
||||
long seconds = totalSeconds % 60;
|
||||
timeLabel.setText(String.format("%02d:%02d", minutes, seconds));
|
||||
}
|
||||
}
|
||||
124
src/Dessin.java
124
src/Dessin.java
@@ -2,87 +2,101 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* La classe <code>Dessin</code> gère uniquement le dessin du pendu
|
||||
* La classe <code>Dessin</code> gère uniquement le dessin du pendu,
|
||||
* avec révélation progressive en fonction du nombre d'erreurs (stage).
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Adrien
|
||||
* @version 1.1
|
||||
* author Adrien
|
||||
* Date : 08-10-2025
|
||||
* Licence :
|
||||
*/
|
||||
public class Dessin extends JPanel {
|
||||
|
||||
/** Nombre d'étapes max pour le personnage (hors potence). */
|
||||
public static final int MAX_STAGE = 6;
|
||||
|
||||
/** Étape actuelle (erreurs) : 0..6 */
|
||||
private int stage = 0;
|
||||
|
||||
// --- Constructeur ---
|
||||
public Dessin() {
|
||||
// Taille préférée pour s'intégrer dans Fenetre
|
||||
setPreferredSize(new Dimension(600, 350));
|
||||
setBackground(new Color(245, 245, 245));
|
||||
}
|
||||
|
||||
/** Définit l'étape (nb d'erreurs) et redessine. */
|
||||
public void setStage(int newStage) {
|
||||
int clamped = Math.max(0, Math.min(newStage, MAX_STAGE));
|
||||
if (clamped != this.stage) {
|
||||
this.stage = clamped;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public int getStage() { return stage; }
|
||||
|
||||
// --- Dessin principal ---
|
||||
@Override
|
||||
protected void paintComponent(Graphics graphics) {
|
||||
super.paintComponent(graphics);
|
||||
|
||||
// Anti-aliasing pour des traits plus doux
|
||||
Graphics2D graphics2D = (Graphics2D) graphics.create();
|
||||
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics2D.setStroke(new BasicStroke(3f));
|
||||
graphics2D.setColor(Color.DARK_GRAY);
|
||||
Graphics2D g2 = (Graphics2D) graphics.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g2.setStroke(new BasicStroke(3f));
|
||||
g2.setColor(Color.DARK_GRAY);
|
||||
|
||||
// Repères et proportions
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int marginPixels = Math.min(width, height) / 12; // marge proportionnelle
|
||||
int margin = Math.min(width, height) / 12;
|
||||
|
||||
// Potence : socle
|
||||
int baseYCoordinate = height - marginPixels;
|
||||
graphics2D.drawLine(marginPixels, baseYCoordinate, width / 2, baseYCoordinate);
|
||||
|
||||
// Montant vertical
|
||||
int postXCoordinate = marginPixels + (width / 12);
|
||||
graphics2D.drawLine(postXCoordinate, baseYCoordinate, postXCoordinate, marginPixels);
|
||||
|
||||
// Traverse horizontale
|
||||
// Potence (toujours affichée)
|
||||
int baseY = height - margin;
|
||||
g2.drawLine(margin, baseY, width / 2, baseY); // socle
|
||||
int postX = margin + (width / 12);
|
||||
g2.drawLine(postX, baseY, postX, margin); // montant
|
||||
int beamLength = width / 3;
|
||||
graphics2D.drawLine(postXCoordinate, marginPixels, postXCoordinate + beamLength, marginPixels);
|
||||
g2.drawLine(postX, margin, postX + beamLength, margin); // traverse
|
||||
g2.drawLine(postX, margin + height / 10, postX + width / 12, margin); // renfort
|
||||
|
||||
// Renfort diagonal
|
||||
graphics2D.drawLine(postXCoordinate, marginPixels + height / 10, postXCoordinate + width / 12, marginPixels);
|
||||
// Corde (toujours)
|
||||
int ropeX = postX + beamLength;
|
||||
int ropeTopY = margin;
|
||||
int ropeBottomY = margin + height / 12;
|
||||
g2.drawLine(ropeX, ropeTopY, ropeX, ropeBottomY);
|
||||
|
||||
// Corde
|
||||
int ropeXCoordinate = postXCoordinate + beamLength;
|
||||
int ropeTopYCoordinate = marginPixels;
|
||||
int ropeBottomYCoordinate = marginPixels + height / 12;
|
||||
graphics2D.drawLine(ropeXCoordinate, ropeTopYCoordinate, ropeXCoordinate, ropeBottomYCoordinate);
|
||||
// Géométrie du personnage
|
||||
int headR = Math.min(width, height) / 16;
|
||||
int headCX = ropeX;
|
||||
int headCY = ropeBottomY + headR;
|
||||
int bodyTopY = headCY + headR;
|
||||
int bodyBottomY = bodyTopY + height / 6;
|
||||
int armSpan = width / 10;
|
||||
int shouldersY = bodyTopY + height / 24;
|
||||
int legSpan = width / 12;
|
||||
|
||||
// Personnage : tête
|
||||
int headRadiusPixels = Math.min(width, height) / 16;
|
||||
int headCenterX = ropeXCoordinate;
|
||||
int headCenterY = ropeBottomYCoordinate + headRadiusPixels;
|
||||
graphics2D.drawOval(headCenterX - headRadiusPixels, headCenterY - headRadiusPixels, headRadiusPixels * 2, headRadiusPixels * 2);
|
||||
|
||||
// Corps
|
||||
int bodyTopYCoordinate = headCenterY + headRadiusPixels;
|
||||
int bodyBottomYCoordinate = bodyTopYCoordinate + height / 6;
|
||||
graphics2D.drawLine(headCenterX, bodyTopYCoordinate, headCenterX, bodyBottomYCoordinate);
|
||||
|
||||
// Bras
|
||||
int armSpanPixels = width / 10;
|
||||
int shouldersYCoordinate = bodyTopYCoordinate + height / 24;
|
||||
graphics2D.drawLine(headCenterX, shouldersYCoordinate, headCenterX - armSpanPixels, shouldersYCoordinate + height / 20);
|
||||
graphics2D.drawLine(headCenterX, shouldersYCoordinate, headCenterX + armSpanPixels, shouldersYCoordinate + height / 20);
|
||||
|
||||
// Jambes
|
||||
int legSpanPixels = width / 12;
|
||||
graphics2D.drawLine(headCenterX, bodyBottomYCoordinate, headCenterX - legSpanPixels, bodyBottomYCoordinate + height / 8);
|
||||
graphics2D.drawLine(headCenterX, bodyBottomYCoordinate, headCenterX + legSpanPixels, bodyBottomYCoordinate + height / 8);
|
||||
|
||||
graphics2D.dispose();
|
||||
// Étapes du personnage
|
||||
if (stage >= 1) { // tête
|
||||
g2.drawOval(headCX - headR, headCY - headR, headR * 2, headR * 2);
|
||||
}
|
||||
if (stage >= 2) { // corps
|
||||
g2.drawLine(headCX, bodyTopY, headCX, bodyBottomY);
|
||||
}
|
||||
if (stage >= 3) { // bras gauche
|
||||
g2.drawLine(headCX, shouldersY, headCX - armSpan, shouldersY + height / 20);
|
||||
}
|
||||
if (stage >= 4) { // bras droit
|
||||
g2.drawLine(headCX, shouldersY, headCX + armSpan, shouldersY + height / 20);
|
||||
}
|
||||
if (stage >= 5) { // jambe gauche
|
||||
g2.drawLine(headCX, bodyBottomY, headCX - legSpan, bodyBottomY + height / 8);
|
||||
}
|
||||
if (stage >= 6) { // jambe droite
|
||||
g2.drawLine(headCX, bodyBottomY, headCX + legSpan, bodyBottomY + height / 8);
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
// Affichage
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
public String toString() { return ""; }
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ public class Fenetre {
|
||||
private JLabel wordLabel;
|
||||
private JTextField letterInput;
|
||||
private JButton sendButton;
|
||||
private JPanel drawZone; // instance de Dessin
|
||||
private JPanel drawZone;
|
||||
private Chronometre chronometre;
|
||||
private JLabel scoreLabel;
|
||||
|
||||
// --- Constructeur ---
|
||||
public Fenetre() {
|
||||
@@ -32,6 +34,17 @@ public class Fenetre {
|
||||
root.setLayout(new BoxLayout(root, BoxLayout.Y_AXIS));
|
||||
window.setContentPane(root);
|
||||
|
||||
// Barre supérieure : chrono + score
|
||||
JPanel topBar = new JPanel(new BorderLayout());
|
||||
chronometre = new Chronometre();
|
||||
topBar.add(chronometre, BorderLayout.CENTER);
|
||||
scoreLabel = new JLabel("Score : " + Score.BASE, SwingConstants.RIGHT);
|
||||
scoreLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 12));
|
||||
scoreLabel.setFont(new Font("Segoe UI", Font.BOLD, 16));
|
||||
topBar.add(scoreLabel, BorderLayout.EAST);
|
||||
root.add(topBar);
|
||||
chronometre.start(); // démarrage automatique
|
||||
|
||||
drawZone = new Dessin();
|
||||
root.add(drawZone);
|
||||
|
||||
@@ -84,15 +97,6 @@ public class Fenetre {
|
||||
public JButton getSendButton() { return sendButton; }
|
||||
public JLabel getWordLabel() { return wordLabel; }
|
||||
public JPanel getDrawZone() { return drawZone; }
|
||||
|
||||
// --- Méthode principale de test ---
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Fenetre f = new Fenetre();
|
||||
// On passe le handler directement ici (pas de setOnLetterSubmitted)
|
||||
new Event(f, ch ->
|
||||
JOptionPane.showMessageDialog(f.getWindow(), "Lettre reçue : " + ch + " (sans logique de jeu)")
|
||||
);
|
||||
});
|
||||
}
|
||||
public Chronometre getChronometre() { return chronometre; }
|
||||
public JLabel getScoreLabel() { return scoreLabel; }
|
||||
}
|
||||
75
src/MenuDifficulte.java
Normal file
75
src/MenuDifficulte.java
Normal file
@@ -0,0 +1,75 @@
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Fenêtre de sélection de la difficulté (Facile / Moyen / Difficile).
|
||||
* Notifie le choix via un Consumer<String> fourni au constructeur.
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Adrien
|
||||
* Date : 08-10-2025
|
||||
* Licence :
|
||||
*/
|
||||
public class MenuDifficulte implements ActionListener {
|
||||
|
||||
private final JFrame frame;
|
||||
private final Consumer<String> onDifficultyChosen;
|
||||
|
||||
/** Construit la fenêtre et prépare les boutons. */
|
||||
public MenuDifficulte(Consumer<String> onDifficultyChosen) {
|
||||
this.onDifficultyChosen = onDifficultyChosen;
|
||||
|
||||
frame = new JFrame("Jeu du Pendu — Choix de difficulté");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setSize(400, 250);
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setLayout(new BorderLayout(0, 10));
|
||||
|
||||
JLabel title = new JLabel("Choisissez une difficulté", SwingConstants.CENTER);
|
||||
title.setFont(new Font("Segoe UI", Font.BOLD, 18));
|
||||
title.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 10));
|
||||
frame.add(title, BorderLayout.NORTH);
|
||||
|
||||
JPanel buttonsPanel = new JPanel(new GridLayout(1, 3, 10, 10));
|
||||
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
|
||||
JButton easyBtn = new JButton("Facile");
|
||||
JButton mediumBtn = new JButton("Moyen");
|
||||
JButton hardBtn = new JButton("Difficile");
|
||||
|
||||
easyBtn.setActionCommand("Facile");
|
||||
mediumBtn.setActionCommand("Moyen");
|
||||
hardBtn.setActionCommand("Difficile");
|
||||
|
||||
easyBtn.addActionListener(this);
|
||||
mediumBtn.addActionListener(this);
|
||||
hardBtn.addActionListener(this);
|
||||
|
||||
buttonsPanel.add(easyBtn);
|
||||
buttonsPanel.add(mediumBtn);
|
||||
buttonsPanel.add(hardBtn);
|
||||
frame.add(buttonsPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/** Affiche la fenêtre. */
|
||||
public void show() { frame.setVisible(true); }
|
||||
|
||||
/** Ferme la fenêtre. */
|
||||
public void close() { frame.dispose(); }
|
||||
|
||||
/** Accès optionnel au JFrame. */
|
||||
public JFrame getFrame() { return frame; }
|
||||
|
||||
/** Réception des clics sur les boutons. */
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
String difficulty = actionEvent.getActionCommand(); // "Facile" | "Moyen" | "Difficile"
|
||||
frame.dispose();
|
||||
if (onDifficultyChosen != null) {
|
||||
onDifficultyChosen.accept(difficulty);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/Mots.java
Normal file
58
src/Mots.java
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
/**
|
||||
* La classe <code>Mots</code>
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Aurélien
|
||||
* Date : 08-10-25
|
||||
* Licence :
|
||||
*/
|
||||
public final class Mots {
|
||||
//Attributs
|
||||
public static final short dictionarysize = 32 ;
|
||||
public static final String[] dictionary = {
|
||||
"Magnifique",
|
||||
"Etoile",
|
||||
"Voyage",
|
||||
"Biscuit",
|
||||
"Refrigerateur",
|
||||
"Courage",
|
||||
"Avion",
|
||||
"Explorateur",
|
||||
"Montagne",
|
||||
"Philosophie",
|
||||
"Lumiere",
|
||||
"Ethernet",
|
||||
"Architecture",
|
||||
"Ocean",
|
||||
"Liberte",
|
||||
"Aventure",
|
||||
"Cerise",
|
||||
"Harmonieux",
|
||||
"Informatique",
|
||||
"Pluie",
|
||||
"Equilibriste",
|
||||
"Papillon",
|
||||
"Saisons",
|
||||
"Liberte",
|
||||
"Alphabet",
|
||||
"Musique",
|
||||
"Translucent",
|
||||
"Passion",
|
||||
"Etreindre",
|
||||
"Poetique",
|
||||
"Serenite",
|
||||
"Révolution"
|
||||
};
|
||||
|
||||
//Constructeur
|
||||
private Mots() { //N'a pas pour but d'être instanciée
|
||||
throw new UnsupportedOperationException("The \"Fichier\" class cannot be instanced !");
|
||||
}
|
||||
//Méthodes
|
||||
|
||||
//Affichage
|
||||
public String toString() {
|
||||
return "" ;
|
||||
}
|
||||
}
|
||||
121
src/Partie.java
121
src/Partie.java
@@ -1,23 +1,136 @@
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* La classe <code>Partie</code>
|
||||
*
|
||||
* @version
|
||||
* @author
|
||||
* Date :
|
||||
* @version 0.2
|
||||
* @author Aurélien
|
||||
* Date : 08-10-25
|
||||
* Licence :
|
||||
*/
|
||||
public class Partie {
|
||||
//Contantes
|
||||
private static final byte REMAININGTRY = 6 ;
|
||||
private static final byte CARACTERCODESHIFT = 65 ; //Décalage ASCI > 'A'
|
||||
|
||||
//Attributs
|
||||
private char[] secretword ;
|
||||
private byte wordsize ;
|
||||
private boolean[] foundletters ;
|
||||
private boolean[] entriesletters = new boolean[26] ; //Pseudo Alphabée
|
||||
private byte remainingtry = REMAININGTRY ;
|
||||
|
||||
//Constructeur
|
||||
public Partie() {
|
||||
|
||||
this.secretword = generateSecretWord() ;
|
||||
this.wordsize = (byte) secretword.length ;
|
||||
this.foundletters = new boolean[wordsize] ;
|
||||
}
|
||||
//Méthodes
|
||||
public char[] getSecretWord() {
|
||||
return this.secretword ;
|
||||
}
|
||||
|
||||
public boolean[] getFoundLetters() {
|
||||
return this.foundletters ;
|
||||
}
|
||||
|
||||
public byte getRemainingTry() {
|
||||
return this.remainingtry ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Vérifie l'état de la partie en cours.
|
||||
*
|
||||
* @return true si le jeu est fini.
|
||||
*/
|
||||
public boolean gameIsEnding() {
|
||||
if(this.remainingtry <= 0){
|
||||
return true ;
|
||||
}else if(wordIsFound()){
|
||||
return true ;
|
||||
}else{
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la lettre reçu n'a pas déjà été joué puis, met à jour le tableau "entriesletters" et
|
||||
* "foundletters" le cas échéant.
|
||||
*
|
||||
* @return true si la lettre était déjà présente.
|
||||
*/
|
||||
public boolean isAlreadyEntries(char letter) {
|
||||
short caractercode = (short) letter ; //Récupération du code du caractère
|
||||
if(this.entriesletters[caractercode-CARACTERCODESHIFT]){
|
||||
this.remainingtry-- ; //Décrément des essais
|
||||
return true ;
|
||||
}else{
|
||||
boolean isfind = false ;
|
||||
for(byte i = 0 ; i < this.wordsize ; i++){ //Parcours du "secretword"
|
||||
if(this.secretword[i] == letter){
|
||||
this.foundletters[i] = true ;
|
||||
isfind = true ;
|
||||
}
|
||||
}
|
||||
if(isfind == false){
|
||||
this.remainingtry-- ; //Décrément des essais
|
||||
}
|
||||
this.entriesletters[caractercode-CARACTERCODESHIFT] = true ; //Ajout au tableau des lettres jouées
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un mot à partir d'un grand dictionnaire (enfin en principe).
|
||||
*
|
||||
* @return le mot généré.
|
||||
*/
|
||||
private char[] generateSecretWord() {
|
||||
Random random = new Random();
|
||||
byte grain = (byte) random.nextInt(Mots.dictionarysize);
|
||||
char[] word = Mots.dictionary[grain].toUpperCase().toCharArray();
|
||||
return word ;
|
||||
}
|
||||
|
||||
private boolean wordIsFound() {
|
||||
for(byte i = 0 ; i < this.wordsize ; i++){ //Parcours du "secretword"
|
||||
if(!this.foundletters[i]){ //Si une lettre n'est pas trouvé
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
return true ;
|
||||
}
|
||||
|
||||
|
||||
//Affichage
|
||||
public String toString() {
|
||||
return "" ;
|
||||
}
|
||||
|
||||
//Tests
|
||||
public static void main(String[] args){
|
||||
char[] test = {'E','O','M','I','E','D','A','Z','N','L','C','R','P','H','T','S'};
|
||||
byte size = (byte) test.length ;
|
||||
boolean status ;
|
||||
|
||||
Partie game = new Partie();
|
||||
System.out.println("Trick > " + String.valueOf(game.secretword) + "\n");
|
||||
for(byte i = 0 ; i < size && !game.gameIsEnding() ; i++){
|
||||
System.out.println("Essais restants : " + game.getRemainingTry());
|
||||
status = game.isAlreadyEntries(test[i]);
|
||||
for(byte l = 0 ; l < game.wordsize ; l++){ //Parcours du "secretword"
|
||||
if(game.foundletters[l] == true){
|
||||
System.out.print(game.getSecretWord()[l] + " ");
|
||||
}else{
|
||||
System.out.print("_ ");
|
||||
}
|
||||
}
|
||||
System.out.println(""); //Lisibilité
|
||||
//System.out.println("Lettres : " + game.entriesletters);
|
||||
}
|
||||
System.out.println("Essais restants : " + game.getRemainingTry());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
131
src/Pendu.java
131
src/Pendu.java
@@ -1,14 +1,135 @@
|
||||
import javax.swing.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* La classe <code>Pendu</code>
|
||||
* Point d'entrée : affiche le menu de difficulté puis lance la fenêtre du jeu.
|
||||
* Lie Fenetre (vue) et Partie (logique) via un handler.
|
||||
* Met à jour le dessin du pendu à chaque erreur.
|
||||
*
|
||||
* @version
|
||||
* @author
|
||||
* Date :
|
||||
* @version 1.4
|
||||
* author Adrien
|
||||
* Date : 08-10-2025
|
||||
* Licence :
|
||||
*/
|
||||
public class Pendu {
|
||||
public static void main(String[] args){
|
||||
|
||||
public static void main(String[] args) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MenuDifficulte menu = new MenuDifficulte(difficulty -> openGameWindow(difficulty));
|
||||
menu.show();
|
||||
});
|
||||
}
|
||||
|
||||
/** Ouvre la fenêtre du jeu, crée la Partie et branche les événements. */
|
||||
private static void openGameWindow(String difficulty) {
|
||||
Fenetre fenetre = new Fenetre();
|
||||
fenetre.getWindow().setTitle("Jeu du Pendu — " + difficulty);
|
||||
|
||||
Partie partie = new Partie();
|
||||
|
||||
// Affichage initial du mot masqué (construit ici)
|
||||
fenetre.getWordLabel().setText(buildMaskedWord(partie));
|
||||
|
||||
// Stage initial (0 erreur)
|
||||
if (fenetre.getDrawZone() instanceof Dessin) {
|
||||
((Dessin) fenetre.getDrawZone()).setStage(0);
|
||||
}
|
||||
|
||||
// On mémorise les essais initiaux pour calculer les erreurs = initialTries - remainingTry
|
||||
final int initialTries = partie.getRemainingTry();
|
||||
|
||||
// Handler : applique Partie puis met à jour l'UI (mot + dessin + score)
|
||||
Consumer<Character> handler = new GameLetterHandler(fenetre, partie, initialTries);
|
||||
|
||||
// Branchement des événements clavier/bouton
|
||||
new Event(fenetre, handler);
|
||||
}
|
||||
|
||||
/** Construit la chaîne "_ _ A _ _" à partir de l'état de Partie (sans modifier Partie). */
|
||||
private static String buildMaskedWord(Partie partie) {
|
||||
char[] word = partie.getSecretWord();
|
||||
boolean[] found = partie.getFoundLetters();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < word.length; i++) {
|
||||
sb.append(found[i] ? word[i] : '_');
|
||||
if (i < word.length - 1) sb.append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler de lettres :
|
||||
* - applique Partie.isAlreadyEntries
|
||||
* - met à jour le mot affiché
|
||||
* - calcule errors = initialTries - remainingTry, puis stage = min(errors, Dessin.MAX_STAGE)
|
||||
* - met à jour le score
|
||||
* - gère la fin de partie
|
||||
*/
|
||||
private static class GameLetterHandler implements Consumer<Character> {
|
||||
private final Fenetre fenetre;
|
||||
private final Partie partie;
|
||||
private final int initialTries; // essais au démarrage (peut être 11 avec ta Partie)
|
||||
private final Dessin dessinPanel;
|
||||
|
||||
GameLetterHandler(Fenetre fenetre, Partie partie, int initialTries) {
|
||||
this.fenetre = fenetre;
|
||||
this.partie = partie;
|
||||
this.initialTries = initialTries;
|
||||
this.dessinPanel = (fenetre.getDrawZone() instanceof Dessin)
|
||||
? (Dessin) fenetre.getDrawZone() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Character ch) {
|
||||
boolean alreadyPlayed = partie.isAlreadyEntries(ch);
|
||||
|
||||
// Mise à jour du mot
|
||||
fenetre.getWordLabel().setText(buildMaskedWord(partie));
|
||||
|
||||
// Erreurs -> stage pour le dessin (borné à MAX_STAGE)
|
||||
int errors = Math.max(0, initialTries - partie.getRemainingTry());
|
||||
if (dessinPanel != null) {
|
||||
int stage = Math.min(errors, Dessin.MAX_STAGE);
|
||||
dessinPanel.setStage(stage);
|
||||
}
|
||||
|
||||
// Mise à jour du score courant (si tu utilises Score + Chronometre)
|
||||
long elapsed = (fenetre.getChronometre() != null)
|
||||
? fenetre.getChronometre().getElapsedMillis() : 0L;
|
||||
if (fenetre.getScoreLabel() != null) {
|
||||
int score = Score.compute(errors, elapsed);
|
||||
fenetre.getScoreLabel().setText("Score : " + score);
|
||||
}
|
||||
|
||||
if (alreadyPlayed) {
|
||||
JOptionPane.showMessageDialog(fenetre.getWindow(),
|
||||
"Lettre déjà jouée : " + ch + "\nEssais restants : " + partie.getRemainingTry());
|
||||
}
|
||||
|
||||
// Fin de partie
|
||||
if (partie.gameIsEnding()) {
|
||||
// Stoppe le chronomètre pour figer le temps
|
||||
if (fenetre.getChronometre() != null) {
|
||||
fenetre.getChronometre().stop();
|
||||
}
|
||||
// Score final (si Score/Chronometre présents)
|
||||
long finalElapsed = (fenetre.getChronometre() != null)
|
||||
? fenetre.getChronometre().getElapsedMillis() : elapsed;
|
||||
int finalErrors = Math.max(0, initialTries - partie.getRemainingTry());
|
||||
int finalScore = Score.compute(finalErrors, finalElapsed);
|
||||
|
||||
boolean win = !fenetre.getWordLabel().getText().contains("_");
|
||||
String msg = (win
|
||||
? "Bravo ! Mot trouvé : "
|
||||
: "Perdu ! Le mot était : ")
|
||||
+ String.valueOf(partie.getSecretWord())
|
||||
+ (fenetre.getScoreLabel() != null ? "\nScore : " + finalScore : "");
|
||||
|
||||
JOptionPane.showMessageDialog(fenetre.getWindow(), msg);
|
||||
|
||||
fenetre.getLetterInput().setEnabled(false);
|
||||
fenetre.getSendButton().setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/Score.java
Normal file
36
src/Score.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Calcule le score du pendu à partir du nombre d'erreurs et du temps écoulé.
|
||||
*
|
||||
* Formule (simple et configurable) :
|
||||
* score = max(0, BASE - erreurs * ERROR_PENALTY - secondes * TIME_PENALTY_PER_SEC)
|
||||
*
|
||||
* @version 1.0
|
||||
* @author Adrien
|
||||
* Date : 08-10-2025
|
||||
* Licence :
|
||||
*/
|
||||
public final class Score {
|
||||
|
||||
/** Score de départ. */
|
||||
public static final int BASE = 1000;
|
||||
|
||||
/** Malus par erreur. (6 erreurs max par défaut) */
|
||||
public static final int ERROR_PENALTY = 120;
|
||||
|
||||
/** Malus par seconde écoulée. */
|
||||
public static final double TIME_PENALTY_PER_SEC = 1.0;
|
||||
|
||||
private Score() {}
|
||||
|
||||
/**
|
||||
* - nombre d'erreurs (>=0)
|
||||
* - temps écoulé en millisecondes
|
||||
* - score >= 0
|
||||
*/
|
||||
public static int compute(int errors, long elapsedMillis) {
|
||||
if (errors < 0) errors = 0;
|
||||
double timePenalty = (elapsedMillis / 1000.0) * TIME_PENALTY_PER_SEC;
|
||||
int value = (int)Math.round(BASE - errors * ERROR_PENALTY - timePenalty);
|
||||
return Math.max(0, value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user