diff --git a/HangmanGUI.java b/HangmanGUI.java new file mode 100644 index 0000000..0a1bc9e --- /dev/null +++ b/HangmanGUI.java @@ -0,0 +1,263 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +/** + * Hangman game GUI. + * Variables/methods and comments in English. + * User-facing texts remain in French. + */ +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" + }; + + private String secretWord; + private final Set found = new HashSet<>(); + private final Set tried = new HashSet<>(); + private int errors = 0; + 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 + + 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(); + + /** Drawing panel for the hangman, progressive according to error count. */ + 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(); + int baseY = h - 40; // ground level + + 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) { + gg.drawLine(220, 145, 190, 165); + gg.drawLine(220, 145, 250, 165); + } + + if (errors >= 8) { + gg.drawLine(220, 200, 200, 240); + gg.drawLine(220, 200, 240, 240); + } + } + } + + /** Entry point: create and show the game window. */ + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + HangmanGUI app = new HangmanGUI(); + app.setVisible(true); + }); + } + + /** Constructor: window setup, UI setup, and start the game. */ + public HangmanGUI() { + super("Jeu du Pendu"); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setSize(520, 520); + setLocationRelativeTo(null); + setupUI(); + startGame(); + } + + /** Prepare the layout and interactions. */ + 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()); + input.addActionListener(e -> onGuess()); + addWindowListener(new WindowAdapter() { + @Override public void windowOpened(WindowEvent e) { input.requestFocusInWindow(); } + }); + } + + /** Start a game. */ + 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(); + } + + /** Handle a guess: validate input, update state, check for end of game. */ + private void onGuess() { + if (gameOver) return; // block input after game end + + 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(); + } + + /** Refresh labels/drawing and handle end-of-game. */ + 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); + wordLbl.setText(spaced(secretWord)); // pretty display of the word + gameOver = true; + disableInput(); + } else if (errors >= MAX_ERRORS) { + infoLbl.setText("Perdu ! Le mot était : " + secretWord); + wordLbl.setText(spaced(secretWord)); + gameOver = true; + disableInput(); + } + + // force UI refresh + revalidate(); + repaint(); + } + + /** Disable controls after the game ends. */ + private void disableInput() { + input.setEditable(false); + guessBtn.setEnabled(false); + } + + /** Pick a random word. */ + private String pickWord() { + Random rnd = new Random(); + return words[rnd.nextInt(words.length)].toLowerCase(Locale.ROOT); + } + + /** Build the masked word based on found letters. */ + private String maskWord(String secret, Set 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(); + } + + /** True if all letters have been found. */ + private boolean isWin(String secret, Set 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; + } + + /** Sort and join letters for display. */ + private String joinChars(Set set) { + java.util.List 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(); + } + + /** Add spaces between letters for nicer reading. */ + 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(); + } +}