diff --git a/Jeu_pendu/Back/Game.java b/Jeu_pendu/Back/Game.java index 6d9f40e..6e63002 100644 --- a/Jeu_pendu/Back/Game.java +++ b/Jeu_pendu/Back/Game.java @@ -7,7 +7,18 @@ import java.util.Set; /** * Logique principale du jeu du pendu (back). - * Gère le mot, les lettres trouvées, et les conditions de victoire/défaite. + * 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; @@ -16,21 +27,32 @@ public class Game { 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 */ + /** 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 { - errors++; + addScore(-5); + if (isLose()) end(false); return Result.MISS; } } @@ -62,7 +84,7 @@ public class Game { return errors >= maxErrors; } - /** Renvoie le nombre d'erreurs actuelles */ + /** Nombre d'erreurs actuelles */ public int getErrors() { return errors; } /** Liste les lettres déjà essayées */ @@ -73,4 +95,38 @@ public class Game { for (Character ch : sorted) out.add(String.valueOf(ch)); return out; } -} \ No newline at end of file + + // ---------- 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; + } +} diff --git a/Jeu_pendu/Front/GameUI.java b/Jeu_pendu/Front/GameUI.java index b973411..4f66674 100644 --- a/Jeu_pendu/Front/GameUI.java +++ b/Jeu_pendu/Front/GameUI.java @@ -1,76 +1,84 @@ package front; import back.*; + import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; /** - * Interface graphique (Swing) du jeu du pendu. - * - Affiche une image différente à chaque erreur. - * - Champ de saisie pour entrer une lettre. - * - Texte pour le mot masqué et les lettres essayées. + * Interface graphique du jeu du pendu. */ public class GameUI { private JFrame frame; - private JLabel imgLabel; - private JLabel wordLabel; - private JLabel triedLabel; + private JLabel imgLabel, wordLabel, triedLabel, scoreLabel, timeLabel; private JTextField input; - private JButton tryBtn; - private JButton newGameBtn; - + private JButton tryBtn, newGameBtn; private Game game; private String currentWord; + private Timer timer; - /** Affiche la fenêtre principale */ + /** Lance la fenêtre et démarre une partie */ public void show() { - setupUI(); + setupWindow(); + setupLayout(); + setupActions(); startNewGame(); frame.setVisible(true); } - /** Initialise les composants Swing */ - private void setupUI() { + /** Crée la fenêtre principale */ + private void setupWindow() { frame = new JFrame("Jeu du Pendu"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(520, 520); + frame.setSize(560, 560); frame.setLocationRelativeTo(null); frame.setLayout(new BorderLayout(12, 12)); + } - // Image du pendu + /** Construit les composants et le layout */ + private void setupLayout() { imgLabel = new JLabel("", SwingConstants.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 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); - - newGameBtn = new JButton("Nouvelle partie"); bottom.add(inputPanel, BorderLayout.WEST); bottom.add(newGameBtn, BorderLayout.EAST); 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); + /** 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; + } - // Actions + /** 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 */ @@ -79,47 +87,68 @@ public class GameUI { game = new Game(currentWord, 7); input.setText(""); input.requestFocusInWindow(); + if (!timer.isRunning()) timer.start(); refreshUI(); } - /** Gère le clic sur "Essayer" ou Entrée dans le champ */ + /** Gère le clic ou l'appui sur Entrée */ private void onTry(ActionEvent e) { String text = input.getText(); if (!Check.isLetter(text)) { - JOptionPane.showMessageDialog(frame, "Tape une seule lettre (A-Z)."); + showMsg("Tape une seule lettre (A-Z)."); input.requestFocusInWindow(); input.selectAll(); return; } - char c = Character.toLowerCase(text.charAt(0)); - Result res = game.play(c); - - switch (res) { - case ALREADY: JOptionPane.showMessageDialog(frame, "Lettre déjà utilisée."); - break; - case HIT: - break; - case MISS: - break; - } - + Result res = game.play(Character.toLowerCase(text.charAt(0))); + handleResult(res); input.setText(""); refreshUI(); + checkEnd(); + } - if (game.isWin()) { - refreshUI(); - JOptionPane.showMessageDialog(frame, "Bravo ! Le mot était : " + currentWord); - } else if (game.isLose()) { - refreshUI(); - JOptionPane.showMessageDialog(frame, "Perdu ! Le mot était : " + currentWord); + /** 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 } } - /** Met à jour l'image et les textes selon l'état courant */ + /** Vérifie si la partie est finie */ + private void checkEnd() { + if (game.isWin() || game.isLose()) { + timer.stop(); + game.end(game.isWin()); + String msg = (game.isWin() ? "Bravo !" : "Perdu !") + + " Le mot était : " + currentWord + + "\nScore final : " + game.getScore() + + "\nTemps : " + game.getElapsedSeconds() + "s"; + showMsg(msg); + } + } + + /** Met à jour tout l'affichage */ 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(); } -} \ No newline at end of file + + /** Met à jour uniquement score + chrono */ + 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); + } +} diff --git a/TD3 - DEV5.1.pdf b/TD3 - DEV5.1.pdf index 7a69e5a..509685d 100644 Binary files a/TD3 - DEV5.1.pdf and b/TD3 - DEV5.1.pdf differ