16 Commits

Author SHA1 Message Date
Clement JANNAIRE ca5c12a7ba ajout du chrono et score 2025-10-08 15:04:37 +02:00
Clemence DUCREUX a7d9f17ba8 README.md 2025-10-08 14:51:14 +02:00
Clement JANNAIRE 9b7ed446b2 images 2025-10-08 14:48:28 +02:00
Clemence DUCREUX b707e37787 Réparation 2025-10-08 14:46:44 +02:00
Clemence DUCREUX 6f74af39d9 ? 2025-10-08 14:38:58 +02:00
Clemence DUCREUX 972ff65f63 Merge branch 'master' of https://grond.iut-fbleau.fr/jannaire/TD3_DEV51_DUCREUX_JANNAIRE 2025-10-08 14:36:29 +02:00
Clemence DUCREUX d74e119af3 README.md 2025-10-08 14:25:18 +02:00
Clement JANNAIRE d79aec8248 modification des images 2025-10-08 14:19:44 +02:00
Clement JANNAIRE b4c8b1d156 Erreur nom fichier 2025-10-08 12:14:42 +02:00
Clemence DUCREUX e0f727d76b correction 2025-10-08 12:13:04 +02:00
Clemence DUCREUX bd4a204d25 correction 2025-10-08 12:11:52 +02:00
Clemence DUCREUX fae2ecf2c8 Merge branch 'DUCREUX' 2025-10-08 12:05:11 +02:00
Clemence DUCREUX 7679f1f35b main 2025-10-08 12:02:13 +02:00
Clement JANNAIRE 93e31b0aac ajout du back et bibliotheque 2025-10-08 11:58:51 +02:00
Clemence DUCREUX fd393bfc85 front + assets 2025-10-08 11:57:29 +02:00
Clemence DUCREUX 89aad809a1 organisation 2025-10-08 09:49:46 +02:00
25 changed files with 1302 additions and 0 deletions
+13
View 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
View 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 : + (restant * 10) + bonus temps
* * bonus temps = max(0, 60 - secondes) * 2 (jusqu'à 120 pts si < 60s)
* - 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
View File
@@ -0,0 +1,6 @@
package back;
/**
* Résultat possible d'une tentative.
*/
public enum Result { HIT, MISS, ALREADY }
+85
View File
@@ -0,0 +1,85 @@
package back;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* Fournit les mots pour le jeu.
* - Lit d'abord "bibliothèque/mots.txt" (UTF-8), 1 mot par ligne.
* - Ignore les lignes vides et celles qui commencent par '#'.
* - Si le fichier est introuvable ou vide, bascule sur une liste par défaut.
*/
public class Words {
/** Chemin du fichier de mots (relatif à la racine du projet). */
private static final Path WORDS_PATH = Paths.get("Bibliotheque", "mots.txt");
/** Liste de secours si le fichier n'est pas disponible. */
private static final List<String> DEFAULT = List.of(
"algorithm", "variable", "function", "interface", "inheritance",
"exception", "compiler", "database", "network", "architecture",
"iteration", "recursion", "encryption", "framework", "protocol"
);
/** RNG partagé et cache des mots chargés. */
private static final SecureRandom RNG = new SecureRandom();
private static volatile List<String> CACHE = null;
/**
* 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;
}
}
File diff suppressed because it is too large Load Diff
+62
View 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);
}
}
}
+125
View File
@@ -0,0 +1,125 @@
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.
*/
public class GameUI {
private JFrame frame;
private JLabel imgLabel;
private JLabel wordLabel;
private JLabel triedLabel;
private JTextField input;
private JButton tryBtn;
private JButton newGameBtn;
private Game game;
private String currentWord;
/** Affiche la fenêtre principale */
public void show() {
setupUI();
startNewGame();
frame.setVisible(true);
}
/** Initialise les composants Swing */
private void setupUI() {
frame = new JFrame("Jeu du Pendu");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(520, 520);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout(12, 12));
// Image du pendu
imgLabel = new JLabel("", SwingConstants.CENTER);
frame.add(imgLabel, BorderLayout.CENTER);
// Panneau bas: saisie + actions
JPanel bottom = new JPanel(new BorderLayout(8, 8));
JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
input = new JTextField(5);
tryBtn = new JButton("Essayer");
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);
// Actions
tryBtn.addActionListener(this::onTry);
input.addActionListener(this::onTry);
newGameBtn.addActionListener(e -> startNewGame());
}
/** Démarre une nouvelle partie */
private void startNewGame() {
currentWord = Words.random();
game = new Game(currentWord, 7);
input.setText("");
input.requestFocusInWindow();
refreshUI();
}
/** Gère le clic sur "Essayer" ou Entrée dans le champ */
private void onTry(ActionEvent e) {
String text = input.getText();
if (!Check.isLetter(text)) {
JOptionPane.showMessageDialog(frame, "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;
}
input.setText("");
refreshUI();
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);
}
}
/** Met à jour l'image et les textes selon l'état courant */
private void refreshUI() {
imgLabel.setIcon(Gallows.icon(game.getErrors()));
wordLabel.setText("Mot : " + game.maskedWord());
triedLabel.setText("Lettres essayées : " + String.join(", ", game.triedLetters()));
frame.repaint();
}
}
+17
View File
@@ -0,0 +1,17 @@
package main;
import front.GameUI;
/**
* Point d'entrée du programme.
* Lance l'interface graphique du jeu du pendu.
*/
public class Main {
public static void main(String[] args) {
// Démarre l'UI Swing sur le thread de l'EDT
javax.swing.SwingUtilities.invokeLater(() -> {
GameUI ui = new GameUI();
ui.show();
});
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

+27
View File
@@ -0,0 +1,27 @@
- **bibliothèque/mots.txt** → liste des mots à deviner (1 par ligne)
- **assets/images/** → images du pendu (de 0.png à 6.png)
---
## Fonctionnalités
Affichage dune **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
Messages de victoire / défaite
---
## 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.
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.