| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  | import javax.swing.*;
 | 
					
						
							|  |  |  | import java.awt.*;
 | 
					
						
							|  |  |  | import java.awt.event.*;
 | 
					
						
							|  |  |  | import java.util.*;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |  * Hangman game GUI.
 | 
					
						
							|  |  |  |  * Variables/methods and comments in English.
 | 
					
						
							|  |  |  |  * User-facing texts remain in French.
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |  */
 | 
					
						
							|  |  |  | public class HangmanGUI extends JFrame {
 | 
					
						
							|  |  |  |     private final String[] words = {
 | 
					
						
							|  |  |  |         "ordinateur","voiture","maison","soleil","lumiere",
 | 
					
						
							|  |  |  |         "fromage","chocolat","montagne","riviere","plage",
 | 
					
						
							|  |  |  |         "oiseau","papillon","musique","chateau","livre",
 | 
					
						
							|  |  |  |         "telephone","aventure","mystere","foret","fleur",
 | 
					
						
							|  |  |  |         "chapeau","nuage","horloge","chaise","fenetre",
 | 
					
						
							|  |  |  |         "paysage","bouteille","parapluie","clavier","souris",
 | 
					
						
							|  |  |  |         "brouillard","village","histoire","cerise","pomme",
 | 
					
						
							|  |  |  |         "banane","poisson","arbre","cascade","cheval"
 | 
					
						
							|  |  |  |     };
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private String secretWord;
 | 
					
						
							|  |  |  |     private final Set<Character> found = new HashSet<>();
 | 
					
						
							|  |  |  |     private final Set<Character> tried = new HashSet<>();
 | 
					
						
							|  |  |  |     private int errors = 0;
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     private static final int MAX_ERRORS = 8; // 8 steps: base, post, beam, rope, head, body, arms, legs
 | 
					
						
							|  |  |  |     private boolean gameOver = false;        // indicates if the game is finished
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     private final JLabel wordLbl = new JLabel("", SwingConstants.CENTER);
 | 
					
						
							|  |  |  |     private final JLabel triedLbl = new JLabel("", SwingConstants.CENTER);
 | 
					
						
							|  |  |  |     private final JLabel infoLbl  = new JLabel("Entrez une lettre (a-z)", SwingConstants.CENTER);
 | 
					
						
							|  |  |  |     private final JTextField input = new JTextField();
 | 
					
						
							|  |  |  |     private final JButton guessBtn = new JButton("Proposer");
 | 
					
						
							|  |  |  |     private final HangPanel hangPanel = new HangPanel();
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Drawing panel for the hangman, progressive according to error count. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private static class HangPanel extends JPanel {
 | 
					
						
							|  |  |  |         private int errors = 0;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public void setErrors(int e) {
 | 
					
						
							|  |  |  |             this.errors = Math.max(0, Math.min(e, MAX_ERRORS));
 | 
					
						
							|  |  |  |             repaint();
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Override
 | 
					
						
							|  |  |  |         protected void paintComponent(Graphics g) {
 | 
					
						
							|  |  |  |             super.paintComponent(g);
 | 
					
						
							|  |  |  |             Graphics2D gg = (Graphics2D) g;
 | 
					
						
							|  |  |  |             gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             int w = getWidth(), h = getHeight();
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |             int baseY = h - 40; // ground level
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 1) gg.drawLine(50, baseY, w - 50, baseY);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 2) gg.drawLine(100, baseY, 100, 60);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 3) gg.drawLine(100, 60, 220, 60);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 4) gg.drawLine(220, 60, 220, 90);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 5) gg.drawOval(200, 90, 40, 40);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 6) gg.drawLine(220, 130, 220, 200);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 7) {
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |                 gg.drawLine(220, 145, 190, 165); 
 | 
					
						
							|  |  |  |                 gg.drawLine(220, 145, 250, 165);
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |             }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (errors >= 8) {
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |                 gg.drawLine(220, 200, 200, 240); 
 | 
					
						
							|  |  |  |                 gg.drawLine(220, 200, 240, 240); 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |             }
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Entry point: create and show the game window. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     public static void main(String[] args) {
 | 
					
						
							|  |  |  |         SwingUtilities.invokeLater(() -> {
 | 
					
						
							|  |  |  |             HangmanGUI app = new HangmanGUI();
 | 
					
						
							|  |  |  |             app.setVisible(true);
 | 
					
						
							|  |  |  |         });
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Constructor: window setup, UI setup, and start the game. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     public HangmanGUI() {
 | 
					
						
							|  |  |  |         super("Jeu du Pendu");
 | 
					
						
							|  |  |  |         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 | 
					
						
							|  |  |  |         setSize(520, 520);
 | 
					
						
							|  |  |  |         setLocationRelativeTo(null);
 | 
					
						
							|  |  |  |         setupUI();
 | 
					
						
							|  |  |  |         startGame();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Prepare the layout and interactions. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private void setupUI() {
 | 
					
						
							|  |  |  |         wordLbl.setFont(new Font(Font.MONOSPACED, Font.BOLD, 28));
 | 
					
						
							|  |  |  |         triedLbl.setFont(new Font("SansSerif", Font.PLAIN, 14));
 | 
					
						
							|  |  |  |         infoLbl.setFont(new Font("SansSerif", Font.PLAIN, 14));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         JPanel top = new JPanel(new GridLayout(3, 1, 8, 8));
 | 
					
						
							|  |  |  |         top.add(wordLbl);
 | 
					
						
							|  |  |  |         top.add(triedLbl);
 | 
					
						
							|  |  |  |         top.add(infoLbl);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         JPanel controls = new JPanel(new BorderLayout(8, 8));
 | 
					
						
							|  |  |  |         controls.add(input, BorderLayout.CENTER);
 | 
					
						
							|  |  |  |         controls.add(guessBtn, BorderLayout.EAST);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         hangPanel.setPreferredSize(new Dimension(480, 300));
 | 
					
						
							|  |  |  |         JPanel center = new JPanel(new BorderLayout());
 | 
					
						
							|  |  |  |         center.add(hangPanel, BorderLayout.CENTER);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         JPanel main = new JPanel(new BorderLayout(10, 10));
 | 
					
						
							|  |  |  |         main.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
 | 
					
						
							|  |  |  |         main.add(top, BorderLayout.NORTH);
 | 
					
						
							|  |  |  |         main.add(center, BorderLayout.CENTER);
 | 
					
						
							|  |  |  |         main.add(controls, BorderLayout.SOUTH);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         setContentPane(main);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         guessBtn.addActionListener(e -> onGuess());
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |         input.addActionListener(e -> onGuess()); 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |         addWindowListener(new WindowAdapter() {
 | 
					
						
							|  |  |  |             @Override public void windowOpened(WindowEvent e) { input.requestFocusInWindow(); }
 | 
					
						
							|  |  |  |         });
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Start a  game. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private void startGame() {
 | 
					
						
							|  |  |  |         secretWord = pickWord();
 | 
					
						
							|  |  |  |         found.clear();
 | 
					
						
							|  |  |  |         tried.clear();
 | 
					
						
							|  |  |  |         errors = 0;
 | 
					
						
							|  |  |  |         gameOver = false;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         input.setText("");
 | 
					
						
							|  |  |  |         input.setEditable(true);
 | 
					
						
							|  |  |  |         guessBtn.setEnabled(true);
 | 
					
						
							|  |  |  |         infoLbl.setText("Entrez une lettre (a-z)");
 | 
					
						
							|  |  |  |         updateUIState();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Handle a guess: validate input, update state, check for end of game. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private void onGuess() {
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |         if (gameOver) return; // block input after game end
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         String s = input.getText().trim().toLowerCase(Locale.ROOT);
 | 
					
						
							|  |  |  |         if (s.length() != 1 || s.charAt(0) < 'a' || s.charAt(0) > 'z') {
 | 
					
						
							|  |  |  |             infoLbl.setText("Veuillez entrer une seule lettre (a-z).");
 | 
					
						
							|  |  |  |             input.selectAll();
 | 
					
						
							|  |  |  |             input.requestFocusInWindow();
 | 
					
						
							|  |  |  |             return;
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |         char c = s.charAt(0);
 | 
					
						
							|  |  |  |         if (tried.contains(c)) {
 | 
					
						
							|  |  |  |             infoLbl.setText("Lettre déjà essayée : " + c);
 | 
					
						
							|  |  |  |             input.selectAll();
 | 
					
						
							|  |  |  |             input.requestFocusInWindow();
 | 
					
						
							|  |  |  |             return;
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tried.add(c);
 | 
					
						
							|  |  |  |         boolean hit = false;
 | 
					
						
							|  |  |  |         for (int i = 0; i < secretWord.length(); i++) {
 | 
					
						
							|  |  |  |             if (Character.toLowerCase(secretWord.charAt(i)) == c) {
 | 
					
						
							|  |  |  |                 found.add(c);
 | 
					
						
							|  |  |  |                 hit = true;
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |         if (!hit) {
 | 
					
						
							|  |  |  |             errors++;
 | 
					
						
							|  |  |  |             infoLbl.setText("Mauvaise lettre : " + c);
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             infoLbl.setText("Bonne lettre : " + c);
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         updateUIState();
 | 
					
						
							|  |  |  |         input.selectAll();
 | 
					
						
							|  |  |  |         input.requestFocusInWindow();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Refresh labels/drawing and handle end-of-game. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private void updateUIState() {
 | 
					
						
							|  |  |  |         wordLbl.setText(maskWord(secretWord, found));
 | 
					
						
							|  |  |  |         triedLbl.setText("Lettres essayées : " + joinChars(tried));
 | 
					
						
							|  |  |  |         hangPanel.setErrors(errors);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (isWin(secretWord, found)) {
 | 
					
						
							|  |  |  |             infoLbl.setText("Bravo ! Vous avez trouvé le mot : " + secretWord);
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |             wordLbl.setText(spaced(secretWord)); // pretty display of the word
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |             gameOver = true;
 | 
					
						
							|  |  |  |             disableInput();
 | 
					
						
							|  |  |  |         } else if (errors >= MAX_ERRORS) {
 | 
					
						
							|  |  |  |             infoLbl.setText("Perdu ! Le mot était : " + secretWord);
 | 
					
						
							|  |  |  |             wordLbl.setText(spaced(secretWord));
 | 
					
						
							|  |  |  |             gameOver = true;
 | 
					
						
							|  |  |  |             disableInput();
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |         // force UI refresh
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |         revalidate();
 | 
					
						
							|  |  |  |         repaint();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Disable controls after the game ends. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private void disableInput() {
 | 
					
						
							|  |  |  |         input.setEditable(false);
 | 
					
						
							|  |  |  |         guessBtn.setEnabled(false);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Pick a random word. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private String pickWord() {
 | 
					
						
							|  |  |  |         Random rnd = new Random();
 | 
					
						
							|  |  |  |         return words[rnd.nextInt(words.length)].toLowerCase(Locale.ROOT);
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Build the masked word based on found letters. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private String maskWord(String secret, Set<Character> foundSet) {
 | 
					
						
							|  |  |  |         StringBuilder sb = new StringBuilder();
 | 
					
						
							|  |  |  |         for (int i = 0; i < secret.length(); i++) {
 | 
					
						
							|  |  |  |             char ch = secret.charAt(i);
 | 
					
						
							|  |  |  |             char lc = Character.toLowerCase(ch);
 | 
					
						
							|  |  |  |             if (Character.isLetter(lc) && !foundSet.contains(lc)) sb.append('_');
 | 
					
						
							|  |  |  |             else sb.append(ch);
 | 
					
						
							|  |  |  |             if (i < secret.length() - 1) sb.append(' ');
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |         return sb.toString();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** True if all letters have been found. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private boolean isWin(String secret, Set<Character> foundSet) {
 | 
					
						
							|  |  |  |         for (int i = 0; i < secret.length(); i++) {
 | 
					
						
							|  |  |  |             char lc = Character.toLowerCase(secret.charAt(i));
 | 
					
						
							|  |  |  |             if (Character.isLetter(lc) && !foundSet.contains(lc)) return false;
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |         return true;
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Sort and join letters for display. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private String joinChars(Set<Character> set) {
 | 
					
						
							|  |  |  |         java.util.List<Character> list = new ArrayList<>(set);
 | 
					
						
							|  |  |  |         Collections.sort(list);
 | 
					
						
							|  |  |  |         StringBuilder sb = new StringBuilder();
 | 
					
						
							|  |  |  |         for (int i = 0; i < list.size(); i++) {
 | 
					
						
							|  |  |  |             sb.append(list.get(i));
 | 
					
						
							|  |  |  |             if (i < list.size() - 1) sb.append(' ');
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |         return sb.toString();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-08 14:33:48 +02:00
										 |  |  |     /** Add spaces between letters for nicer reading. */
 | 
					
						
							| 
									
										
										
										
											2025-10-08 12:17:50 +02:00
										 |  |  |     private String spaced(String s) {
 | 
					
						
							|  |  |  |         StringBuilder sb = new StringBuilder();
 | 
					
						
							|  |  |  |         for (int i = 0; i < s.length(); i++) {
 | 
					
						
							|  |  |  |             sb.append(s.charAt(i));
 | 
					
						
							|  |  |  |             if (i < s.length() - 1) sb.append(' ');
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |         return sb.toString();
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | }
 |