Compare commits

...

3 Commits

Author SHA1 Message Date
2dc9caee23 Merge branch 'JANNAIRE' 2025-10-08 17:08:13 +02:00
01835f841a incroyable 2025-10-08 17:07:11 +02:00
d8647f43ac reparation peut etre 2025-10-08 17:02:53 +02:00
21 changed files with 183 additions and 129 deletions

View File

@@ -2,95 +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.
* Déclenche un chargement paresseux (lazy-load) si nécessaire.
*/
public static String random() {
ensureLoaded();
return CACHE.get(RNG.nextInt(CACHE.size()));
}
/**
* Recharge les mots depuis le fichier. Utile si modification de mots.txt à chaud.
*/
public static synchronized void reload() {
CACHE = loadFromFileOrDefault();
}
/** Garantit que le cache est initialisé. */
private static void ensureLoaded() {
if (CACHE == null) {
synchronized (Words.class) {
if (CACHE == null) {
CACHE = loadFromFileOrDefault();
}
}
}
}
/** Tente de charger depuis le fichier, sinon renvoie la liste par défaut. */
private static List<String> loadFromFileOrDefault() {
List<String> fromFile = readUtf8Lines(WORDS_PATH);
if (fromFile.isEmpty()) return DEFAULT;
return fromFile;
}
/**
* Lit toutes les lignes UTF-8 depuis le chemin fourni,
* en filtrant vides et commentaires (# ...).
*/
private static List<String> readUtf8Lines(Path path) {
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;
}
/* Retourne la liste complète des mots disponibles */
public static List<String> all() { public static List<String> all() {
ensureLoaded(); ensureLoaded();
return new ArrayList<>(CACHE); return new ArrayList<>(CACHE);
} }
/* Petit utilitaire pour afficher un mot masqué dans les messages */ /** Renvoie un mot aléatoire dans tout le dictionnaire. */
public static String hiddenWord(Game g) { public static String random() {
return g.maskedWord().replace(' ', '_'); ensureLoaded();
return CACHE.get(RNG.nextInt(CACHE.size()));
}
/** Renvoie un mot aléatoire de moins de 8 lettres (sinon bascule sur random()). */
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() {
CACHE = loadFromFileOrDefault();
}
// -------------------- internes --------------------
/** Charge le cache si nécessaire (thread-safe). */
private static void ensureLoaded() {
if (CACHE == null) {
synchronized (Words.class) {
if (CACHE == null) CACHE = loadFromFileOrDefault();
}
}
}
/** Tente chaque chemin candidat, sinon retourne DEFAULT mélangé. */
private static List<String> loadFromFileOrDefault() {
List<String> data = readFirstExistingCandidate();
if (data.isEmpty()) {
data = new ArrayList<>(DEFAULT);
}
Collections.shuffle(data, RNG); // casse tout déterminisme initial
return data;
}
/** Lit le premier fichier existant parmi les candidats. */
private static List<String> readFirstExistingCandidate() {
for (Path p : CANDIDATES) {
List<String> list = readUtf8Trimmed(p);
if (!list.isEmpty()) return list;
}
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

@@ -9,64 +9,67 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Interface graphique du jeu du pendu avec gestion des difficultés. * Interface graphique du pendu avec niveaux :
* - Niveau 1 : mots de moins de 8 lettres * - 1 : mots < 8 lettres
* - Niveau 2 : mots de 8 lettres ou plus * - 2 : mots 8 lettres
* - Niveau 3 : deux mots à deviner à la suite (un court + un long) * - 3 : deux mots (score + chrono cumulés)
* Toutes les méthodes ≤ 50 lignes. * 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, wordLabel, triedLabel, scoreLabel, timeLabel; private JLabel imgLabel, wordLabel, triedLabel, scoreLabel, timeLabel;
private JTextField input; private JTextField input;
private JButton tryBtn, quitBtn, menuBtn; private JButton tryBtn, newBtn, menuBtn, quitBtn;
private Game game; private Game game;
private List<String> words; private List<String> words;
private int index = 0; private int index = 0;
private int level; private final int level;
private String currentWord = ""; private String currentWord = "";
private Timer timer; private Timer timer;
/** Constructeur : reçoit la difficulté (1, 2 ou 3) */ // Cumul de session (niveau 3)
private long sessionStartNano = -1L;
private int sessionScore = 0;
/** Reçoit la difficulté (1, 2, 3). */
public GameUI(int level) { public GameUI(int level) {
this.level = level; this.level = level;
} }
/** Affiche la fenêtre et lance la partie (ou séquence) */ /** Affiche la fenêtre et lance la session. */
public void show() { public void show() {
setupWindow(); setupWindow();
setupLayout(); setupLayout();
setupActions(); setupActions();
prepareWords(); startNewSession();
startRound();
frame.setVisible(true); frame.setVisible(true);
} }
/** Prépare la liste des mots selon la difficulté choisie */ /** Démarre une nouvelle session (nouveaux mots, reset chrono/score session). */
private void prepareWords() { private void startNewSession() {
words = new ArrayList<String>(); sessionStartNano = System.nanoTime();
List<String> all = Words.all(); sessionScore = 0;
index = 0;
if (level == 1) { // mots courts prepareWords();
for (String w : all) if (w.length() < 8) words.add(w); startRound();
} else if (level == 2) { // mots longs
for (String w : all) if (w.length() >= 8) words.add(w);
} else if (level == 3) { // un court + un long
String shortW = null, longW = null;
for (String w : all) {
if (shortW == null && w.length() < 8) shortW = w;
if (longW == null && w.length() >= 8) longW = w;
if (shortW != null && longW != null) break;
}
if (shortW != null) words.add(shortW);
if (longW != null) words.add(longW);
}
if (words.isEmpty()) words.add(Words.random());
} }
/** Crée la fenêtre principale */ /** Ppare 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() { 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);
@@ -75,7 +78,7 @@ public class GameUI {
frame.setLayout(new BorderLayout(12, 12)); frame.setLayout(new BorderLayout(12, 12));
} }
/** Construit la mise en page et les composants */ /** Layout + composants. */
private void setupLayout() { private void setupLayout() {
imgLabel = new JLabel("", SwingConstants.CENTER); imgLabel = new JLabel("", SwingConstants.CENTER);
frame.add(imgLabel, BorderLayout.CENTER); frame.add(imgLabel, BorderLayout.CENTER);
@@ -90,14 +93,15 @@ public class GameUI {
top.add(buildTopLine(triedLabel, timeLabel)); top.add(buildTopLine(triedLabel, timeLabel));
frame.add(top, BorderLayout.NORTH); frame.add(top, BorderLayout.NORTH);
// --- Bas : boutons + saisie
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)); JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
menuBtn = new JButton("Menu"); menuBtn = new JButton("Menu");
@@ -110,7 +114,7 @@ public class GameUI {
frame.add(bottom, BorderLayout.SOUTH); frame.add(bottom, BorderLayout.SOUTH);
} }
/** Construit une ligne (label gauche + espace + label droit) */ /** Ligne dinfos (gauche/droite). */
private JPanel buildTopLine(JLabel left, JLabel right) { private JPanel buildTopLine(JLabel left, JLabel right) {
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT)); JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
p.add(left); p.add(left);
@@ -119,16 +123,17 @@ public class GameUI {
return p; return p;
} }
/** Branche les actions + timer */ /** Actions + timer. */
private void setupActions() { private void setupActions() {
tryBtn.addActionListener(this::onTry); tryBtn.addActionListener(this::onTry);
input.addActionListener(this::onTry); input.addActionListener(this::onTry);
quitBtn.addActionListener(e -> frame.dispose()); newBtn.addActionListener(e -> startNewSession());
menuBtn.addActionListener(e -> returnToMenu()); menuBtn.addActionListener(e -> returnToMenu());
quitBtn.addActionListener(e -> frame.dispose());
timer = new Timer(1000, e -> refreshStatsOnly()); timer = new Timer(1000, e -> refreshStatsOnly());
} }
/** Retour au menu principal */ /** Retour vers le menu de sélection. */
private void returnToMenu() { private void returnToMenu() {
timer.stop(); timer.stop();
frame.dispose(); frame.dispose();
@@ -136,11 +141,12 @@ public class GameUI {
menu.show(); menu.show();
} }
/** Démarre un nouveau mot (ou termine au niveau 3) */ /** Démarre un nouveau mot (ou termine la session si plus de mots). */
private void startRound() { private void startRound() {
if (index >= words.size()) { if (index >= words.size()) {
showMsg("🎉 Niveau terminé !"); int total = (level == 3) ? sessionScore : game.getScore();
frame.dispose(); long secs = (level == 3) ? getSessionSeconds() : game.getElapsedSeconds();
showMsg("Niveau terminé !\nScore total : " + total + "\nTemps : " + secs + "s");
return; return;
} }
currentWord = words.get(index++); currentWord = words.get(index++);
@@ -151,7 +157,7 @@ public class GameUI {
refreshUI(); refreshUI();
} }
/** Tente une lettre (clic bouton ou Entrée) */ /** 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)) {
@@ -167,24 +173,28 @@ public class GameUI {
checkEnd(); checkEnd();
} }
/** Vérifie la fin du mot et enchaîne si besoin (niveau 3) */ /** Fin du mot → affiche popup et enchaîne (lvl 3 cumule score/chrono). */
private void checkEnd() { private void checkEnd() {
if (!(game.isWin() || game.isLose())) return; if (!(game.isWin() || game.isLose())) return;
timer.stop();
game.end(game.isWin()); game.end(game.isWin());
String verdict = game.isWin() ? "Bravo !" : "Perdu !"; int displayScore = (level == 3) ? sessionScore + game.getScore() : game.getScore();
String msg = verdict + " Le mot était : " + currentWord long displaySecs = (level == 3) ? getSessionSeconds() : game.getElapsedSeconds();
+ "\nScore : " + game.getScore()
+ "\nTemps : " + game.getElapsedSeconds() + "s"; showMsg((game.isWin() ? "Bravo !" : "Perdu !")
showMsg(msg); + " Le mot était : " + currentWord
+ "\nScore : " + displayScore
+ "\nTemps : " + displaySecs + "s");
if (level == 3 && index < words.size()) { if (level == 3 && index < words.size()) {
sessionScore += game.getScore();
startRound(); startRound();
} else {
timer.stop();
} }
} }
/** Rafraîchit image + textes + stats */ /** 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());
@@ -193,13 +203,23 @@ public class GameUI {
frame.repaint(); frame.repaint();
} }
/** Rafraîchit uniquement score + chrono */ /** Refresh stats (score/chrono). */
private void refreshStatsOnly() { private void refreshStatsOnly() {
scoreLabel.setText("Score : " + game.getScore()); int s = (level == 3) ? sessionScore + game.getScore() : game.getScore();
timeLabel.setText("Temps : " + game.getElapsedSeconds() + "s"); long t = (level == 3) ? getSessionSeconds() : game.getElapsedSeconds();
scoreLabel.setText("Score : " + s);
timeLabel.setText("Temps : " + t + "s");
} }
/** Affiche un message */ /** 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) { private void showMsg(String msg) {
JOptionPane.showMessageDialog(frame, msg); JOptionPane.showMessageDialog(frame, msg);
} }

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.