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 \ | 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)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										122
									
								
								src/Dessin.java
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								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); | ||||||
|  |         } | ||||||
|  |         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); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // Corps |         g2.dispose(); | ||||||
|         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(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 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()); | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										139
									
								
								src/Pendu.java
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								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. | ||||||
| * @version |  * Met à jour le dessin du pendu à chaque erreur. | ||||||
| * @author |  * | ||||||
| * Date : |  * @version 1.4 | ||||||
| * Licence : |  * author Adrien | ||||||
| */ |  * Date : 08-10-2025 | ||||||
|  |  * 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