diff --git a/Makefile b/Makefile index c9a06ae..98d213b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ SRC_DIR = src OUT_DIR = out PACKAGE = fr/iut/Projet -MAIN_CLASS = fr.iut.Projet.Random_word +MAIN_CLASS = fr.iut.Projet.Display # === Règle principale === all: compile run @@ -11,7 +11,9 @@ all: compile run compile: @echo "Compilation du projet..." @mkdir -p $(OUT_DIR) - @javac -d $(OUT_DIR) $(SRC_DIR)/$(PACKAGE)/Random_word.java + # Compilation de tous les fichiers Java du package + @javac -d $(OUT_DIR) $(SRC_DIR)/$(PACKAGE)/*.java + # Copier Word.txt dans le dossier de sortie @cp $(SRC_DIR)/$(PACKAGE)/Word.txt $(OUT_DIR)/$(PACKAGE)/ @echo "Compilation terminée." diff --git a/out/fr/iut/Projet/Action$1.class b/out/fr/iut/Projet/Action$1.class new file mode 100644 index 0000000..4256abf Binary files /dev/null and b/out/fr/iut/Projet/Action$1.class differ diff --git a/out/fr/iut/Projet/Action.class b/out/fr/iut/Projet/Action.class new file mode 100644 index 0000000..654fe3b Binary files /dev/null and b/out/fr/iut/Projet/Action.class differ diff --git a/out/fr/iut/Projet/Display.class b/out/fr/iut/Projet/Display.class new file mode 100644 index 0000000..7e32efd Binary files /dev/null and b/out/fr/iut/Projet/Display.class differ diff --git a/out/fr/iut/Projet/PlayButtonListener.class b/out/fr/iut/Projet/PlayButtonListener.class new file mode 100644 index 0000000..1b41bc0 Binary files /dev/null and b/out/fr/iut/Projet/PlayButtonListener.class differ diff --git a/out/fr/iut/Projet/Random_word.class b/out/fr/iut/Projet/Random_word.class index fc4c7a0..36fc137 100644 Binary files a/out/fr/iut/Projet/Random_word.class and b/out/fr/iut/Projet/Random_word.class differ diff --git a/src/fr/iut/Projet/Action.java b/src/fr/iut/Projet/Action.java index ee63c94..5bcaf5e 100644 --- a/src/fr/iut/Projet/Action.java +++ b/src/fr/iut/Projet/Action.java @@ -4,28 +4,228 @@ import javax.swing.*; import java.awt.*; /** - * Classe Action représentant la fenêtre principale du jeu "Hanging Man". - * Affiche un message de bienvenue. + * Classe qui représente l'interface graphique du jeu du pendu. + * Elle gère l'affichage du mot caché, des lettres incorrectes, des vies + * et permet de saisir des lettres. Un bouton permet de relancer le jeu. */ public class Action { + // Fenêtre principale du jeu + private JFrame gameFrame; + + // Labels pour afficher le mot, les lettres incorrectes et les vies + private JLabel wordLabel; + private JLabel incorrectLettersLabel; + private JLabel livesLabel; + + // Champ de saisie pour entrer une lettre + private JTextField letterInputField; + + // Instance du jeu + private Random_word game; + /** - * Constructeur. Crée et affiche la fenêtre du jeu avec un message. + * Constructeur de la classe Action. + * Initialise le jeu, les composants graphiques et affiche la fenêtre. */ public Action() { - // Création de la fenêtre du jeu - JFrame gameFrame = new JFrame("Hanging Man"); - gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - gameFrame.setSize(1040, 1000); - gameFrame.setLayout(new BorderLayout()); - - // Label de bienvenue - JLabel label = new JLabel("Bienvenue dans le jeu !"); - label.setFont(new Font("Arial", Font.BOLD, 28)); - label.setHorizontalAlignment(SwingConstants.CENTER); - - // Ajouter le label et afficher la fenêtre - gameFrame.add(label, BorderLayout.CENTER); + game = new Random_word(); + initializeComponents(); + layoutComponents(); + letterInputField.addActionListener(e -> handleGuess()); gameFrame.setVisible(true); } + + // ==================== Initialisation des composants ==================== + + /** + * Initialise tous les composants graphiques : + * - Fenêtre principale + * - Labels pour le mot, les vies et les lettres incorrectes + * - Champ de saisie pour entrer les lettres + */ + private void initializeComponents() { + gameFrame = new JFrame("Hanging Man"); + gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + gameFrame.setSize(600, 400); + + wordLabel = new JLabel(game.getHiddenWord()); + wordLabel.setFont(new Font("Arial", Font.BOLD, 32)); + wordLabel.setHorizontalAlignment(SwingConstants.CENTER); + + livesLabel = new JLabel("Lives: " + game.getLives()); + livesLabel.setFont(new Font("Arial", Font.BOLD, 20)); + livesLabel.setHorizontalAlignment(SwingConstants.CENTER); + + incorrectLettersLabel = new JLabel("Incorrect letters: " + game.getIncorrectLetters()); + incorrectLettersLabel.setFont(new Font("Arial", Font.PLAIN, 20)); + incorrectLettersLabel.setHorizontalAlignment(SwingConstants.CENTER); + + letterInputField = new JTextField(3); + letterInputField.setFont(new Font("Arial", Font.PLAIN, 24)); + } + + // ==================== Layout des composants ==================== + + /** + * Dispose les composants dans la fenêtre principale en utilisant GridBagLayout. + * Sépare la fenêtre en deux parties : le haut (mot et vies) et le bas (lettres incorrectes, saisie, bouton restart) + */ + private void layoutComponents() { + JPanel mainPanel = new JPanel(new GridBagLayout()); + GridBagConstraints mainConstraints = new GridBagConstraints(); + mainConstraints.gridx = 0; + mainConstraints.fill = GridBagConstraints.BOTH; + mainConstraints.insets = new Insets(5, 5, 5, 5); + + JPanel topPanel = createTopPanel(); + mainConstraints.gridy = 0; + mainConstraints.weighty = 3.0; + mainConstraints.weightx = 1.0; + mainPanel.add(topPanel, mainConstraints); + + JPanel bottomPanel = createBottomPanel(); + mainConstraints.gridy = 1; + mainConstraints.weighty = 1.0; + mainPanel.add(bottomPanel, mainConstraints); + + gameFrame.add(mainPanel, BorderLayout.CENTER); + } + + /** + * Crée le panneau supérieur avec : + * - Le label des vies (à gauche) + * - Le label du mot caché (à droite) + * @return JPanel configuré pour la partie supérieure + */ + private JPanel createTopPanel() { + JPanel topPanel = new JPanel(new GridBagLayout()); + + // Panel pour les vies + JPanel livesPanel = new JPanel(new GridBagLayout()); + GridBagConstraints livesConstraints = new GridBagConstraints(); + livesConstraints.anchor = GridBagConstraints.CENTER; + livesConstraints.fill = GridBagConstraints.NONE; + livesPanel.add(livesLabel, livesConstraints); + + // Panel pour le mot + JPanel wordPanel = new JPanel(new GridBagLayout()); + GridBagConstraints wordConstraints = new GridBagConstraints(); + wordConstraints.anchor = GridBagConstraints.CENTER; + wordConstraints.fill = GridBagConstraints.NONE; + wordPanel.add(wordLabel, wordConstraints); + + // Ajout des panels dans topPanel + GridBagConstraints leftConstraints = new GridBagConstraints(); + leftConstraints.gridx = 0; + leftConstraints.weightx = 0.5; + leftConstraints.weighty = 1.0; + leftConstraints.fill = GridBagConstraints.BOTH; + topPanel.add(livesPanel, leftConstraints); + + GridBagConstraints rightConstraints = new GridBagConstraints(); + rightConstraints.gridx = 1; + rightConstraints.weightx = 0.5; + rightConstraints.weighty = 1.0; + rightConstraints.fill = GridBagConstraints.BOTH; + topPanel.add(wordPanel, rightConstraints); + + return topPanel; + } + + /** + * Crée le panneau inférieur avec : + * - Le label des lettres incorrectes + * - Le champ de saisie pour entrer une lettre + * - Le bouton "Restart" en bas à droite + * @return JPanel configuré pour la partie inférieure + */ + private JPanel createBottomPanel() { + JPanel bottomPanel = new JPanel(); + bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.Y_AXIS)); + + // Label des lettres incorrectes + incorrectLettersLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + bottomPanel.add(incorrectLettersLabel); + + // Champ de saisie + JLabel promptLabel = new JLabel("Enter a letter:"); + promptLabel.setFont(new Font("Arial", Font.PLAIN, 16)); + promptLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + JPanel inputRow = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5)); + inputRow.add(promptLabel); + inputRow.add(letterInputField); + inputRow.setAlignmentX(Component.CENTER_ALIGNMENT); + + bottomPanel.add(Box.createVerticalStrut(10)); + bottomPanel.add(inputRow); + + // Bouton "Restart" en bas à droite + JButton restartButton = new JButton("Restart"); + restartButton.addActionListener(new PlayButtonListener(gameFrame)); + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.add(restartButton); + + bottomPanel.add(Box.createVerticalStrut(10)); + bottomPanel.add(buttonPanel); + + return bottomPanel; + } + + // ==================== Gestion du jeu ==================== + + /** + * Gère la saisie d'une lettre par le joueur. + * Vérifie la validité de la lettre, met à jour l'affichage et termine le jeu si nécessaire. + */ + private void handleGuess() { + String inputText = letterInputField.getText(); + letterInputField.setText(""); + + if (!isValidInput(inputText)) return; + + char guessedLetter = inputText.charAt(0); + String message = game.guessLetter(guessedLetter); + updateUI(); + + if (game.isGameOver()) endGame(message); + } + + /** + * Vérifie si la saisie de l'utilisateur est valide : + * - Une seule lettre + * - Caractère alphabétique + * @param inputText texte saisi par l'utilisateur + * @return true si la saisie est valide, false sinon + */ + private boolean isValidInput(String inputText) { + if (inputText.length() != 1 || !Character.isLetter(inputText.charAt(0))) { + JOptionPane.showMessageDialog(gameFrame, "Please enter a single letter!"); + return false; + } + return true; + } + + /** + * Met à jour l'affichage des labels : + * - Mot caché + * - Lettres incorrectes + * - Nombre de vies restantes + */ + private void updateUI() { + wordLabel.setText(game.getHiddenWord()); + incorrectLettersLabel.setText("Incorrect letters: " + game.getIncorrectLetters()); + livesLabel.setText("Lives: " + game.getLives()); + } + + /** + * Termine le jeu et affiche un message. + * Désactive le champ de saisie. + * @param message Message à afficher (victoire ou défaite) + */ + private void endGame(String message) { + JOptionPane.showMessageDialog(gameFrame, message); + letterInputField.setEditable(false); + } } diff --git a/src/fr/iut/Projet/Random_word.java b/src/fr/iut/Projet/Random_word.java index bb59230..bc57dcd 100644 --- a/src/fr/iut/Projet/Random_word.java +++ b/src/fr/iut/Projet/Random_word.java @@ -5,132 +5,107 @@ import java.util.*; public class Random_word { - public static void main(String[] args) { - // Récupère un mot aléatoire depuis le fichier Word.txt - String randomword = getRandomWord(); + private String secretWord; + private char[] hiddenWord; + private Set lettersGuessed; + private Set incorrectLetters; + private int lives; - if (randomword == null) { - System.err.println("Impossible de choisir un mot aléatoire !"); - return; + /** + * Constructeur : sélectionne un mot aléatoire et initialise le jeu. + */ + public Random_word() { + this.secretWord = getRandomWord(); + if (this.secretWord == null) { + throw new RuntimeException("Impossible de choisir un mot aléatoire !"); } - - // Démarre le jeu avec le mot choisi - play(randomword); + this.lives = 8; + this.hiddenWord = new char[secretWord.length()]; + Arrays.fill(this.hiddenWord, '_'); + this.lettersGuessed = new HashSet<>(); + this.incorrectLetters = new HashSet<>(); } /** * Lit les mots dans "Word.txt" et retourne un mot aléatoire. - * @return un mot aléatoire en minuscules, ou null si le fichier est introuvable ou vide */ - public static String getRandomWord() { - // Charge le fichier Word.txt depuis le package + private String getRandomWord() { InputStream is = Random_word.class.getResourceAsStream("Word.txt"); - - if (is == null) { - System.err.println("Le fichier 'Word.txt' est introuvable dans le package !"); - return null; - } + if (is == null) return null; String randomWord = null; Random random = new Random(); - - try (BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(is))) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { String line; int count = 0; - // Parcourt chaque ligne du fichier - while ((line = bufferedreader.readLine()) != null) { + while ((line = br.readLine()) != null) { line = line.trim(); if (!line.isEmpty()) { count++; - // Sélection aléatoire d'un mot (méthode reservoir sampling) if (random.nextInt(count) == 0) { randomWord = line; } } } } catch (IOException e) { - System.err.println("Erreur lors de la lecture du fichier : " + e.getMessage()); + e.printStackTrace(); } - return randomWord != null ? randomWord.toLowerCase() : null; } /** - * Affiche l'état actuel du mot et les lettres incorrectes. - * @param hiddenWord le mot masqué avec les lettres découvertes - * @param incorrectLetters les lettres déjà essayées mais incorrectes + * Tente une lettre, met à jour l'état du jeu et retourne un message. */ - private static void showwordstatus(char[] hiddenWord, Set incorrectLetters) { - System.out.print("\nMot actuel : "); - for (char Character : hiddenWord) { - System.out.print(Character + " "); + public String guessLetter(char letter) { + letter = Character.toLowerCase(letter); + if (lettersGuessed.contains(letter) || incorrectLetters.contains(letter)) { + return "Vous avez déjà essayé cette lettre !"; + } + + if (secretWord.indexOf(letter) >= 0) { + lettersGuessed.add(letter); + for (int i = 0; i < secretWord.length(); i++) { + if (secretWord.charAt(i) == letter) { + hiddenWord[i] = letter; + } + } + if (isWon()) { + return "Félicitations ! Vous avez trouvé le mot : " + secretWord; + } + return "Bien joué !"; + } else { + incorrectLetters.add(letter); + lives--; + if (lives <= 0) { + return "Vous avez perdu ! Le mot était : " + secretWord; + } + return "Mauvaise lettre ! Il vous reste " + lives + " vies."; } - System.out.println(); - System.out.println("Lettres incorrectes : " + incorrectLetters); } - /** - * Logique principale du jeu : gère les essais, met à jour l'état du mot, - * suit le nombre de vies restantes, et détermine la victoire ou la défaite. - * @param secretWord le mot que le joueur doit deviner - */ - public static void play(String secretWord) { - Scanner scanner = new Scanner(System.in); - Set lettersGuessed = new HashSet<>(); - Set incorrectLetters = new HashSet<>(); - int lives = 8; // nombre d'essais + public String getHiddenWord() { + StringBuilder sb = new StringBuilder(); + for (char c : hiddenWord) sb.append(c).append(' '); + return sb.toString().trim(); + } - char[] hiddenWord = new char[secretWord.length()]; - Arrays.fill(hiddenWord, '_'); + public String getIncorrectLetters() { + return incorrectLetters.toString(); + } - System.out.println("Bienvenue dans le jeu du mot mystère !"); - System.out.println("Le mot contient " + secretWord.length() + " lettres."); + public int getLives() { + return lives; + } - while (lives > 0) { - // Affiche le mot actuel et les lettres incorrectes - showwordstatus(hiddenWord, incorrectLetters); + public boolean isGameOver() { + return lives <= 0 || isWon(); + } - System.out.print("Entrez une lettre : "); - String input = scanner.nextLine().toLowerCase(); + public boolean isWon() { + return secretWord.equals(String.valueOf(hiddenWord)); + } - // Vérifie que l'entrée est valide - if (input.length() != 1 || !Character.isLetter(input.charAt(0))) { - System.out.println("Entrez une seule lettre !"); - continue; - } - - char letter = input.charAt(0); - - // Vérifie si la lettre a déjà été essayée - if (lettersGuessed.contains(letter) || incorrectLetters.contains(letter)) { - System.out.println("Vous avez déjà essayé cette lettre !"); - continue; - } - - // Vérifie si la lettre fait partie du mot secret - if (secretWord.indexOf(letter) >= 0) { - lettersGuessed.add(letter); - for (int i = 0; i < secretWord.length(); i++) { - if (secretWord.charAt(i) == letter) { - hiddenWord[i] = letter; - } - } - System.out.println("Bien joué !"); - } else { - // Lettre incorrecte : décrémente le nombre de vies - incorrectLetters.add(letter); - lives--; - System.out.println("Mauvaise lettre ! Il vous reste " + lives + " vies."); - } - - // Vérifie si le mot complet a été trouvé - if (String.valueOf(hiddenWord).equals(secretWord)) { - System.out.println("\nFélicitations ! Vous avez trouvé le mot : " + secretWord); - return; - } - } - - // Le joueur a perdu - System.out.println("\nVous avez perdu ! Le mot était : " + secretWord); + public String getSecretWord() { + return secretWord; } }