forked from menault/TD3_DEV51_Qualite_Algo
		
	
							
								
								
									
										27
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								Makefile
									
									
									
									
									
								
							| @@ -15,16 +15,22 @@ 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)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 | ||||
| @@ -33,6 +39,21 @@ $(OUT)Dessin.class : $(IN)Dessin.java | ||||
| $(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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -10,7 +10,7 @@ import java.util.Random; | ||||
| */ | ||||
| public class Partie { | ||||
|    //Contantes | ||||
|    private static final byte REMAININGTRY = 11 ; | ||||
|    private static final byte REMAININGTRY = 6 ; | ||||
|    private static final byte CARACTERCODESHIFT = 65 ;  //Décalage ASCI > 'A' | ||||
|  | ||||
|    //Attributs | ||||
| @@ -133,3 +133,4 @@ public class Partie { | ||||
|       System.out.println("Essais restants : " + game.getRemainingTry()); | ||||
|    } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										137
									
								
								src/Pendu.java
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								src/Pendu.java
									
									
									
									
									
								
							| @@ -1,14 +1,135 @@ | ||||
| import javax.swing.*; | ||||
| import java.util.function.Consumer; | ||||
|  | ||||
| /** | ||||
| * La classe <code>Pendu</code> | ||||
| * | ||||
| * @version | ||||
| * @author | ||||
| * Date : | ||||
| * Licence : | ||||
| */ | ||||
|  * 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 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