Compare commits
	
		
			35 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8b5b30ea7b | |||
| 960c8c7878 | |||
| 2fe044753a | |||
| 2dc9caee23 | |||
| 01835f841a | |||
| d8647f43ac | |||
| cc14043782 | |||
| d75f05bee0 | |||
| 64108a95e9 | |||
| c0d2e0e5e2 | |||
| 78333b0c32 | |||
| 5b7f341011 | |||
| 2c611bc6fd | |||
| 8b079bc103 | |||
| 790c3faff2 | |||
| 3f8a86866a | |||
| ee85d6f8f4 | |||
| d2dd0bb982 | |||
| 485b63357e | |||
| ca5c12a7ba | |||
| a7d9f17ba8 | |||
| 9b7ed446b2 | |||
| b707e37787 | |||
| 6f74af39d9 | |||
| 972ff65f63 | |||
| d74e119af3 | |||
| d79aec8248 | |||
| b4c8b1d156 | |||
| e0f727d76b | |||
| bd4a204d25 | |||
| fae2ecf2c8 | |||
| 7679f1f35b | |||
| 93e31b0aac | |||
| fd393bfc85 | |||
| 89aad809a1 | 
							
								
								
									
										13
									
								
								Jeu_pendu/Back/Check.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | ||||
| package back; | ||||
|  | ||||
| /** | ||||
|  * Vérifie la validité des entrées utilisateur. | ||||
|  */ | ||||
| public class Check { | ||||
|     /** Retourne vrai si la saisie est une seule lettre alphabétique */ | ||||
|     public static boolean isLetter(String value) { | ||||
|         if (value == null) return false; | ||||
|         String trimmed = value.trim(); | ||||
|         return trimmed.length() == 1 && Character.isLetter(trimmed.charAt(0)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										132
									
								
								Jeu_pendu/Back/Game.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,132 @@ | ||||
| package back; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| /** | ||||
|  * Logique principale du jeu du pendu (back). | ||||
|  * 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 Game { | ||||
|     private final String word; | ||||
|     private final Set<Character> correct = new HashSet<>(); | ||||
|     private final Set<Character> all = new HashSet<>(); | ||||
|     private final int maxErrors; | ||||
|     private int errors; | ||||
|  | ||||
|     // --- Score & chrono --- | ||||
|     private int score = 0; | ||||
|     private long startNano;       // début de partie (System.nanoTime) | ||||
|     private long endNano = -1L;   // fin (sinon -1 = en cours) | ||||
|  | ||||
|     public Game(String word, int maxErrors) { | ||||
|         this.word = word.toLowerCase(); | ||||
|         this.maxErrors = maxErrors; | ||||
|         this.startNano = System.nanoTime(); // démarre le chrono à la création | ||||
|     } | ||||
|  | ||||
|     /** Tente une lettre et renvoie le résultat + ajuste le score */ | ||||
|     public Result play(char letter) { | ||||
|         char c = Character.toLowerCase(letter); | ||||
|         if (all.contains(c)) return Result.ALREADY; | ||||
|  | ||||
|         all.add(c); | ||||
|         if (word.indexOf(c) >= 0) { | ||||
|             correct.add(c); | ||||
|             addScore(10); | ||||
|             // Si la lettre trouvée fait gagner immédiatement, on finalise ici | ||||
|             if (isWin()) end(true); | ||||
|             return Result.HIT; | ||||
|         } else { | ||||
|             addScore(-5); | ||||
|             if (isLose()) end(false); | ||||
|             return Result.MISS; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** Retourne le mot masqué avec les lettres trouvées */ | ||||
|     public String maskedWord() { | ||||
|         StringBuilder sb = new StringBuilder(); | ||||
|         for (int i = 0; i < word.length(); i++) { | ||||
|             char c = word.charAt(i); | ||||
|             if (!Character.isLetter(c)) sb.append(c); | ||||
|             else if (correct.contains(c)) sb.append(c); | ||||
|             else sb.append('_'); | ||||
|             if (i < word.length() - 1) sb.append(' '); | ||||
|         } | ||||
|         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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // --- utilitaires privés --- | ||||
|     private void addScore(int delta) { | ||||
|         if (delta < 0) { | ||||
|             // lettre ratée => +1 erreur | ||||
|             errors++; | ||||
|         } | ||||
|         score += delta; | ||||
|         if (score < 0) score = 0; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								Jeu_pendu/Back/Result.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| package back; | ||||
|  | ||||
| /** | ||||
|  * Résultat possible d'une tentative. | ||||
|  */ | ||||
| public enum Result { HIT, MISS, ALREADY } | ||||
							
								
								
									
										130
									
								
								Jeu_pendu/Back/Words.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,130 @@ | ||||
| package back; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.nio.file.*; | ||||
| import java.security.SecureRandom; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Gère la bibliothèque de mots (chargement + sélection aléatoire). | ||||
|  * - Lit "bibliothèque/mots.txt" OU "Bibliotheque/mots.txt" (UTF-8), 1 mot par ligne. | ||||
|  * - Ignore lignes vides et commentaires (#...). | ||||
|  * - Fournit des helpers pour tirer des mots selon la difficulté. | ||||
|  */ | ||||
| public class Words { | ||||
|  | ||||
|     /** Chemins possibles (accents/casse) pour maximiser la compatibilité. */ | ||||
|     private static final Path[] CANDIDATES = new Path[] { | ||||
|             Paths.get("bibliothèque", "mots.txt"), | ||||
|             Paths.get("Bibliotheque", "mots.txt") | ||||
|     }; | ||||
|  | ||||
|     /** Liste de secours si aucun fichier trouvé/valide. */ | ||||
|     private static final List<String> DEFAULT = new ArrayList<>(); | ||||
|     static { | ||||
|         Collections.addAll(DEFAULT, | ||||
|                 "algorithm","variable","function","interface","inheritance", | ||||
|                 "exception","compiler","database","network","architecture", | ||||
|                 "iteration","recursion","encryption","framework","protocol", | ||||
|                 "java","pendu","ordinateur","developpement","interface" | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** Cache mémoire + RNG. */ | ||||
|     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. */ | ||||
|     public static List<String> all() { | ||||
|         ensureLoaded(); | ||||
|         return new ArrayList<>(CACHE); | ||||
|     } | ||||
|  | ||||
|     /** Renvoie un mot aléatoire dans tout le dictionnaire. */ | ||||
|     public static String random() { | ||||
|         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 d’exé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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										835
									
								
								Jeu_pendu/Bibliotheque/mots.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										62
									
								
								Jeu_pendu/Front/Gallows.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,62 @@ | ||||
| package front; | ||||
|  | ||||
| import javax.swing.*; | ||||
| import java.awt.*; | ||||
| import java.io.File; | ||||
|  | ||||
| /** | ||||
|  * Gère les images du pendu (une par étape). | ||||
|  * - Charge les images depuis assets | ||||
|  */ | ||||
| public class Gallows { | ||||
|     /** Dossier d'assets relatif à la racine du projet */ | ||||
|     private static final String ASSETS_DIR = "assets" + File.separator + "images"; | ||||
|     /** Nombre d'étapes (0 = aucun membre, 7 = étape finale) */ | ||||
|     private static final int MAX_STAGE = 6; | ||||
|  | ||||
|     private static final ImageIcon[] ICONS = new ImageIcon[MAX_STAGE + 1]; | ||||
|  | ||||
|     static { | ||||
|         for (int i = 0; i <= MAX_STAGE; i++) { | ||||
|             String path = ASSETS_DIR + File.separator + i + ".png"; | ||||
|             File f = new File(path); | ||||
|             if (f.exists()) { | ||||
|                 ICONS[i] = new ImageIcon(path); | ||||
|             } else { | ||||
|                 // Placeholder si fichier manquant | ||||
|                 ICONS[i] = placeholder("Missing: " + i + ".png"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retourne l'icône correspondant au nombre d'erreurs. | ||||
|      */ | ||||
|     public static ImageIcon icon(int errors) { | ||||
|         int idx = Math.max(0, Math.min(errors, MAX_STAGE)); | ||||
|         return ICONS[idx]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Crée une petite image placeholder (si un asset est manquant). | ||||
|      */ | ||||
|     private static ImageIcon placeholder(String text) { | ||||
|         int w = 320, h = 240; | ||||
|         Image img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); | ||||
|         Graphics2D g = (Graphics2D) img.getGraphics(); | ||||
|         g.setColor(Color.LIGHT_GRAY); | ||||
|         g.fillRect(0, 0, w, h); | ||||
|         g.setColor(Color.DARK_GRAY); | ||||
|         g.drawRect(0, 0, w - 1, h - 1); | ||||
|         g.drawString(text, 10, h / 2); | ||||
|         g.dispose(); | ||||
|         return new ImageIcon(img); | ||||
|     } | ||||
|  | ||||
|     // Import manquant pour BufferedImage | ||||
|     private static class BufferedImage extends java.awt.image.BufferedImage { | ||||
|         public BufferedImage(int width, int height, int imageType) { | ||||
|             super(width, height, imageType); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										235
									
								
								Jeu_pendu/Front/GameUI.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,235 @@ | ||||
| package front; | ||||
|  | ||||
| import back.*; | ||||
|  | ||||
| import javax.swing.*; | ||||
| import java.awt.*; | ||||
| import java.awt.event.ActionEvent; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Interface graphique du pendu avec niveaux : | ||||
|  *  - facile : mots < 8 lettres | ||||
|  *  - moyen : mots ≥ 8 lettres | ||||
|  *  - difficile : deux mots (score + chrono cumulés) | ||||
|  * Boutons : Essayer / Nouvelle partie / Menu / Quitter. | ||||
|  * (Toutes les méthodes ≤ 50 lignes) | ||||
|  */ | ||||
| public class GameUI { | ||||
|     private JFrame frame; | ||||
|     private JLabel imgLabel, wordLabel, triedLabel, scoreLabel, timeLabel; | ||||
|     private JTextField input; | ||||
|     private JButton tryBtn, newBtn, menuBtn, quitBtn; | ||||
|  | ||||
|     private Game game; | ||||
|     private List<String> words; | ||||
|     private int index = 0; | ||||
|     private final int level; | ||||
|     private String currentWord = ""; | ||||
|     private Timer timer; | ||||
|  | ||||
|     // Cumul de session (niveau difficile) | ||||
|     private long sessionStartNano = -1L; | ||||
|     private int sessionScore = 0; | ||||
|  | ||||
|     /** Reçoit la difficulté (facile, moyen, difficile). */ | ||||
|     public GameUI(int level) { | ||||
|         this.level = level; | ||||
|     } | ||||
|  | ||||
|     /** Affiche la fenêtre et lance la session. */ | ||||
|     public void show() { | ||||
|         setupWindow(); | ||||
|         setupLayout(); | ||||
|         setupActions(); | ||||
|         startNewSession(); | ||||
|         frame.setVisible(true); | ||||
|     } | ||||
|  | ||||
|     /** Démarre une nouvelle session (nouveaux mots, reset chrono/score session). */ | ||||
|     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.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | ||||
|         frame.setSize(560, 560); | ||||
|         frame.setLocationRelativeTo(null); | ||||
|         frame.setLayout(new BorderLayout(12, 12)); | ||||
|     } | ||||
|  | ||||
|     /** Layout + composants. */ | ||||
|     private void setupLayout() { | ||||
|         imgLabel = new JLabel("", SwingConstants.CENTER); | ||||
|         frame.add(imgLabel, BorderLayout.CENTER); | ||||
|  | ||||
|         wordLabel = new JLabel("Mot : "); | ||||
|         triedLabel = new JLabel("Lettres essayées : "); | ||||
|         scoreLabel = new JLabel("Score : 0"); | ||||
|         timeLabel = new JLabel("Temps : 0s"); | ||||
|  | ||||
|         JLabel titleLabel = new JLabel("Sauver Michel!!", SwingConstants.CENTER); | ||||
|         titleLabel.setFont(new Font("Arial", Font.BOLD, 22)); | ||||
|         titleLabel.setForeground(Color.RED); | ||||
|  | ||||
|         JPanel top = new JPanel(new BorderLayout()); | ||||
|         JPanel infoPanel = new JPanel(new GridLayout(2, 1)); | ||||
|         infoPanel.add(buildTopLine(wordLabel, scoreLabel)); | ||||
|         infoPanel.add(buildTopLine(triedLabel, timeLabel)); | ||||
|          | ||||
|         top.add(titleLabel, BorderLayout.NORTH); | ||||
|         top.add(infoPanel, BorderLayout.CENTER); | ||||
|         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"); | ||||
|         newBtn = new JButton("Nouvelle partie"); | ||||
|         inputPanel.add(new JLabel("Lettre :")); | ||||
|         inputPanel.add(input); | ||||
|         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); | ||||
|  | ||||
|         bottom.add(inputPanel, BorderLayout.WEST); | ||||
|         bottom.add(actionPanel, BorderLayout.EAST); | ||||
|         frame.add(bottom, BorderLayout.SOUTH); | ||||
|     } | ||||
|  | ||||
|     /** Ligne d’infos (gauche/droite). */ | ||||
|     private JPanel buildTopLine(JLabel left, JLabel right) { | ||||
|         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); | ||||
|         input.setText(""); | ||||
|         input.requestFocusInWindow(); | ||||
|         if (!timer.isRunning()) timer.start(); | ||||
|         refreshUI(); | ||||
|     } | ||||
|  | ||||
|     /** Tente une lettre (bouton/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))); | ||||
|         if (res == Result.ALREADY) showMsg("Lettre déjà utilisée."); | ||||
|         input.setText(""); | ||||
|         refreshUI(); | ||||
|         checkEnd(); | ||||
|     } | ||||
|  | ||||
|     /** Fin du mot → affiche popup et enchaîne (lvl 3 cumule score/chrono). */ | ||||
|     private void checkEnd() { | ||||
|         if (!(game.isWin() || game.isLose())) return; | ||||
|  | ||||
|         game.end(game.isWin()); | ||||
|         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(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** Refresh complet. */ | ||||
|     private void refreshUI() { | ||||
|         imgLabel.setIcon(Gallows.icon(game.getErrors())); | ||||
|         wordLabel.setText("Mot : " + game.maskedWord()); | ||||
|         triedLabel.setText("Lettres essayées : " + String.join(", ", game.triedLetters())); | ||||
|         refreshStatsOnly(); | ||||
|         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); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										50
									
								
								Jeu_pendu/Front/MenuUI.java
									
									
									
									
									
										Normal 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(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								Jeu_pendu/Main/Main.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,16 @@ | ||||
| package main; | ||||
|  | ||||
| import front.MenuUI; | ||||
|  | ||||
| /** | ||||
| * Point d'entrée du programme. | ||||
| * Affiche le menu de sélection avant de lancer le jeu. | ||||
| */ | ||||
| public class Main { | ||||
|     public static void main(String[] args) { | ||||
|         javax.swing.SwingUtilities.invokeLater(() -> { | ||||
|             MenuUI menu = new MenuUI(); | ||||
|             menu.show(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/0.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Jeu_pendu/assets/images/6.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.4 KiB | 
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,31 @@ | ||||
| - **bibliothèque/mots.txt** → liste des mots à deviner (1 par ligne)   | ||||
| - **assets/images/** → images du pendu (de 0.png à 6.png)   | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Fonctionnalités | ||||
|   | ||||
| - Affichage d’une **image différente du pendu** à chaque erreur   | ||||
| - Lecture de mots depuis un **fichier externe** (`bibliothèque/mots.txt`)   | ||||
| - Validation des entrées (une seule LETTRE à la fois)   | ||||
| - Bouton **“Nouvelle partie”** pour rejouer sans relancer le programme | ||||
| - Bouton **“Quitter”** pour quitter le programme | ||||
| - Bouton **“Menu”** pour retourner à la page menu et pouvoir rechoisir le niveau de difficulté | ||||
| - Messages de victoire / défaite | ||||
| - Score + Chronomètre en direct | ||||
| - Sélection du niveau dans le menu avant de jouer (Facile, Moyen, Difficile) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Compilation et exécution | ||||
| Se placer préalablement dans Jeu_pendu | ||||
|  | ||||
| ### Compilation | ||||
| ```javac -d out $(find -name "*.java")``` | ||||
|  | ||||
| ### Exécution | ||||
| ```java -cp out main.Main``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| Mini projet effectuer par Clément JANNAIRE et Clémence DUCREUX. | ||||