diff --git a/Makefile b/Makefile
index 8833e5c..f6b0108 100644
--- a/Makefile
+++ b/Makefile
@@ -15,16 +15,22 @@ JCFLAGS = -encoding UTF-8 -implicit:none -cp $(OUT) -d $(OUT)
 CLASSFILES = Pendu.class \
 	Partie.class \
 	Fenetre.class \
-	Dessin.class
+	Dessin.class \
+	Mots.class \
+	Event.class \
+	LetterInputFilter.class \
+	MenuDifficulte.class \
+	Chronometre.class \
+	Score.class
 
 # Dépendances
-$(OUT)Pendu.class : $(IN)Pendu.java $(OUT)Partie.class $(OUT)Fenetre.class
+$(OUT)Pendu.class : $(IN)Pendu.java $(OUT)Partie.class $(OUT)Fenetre.class $(OUT)Event.class $(OUT)MenuDifficulte.class $(OUT)Score.class
 	$(JC) $(JCFLAGS) $<
 
 $(OUT)Partie.class : $(IN)Partie.java $(OUT)Mots.class
 	$(JC) $(JCFLAGS) $<
 
-$(OUT)Fenetre.class : $(IN)Fenetre.java $(OUT)Partie.class $(OUT)Dessin.class
+$(OUT)Fenetre.class : $(IN)Fenetre.java $(OUT)Partie.class $(OUT)Dessin.class $(OUT)Chronometre.class $(OUT)Score.class
 	$(JC) $(JCFLAGS) $<
 
 $(OUT)Dessin.class : $(IN)Dessin.java
@@ -33,6 +39,21 @@ $(OUT)Dessin.class : $(IN)Dessin.java
 $(OUT)Mots.class : $(IN)Mots.java
 	$(JC) $(JCFLAGS) $<
 
+$(OUT)Event.class : $(IN)Event.java $(OUT)Fenetre.class $(OUT)LetterInputFilter.class
+	$(JC) $(JCFLAGS) $<
+
+$(OUT)LetterInputFilter.class : $(IN)LetterInputFilter.java $(OUT)Fenetre.class
+	$(JC) $(JCFLAGS) $<
+
+$(OUT)MenuDifficulte.class : $(IN)MenuDifficulte.java
+	$(JC) $(JCFLAGS) $<
+
+$(OUT)Chronometre.class : $(IN)Chronometre.java
+	$(JC) $(JCFLAGS) $<
+
+$(OUT)Score.class : $(IN)Score.java
+	$(JC) $(JCFLAGS) $<
+
 # Commandes
 Pendu : $(OUT)Pendu.class
 
diff --git a/src/Chronometre.java b/src/Chronometre.java
new file mode 100644
index 0000000..609f020
--- /dev/null
+++ b/src/Chronometre.java
@@ -0,0 +1,83 @@
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * Composant chronomètre (mm:ss) pour le jeu du pendu.
+ * - Démarre/stoppe/réinitialise le temps
+ * - S'affiche comme une barre en haut de la fenêtre
+ *
+ * @version 1.0
+ * @author Adrien
+ * Date : 08-10-2025
+ * Licence :
+ */
+public class Chronometre extends JPanel implements ActionListener {
+
+    private final JLabel timeLabel = new JLabel("00:00", SwingConstants.CENTER);
+    private final Timer timer; // tick chaque seconde
+    private boolean running = false;
+    private long startMillis = 0L;
+    private long accumulatedMillis = 0L;
+
+    public Chronometre() {
+        setLayout(new BorderLayout());
+        setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+        timeLabel.setFont(new Font("Segoe UI", Font.BOLD, 20));
+        add(timeLabel, BorderLayout.CENTER);
+
+        // Timer Swing (1s)
+        timer = new Timer(1000, this);
+    }
+
+    /** Démarre le chronomètre (ou reprend si en pause). */
+    public void start() {
+        if (!running) {
+            running = true;
+            startMillis = System.currentTimeMillis();
+            timer.start();
+            repaint();
+        }
+    }
+
+    /** Met en pause le chronomètre. */
+    public void stop() {
+        if (running) {
+            running = false;
+            accumulatedMillis += System.currentTimeMillis() - startMillis;
+            timer.stop();
+            repaint();
+        }
+    }
+
+    /** Remet le chronomètre à 00:00 (sans le relancer). */
+    public void reset() {
+        running = false;
+        startMillis = 0L;
+        accumulatedMillis = 0L;
+        timer.stop();
+        updateLabel(0L);
+    }
+
+    /** Temps écoulé total (en millisecondes). */
+    public long getElapsedMillis() {
+        long now = System.currentTimeMillis();
+        long runningMillis = running ? (now - startMillis) : 0L;
+        return accumulatedMillis + runningMillis;
+    }
+
+    /** Tick du timer : met à jour l'affichage. */
+    @Override
+    public void actionPerformed(ActionEvent actionEvent) {
+        updateLabel(getElapsedMillis());
+    }
+
+    // --- interne ---
+    private void updateLabel(long elapsedMillis) {
+        long totalSeconds = elapsedMillis / 1000;
+        long minutes = totalSeconds / 60;
+        long seconds = totalSeconds % 60;
+        timeLabel.setText(String.format("%02d:%02d", minutes, seconds));
+    }
+}
diff --git a/src/Dessin.java b/src/Dessin.java
index c90cfaf..27a8f12 100644
--- a/src/Dessin.java
+++ b/src/Dessin.java
@@ -2,87 +2,101 @@ import javax.swing.*;
 import java.awt.*;
 
 /**
-* La classe Dessin gère uniquement le dessin du pendu
+* La classe Dessin gère uniquement le dessin du pendu,
+* avec révélation progressive en fonction du nombre d'erreurs (stage).
 *
-* @version 1.0
-* @author Adrien
+* @version 1.1
+* author Adrien
 * Date : 08-10-2025
-* Licence :
 */
 public class Dessin extends JPanel {
 
+    /** Nombre d'étapes max pour le personnage (hors potence). */
+    public static final int MAX_STAGE = 6;
+
+    /** Étape actuelle (erreurs) : 0..6 */
+    private int stage = 0;
+
     // --- Constructeur ---
     public Dessin() {
-        // Taille préférée pour s'intégrer dans Fenetre
         setPreferredSize(new Dimension(600, 350));
         setBackground(new Color(245, 245, 245));
     }
 
+    /** Définit l'étape (nb d'erreurs) et redessine. */
+    public void setStage(int newStage) {
+        int clamped = Math.max(0, Math.min(newStage, MAX_STAGE));
+        if (clamped != this.stage) {
+            this.stage = clamped;
+            repaint();
+        }
+    }
+
+    public int getStage() { return stage; }
+
     // --- Dessin principal ---
     @Override
     protected void paintComponent(Graphics graphics) {
         super.paintComponent(graphics);
 
-        // Anti-aliasing pour des traits plus doux
-        Graphics2D graphics2D = (Graphics2D) graphics.create();
-        graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-        graphics2D.setStroke(new BasicStroke(3f));
-        graphics2D.setColor(Color.DARK_GRAY);
+        Graphics2D g2 = (Graphics2D) graphics.create();
+        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g2.setStroke(new BasicStroke(3f));
+        g2.setColor(Color.DARK_GRAY);
 
         // Repères et proportions
         int width = getWidth();
         int height = getHeight();
-        int marginPixels = Math.min(width, height) / 12; // marge proportionnelle
+        int margin = Math.min(width, height) / 12;
 
-        // Potence : socle
-        int baseYCoordinate = height - marginPixels;
-        graphics2D.drawLine(marginPixels, baseYCoordinate, width / 2, baseYCoordinate);
-
-        // Montant vertical
-        int postXCoordinate = marginPixels + (width / 12);
-        graphics2D.drawLine(postXCoordinate, baseYCoordinate, postXCoordinate, marginPixels);
-
-        // Traverse horizontale
+        // Potence (toujours affichée)
+        int baseY = height - margin;
+        g2.drawLine(margin, baseY, width / 2, baseY);                 // socle
+        int postX = margin + (width / 12);
+        g2.drawLine(postX, baseY, postX, margin);                      // montant
         int beamLength = width / 3;
-        graphics2D.drawLine(postXCoordinate, marginPixels, postXCoordinate + beamLength, marginPixels);
+        g2.drawLine(postX, margin, postX + beamLength, margin);        // traverse
+        g2.drawLine(postX, margin + height / 10, postX + width / 12, margin); // renfort
 
-        // Renfort diagonal
-        graphics2D.drawLine(postXCoordinate, marginPixels + height / 10, postXCoordinate + width / 12, marginPixels);
+        // Corde (toujours)
+        int ropeX = postX + beamLength;
+        int ropeTopY = margin;
+        int ropeBottomY = margin + height / 12;
+        g2.drawLine(ropeX, ropeTopY, ropeX, ropeBottomY);
 
-        // Corde
-        int ropeXCoordinate = postXCoordinate + beamLength;
-        int ropeTopYCoordinate = marginPixels;
-        int ropeBottomYCoordinate = marginPixels + height / 12;
-        graphics2D.drawLine(ropeXCoordinate, ropeTopYCoordinate, ropeXCoordinate, ropeBottomYCoordinate);
+        // Géométrie du personnage
+        int headR = Math.min(width, height) / 16;
+        int headCX = ropeX;
+        int headCY = ropeBottomY + headR;
+        int bodyTopY = headCY + headR;
+        int bodyBottomY = bodyTopY + height / 6;
+        int armSpan = width / 10;
+        int shouldersY = bodyTopY + height / 24;
+        int legSpan = width / 12;
 
-        // Personnage : tête
-        int headRadiusPixels = Math.min(width, height) / 16;
-        int headCenterX = ropeXCoordinate;
-        int headCenterY = ropeBottomYCoordinate + headRadiusPixels;
-        graphics2D.drawOval(headCenterX - headRadiusPixels, headCenterY - headRadiusPixels, headRadiusPixels * 2, headRadiusPixels * 2);
+        // Étapes du personnage
+        if (stage >= 1) { // tête
+            g2.drawOval(headCX - headR, headCY - headR, headR * 2, headR * 2);
+        }
+        if (stage >= 2) { // corps
+            g2.drawLine(headCX, bodyTopY, headCX, bodyBottomY);
+        }
+        if (stage >= 3) { // bras gauche
+            g2.drawLine(headCX, shouldersY, headCX - armSpan, shouldersY + height / 20);
+        }
+        if (stage >= 4) { // bras droit
+            g2.drawLine(headCX, shouldersY, headCX + armSpan, shouldersY + height / 20);
+        }
+        if (stage >= 5) { // jambe gauche
+            g2.drawLine(headCX, bodyBottomY, headCX - legSpan, bodyBottomY + height / 8);
+        }
+        if (stage >= 6) { // jambe droite
+            g2.drawLine(headCX, bodyBottomY, headCX + legSpan, bodyBottomY + height / 8);
+        }
 
-        // Corps
-        int bodyTopYCoordinate = headCenterY + headRadiusPixels;
-        int bodyBottomYCoordinate = bodyTopYCoordinate + height / 6;
-        graphics2D.drawLine(headCenterX, bodyTopYCoordinate, headCenterX, bodyBottomYCoordinate);
-
-        // Bras
-        int armSpanPixels = width / 10;
-        int shouldersYCoordinate = bodyTopYCoordinate + height / 24;
-        graphics2D.drawLine(headCenterX, shouldersYCoordinate, headCenterX - armSpanPixels, shouldersYCoordinate + height / 20);
-        graphics2D.drawLine(headCenterX, shouldersYCoordinate, headCenterX + armSpanPixels, shouldersYCoordinate + height / 20);
-
-        // Jambes
-        int legSpanPixels = width / 12;
-        graphics2D.drawLine(headCenterX, bodyBottomYCoordinate, headCenterX - legSpanPixels, bodyBottomYCoordinate + height / 8);
-        graphics2D.drawLine(headCenterX, bodyBottomYCoordinate, headCenterX + legSpanPixels, bodyBottomYCoordinate + height / 8);
-
-        graphics2D.dispose();
+        g2.dispose();
     }
 
-    // Affichage
     @Override
-    public String toString() {
-        return "";
-    }
+    public String toString() { return ""; }
 }
diff --git a/src/Fenetre.java b/src/Fenetre.java
index a62c14e..dac077f 100644
--- a/src/Fenetre.java
+++ b/src/Fenetre.java
@@ -22,7 +22,9 @@ public class Fenetre {
     private JLabel wordLabel;
     private JTextField letterInput;
     private JButton sendButton;
-    private JPanel drawZone; // instance de Dessin
+    private JPanel drawZone;
+    private Chronometre chronometre;
+    private JLabel scoreLabel; 
 
     // --- Constructeur ---
     public Fenetre() {
@@ -32,6 +34,17 @@ public class Fenetre {
         root.setLayout(new BoxLayout(root, BoxLayout.Y_AXIS));
         window.setContentPane(root);
 
+        // Barre supérieure : chrono + score
+        JPanel topBar = new JPanel(new BorderLayout());
+        chronometre = new Chronometre();
+        topBar.add(chronometre, BorderLayout.CENTER);
+        scoreLabel = new JLabel("Score : " + Score.BASE, SwingConstants.RIGHT);
+        scoreLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 12));
+        scoreLabel.setFont(new Font("Segoe UI", Font.BOLD, 16));
+        topBar.add(scoreLabel, BorderLayout.EAST);
+        root.add(topBar);
+        chronometre.start(); // démarrage automatique
+
         drawZone = new Dessin();
         root.add(drawZone);
 
@@ -84,15 +97,6 @@ public class Fenetre {
     public JButton getSendButton() { return sendButton; }
     public JLabel getWordLabel() { return wordLabel; }
     public JPanel getDrawZone() { return drawZone; }
-
-    // --- Méthode principale de test ---
-    public static void main(String[] args) {
-        SwingUtilities.invokeLater(() -> {
-            Fenetre f = new Fenetre();
-            // On passe le handler directement ici (pas de setOnLetterSubmitted)
-            new Event(f, ch ->
-                JOptionPane.showMessageDialog(f.getWindow(), "Lettre reçue : " + ch + " (sans logique de jeu)")
-            );
-        });
-    }
+    public Chronometre getChronometre() { return chronometre; }
+    public JLabel getScoreLabel() { return scoreLabel; }
 }
\ No newline at end of file
diff --git a/src/MenuDifficulte.java b/src/MenuDifficulte.java
new file mode 100644
index 0000000..81af18c
--- /dev/null
+++ b/src/MenuDifficulte.java
@@ -0,0 +1,75 @@
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.function.Consumer;
+
+/**
+ * Fenêtre de sélection de la difficulté (Facile / Moyen / Difficile).
+ * Notifie le choix via un Consumer fourni au constructeur.
+ *
+ * @version 1.0
+ * @author Adrien
+ * Date : 08-10-2025
+ * Licence :
+ */
+public class MenuDifficulte implements ActionListener {
+
+    private final JFrame frame;
+    private final Consumer onDifficultyChosen;
+
+    /** Construit la fenêtre et prépare les boutons. */
+    public MenuDifficulte(Consumer onDifficultyChosen) {
+        this.onDifficultyChosen = onDifficultyChosen;
+
+        frame = new JFrame("Jeu du Pendu — Choix de difficulté");
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.setSize(400, 250);
+        frame.setLocationRelativeTo(null);
+        frame.setLayout(new BorderLayout(0, 10));
+
+        JLabel title = new JLabel("Choisissez une difficulté", SwingConstants.CENTER);
+        title.setFont(new Font("Segoe UI", Font.BOLD, 18));
+        title.setBorder(BorderFactory.createEmptyBorder(20, 10, 0, 10));
+        frame.add(title, BorderLayout.NORTH);
+
+        JPanel buttonsPanel = new JPanel(new GridLayout(1, 3, 10, 10));
+        buttonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+        JButton easyBtn = new JButton("Facile");
+        JButton mediumBtn = new JButton("Moyen");
+        JButton hardBtn = new JButton("Difficile");
+
+        easyBtn.setActionCommand("Facile");
+        mediumBtn.setActionCommand("Moyen");
+        hardBtn.setActionCommand("Difficile");
+
+        easyBtn.addActionListener(this);
+        mediumBtn.addActionListener(this);
+        hardBtn.addActionListener(this);
+
+        buttonsPanel.add(easyBtn);
+        buttonsPanel.add(mediumBtn);
+        buttonsPanel.add(hardBtn);
+        frame.add(buttonsPanel, BorderLayout.CENTER);
+    }
+
+    /** Affiche la fenêtre. */
+    public void show() { frame.setVisible(true); }
+
+    /** Ferme la fenêtre. */
+    public void close() { frame.dispose(); }
+
+    /** Accès optionnel au JFrame. */
+    public JFrame getFrame() { return frame; }
+
+    /** Réception des clics sur les boutons. */
+    @Override
+    public void actionPerformed(ActionEvent actionEvent) {
+        String difficulty = actionEvent.getActionCommand(); // "Facile" | "Moyen" | "Difficile"
+        frame.dispose();
+        if (onDifficultyChosen != null) {
+            onDifficultyChosen.accept(difficulty);
+        }
+    }
+}
diff --git a/src/Partie.java b/src/Partie.java
index 0052a7a..84e2f8f 100644
--- a/src/Partie.java
+++ b/src/Partie.java
@@ -10,7 +10,7 @@ import java.util.Random;
 */
 public class Partie {
    //Contantes
-   private static final byte REMAININGTRY = 11 ;
+   private static final byte REMAININGTRY = 6 ;
    private static final byte CARACTERCODESHIFT = 65 ;  //Décalage ASCI > 'A'
 
    //Attributs
@@ -133,3 +133,4 @@ public class Partie {
       System.out.println("Essais restants : " + game.getRemainingTry());
    }
 }
+
diff --git a/src/Pendu.java b/src/Pendu.java
index e478d9c..cfebb1d 100644
--- a/src/Pendu.java
+++ b/src/Pendu.java
@@ -1,14 +1,135 @@
+import javax.swing.*;
+import java.util.function.Consumer;
 
 /**
-* La classe Pendu
-*
-* @version
-* @author
-* Date :
-* Licence :
-*/
+ * Point d'entrée : affiche le menu de difficulté puis lance la fenêtre du jeu.
+ * Lie Fenetre (vue) et Partie (logique) via un handler.
+ * Met à jour le dessin du pendu à chaque erreur.
+ *
+ * @version 1.4
+ * author Adrien
+ * Date : 08-10-2025
+ * Licence :
+ */
 public class Pendu {
-   public static void main(String[] args){
 
-   }
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(() -> {
+            MenuDifficulte menu = new MenuDifficulte(difficulty -> openGameWindow(difficulty));
+            menu.show();
+        });
+    }
+
+    /** Ouvre la fenêtre du jeu, crée la Partie et branche les événements. */
+    private static void openGameWindow(String difficulty) {
+        Fenetre fenetre = new Fenetre();
+        fenetre.getWindow().setTitle("Jeu du Pendu — " + difficulty);
+
+        Partie partie = new Partie();
+
+        // Affichage initial du mot masqué (construit ici)
+        fenetre.getWordLabel().setText(buildMaskedWord(partie));
+
+        // Stage initial (0 erreur)
+        if (fenetre.getDrawZone() instanceof Dessin) {
+            ((Dessin) fenetre.getDrawZone()).setStage(0);
+        }
+
+        // On mémorise les essais initiaux pour calculer les erreurs = initialTries - remainingTry
+        final int initialTries = partie.getRemainingTry();
+
+        // Handler : applique Partie puis met à jour l'UI (mot + dessin + score)
+        Consumer handler = new GameLetterHandler(fenetre, partie, initialTries);
+
+        // Branchement des événements clavier/bouton
+        new Event(fenetre, handler);
+    }
+
+    /** Construit la chaîne "_ _ A _ _" à partir de l'état de Partie (sans modifier Partie). */
+    private static String buildMaskedWord(Partie partie) {
+        char[] word = partie.getSecretWord();
+        boolean[] found = partie.getFoundLetters();
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < word.length; i++) {
+            sb.append(found[i] ? word[i] : '_');
+            if (i < word.length - 1) sb.append(' ');
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Handler de lettres :
+     * - applique Partie.isAlreadyEntries
+     * - met à jour le mot affiché
+     * - calcule errors = initialTries - remainingTry, puis stage = min(errors, Dessin.MAX_STAGE)
+     * - met à jour le score
+     * - gère la fin de partie
+     */
+    private static class GameLetterHandler implements Consumer {
+        private final Fenetre fenetre;
+        private final Partie partie;
+        private final int initialTries;   // essais au démarrage (peut être 11 avec ta Partie)
+        private final Dessin dessinPanel;
+
+        GameLetterHandler(Fenetre fenetre, Partie partie, int initialTries) {
+            this.fenetre = fenetre;
+            this.partie = partie;
+            this.initialTries = initialTries;
+            this.dessinPanel = (fenetre.getDrawZone() instanceof Dessin)
+                    ? (Dessin) fenetre.getDrawZone() : null;
+        }
+
+        @Override
+        public void accept(Character ch) {
+            boolean alreadyPlayed = partie.isAlreadyEntries(ch);
+
+            // Mise à jour du mot
+            fenetre.getWordLabel().setText(buildMaskedWord(partie));
+
+            // Erreurs -> stage pour le dessin (borné à MAX_STAGE)
+            int errors = Math.max(0, initialTries - partie.getRemainingTry());
+            if (dessinPanel != null) {
+                int stage = Math.min(errors, Dessin.MAX_STAGE);
+                dessinPanel.setStage(stage);
+            }
+
+            // Mise à jour du score courant (si tu utilises Score + Chronometre)
+            long elapsed = (fenetre.getChronometre() != null)
+                    ? fenetre.getChronometre().getElapsedMillis() : 0L;
+            if (fenetre.getScoreLabel() != null) {
+                int score = Score.compute(errors, elapsed);
+                fenetre.getScoreLabel().setText("Score : " + score);
+            }
+
+            if (alreadyPlayed) {
+                JOptionPane.showMessageDialog(fenetre.getWindow(),
+                        "Lettre déjà jouée : " + ch + "\nEssais restants : " + partie.getRemainingTry());
+            }
+
+            // Fin de partie
+            if (partie.gameIsEnding()) {
+                // Stoppe le chronomètre pour figer le temps
+                if (fenetre.getChronometre() != null) {
+                    fenetre.getChronometre().stop();
+                }
+                // Score final (si Score/Chronometre présents)
+                long finalElapsed = (fenetre.getChronometre() != null)
+                        ? fenetre.getChronometre().getElapsedMillis() : elapsed;
+                int finalErrors = Math.max(0, initialTries - partie.getRemainingTry());
+                int finalScore = Score.compute(finalErrors, finalElapsed);
+
+                boolean win = !fenetre.getWordLabel().getText().contains("_");
+                String msg = (win
+                        ? "Bravo ! Mot trouvé : "
+                        : "Perdu ! Le mot était : ")
+                        + String.valueOf(partie.getSecretWord())
+                        + (fenetre.getScoreLabel() != null ? "\nScore : " + finalScore : "");
+
+                JOptionPane.showMessageDialog(fenetre.getWindow(), msg);
+
+                fenetre.getLetterInput().setEnabled(false);
+                fenetre.getSendButton().setEnabled(false);
+            }
+        }
+    }
 }
diff --git a/src/Score.java b/src/Score.java
new file mode 100644
index 0000000..2bed1b3
--- /dev/null
+++ b/src/Score.java
@@ -0,0 +1,36 @@
+/**
+ * Calcule le score du pendu à partir du nombre d'erreurs et du temps écoulé.
+ *
+ * Formule (simple et configurable) :
+ * score = max(0, BASE - erreurs * ERROR_PENALTY - secondes * TIME_PENALTY_PER_SEC)
+ *
+ * @version 1.0
+ * @author Adrien
+ * Date : 08-10-2025
+ * Licence :
+ */
+public final class Score {
+
+    /** Score de départ. */
+    public static final int BASE = 1000;
+
+    /** Malus par erreur. (6 erreurs max par défaut) */
+    public static final int ERROR_PENALTY = 120;
+
+    /** Malus par seconde écoulée. */
+    public static final double TIME_PENALTY_PER_SEC = 1.0;
+
+    private Score() {}
+
+    /**
+     * - nombre d'erreurs (>=0)
+     * - temps écoulé en millisecondes
+     * - score >= 0
+     */
+    public static int compute(int errors, long elapsedMillis) {
+        if (errors < 0) errors = 0;
+        double timePenalty = (elapsedMillis / 1000.0) * TIME_PENALTY_PER_SEC;
+        int value = (int)Math.round(BASE - errors * ERROR_PENALTY - timePenalty);
+        return Math.max(0, value);
+    }
+}