forked from menault/TD3_DEV51_Qualite_Algo
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			77b3158262
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5e70bcbb63 | |||
| e649d30b33 | |||
| 584e8dc3c6 | |||
| 82b4e0ffd1 | |||
| 9cb9088b81 | |||
| 87909bbe35 | 
							
								
								
									
										27
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								Makefile
									
									
									
									
									
								
							@@ -15,16 +15,22 @@ JCFLAGS = -encoding UTF-8 -implicit:none -cp $(OUT) -d $(OUT)
 | 
				
			|||||||
CLASSFILES = Pendu.class \
 | 
					CLASSFILES = Pendu.class \
 | 
				
			||||||
	Partie.class \
 | 
						Partie.class \
 | 
				
			||||||
	Fenetre.class \
 | 
						Fenetre.class \
 | 
				
			||||||
	Dessin.class
 | 
						Dessin.class \
 | 
				
			||||||
 | 
						Mots.class \
 | 
				
			||||||
 | 
						Event.class \
 | 
				
			||||||
 | 
						LetterInputFilter.class \
 | 
				
			||||||
 | 
						MenuDifficulte.class \
 | 
				
			||||||
 | 
						Chronometre.class \
 | 
				
			||||||
 | 
						Score.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Dépendances
 | 
					# 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) $<
 | 
						$(JC) $(JCFLAGS) $<
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(OUT)Partie.class : $(IN)Partie.java $(OUT)Mots.class
 | 
					$(OUT)Partie.class : $(IN)Partie.java $(OUT)Mots.class
 | 
				
			||||||
	$(JC) $(JCFLAGS) $<
 | 
						$(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) $<
 | 
						$(JC) $(JCFLAGS) $<
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(OUT)Dessin.class : $(IN)Dessin.java
 | 
					$(OUT)Dessin.class : $(IN)Dessin.java
 | 
				
			||||||
@@ -33,6 +39,21 @@ $(OUT)Dessin.class : $(IN)Dessin.java
 | 
				
			|||||||
$(OUT)Mots.class : $(IN)Mots.java
 | 
					$(OUT)Mots.class : $(IN)Mots.java
 | 
				
			||||||
	$(JC) $(JCFLAGS) $<
 | 
						$(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
 | 
					# Commandes
 | 
				
			||||||
Pendu : $(OUT)Pendu.class
 | 
					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.*;
 | 
					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
 | 
					* @version 1.1
 | 
				
			||||||
* @author Adrien
 | 
					* author Adrien
 | 
				
			||||||
* Date : 08-10-2025
 | 
					* Date : 08-10-2025
 | 
				
			||||||
* Licence :
 | 
					 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
public class Dessin extends JPanel {
 | 
					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 ---
 | 
					    // --- Constructeur ---
 | 
				
			||||||
    public Dessin() {
 | 
					    public Dessin() {
 | 
				
			||||||
        // Taille préférée pour s'intégrer dans Fenetre
 | 
					 | 
				
			||||||
        setPreferredSize(new Dimension(600, 350));
 | 
					        setPreferredSize(new Dimension(600, 350));
 | 
				
			||||||
        setBackground(new Color(245, 245, 245));
 | 
					        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 ---
 | 
					    // --- Dessin principal ---
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void paintComponent(Graphics graphics) {
 | 
					    protected void paintComponent(Graphics graphics) {
 | 
				
			||||||
        super.paintComponent(graphics);
 | 
					        super.paintComponent(graphics);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Anti-aliasing pour des traits plus doux
 | 
					        Graphics2D g2 = (Graphics2D) graphics.create();
 | 
				
			||||||
        Graphics2D graphics2D = (Graphics2D) graphics.create();
 | 
					        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 | 
				
			||||||
        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 | 
					        g2.setStroke(new BasicStroke(3f));
 | 
				
			||||||
        graphics2D.setStroke(new BasicStroke(3f));
 | 
					        g2.setColor(Color.DARK_GRAY);
 | 
				
			||||||
        graphics2D.setColor(Color.DARK_GRAY);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Repères et proportions
 | 
					        // Repères et proportions
 | 
				
			||||||
        int width = getWidth();
 | 
					        int width = getWidth();
 | 
				
			||||||
        int height = getHeight();
 | 
					        int height = getHeight();
 | 
				
			||||||
        int marginPixels = Math.min(width, height) / 12; // marge proportionnelle
 | 
					        int margin = Math.min(width, height) / 12;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Potence : socle
 | 
					        // Potence (toujours affichée)
 | 
				
			||||||
        int baseYCoordinate = height - marginPixels;
 | 
					        int baseY = height - margin;
 | 
				
			||||||
        graphics2D.drawLine(marginPixels, baseYCoordinate, width / 2, baseYCoordinate);
 | 
					        g2.drawLine(margin, baseY, width / 2, baseY);                 // socle
 | 
				
			||||||
 | 
					        int postX = margin + (width / 12);
 | 
				
			||||||
        // Montant vertical
 | 
					        g2.drawLine(postX, baseY, postX, margin);                      // montant
 | 
				
			||||||
        int postXCoordinate = marginPixels + (width / 12);
 | 
					 | 
				
			||||||
        graphics2D.drawLine(postXCoordinate, baseYCoordinate, postXCoordinate, marginPixels);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Traverse horizontale
 | 
					 | 
				
			||||||
        int beamLength = width / 3;
 | 
					        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
 | 
					        // Corde (toujours)
 | 
				
			||||||
        graphics2D.drawLine(postXCoordinate, marginPixels + height / 10, postXCoordinate + width / 12, marginPixels);
 | 
					        int ropeX = postX + beamLength;
 | 
				
			||||||
 | 
					        int ropeTopY = margin;
 | 
				
			||||||
 | 
					        int ropeBottomY = margin + height / 12;
 | 
				
			||||||
 | 
					        g2.drawLine(ropeX, ropeTopY, ropeX, ropeBottomY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Corde
 | 
					        // Géométrie du personnage
 | 
				
			||||||
        int ropeXCoordinate = postXCoordinate + beamLength;
 | 
					        int headR = Math.min(width, height) / 16;
 | 
				
			||||||
        int ropeTopYCoordinate = marginPixels;
 | 
					        int headCX = ropeX;
 | 
				
			||||||
        int ropeBottomYCoordinate = marginPixels + height / 12;
 | 
					        int headCY = ropeBottomY + headR;
 | 
				
			||||||
        graphics2D.drawLine(ropeXCoordinate, ropeTopYCoordinate, ropeXCoordinate, ropeBottomYCoordinate);
 | 
					        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
 | 
					        // Étapes du personnage
 | 
				
			||||||
        int headRadiusPixels = Math.min(width, height) / 16;
 | 
					        if (stage >= 1) { // tête
 | 
				
			||||||
        int headCenterX = ropeXCoordinate;
 | 
					            g2.drawOval(headCX - headR, headCY - headR, headR * 2, headR * 2);
 | 
				
			||||||
        int headCenterY = ropeBottomYCoordinate + headRadiusPixels;
 | 
					        }
 | 
				
			||||||
        graphics2D.drawOval(headCenterX - headRadiusPixels, headCenterY - headRadiusPixels, headRadiusPixels * 2, headRadiusPixels * 2);
 | 
					        if (stage >= 2) { // corps
 | 
				
			||||||
 | 
					            g2.drawLine(headCX, bodyTopY, headCX, bodyBottomY);
 | 
				
			||||||
        // Corps
 | 
					        }
 | 
				
			||||||
        int bodyTopYCoordinate = headCenterY + headRadiusPixels;
 | 
					        if (stage >= 3) { // bras gauche
 | 
				
			||||||
        int bodyBottomYCoordinate = bodyTopYCoordinate + height / 6;
 | 
					            g2.drawLine(headCX, shouldersY, headCX - armSpan, shouldersY + height / 20);
 | 
				
			||||||
        graphics2D.drawLine(headCenterX, bodyTopYCoordinate, headCenterX, bodyBottomYCoordinate);
 | 
					        }
 | 
				
			||||||
 | 
					        if (stage >= 4) { // bras droit
 | 
				
			||||||
        // Bras
 | 
					            g2.drawLine(headCX, shouldersY, headCX + armSpan, shouldersY + height / 20);
 | 
				
			||||||
        int armSpanPixels = width / 10;
 | 
					        }
 | 
				
			||||||
        int shouldersYCoordinate = bodyTopYCoordinate + height / 24;
 | 
					        if (stage >= 5) { // jambe gauche
 | 
				
			||||||
        graphics2D.drawLine(headCenterX, shouldersYCoordinate, headCenterX - armSpanPixels, shouldersYCoordinate + height / 20);
 | 
					            g2.drawLine(headCX, bodyBottomY, headCX - legSpan, bodyBottomY + height / 8);
 | 
				
			||||||
        graphics2D.drawLine(headCenterX, shouldersYCoordinate, headCenterX + armSpanPixels, shouldersYCoordinate + height / 20);
 | 
					        }
 | 
				
			||||||
 | 
					        if (stage >= 6) { // jambe droite
 | 
				
			||||||
        // Jambes
 | 
					            g2.drawLine(headCX, bodyBottomY, headCX + legSpan, bodyBottomY + height / 8);
 | 
				
			||||||
        int legSpanPixels = width / 12;
 | 
					        }
 | 
				
			||||||
        graphics2D.drawLine(headCenterX, bodyBottomYCoordinate, headCenterX - legSpanPixels, bodyBottomYCoordinate + height / 8);
 | 
					
 | 
				
			||||||
        graphics2D.drawLine(headCenterX, bodyBottomYCoordinate, headCenterX + legSpanPixels, bodyBottomYCoordinate + height / 8);
 | 
					        g2.dispose();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        graphics2D.dispose();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Affichage
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String toString() {
 | 
					    public String toString() { return ""; }
 | 
				
			||||||
        return "";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,9 @@ public class Fenetre {
 | 
				
			|||||||
    private JLabel wordLabel;
 | 
					    private JLabel wordLabel;
 | 
				
			||||||
    private JTextField letterInput;
 | 
					    private JTextField letterInput;
 | 
				
			||||||
    private JButton sendButton;
 | 
					    private JButton sendButton;
 | 
				
			||||||
    private JPanel drawZone; // instance de Dessin
 | 
					    private JPanel drawZone;
 | 
				
			||||||
 | 
					    private Chronometre chronometre;
 | 
				
			||||||
 | 
					    private JLabel scoreLabel; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // --- Constructeur ---
 | 
					    // --- Constructeur ---
 | 
				
			||||||
    public Fenetre() {
 | 
					    public Fenetre() {
 | 
				
			||||||
@@ -32,6 +34,17 @@ public class Fenetre {
 | 
				
			|||||||
        root.setLayout(new BoxLayout(root, BoxLayout.Y_AXIS));
 | 
					        root.setLayout(new BoxLayout(root, BoxLayout.Y_AXIS));
 | 
				
			||||||
        window.setContentPane(root);
 | 
					        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();
 | 
					        drawZone = new Dessin();
 | 
				
			||||||
        root.add(drawZone);
 | 
					        root.add(drawZone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -84,15 +97,6 @@ public class Fenetre {
 | 
				
			|||||||
    public JButton getSendButton() { return sendButton; }
 | 
					    public JButton getSendButton() { return sendButton; }
 | 
				
			||||||
    public JLabel getWordLabel() { return wordLabel; }
 | 
					    public JLabel getWordLabel() { return wordLabel; }
 | 
				
			||||||
    public JPanel getDrawZone() { return drawZone; }
 | 
					    public JPanel getDrawZone() { return drawZone; }
 | 
				
			||||||
 | 
					    public Chronometre getChronometre() { return chronometre; }
 | 
				
			||||||
    // --- Méthode principale de test ---
 | 
					    public JLabel getScoreLabel() { return scoreLabel; }
 | 
				
			||||||
    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)")
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										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);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,7 +10,7 @@ import java.util.Random;
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
public class Partie {
 | 
					public class Partie {
 | 
				
			||||||
   //Contantes
 | 
					   //Contantes
 | 
				
			||||||
   private static final byte REMAININGTRY = 11 ;
 | 
					   private static final byte REMAININGTRY = 6 ;
 | 
				
			||||||
   private static final byte CARACTERCODESHIFT = 65 ;  //Décalage ASCI > 'A'
 | 
					   private static final byte CARACTERCODESHIFT = 65 ;  //Décalage ASCI > 'A'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   //Attributs
 | 
					   //Attributs
 | 
				
			||||||
@@ -133,3 +133,4 @@ public class Partie {
 | 
				
			|||||||
      System.out.println("Essais restants : " + game.getRemainingTry());
 | 
					      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
 | 
					 * @version 1.4
 | 
				
			||||||
* @author
 | 
					 * author Adrien
 | 
				
			||||||
* Date :
 | 
					 * Date : 08-10-2025
 | 
				
			||||||
 * Licence :
 | 
					 * Licence :
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class Pendu {
 | 
					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