Compare commits

3 Commits

11 changed files with 306 additions and 139 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -6,180 +6,204 @@ import java.awt.*;
/**
* Classe principale qui gère l'interface graphique du jeu du pendu.
*
* Cette classe crée la fenêtre du jeu, affiche :
* - le mot caché avec les lettres devinées,
* - les lettres incorrectes,
* - le dessin du pendu (via {@link Affiche}),
* et permet à l'utilisateur de saisir des lettres.
* Elle gère également la logique de mise à jour, la fin de partie,
* le redémarrage et le changement de difficulté en utilisant {@link PlayButtonListener}.
* Affiche le mot caché, les lettres incorrectes, le dessin du pendu, le timer et le score.
* Gère également les interactions avec le joueur, la fin de partie et le redémarrage.
*/
public class Action {
/** Fenêtre principale du jeu */
private Score score;
private JFrame gameFrame;
/** Label affichant le mot caché */
private JLabel wordLabel;
/** Label affichant les lettres incorrectes */
private JLabel incorrectLettersLabel;
/** Champ de texte pour saisir une lettre */
private JTextField letterInputField;
/** Instance du jeu avec la logique (mot secret, lettres, vies, etc.) */
private Random_word game;
/** Composant graphique qui dessine le pendu */
private Affiche affiche;
/** Niveau de difficulté courant ("facile", "moyen", "difficile") */
private GameTimer gameTimer;
private String difficulty;
/**
* Constructeur de la classe Action.
* Initialise le jeu avec la difficulté choisie, crée et dispose les composants.
*
* @param difficulty Niveau de difficulté pour le mot à deviner.
* Constructeur : initialise le jeu selon la difficulté choisie,
* crée tous les composants graphiques et démarre le timer.
* @param difficulty Niveau de difficulté ("facile", "moyen", "difficile", "hell")
*/
public Action(String difficulty) {
this.difficulty = difficulty;
// Création du jeu avec le mot choisi selon la difficulté
game = new Random_word(difficulty);
// Initialisation des composants graphiques
// Initialisation
initializeComponents();
layoutComponents();
setupListeners();
// Gestion de la saisie utilisateur
letterInputField.addActionListener(e -> handleGuess());
// Affiche la fenêtre
// Démarre le timer et affiche la fenêtre
gameTimer.start();
gameFrame.setVisible(true);
}
// ==================== Initialisation des composants ====================
// ==================== Initialisation ====================
/**
* Initialise tous les composants graphiques du jeu :
* - fenêtre principale
* - dessin du pendu
* - labels pour le mot et les lettres incorrectes
* - champ de saisie
*/
/** Initialise les composants principaux : fenêtre, jeu, timer et score */
private void initializeComponents() {
initializeFrameAndPanels();
initializeGameComponents();
initializeTimerAndScore();
}
/** Initialise la fenêtre principale */
private void initializeFrameAndPanels() {
gameFrame = new JFrame("Hanging Man - " + difficulty);
gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameFrame.setSize(700, 500);
}
// Composant graphique pour le pendu
/** Initialise les composants du jeu : pendu, mot caché, lettres incorrectes, champ de saisie */
private void initializeGameComponents() {
affiche = new Affiche();
affiche.setPreferredSize(new Dimension(350, 400));
affiche.setBackground(Color.WHITE);
affiche.setOpaque(true);
// Label affichant le mot caché
wordLabel = new JLabel(game.getHiddenWord());
wordLabel.setFont(new Font("Arial", Font.BOLD, 32));
wordLabel.setHorizontalAlignment(SwingConstants.CENTER);
// Label affichant les lettres incorrectes
incorrectLettersLabel = new JLabel("Incorrect letters: " + game.getIncorrectLetters());
incorrectLettersLabel.setFont(new Font("Arial", Font.PLAIN, 20));
incorrectLettersLabel.setHorizontalAlignment(SwingConstants.CENTER);
// Champ pour saisir une lettre
letterInputField = new JTextField(3);
letterInputField.setFont(new Font("Arial", Font.PLAIN, 24));
}
/** Initialise le timer et le score, avec un listener pour diminuer le score chaque seconde */
private void initializeTimerAndScore() {
gameTimer = new GameTimer();
score = new Score();
// Listener pour diminuer le score chaque seconde et finir après 60s
gameTimer.setTimerListener(() -> {
score.decreaseBySecond(); // comportement normal selon le temps
// Si 60 secondes écoulées et que la partie n'est pas finie
if (gameTimer.getSecondsElapsed() >= 60 && !game.isGameOver()) {
score.setScoreToZero(); // on met le score à 0
endGame("Temps écoulé ! Vous avez perdu !");
}
});
}
/** Ajoute les listeners sur les composants */
private void setupListeners() {
letterInputField.addActionListener(e -> handleGuess());
}
// ==================== Mise en page ====================
/**
* Dispose tous les composants graphiques dans la fenêtre.
* Utilise un BorderLayout principal avec :
* - panneau gauche pour le dessin du pendu
* - panneau droit pour les interactions utilisateur
*/
/** Organise la mise en page globale (BorderLayout : gauche pendu, droite info et saisie) */
private void layoutComponents() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(createLeftPanel(), BorderLayout.WEST);
mainPanel.add(createRightPanel(), BorderLayout.CENTER);
gameFrame.add(mainPanel);
}
// Panneau gauche : dessin du pendu
/** Crée le panneau gauche avec le dessin du pendu */
private JPanel createLeftPanel() {
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.add(affiche, BorderLayout.CENTER);
mainPanel.add(leftPanel, BorderLayout.WEST);
return leftPanel;
}
// Panneau droit : interface du jeu (mot, lettres, saisie, boutons)
/** Crée le panneau droit avec mot, lettres incorrectes, saisie, boutons et timer/score */
private JPanel createRightPanel() {
JPanel rightPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(5, 5, 5, 5);
// Label du mot caché
addWordAndIncorrectLetters(rightPanel, gbc);
addInputRow(rightPanel, gbc);
addButtons(rightPanel, gbc);
addTimerAndScore(rightPanel, gbc);
return rightPanel;
}
/** Ajoute le mot caché et les lettres incorrectes dans le panneau */
private void addWordAndIncorrectLetters(JPanel panel, GridBagConstraints gbc) {
gbc.gridy = 0;
gbc.weighty = 1.0;
rightPanel.add(wordLabel, gbc);
panel.add(wordLabel, gbc);
// Label des lettres incorrectes
gbc.gridy = 1;
rightPanel.add(incorrectLettersLabel, gbc);
panel.add(incorrectLettersLabel, gbc);
}
// Champ de saisie
/** Ajoute le champ de saisie pour les lettres */
private void addInputRow(JPanel panel, GridBagConstraints gbc) {
gbc.gridy = 2;
JPanel inputRow = new JPanel();
inputRow.add(new JLabel("Enter a letter:"));
inputRow.add(letterInputField);
rightPanel.add(inputRow, gbc);
panel.add(inputRow, gbc);
}
// Bouton de redémarrage
/** Ajoute les boutons restart et changement de difficulté */
private void addButtons(JPanel panel, GridBagConstraints gbc) {
gbc.gridy = 3;
JButton restartButton = new JButton("Restart");
restartButton.addActionListener(e -> restartGame());
rightPanel.add(restartButton, gbc);
panel.add(restartButton, gbc);
// Bouton pour changer la difficulté
gbc.gridy = 4;
JButton changeDifficultyButton = new JButton("Changer la difficulté");
changeDifficultyButton.addActionListener(e -> showDifficultyDialog());
rightPanel.add(changeDifficultyButton, gbc);
mainPanel.add(rightPanel, BorderLayout.CENTER);
// Ajout du panneau principal à la fenêtre
gameFrame.add(mainPanel);
panel.add(changeDifficultyButton, gbc);
}
// ==================== Gestion du jeu ====================
/** Ajoute le timer et le score en haut à droite */
private void addTimerAndScore(JPanel panel, GridBagConstraints gbc) {
gbc.gridy = 5;
JPanel topRightPanel = new JPanel(new BorderLayout());
topRightPanel.add(gameTimer.getTimerLabel(), BorderLayout.WEST);
topRightPanel.add(score.getScoreLabel(), BorderLayout.EAST);
panel.add(topRightPanel, gbc);
}
/**
* Traite la saisie d'une lettre par l'utilisateur.
* Met à jour l'état du jeu et l'affichage.
*/
// ==================== Gestion des actions ====================
/** Traite la saisie d'une lettre par le joueur */
private void handleGuess() {
String inputText = letterInputField.getText();
letterInputField.setText(""); // efface le champ après saisie
letterInputField.setText("");
if (!isValidInput(inputText)) return;
char guessedLetter = inputText.charAt(0);
String message = game.guessLetter(guessedLetter);
// Mise à jour de l'interface
updateScore(message);
updateUI();
// Si la partie est terminée, affiche le résultat
if (game.isGameOver()) endGame(message);
if (game.isGameOver()) {
if (game.isWon()) {
score.increaseByWordFound();
}
endGame(message);
}
}
/**
* Vérifie que l'utilisateur a saisi une seule lettre valide.
*
* @param inputText texte saisi
* @return true si la saisie est valide
*/
/** Met à jour le score selon le résultat de la lettre */
private void updateScore(String message) {
if (message.contains("Bien joué")) {
score.increaseByCorrectLetter();
} else if (message.contains("Mauvaise lettre")) {
score.decreaseByWrongLetter();
}
}
/** Vérifie que la saisie est une seule lettre */
private boolean isValidInput(String inputText) {
if (inputText.length() != 1 || !Character.isLetter(inputText.charAt(0))) {
JOptionPane.showMessageDialog(gameFrame, "Please enter a single letter!");
@@ -188,56 +212,39 @@ public class Action {
return true;
}
/**
* Met à jour l'affichage du mot caché, des lettres incorrectes et du dessin du pendu.
*/
/** Met à jour l'affichage du mot, des lettres incorrectes et des vies du pendu */
private void updateUI() {
wordLabel.setText(game.getHiddenWord());
incorrectLettersLabel.setText("Incorrect letters: " + game.getIncorrectLetters());
affiche.setLives(game.getLives());
}
/**
* Termine la partie et affiche le message de résultat.
* Désactive le champ de saisie.
*
* @param message message à afficher (victoire/défaite)
*/
/** Termine la partie, arrête le timer et affiche le message */
private void endGame(String message) {
gameTimer.stop();
if (game.isWon()) {
affiche.setYouWin(true);
} else {
affiche.setLives(0);
score.setScoreToZero(); // <- on met le score à 0 en cas de défaite
}
JOptionPane.showMessageDialog(gameFrame, message);
letterInputField.setEditable(false);
}
// ==================== Redémarrage ====================
/**
* Redémarre le jeu avec la même difficulté en utilisant PlayButtonListener.
*/
/** Redémarre le jeu avec la même difficulté */
private void restartGame() {
gameFrame.dispose(); // ferme la fenêtre actuelle
// Crée un JFrame temporaire pour utiliser PlayButtonListener
gameFrame.dispose();
JFrame tempFrame = new JFrame();
new PlayButtonListener(tempFrame, difficulty).actionPerformed(null);
// tempFrame ne sera jamais affiché, il sert juste à passer la référence
}
// ==================== Changement de difficulté ====================
/**
* Affiche une boîte de dialogue pour choisir une nouvelle difficulté
* et relance une partie avec cette difficulté en utilisant PlayButtonListener.
*/
/** Affiche le dialogue pour changer la difficulté et relance le jeu */
private void showDifficultyDialog() {
String[] options = {"Facile", "Moyen", "Difficile"};
// Boîte de dialogue avec trois options
String[] options = {"Facile", "Moyen", "Difficile", "Hell"};
int choice = JOptionPane.showOptionDialog(
gameFrame,
"Choisissez la difficulté :",
@@ -249,28 +256,16 @@ public class Action {
options[0]
);
// Si l'utilisateur a choisi une option
if (choice >= 0) {
String newDifficulty;
switch (choice) {
case 0:
newDifficulty = "facile";
break;
case 1:
newDifficulty = "moyen";
break;
case 2:
newDifficulty = "difficile";
break;
default:
newDifficulty = "moyen";
break;
case 0: newDifficulty = "facile"; break;
case 1: newDifficulty = "moyen"; break;
case 2: newDifficulty = "difficile"; break;
case 3: newDifficulty = "hell"; break;
default: newDifficulty = "moyen"; break;
}
// Ferme la fenêtre actuelle
gameFrame.dispose();
// Crée un JFrame temporaire pour utiliser PlayButtonListener
JFrame tempFrame = new JFrame();
new PlayButtonListener(tempFrame, newDifficulty).actionPerformed(null);
}

View File

@@ -29,9 +29,10 @@ public class Display {
JButton easyButton = new JButton("Facile");
JButton mediumButton = new JButton("Moyen");
JButton hardButton = new JButton("Difficile");
JButton hellButton = new JButton("Hell");
// Mise en forme des boutons
JButton[] buttons = { easyButton, mediumButton, hardButton };
JButton[] buttons = { easyButton, mediumButton, hardButton, hellButton};
for (JButton button : buttons) {
button.setAlignmentX(Component.CENTER_ALIGNMENT);
button.setPreferredSize(new Dimension(300, 100));
@@ -43,6 +44,7 @@ public class Display {
easyButton.addActionListener(new PlayButtonListener(frame, "facile"));
mediumButton.addActionListener(new PlayButtonListener(frame, "moyen"));
hardButton.addActionListener(new PlayButtonListener(frame, "difficile"));
hellButton.addActionListener(new PlayButtonListener(frame, "hell"));
// Ajout des composants à la fenêtre
frame.add(Box.createVerticalGlue());
@@ -53,6 +55,8 @@ public class Display {
frame.add(mediumButton);
frame.add(Box.createVerticalStrut(20));
frame.add(hardButton);
frame.add(Box.createVerticalStrut(20));
frame.add(hellButton);
frame.add(Box.createVerticalGlue());
// Affichage de la fenêtre

View File

@@ -0,0 +1,79 @@
package fr.iut.Projet;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Classe GameTimer qui gère le temps écoulé sur un mot.
*
* Affiche le temps en secondes dans un JLabel et peut déclencher un listener
* à chaque seconde (par exemple pour réduire le score ou vérifier la limite de temps).
*/
public class GameTimer {
/** Label affichant le temps écoulé */
private JLabel timerLabel;
/** Timer Swing qui incrémente le temps chaque seconde */
private Timer timer;
/** Compteur de secondes écoulées */
private int secondsElapsed;
/** Listener appelé à chaque tick (chaque seconde) */
private Runnable timerListener;
/** Constructeur : crée le label et initialise le timer */
public GameTimer() {
this.secondsElapsed = 0;
this.timerLabel = new JLabel("Temps : 0 s");
timerLabel.setHorizontalAlignment(SwingConstants.RIGHT);
// Timer qui déclenche l'action toutes les 1000ms (1 seconde)
this.timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
secondsElapsed++;
timerLabel.setText("Temps : " + secondsElapsed + " s");
// Appel du listener pour gérer le score ou la fin de partie
if (timerListener != null) {
timerListener.run();
}
}
});
}
/** Définit le listener appelé à chaque seconde */
public void setTimerListener(Runnable listener) {
this.timerListener = listener;
}
/** Démarre le timer */
public void start() {
secondsElapsed = 0;
timer.start();
}
/** Arrête le timer */
public void stop() {
timer.stop();
}
/** Remet le compteur à zéro */
public void reset() {
secondsElapsed = 0;
timerLabel.setText("Temps : 0 s");
}
/** @return le JLabel contenant le temps écoulé */
public JLabel getTimerLabel() {
return timerLabel;
}
/** @return le temps écoulé en secondes */
public int getSecondsElapsed() {
return secondsElapsed;
}
}

View File

@@ -6,16 +6,17 @@ import java.util.*;
/**
* Classe gérant la logique du jeu du pendu.
*
* Cette classe choisit un mot aléatoire selon la difficulté,
* garde en mémoire le mot secret, les lettres devinées,
* les lettres incorrectes, et le nombre de vies restantes.
* Cette classe sélectionne un mot (ou une combinaison de mots) aléatoire
* selon la difficulté, conserve l'état du jeu (mot secret, lettres devinées,
* lettres incorrectes, nombre de vies) et fournit des méthodes pour deviner
* des lettres et vérifier l'état du jeu.
*/
public class Random_word {
/** Mot secret que le joueur doit deviner */
private String secretWord;
/** Tableau représentant le mot caché avec les lettres découvertes */
/** Tableau représentant le mot caché avec les lettres découvertes et espaces */
private char[] hiddenWord;
/** Lettres correctement devinées par le joueur */
@@ -31,19 +32,37 @@ public class Random_word {
* Constructeur : sélectionne un mot aléatoire selon la difficulté
* et initialise les structures du jeu.
*
* @param difficulty Niveau de difficulté ("facile", "moyen", "difficile")
* @param difficulty Niveau de difficulté ("facile", "moyen", "difficile", "hell")
* @throws RuntimeException si aucun mot n'a pu être choisi
*/
public Random_word(String difficulty) {
this.secretWord = getRandomWord(difficulty);
if (difficulty.equalsIgnoreCase("hell")) {
// En mode Hell, on choisit deux mots faciles et on les concatène avec un espace
String firstWord = getRandomWord("facile");
String secondWord = getRandomWord("facile");
if (firstWord == null || secondWord == null) {
throw new RuntimeException("Impossible de choisir deux mots pour le mode Hell !");
}
this.secretWord = firstWord + " " + secondWord;
} else {
// Pour les autres difficultés, on choisit un seul mot correspondant
this.secretWord = getRandomWord(difficulty);
}
if (this.secretWord == null) {
throw new RuntimeException("Impossible de choisir un mot aléatoire !");
}
this.lives = 8; // nombre de vies par défaut
this.lives = 8; // nombre de vies par défaut
this.hiddenWord = new char[secretWord.length()];
Arrays.fill(this.hiddenWord, '_'); // initialise le mot caché avec des underscores
// Initialise le mot caché : underscore pour les lettres, espace conservé
for (int i = 0; i < secretWord.length(); i++) {
hiddenWord[i] = (secretWord.charAt(i) == ' ') ? ' ' : '_';
}
this.lettersGuessed = new HashSet<>();
this.incorrectLetters = new HashSet<>();
}
@@ -90,7 +109,7 @@ public class Random_word {
line = line.trim();
if (!line.isEmpty()) {
count++;
// Choix aléatoire d'une ligne avec la méthode de Reservoir Sampling
// Choix aléatoire d'une ligne avec Reservoir Sampling
if (random.nextInt(count) == 0) {
randomWord = line;
}
@@ -105,10 +124,10 @@ public class Random_word {
/**
* Tente une lettre proposée par le joueur.
* Met à jour l'état du jeu (mot caché, lettres incorrectes, vies).
* Met à jour l'état du jeu (mot caché, lettres incorrectes, vies restantes).
*
* @param letter lettre proposée
* @return message d'information pour le joueur
* @return message d'information pour le joueur (succès, erreur ou fin de partie)
*/
public String guessLetter(char letter) {
letter = Character.toLowerCase(letter);
@@ -118,11 +137,11 @@ public class Random_word {
return "Vous avez déjà essayé cette lettre !";
}
// Si la lettre est dans le mot secret
// Lettre correcte
if (secretWord.indexOf(letter) >= 0) {
lettersGuessed.add(letter);
// Remplace les underscores correspondants par la lettre devinée
// Remplace les underscores par la lettre devinée
for (int i = 0; i < secretWord.length(); i++) {
if (secretWord.charAt(i) == letter) {
hiddenWord[i] = letter;

View File

@@ -0,0 +1,70 @@
package fr.iut.Projet;
import javax.swing.*;
import java.awt.*;
/**
* Classe Score qui gère le score du joueur.
*
* Commence à 100, diminue à chaque seconde et à chaque erreur,
* augmente à chaque bonne lettre et ajoute un bonus si le mot est trouvé.
* Si le joueur perd, le score devient 0.
*/
public class Score {
private int currentScore;
private JLabel scoreLabel;
/** Constructeur : initialise le score à 100 */
public Score() {
this.currentScore = 100;
this.scoreLabel = new JLabel("Score : " + currentScore);
this.scoreLabel.setFont(new Font("Arial", Font.BOLD, 16));
this.scoreLabel.setHorizontalAlignment(SwingConstants.RIGHT);
}
/** Décrémente le score de 1 point chaque seconde */
public void decreaseBySecond() {
currentScore = Math.max(0, currentScore - 1);
updateLabel();
}
/** Décrémente le score de 5 points pour une mauvaise lettre */
public void decreaseByWrongLetter() {
currentScore = Math.max(0, currentScore - 5);
updateLabel();
}
/** Incrémente le score de 10 points pour une bonne lettre */
public void increaseByCorrectLetter() {
currentScore += 10;
updateLabel();
}
/** Incrémente le score de 50 points si le mot est trouvé */
public void increaseByWordFound() {
currentScore += 50;
updateLabel();
}
/** Met le score à zéro (utilisé si le joueur perd) */
public void setScoreToZero() {
currentScore = 0;
updateLabel();
}
/** Met à jour le texte du JLabel */
private void updateLabel() {
scoreLabel.setText("Score : " + currentScore);
}
/** @return le JLabel contenant le score */
public JLabel getScoreLabel() {
return scoreLabel;
}
/** @return le score actuel */
public int getCurrentScore() {
return currentScore;
}
}