Introduction
Ce jeu à cinq dés propose trois choix : sum, pair et straight. Un même lancer peut rapporter des points différents selon le choix effectué. Pour [2, 2, 4, 4, 6], la somme vaut 18, tandis que la paire la plus élevée rapporte 8. Vous allez traduire ces règles courtes en code Rust typé, puis essayer des lancers connus et aléatoires.
Vous devez connaître les fonctions, les tableaux, enum, match et Option en Rust, ainsi que les chemins de base dans un terminal. Cargo et l'interface de saisie sont préparés ; vous implémentez les décisions de calcul des points. Ce laboratoire est indépendant des autres VM. Prévoyez environ 40 minutes, lecture et erreurs courantes comprises ; il s'agit d'une estimation de l'auteur.
Modéliser un choix et calculer la somme
Dans cette étape, lisez les types fournis, prévoyez un score et écrivez une courte somme fonctionnelle dans la véritable bibliothèque du jeu. Commencez dans l’onglet Terminal. La préparation a créé un projet Cargo dans /home/labex/project/dice-game. src/lib.rs est la bibliothèque des règles ; src/main.rs fournit la saisie et les dés aléatoires. Conservez src/main.rs et Cargo.toml sans modification.
La chaîne d'outils se trouve dans /home/labex/.cargo/bin. export ajoute ce répertoire à PATH, la liste des emplacements où le shell cherche les commandes. Ce réglage concerne uniquement cette session Terminal ; répétez-le si vous ouvrez un nouveau shell. cd sélectionne le projet préparé et pwd confirme le répertoire courant.
export PATH=/home/labex/.cargo/bin:$PATH
cd /home/labex/project/dice-game
pwd
Vous devez obtenir /home/labex/project/dice-game. Corrigez une faute de chemin avec le cd montré ; ne réinitialisez pas l’environnement. Avant de modifier, prévoyez la somme de [2, 2, 4, 4, 6]. Elle vaut 18. Vous ferez d’abord fonctionner cette commande de score ordinaire.
Lisez la bibliothèque existante avec cat, qui affiche un fichier sans le modifier :
cat src/lib.rs
Dice est un alias de [u8; 5], exactement cinq valeurs non signées d’un octet. L’interface n’admet que les valeurs 1 à 6. Category nomme trois choix. Category::parse renvoie Some(category) pour un mot reconnu et None pour un mot inconnu. Prévoyez son résultat pour sum et sums, puis trouvez leurs branches match. Le refus d’entrée appartient à l’interface ; une catégorie reconnue peut légitimement valoir zéro.
Les types et méthodes auxiliaires sont fournis. pub rend les noms accessibles à l’interface. impl Category regroupe les méthodes de ce type ; Self y désigne Category. derive fournit copie, affichage de débogage et égalité pour cet enum. name renvoie une chaîne littérale fixe à afficher. Préservez ces définitions. Votre modification se limite à score, dont le corps actuel renvoie zéro pour chaque catégorie.
Conservez un point de retour avant cette modification. mktemp -d crée un nouveau répertoire en remplaçant les six caractères X par un suffixe unique au lieu de réutiliser une ancienne sauvegarde. $() capture le chemin affiché dans la variable de session rs02_before_sum. && n’exécute la copie que si la création a réussi. cp -p copie le code source en préservant ses métadonnées ; les guillemets gardent le chemin de la variable en un seul argument. Cette sauvegarde contient uniquement votre bibliothèque modifiable, pas l’interface ni la configuration Cargo fournies. Si une opération échoue, lisez l’erreur et obtenez une sauvegarde réussie avant de modifier.
rs02_before_sum=$(mktemp -d .edit-before-sum.XXXXXX) && cp -p src/lib.rs "$rs02_before_sum/lib.rs"
Ouvrez ce fichier existant avec nano. -c affiche la position du curseur, utile pour suivre les numéros de ligne du compilateur :
nano -c src/lib.rs
La notation ^ de nano désigne Ctrl. Appuyez sur Ctrl+W, saisissez pub fn score, puis appuyez sur Enter pour trouver la fonction. Utilisez les flèches pour vous déplacer et Backspace/Delete pour modifier les caractères. Lisez de l’accolade ouvrante { à son } correspondant avant de modifier. Ne supprimez pas l’enum ni les méthodes auxiliaires précédentes.
Le code initial nomme les paramètres inutilisés _dice et _category. Renommez uniquement ces noms de paramètres en dice et category pour pouvoir les utiliser ; conservez le nom de fonction et les types des paramètres et du retour. Remplacez son unique expression 0 par une expression match category. Une branche comporte un motif, => et un résultat, par exemple la branche temporaire Category::Pair => 0,. Donnez à Category::Straight le même résultat zéro temporaire. Vous implémenterez ces deux règles ensuite.
Pour Category::Sum, utilisez un bloc qui démarre un accumulateur mutable, parcourt chaque valeur et additionne la valeur élargie. Cette courte boucle montre l’opération numérique :
let mut total = 0;
for face in dice {
total += u16::from(face);
}
u16::from(face) élargit le nombre avant l’addition. Même cinq six tiennent dans u16. Après la boucle, faites de total l’expression finale de la branche Sum : pas de point-virgule après cette expression finale, afin que la branche renvoie la valeur. Fermez les accolades de match et de la fonction. Vous avez implémenté une véritable catégorie dans le programme existant, les deux choix restants étant volontairement inachevés.
Appuyez sur Ctrl+O pour écrire, Enter pour confirmer src/lib.rs, puis Ctrl+X pour quitter nano. Ne saisissez pas de commandes du terminal tant que l’éditeur est ouvert. Relisez la modification enregistrée avant de compiler :
cat src/lib.rs
Cargo compile et lance le programme fourni. --quiet masque les messages habituels de compilation ; -- sépare les options Cargo des arguments du programme. Exécutez les mêmes cinq valeurs que celles prévues :
cargo run --quiet -- score sum 2 2 4 4 6
Dice: [2, 2, 4, 4, 6]
sum: 18
Les dés n’ont pas changé ; la règle choisie a produit les points. Vérifiez aussi que votre boucle prévoit 30 pour cinq six avant de continuer.
Si Cargo signale une erreur de syntaxe, lisez le nom du fichier et le numéro de ligne, rouvrez nano -c src/lib.rs, recherchez pub fn score et corrigez l’accolade, la branche ou l’expression correspondante. Enregistrez et quittez, puis relancez la commande échouée. Tant qu’une modification n’est pas enregistrée, Ctrl+X, puis N à la question d’enregistrement l’abandonne ; cela n’annule pas les modifications déjà écrites.
Pour une erreur déjà enregistrée que vous ne pouvez pas corriger, restaurez votre propre bibliothèque d’avant modification avec la commande ci-dessous. Elle remplace volontairement uniquement le fichier autorisé src/lib.rs ; elle ne restaure ni l’interface ni les fichiers Cargo. La variable de sauvegarde appartient à cette session Terminal. Dans un autre shell, utilisez les instructions conditionnelles ci-dessous pour sélectionner et inspecter votre véritable sauvegarde antérieure.
Uniquement pour récupérer une erreur enregistrée : dans le Terminal d’origine, la variable du point de sauvegarde contient encore son chemin. Dans un nouveau Terminal, répétez d’abord les commandes PATH et de répertoire du projet. Énumérez les sauvegardes de cette phase :
ls -d .edit-before-sum.*
ls -d liste les noms de répertoires plutôt que leur contenu ; * correspond aux suffixes des répertoires uniques de cette phase. Ce sont de vrais noms générés : ne saisissez pas de suffixe inventé. Sans correspondance, confirmez le répertoire du projet et la création de ce point de sauvegarde ; ne copiez pas depuis une autre phase. Pour sélectionner l’un des chemins affichés, lancez :
read rs02_before_sum
Le shell attend une ligne. Collez le nom exact du répertoire choisi et appuyez sur Enter. read stocke cette ligne dans la variable nommée pour cette session. Confirmez le contenu de la sauvegarde avant de restaurer :
cat "$rs02_before_sum/lib.rs"
Elle doit contenir le code initial avant Sum. Si le chemin ou la phase est incorrect, répétez read avec le bon chemin affiché et inspectez à nouveau. Utilisez seulement ensuite la commande de restauration. Ce contrôle vaut aussi dans le Terminal d’origine si vous ne savez plus quel point de sauvegarde sa variable désigne.
cp -p "$rs02_before_sum/lib.rs" src/lib.rs
Cela restaure le code initial : refaites donc l’implémentation de Sum et sa commande de score. Gardez cette session Terminal et votre Sum fonctionnel pour l’étape suivante.
Calculer la meilleure paire et une suite
Dans cette étape, concevez et implémentez les décisions de score restantes tout en conservant la branche Sum fonctionnelle. Continuez dans le même Terminal, dans /home/labex/project/dice-game. Dans un nouveau shell, répétez les commandes précédentes export PATH et cd.
Une paire rapporte deux fois la valeur la plus élevée apparaissant au moins deux fois. Trois, quatre ou cinq valeurs identiques ne contribuent toujours que deux dés. Pour [2, 2, 4, 4, 6], faut-il compter les deux paires ou seulement les quatre ? Seulement les quatre : 8 points. Avec cinq six, le résultat vaut 12, pas 30. Sans paire, zéro est valide.
Une représentation utile est [0u8; 7] : sept compteurs indexés de 0 à 6, avec 0 inutilisé. Pour chaque dé, usize::from(face) convertit sa valeur en indice de tableau ; counts[usize::from(face)] += 1 augmente le compteur de cette valeur. Une seconde boucle peut inspecter les valeurs dans 1..=6, bornes incluses. Quand un compteur vaut au moins deux, remplacez un meilleur score mutable par deux fois cette valeur ; le parcours croissant laisse la meilleure paire admissible. as u16 convertit ce petit résultat numérique au type du score. Vous pouvez organiser un algorithme équivalent de comptage ou de tri ; il doit toujours traiter correctement deux paires disponibles et trois à cinq dés identiques.
Une suite rapporte 15 uniquement pour les valeurs exactement distinctes 1–5 ou 2–6, dans n’importe quel ordre. Prévoyez la différence entre [6, 5, 4, 3, 2] et [1, 2, 3, 4, 6]. La première est une suite ; la seconde a un trou. Copier avec let mut sorted = dice puis appeler sorted.sort() permet de comparer le tableau trié aux deux tableaux exacts sans modifier le lancer original. || signifie qu’une des comparaisons peut être vraie. Décidez quand renvoyer 15 ou zéro ; les doublons ne doivent pas passer.
Préservez la phase déjà fonctionnelle avant de modifier. Comme auparavant, le répertoire unique évite d’écraser une sauvegarde antérieure et la copie ne s’exécute qu’après la réussite de la création du répertoire :
rs02_before_rules=$(mktemp -d .edit-before-rules.XXXXXX) && cp -p src/lib.rs "$rs02_before_rules/lib.rs"
Ouvrez le fichier existant, recherchez pub fn score avec Ctrl+W et modifiez uniquement les branches Pair et Straight. Préservez Sum, l’API publique et toutes les définitions auxiliaires. Écrivez votre implémentation à partir de ces règles au lieu de remplacer le module entier.
nano -c src/lib.rs
Enregistrez avec Ctrl+O, Enter, puis quittez avec Ctrl+X. Avant d’exécuter, relisez vos branches enregistrées et prévoyez chaque résultat :
cat src/lib.rs
cargo run --quiet -- score pair 2 2 4 4 6
Dice: [2, 2, 4, 4, 6]
pair: 8
Comparez maintenant cinq dés identiques, les deux intervalles de suite et les zéros valides :
cargo run --quiet -- score pair 6 6 6 6 6
cargo run --quiet -- score straight 6 5 4 3 2
cargo run --quiet -- score straight 1 2 3 4 5
cargo run --quiet -- score pair 1 2 3 4 6
cargo run --quiet -- score straight 1 2 3 4 6
Les scores attendus sont respectivement 12, 15, 15, 0 et 0. Zéro signifie que la règle reconnue n’a trouvé aucune combinaison rapportant des points, pas que la saisie a été refusée. Choisissez votre propre exemple valide supplémentaire à cinq dés, prévoyez son score pour une catégorie et lancez score avec cette catégorie et les cinq valeurs. Expliquez tout désaccord avec la règle plutôt que modifier l’exemple pour le cacher.
Omettre les cinq valeurs utilise la source aléatoire du système d’exploitation. C’est un jeu local, pas un système de sécurité ou de paris. Exécutez-le une fois ; vous pouvez répéter :
cargo run --quiet -- score pair
Vos dés et votre score peuvent varier et un tirage aléatoire peut se répéter par hasard. Appliquez indépendamment la règle de la meilleure paire aux dés affichés. Un mot inconnu diffère d’un zéro valide : cette faute volontaire quitte avec le statut 1 et affiche Error: choose sum, pair, or straight sans évaluer de lancer :
cargo run --quiet -- score pairs 2 2 4 4 6
Corrigez la catégorie dans la ligne de commande :
cargo run --quiet -- score pair 2 2 4 4 6
Vous devez obtenir pair: 8. Une erreur de compilation vise au contraire le code source : rouvrez nano à score, corrigez la ligne indiquée, enregistrez et réessayez. Au besoin, restaurez votre propre phase Sum fonctionnelle, puis réimplémentez les deux branches restantes :
Uniquement pour récupérer une erreur enregistrée : dans le Terminal d’origine, la variable du point de sauvegarde contient encore son chemin. Dans un nouveau Terminal, répétez d’abord les commandes PATH et de répertoire du projet. Énumérez les sauvegardes de cette phase :
ls -d .edit-before-rules.*
ls -d liste les noms de répertoires plutôt que leur contenu ; * correspond aux suffixes des répertoires uniques de cette phase. Ce sont de vrais noms générés : ne saisissez pas de suffixe inventé. Sans correspondance, confirmez le répertoire du projet et la création de ce point de sauvegarde ; ne copiez pas depuis une autre phase. Pour sélectionner l’un des chemins affichés, lancez :
read rs02_before_rules
Le shell attend une ligne. Collez le nom exact du répertoire choisi et appuyez sur Enter. read stocke cette ligne dans la variable nommée pour cette session. Confirmez le contenu de la sauvegarde avant de restaurer :
cat "$rs02_before_rules/lib.rs"
Elle doit contenir le Sum fonctionnel avant Pair et Straight. Si le chemin ou la phase est incorrect, répétez read avec le bon chemin affiché et inspectez à nouveau. Utilisez seulement ensuite la commande de restauration. Ce contrôle vaut aussi dans le Terminal d’origine si vous ne savez plus quel point de sauvegarde sa variable désigne.
cp -p "$rs02_before_rules/lib.rs" src/lib.rs
Cette commande restaure volontairement uniquement votre bibliothèque modifiable. Ne réinitialisez pas l’environnement et n’écrasez ni src/main.rs ni Cargo.toml. La référence suivante est disponible après votre essai pour comparer ou récupérer une fonction précise ; ce n’est pas la séquence de modification par défaut.
Référence complète de la bibliothèque pour comparaison et récupération
Comparez la branche concernée et transférez uniquement la correction nécessaire avec nano. Relancez ensuite les exemples connus et les cas limites. La bibliothèque complète ci-dessous est aussi l’unique source maintenue utilisée pour préparer les règles de score déjà complètes du laboratoire suivant.
pub type Dice = [u8; 5];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Category {
Sum,
Pair,
Straight,
}
impl Category {
pub fn parse(input: &str) -> Option<Self> {
match input {
"sum" => Some(Self::Sum),
"pair" => Some(Self::Pair),
"straight" => Some(Self::Straight),
_ => None,
}
}
pub fn name(self) -> &'static str {
match self {
Self::Sum => "sum",
Self::Pair => "pair",
Self::Straight => "straight",
}
}
}
pub fn score(dice: Dice, category: Category) -> u16 {
match category {
Category::Sum => {
let mut total = 0;
for face in dice {
total += u16::from(face);
}
total
}
Category::Pair => {
let mut counts = [0u8; 7];
for face in dice {
counts[usize::from(face)] += 1;
}
let mut best = 0;
for face in 1..=6 {
if counts[face] >= 2 {
best = (face * 2) as u16;
}
}
best
}
Category::Straight => {
let mut sorted = dice;
sorted.sort();
if sorted == [1, 2, 3, 4, 5] || sorted == [2, 3, 4, 5, 6] {
15
} else {
0
}
}
}
}
Résumé
Vous avez utilisé une catégorie typée, match, une somme cumulée, des compteurs de valeurs et une copie triée pour exprimer trois règles complètes. Les exemples fixes expliquent les résultats, tandis que les lancers aléatoires permettent d'appliquer les mêmes règles à de nouveaux dés. Une combinaison absente vaut zéro ; une catégorie inconnue est une erreur de saisie.



