10 Commits

Author SHA1 Message Date
01835f841a incroyable 2025-10-08 17:07:11 +02:00
d8647f43ac reparation peut etre 2025-10-08 17:02:53 +02:00
d75f05bee0 ajout d'un bouton retour 2025-10-08 16:20:13 +02:00
64108a95e9 niveau 2025-10-08 16:15:19 +02:00
c0d2e0e5e2 Merge branch 'master' into DUCREUX 2025-10-08 15:48:04 +02:00
78333b0c32 Merge branch 'JANNAIRE' 2025-10-08 15:42:18 +02:00
5b7f341011 rm ou 2025-10-08 15:41:41 +02:00
2c611bc6fd ajout du score et du chrono plus doc 2025-10-08 15:41:08 +02:00
8b079bc103 reparation 2025-10-08 15:26:45 +02:00
485b63357e rm 2025-10-08 15:11:02 +02:00
25 changed files with 427 additions and 255 deletions

View File

@@ -1,155 +1,132 @@
package front; package back;
import back.*; import java.util.ArrayList;
import java.util.HashSet;
import javax.swing.*; import java.util.List;
import java.awt.*; import java.util.Set;
import java.awt.event.ActionEvent;
/** /**
* Interface graphique du jeu du pendu. * Logique principale du jeu du pendu (back).
* (Toutes les méthodes ≤ 50 lignes) * Ajoute un score et un chronomètre.
*
* Règles de score (simples) :
* - Lettre correcte : +10 points
* - Lettre incorrecte: -5 points (le score ne descend pas sous 0)
* - Bonus victoire : nombre d'erreure restante sur 6 fois 10
* - Bonus temps : Chaque seconde restante avant 60s vaut 2 points si tu met plus de 60 secondes tu n'a pas de bonus
* - Défaite : pas de bonus
*
* Chronomètre :
* - Démarre à la création de la partie
* - S'arrête définitivement à la fin (victoire/défaite)
*/ */
public class GameUI { public class Game {
private JFrame frame; private final String word;
private JLabel imgLabel, wordLabel, triedLabel, scoreLabel, timeLabel; private final Set<Character> correct = new HashSet<>();
private JTextField input; private final Set<Character> all = new HashSet<>();
private JButton tryBtn, newGameBtn; private final int maxErrors;
private Game game; private int errors;
private String currentWord;
private Timer timer;
/** Lance la fenêtre et démarre une partie */ // --- Score & chrono ---
public void show() { private int score = 0;
setupWindow(); private long startNano; // début de partie (System.nanoTime)
setupLayout(); private long endNano = -1L; // fin (sinon -1 = en cours)
setupActions();
startNewGame(); public Game(String word, int maxErrors) {
frame.setVisible(true); this.word = word.toLowerCase();
this.maxErrors = maxErrors;
this.startNano = System.nanoTime(); // démarre le chrono à la création
} }
/** Crée la fenêtre principale */ /** Tente une lettre et renvoie le résultat + ajuste le score */
private void setupWindow() { public Result play(char letter) {
frame = new JFrame("Jeu du Pendu"); char c = Character.toLowerCase(letter);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); if (all.contains(c)) return Result.ALREADY;
frame.setSize(560, 560);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout(12, 12));
}
/** Construit les composants et le layout */ all.add(c);
private void setupLayout() { if (word.indexOf(c) >= 0) {
imgLabel = new JLabel("", SwingConstants.CENTER); correct.add(c);
frame.add(imgLabel, BorderLayout.CENTER); addScore(10);
// Si la lettre trouvée fait gagner immédiatement, on finalise ici
wordLabel = new JLabel("Mot : "); if (isWin()) end(true);
triedLabel = new JLabel("Lettres essayées : "); return Result.HIT;
scoreLabel = new JLabel("Score : 0"); } else {
timeLabel = new JLabel("Temps : 0s"); addScore(-5);
if (isLose()) end(false);
JPanel top = new JPanel(new GridLayout(2, 1)); return Result.MISS;
top.add(buildTopLine(wordLabel, scoreLabel));
top.add(buildTopLine(triedLabel, timeLabel));
frame.add(top, BorderLayout.NORTH);
JPanel bottom = new JPanel(new BorderLayout(8, 8));
JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
input = new JTextField(5);
tryBtn = new JButton("Essayer");
newGameBtn = new JButton("Nouvelle partie");
inputPanel.add(new JLabel("Lettre :"));
inputPanel.add(input);
inputPanel.add(tryBtn);
bottom.add(inputPanel, BorderLayout.WEST);
bottom.add(newGameBtn, BorderLayout.EAST);
frame.add(bottom, BorderLayout.SOUTH);
}
/** Crée une ligne du haut avec 2 labels */
private JPanel buildTopLine(JLabel left, JLabel right) {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel.add(left);
panel.add(Box.createHorizontalStrut(24));
panel.add(right);
return panel;
}
/** Ajoute les actions et le timer */
private void setupActions() {
tryBtn.addActionListener(this::onTry);
input.addActionListener(this::onTry);
newGameBtn.addActionListener(e -> startNewGame());
timer = new Timer(1000, e -> refreshStatsOnly());
}
/** Démarre une nouvelle partie */
private void startNewGame() {
currentWord = Words.random();
game = new Game(currentWord, 7);
input.setText("");
input.requestFocusInWindow();
if (!timer.isRunning()) timer.start();
refreshUI();
}
/** Gère le clic ou l'appui sur Entrée */
private void onTry(ActionEvent e) {
String text = input.getText();
if (!Check.isLetter(text)) {
showMsg("Tape une seule lettre (A-Z).");
input.requestFocusInWindow();
input.selectAll();
return;
}
Result res = game.play(Character.toLowerCase(text.charAt(0)));
handleResult(res);
input.setText("");
refreshUI();
checkEnd();
}
/** Réagit selon le résultat d'une tentative */
private void handleResult(Result res) {
switch (res) {
case ALREADY:
showMsg("Lettre déjà utilisée.");
break;
case HIT:
case MISS:
break; // rien, juste refresh
} }
} }
/** Vérifie si la partie est finie */ /** Retourne le mot masqué avec les lettres trouvées */
private void checkEnd() { public String maskedWord() {
if (game.isWin() || game.isLose()) { StringBuilder sb = new StringBuilder();
timer.stop(); for (int i = 0; i < word.length(); i++) {
game.end(game.isWin()); char c = word.charAt(i);
String msg = (game.isWin() ? "Bravo !" : "Perdu !") if (!Character.isLetter(c)) sb.append(c);
+ " Le mot était : " + currentWord else if (correct.contains(c)) sb.append(c);
+ "\nScore final : " + game.getScore() else sb.append('_');
+ "\nTemps : " + game.getElapsedSeconds() + "s"; if (i < word.length() - 1) sb.append(' ');
showMsg(msg); }
return sb.toString();
}
/** Vérifie si le joueur a gagné */
public boolean isWin() {
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (Character.isLetter(c) && !correct.contains(c)) return false;
}
return true;
}
/** Vérifie si le joueur a perdu */
public boolean isLose() {
return errors >= maxErrors;
}
/** Nombre d'erreurs actuelles */
public int getErrors() { return errors; }
/** Liste les lettres déjà essayées */
public List<String> triedLetters() {
List<Character> sorted = new ArrayList<>(all);
sorted.sort(Character::compareTo);
List<String> out = new ArrayList<>();
for (Character ch : sorted) out.add(String.valueOf(ch));
return out;
}
// ---------- Score & Chrono ----------
/** Retourne le score courant */
public int getScore() { return score; }
/** Secondes écoulées depuis le début (si finie, temps figé) */
public long getElapsedSeconds() {
long end = (endNano > 0L) ? endNano : System.nanoTime();
long deltaNs = end - startNano;
if (deltaNs < 0) deltaNs = 0;
return deltaNs / 1_000_000_000L;
}
/** Termine la partie (victoire/défaite) et applique le bonus si gagné */
public void end(boolean win) {
if (endNano > 0L) return; // déjà terminé
endNano = System.nanoTime();
if (win) {
int remaining = Math.max(0, maxErrors - errors);
int timeBonus = (int) Math.max(0, 60 - getElapsedSeconds()) * 2;
addScore(remaining * 10 + timeBonus);
} }
} }
/** Met à jour tout l'affichage */ // --- utilitaires privés ---
private void refreshUI() { private void addScore(int delta) {
imgLabel.setIcon(Gallows.icon(game.getErrors())); if (delta < 0) {
wordLabel.setText("Mot : " + game.maskedWord()); // lettre ratée => +1 erreur
triedLabel.setText("Lettres essayées : " + String.join(", ", game.triedLetters())); errors++;
refreshStatsOnly();
frame.repaint();
} }
score += delta;
/** Met à jour uniquement score + chrono */ if (score < 0) score = 0;
private void refreshStatsOnly() {
scoreLabel.setText("Score : " + game.getScore());
timeLabel.setText("Temps : " + game.getElapsedSeconds() + "s");
}
/** Affiche une boîte de message */
private void showMsg(String msg) {
JOptionPane.showMessageDialog(frame, msg);
} }
} }

View File

@@ -2,84 +2,129 @@ package back;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
/** /**
* Fournit les mots pour le jeu. * Gère la bibliothèque de mots (chargement + sélection aléatoire).
* - Lit d'abord "bibliothèque/mots.txt" (UTF-8), 1 mot par ligne. * - Lit "bibliothèque/mots.txt" OU "Bibliotheque/mots.txt" (UTF-8), 1 mot par ligne.
* - Ignore les lignes vides et celles qui commencent par '#'. * - Ignore lignes vides et commentaires (#...).
* - Si le fichier est introuvable ou vide, bascule sur une liste par défaut. * - Fournit des helpers pour tirer des mots selon la difficulté.
*/ */
public class Words { public class Words {
/** Chemin du fichier de mots (relatif à la racine du projet). */ /** Chemins possibles (accents/casse) pour maximiser la compatibilité. */
private static final Path WORDS_PATH = Paths.get("Bibliotheque", "mots.txt"); private static final Path[] CANDIDATES = new Path[] {
Paths.get("bibliothèque", "mots.txt"),
Paths.get("Bibliotheque", "mots.txt")
};
/** Liste de secours si le fichier n'est pas disponible. */ /** Liste de secours si aucun fichier trouvé/valide. */
private static final List<String> DEFAULT = List.of( private static final List<String> DEFAULT = new ArrayList<>();
"algorithm", "variable", "function", "interface", "inheritance", static {
"exception", "compiler", "database", "network", "architecture", Collections.addAll(DEFAULT,
"iteration", "recursion", "encryption", "framework", "protocol" "algorithm","variable","function","interface","inheritance",
"exception","compiler","database","network","architecture",
"iteration","recursion","encryption","framework","protocol",
"java","pendu","ordinateur","developpement","interface"
); );
}
/** RNG partagé et cache des mots chargés. */ /** Cache mémoire + RNG. */
private static final SecureRandom RNG = new SecureRandom();
private static volatile List<String> CACHE = null; private static volatile List<String> CACHE = null;
private static final SecureRandom RNG = new SecureRandom();
/** /** Renvoie la liste complète (copie) — charge une seule fois si nécessaire. */
* Retourne un mot choisi au hasard depuis le fichier ou la liste par défaut. public static List<String> all() {
* Déclenche un chargement paresseux (lazy-load) si nécessaire. ensureLoaded();
*/ return new ArrayList<>(CACHE);
}
/** Renvoie un mot aléatoire dans tout le dictionnaire. */
public static String random() { public static String random() {
ensureLoaded(); ensureLoaded();
return CACHE.get(RNG.nextInt(CACHE.size())); return CACHE.get(RNG.nextInt(CACHE.size()));
} }
/** /** Renvoie un mot aléatoire de moins de 8 lettres (sinon bascule sur random()). */
* Recharge les mots depuis le fichier. Utile si modification de mots.txt à chaud. public static String randomShortWord() {
*/ ensureLoaded();
List<String> list = new ArrayList<>();
for (String w : CACHE) if (w.length() < 8) list.add(w);
if (list.isEmpty()) return random();
return list.get(RNG.nextInt(list.size()));
}
/** Renvoie un mot aléatoire de 8 lettres ou plus (sinon bascule sur random()). */
public static String randomLongWord() {
ensureLoaded();
List<String> list = new ArrayList<>();
for (String w : CACHE) if (w.length() >= 8) list.add(w);
if (list.isEmpty()) return random();
return list.get(RNG.nextInt(list.size()));
}
/** Renvoie une paire [court, long] pour le niveau difficile. */
public static List<String> randomPair() {
List<String> pair = new ArrayList<>(2);
pair.add(randomShortWord());
pair.add(randomLongWord());
return pair;
}
/** Force le rechargement du fichier (au cas où le contenu change en cours dexécution). */
public static synchronized void reload() { public static synchronized void reload() {
CACHE = loadFromFileOrDefault(); CACHE = loadFromFileOrDefault();
} }
/** Garantit que le cache est initialisé. */ // -------------------- internes --------------------
/** Charge le cache si nécessaire (thread-safe). */
private static void ensureLoaded() { private static void ensureLoaded() {
if (CACHE == null) { if (CACHE == null) {
synchronized (Words.class) { synchronized (Words.class) {
if (CACHE == null) { if (CACHE == null) CACHE = loadFromFileOrDefault();
CACHE = loadFromFileOrDefault();
}
} }
} }
} }
/** Tente de charger depuis le fichier, sinon renvoie la liste par défaut. */ /** Tente chaque chemin candidat, sinon retourne DEFAULT mélangé. */
private static List<String> loadFromFileOrDefault() { private static List<String> loadFromFileOrDefault() {
List<String> fromFile = readUtf8Lines(WORDS_PATH); List<String> data = readFirstExistingCandidate();
if (fromFile.isEmpty()) return DEFAULT; if (data.isEmpty()) {
return fromFile; data = new ArrayList<>(DEFAULT);
}
Collections.shuffle(data, RNG); // casse tout déterminisme initial
return data;
} }
/** /** Lit le premier fichier existant parmi les candidats. */
* Lit toutes les lignes UTF-8 depuis le chemin fourni, private static List<String> readFirstExistingCandidate() {
* en filtrant vides et commentaires (# ...). for (Path p : CANDIDATES) {
*/ List<String> list = readUtf8Trimmed(p);
private static List<String> readUtf8Lines(Path path) { if (!list.isEmpty()) return list;
List<String> result = new ArrayList<>();
try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
lines.map(String::trim)
.filter(s -> !s.isEmpty())
.filter(s -> !s.startsWith("#"))
.forEach(result::add);
} catch (IOException e) {
// Silencieux : on basculera sur DEFAULT
} }
return result; return new ArrayList<>();
}
/** Lit un fichier UTF-8, filtre vides/commentaires, force lowercase. */
private static List<String> readUtf8Trimmed(Path path) {
List<String> out = new ArrayList<>();
try {
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
for (String raw : lines) {
if (raw == null) continue;
String s = raw.trim().toLowerCase();
if (s.isEmpty() || s.startsWith("#")) continue;
// On accepte lettres/accentuées + tiret (simple et robuste)
if (s.matches("[a-zàâçéèêëîïôûùüÿñæœ-]+")) out.add(s);
}
} catch (IOException ignored) {
// on tombera sur DEFAULT
}
return out;
} }
} }

View File

@@ -1,125 +1,226 @@
package front; package front;
import back.*; import back.*;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
/** /**
* Interface graphique (Swing) du jeu du pendu. * Interface graphique du pendu avec niveaux :
* - Affiche une image différente à chaque erreur. * - 1 : mots < 8 lettres
* - Champ de saisie pour entrer une lettre. * - 2 : mots ≥ 8 lettres
* - Texte pour le mot masqué et les lettres essayées. * - 3 : deux mots (score + chrono cumulés)
* Boutons : Essayer / Nouvelle partie / Menu / Quitter.
* (Toutes les méthodes ≤ 50 lignes)
*/ */
public class GameUI { public class GameUI {
private JFrame frame; private JFrame frame;
private JLabel imgLabel; private JLabel imgLabel, wordLabel, triedLabel, scoreLabel, timeLabel;
private JLabel wordLabel;
private JLabel triedLabel;
private JTextField input; private JTextField input;
private JButton tryBtn; private JButton tryBtn, newBtn, menuBtn, quitBtn;
private JButton newGameBtn;
private Game game; private Game game;
private String currentWord; private List<String> words;
private int index = 0;
private final int level;
private String currentWord = "";
private Timer timer;
/** Affiche la fenêtre principale */ // Cumul de session (niveau 3)
private long sessionStartNano = -1L;
private int sessionScore = 0;
/** Reçoit la difficulté (1, 2, 3). */
public GameUI(int level) {
this.level = level;
}
/** Affiche la fenêtre et lance la session. */
public void show() { public void show() {
setupUI(); setupWindow();
startNewGame(); setupLayout();
setupActions();
startNewSession();
frame.setVisible(true); frame.setVisible(true);
} }
/** Initialise les composants Swing */ /** Démarre une nouvelle session (nouveaux mots, reset chrono/score session). */
private void setupUI() { private void startNewSession() {
sessionStartNano = System.nanoTime();
sessionScore = 0;
index = 0;
prepareWords();
startRound();
}
/** Prépare la liste des mots selon le niveau (aléatoire géré dans Words). */
private void prepareWords() {
words = new ArrayList<>();
if (level == 1) {
words.add(Words.randomShortWord());
} else if (level == 2) {
words.add(Words.randomLongWord());
} else if (level == 3) {
words = Words.randomPair(); // [court, long] aléatoires
}
if (words.isEmpty()) words.add(Words.random()); // filet de sécurité
}
/** Fenêtre principale. */
private void setupWindow() {
frame = new JFrame("Jeu du Pendu"); frame = new JFrame("Jeu du Pendu");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(520, 520); frame.setSize(560, 560);
frame.setLocationRelativeTo(null); frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout(12, 12)); frame.setLayout(new BorderLayout(12, 12));
}
// Image du pendu /** Layout + composants. */
private void setupLayout() {
imgLabel = new JLabel("", SwingConstants.CENTER); imgLabel = new JLabel("", SwingConstants.CENTER);
frame.add(imgLabel, BorderLayout.CENTER); frame.add(imgLabel, BorderLayout.CENTER);
// Panneau bas: saisie + actions wordLabel = new JLabel("Mot : ");
triedLabel = new JLabel("Lettres essayées : ");
scoreLabel = new JLabel("Score : 0");
timeLabel = new JLabel("Temps : 0s");
JPanel top = new JPanel(new GridLayout(2, 1));
top.add(buildTopLine(wordLabel, scoreLabel));
top.add(buildTopLine(triedLabel, timeLabel));
frame.add(top, BorderLayout.NORTH);
JPanel bottom = new JPanel(new BorderLayout(8, 8)); JPanel bottom = new JPanel(new BorderLayout(8, 8));
JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
input = new JTextField(5); input = new JTextField(5);
tryBtn = new JButton("Essayer"); tryBtn = new JButton("Essayer");
newBtn = new JButton("Nouvelle partie");
inputPanel.add(new JLabel("Lettre :")); inputPanel.add(new JLabel("Lettre :"));
inputPanel.add(input); inputPanel.add(input);
inputPanel.add(tryBtn); inputPanel.add(tryBtn);
inputPanel.add(newBtn);
JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
menuBtn = new JButton("Menu");
quitBtn = new JButton("Quitter");
actionPanel.add(menuBtn);
actionPanel.add(quitBtn);
newGameBtn = new JButton("Nouvelle partie");
bottom.add(inputPanel, BorderLayout.WEST); bottom.add(inputPanel, BorderLayout.WEST);
bottom.add(newGameBtn, BorderLayout.EAST); bottom.add(actionPanel, BorderLayout.EAST);
frame.add(bottom, BorderLayout.SOUTH); frame.add(bottom, BorderLayout.SOUTH);
// Panneau haut: mot + lettres essayées
JPanel top = new JPanel();
top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
wordLabel = new JLabel("Mot : ");
triedLabel = new JLabel("Lettres essayées : ");
wordLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
triedLabel.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
top.add(wordLabel);
top.add(triedLabel);
frame.add(top, BorderLayout.NORTH);
// Actions
tryBtn.addActionListener(this::onTry);
input.addActionListener(this::onTry);
newGameBtn.addActionListener(e -> startNewGame());
} }
/** Démarre une nouvelle partie */ /** Ligne dinfos (gauche/droite). */
private void startNewGame() { private JPanel buildTopLine(JLabel left, JLabel right) {
currentWord = Words.random(); JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
p.add(left);
p.add(Box.createHorizontalStrut(24));
p.add(right);
return p;
}
/** Actions + timer. */
private void setupActions() {
tryBtn.addActionListener(this::onTry);
input.addActionListener(this::onTry);
newBtn.addActionListener(e -> startNewSession());
menuBtn.addActionListener(e -> returnToMenu());
quitBtn.addActionListener(e -> frame.dispose());
timer = new Timer(1000, e -> refreshStatsOnly());
}
/** Retour vers le menu de sélection. */
private void returnToMenu() {
timer.stop();
frame.dispose();
MenuUI menu = new MenuUI();
menu.show();
}
/** Démarre un nouveau mot (ou termine la session si plus de mots). */
private void startRound() {
if (index >= words.size()) {
int total = (level == 3) ? sessionScore : game.getScore();
long secs = (level == 3) ? getSessionSeconds() : game.getElapsedSeconds();
showMsg("Niveau terminé !\nScore total : " + total + "\nTemps : " + secs + "s");
return;
}
currentWord = words.get(index++);
game = new Game(currentWord, 7); game = new Game(currentWord, 7);
input.setText(""); input.setText("");
input.requestFocusInWindow(); input.requestFocusInWindow();
if (!timer.isRunning()) timer.start();
refreshUI(); refreshUI();
} }
/** Gère le clic sur "Essayer" ou Entrée dans le champ */ /** Tente une lettre (bouton/Entrée). */
private void onTry(ActionEvent e) { private void onTry(ActionEvent e) {
String text = input.getText(); String text = input.getText();
if (!Check.isLetter(text)) { if (!Check.isLetter(text)) {
JOptionPane.showMessageDialog(frame, "Tape une seule lettre (A-Z)."); showMsg("Tape une seule lettre (A-Z).");
input.requestFocusInWindow(); input.requestFocusInWindow();
input.selectAll(); input.selectAll();
return; return;
} }
char c = Character.toLowerCase(text.charAt(0)); Result res = game.play(Character.toLowerCase(text.charAt(0)));
Result res = game.play(c); if (res == Result.ALREADY) showMsg("Lettre déjà utilisée.");
switch (res) {
case ALREADY: JOptionPane.showMessageDialog(frame, "Lettre déjà utilisée.");
break;
case HIT:
break;
case MISS:
break;
}
input.setText(""); input.setText("");
refreshUI(); refreshUI();
checkEnd();
}
if (game.isWin()) { /** Fin du mot → affiche popup et enchaîne (lvl 3 cumule score/chrono). */
refreshUI(); private void checkEnd() {
JOptionPane.showMessageDialog(frame, "Bravo ! Le mot était : " + currentWord); if (!(game.isWin() || game.isLose())) return;
} else if (game.isLose()) {
refreshUI(); game.end(game.isWin());
JOptionPane.showMessageDialog(frame, "Perdu ! Le mot était : " + currentWord); int displayScore = (level == 3) ? sessionScore + game.getScore() : game.getScore();
long displaySecs = (level == 3) ? getSessionSeconds() : game.getElapsedSeconds();
showMsg((game.isWin() ? "Bravo !" : "Perdu !")
+ " Le mot était : " + currentWord
+ "\nScore : " + displayScore
+ "\nTemps : " + displaySecs + "s");
if (level == 3 && index < words.size()) {
sessionScore += game.getScore();
startRound();
} else {
timer.stop();
} }
} }
/** Met à jour l'image et les textes selon l'état courant */ /** Refresh complet. */
private void refreshUI() { private void refreshUI() {
imgLabel.setIcon(Gallows.icon(game.getErrors())); imgLabel.setIcon(Gallows.icon(game.getErrors()));
wordLabel.setText("Mot : " + game.maskedWord()); wordLabel.setText("Mot : " + game.maskedWord());
triedLabel.setText("Lettres essayées : " + String.join(", ", game.triedLetters())); triedLabel.setText("Lettres essayées : " + String.join(", ", game.triedLetters()));
refreshStatsOnly();
frame.repaint(); frame.repaint();
} }
/** Refresh stats (score/chrono). */
private void refreshStatsOnly() {
int s = (level == 3) ? sessionScore + game.getScore() : game.getScore();
long t = (level == 3) ? getSessionSeconds() : game.getElapsedSeconds();
scoreLabel.setText("Score : " + s);
timeLabel.setText("Temps : " + t + "s");
}
/** Secondes écoulées depuis le début de la session (niveau 3). */
private long getSessionSeconds() {
long now = System.nanoTime();
long delta = now - sessionStartNano;
if (delta < 0) delta = 0;
return delta / 1_000_000_000L;
}
/** Popup info. */
private void showMsg(String msg) {
JOptionPane.showMessageDialog(frame, msg);
}
} }

View File

@@ -0,0 +1,50 @@
package front;
import javax.swing.*;
import java.awt.*;
/*
* Menu de démarrage du jeu du pendu.
* Permet de choisir la difficulté (facile, moyen ou difficile).
*/
public class MenuUI {
private JFrame frame;
/**
* Interface graphique de la page d'accueil du jeu du pendu.
*/
public void show() {
frame = new JFrame("Jeu du Pendu - Sélection de la difficulté");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout(12, 12));
JLabel title = new JLabel("Choisis une difficulté", SwingConstants.CENTER);
title.setFont(new Font("Arial", Font.BOLD, 20));
frame.add(title, BorderLayout.NORTH);
JPanel buttons = new JPanel(new GridLayout(3, 1, 10, 10));
JButton easyBtn = new JButton("Niveau Facile");
JButton mediumBtn = new JButton("Niveau Moyen");
JButton hardBtn = new JButton("Niveau Difficile");
buttons.add(easyBtn);
buttons.add(mediumBtn);
buttons.add(hardBtn);
frame.add(buttons, BorderLayout.CENTER);
easyBtn.addActionListener(e -> startGame(1));
mediumBtn.addActionListener(e -> startGame(2));
hardBtn.addActionListener(e -> startGame(3));
frame.setVisible(true);
}
/* Lance le jeu avec le niveau choisi */
private void startGame(int level) {
frame.dispose(); // ferme le menu
GameUI ui = new GameUI(level);
ui.show();
}
}

View File

@@ -1,17 +1,16 @@
package main; package main;
import front.GameUI; import front.MenuUI;
/** /**
* Point d'entrée du programme. * Point d'entrée du programme.
* Lance l'interface graphique du jeu du pendu. * Affiche le menu de sélection avant de lancer le jeu.
*/ */
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
// Démarre l'UI Swing sur le thread de l'EDT
javax.swing.SwingUtilities.invokeLater(() -> { javax.swing.SwingUtilities.invokeLater(() -> {
GameUI ui = new GameUI(); MenuUI menu = new MenuUI();
ui.show(); menu.show();
}); });
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
out/back/Check.class Normal file

Binary file not shown.

BIN
out/back/Game.class Normal file

Binary file not shown.

BIN
out/back/Result.class Normal file

Binary file not shown.

BIN
out/back/Words.class Normal file

Binary file not shown.

Binary file not shown.

BIN
out/front/Gallows.class Normal file

Binary file not shown.

BIN
out/front/GameUI$1.class Normal file

Binary file not shown.

BIN
out/front/GameUI.class Normal file

Binary file not shown.

BIN
out/main/Main.class Normal file

Binary file not shown.