Skip to main content

Command Palette

Search for a command to run...

Le nombre magique

Vous devrez trouver un nombre que le programme a choisi au hasard

Updated
•4 min read•View as Markdown
Le nombre magique

Dans cet exemple nous allons coder un petit programme en Rust qui consiste à rechercher un nombre “magique”.


Repo: https://github.com/CODEheures/rust-guessing-game


Générer le nombre magique

Dans un premier temps, nous créons le projet cargo new guessing_game et importons la crate rand dans la configuration.

[dependencies]
rand = "0.9.2"

Nous allons générer un nombre aléatoire dans une plage donnée.

fn main() {
    let range = 1..=15;
    let number = rand::random_range(range.clone());
    println!("Trouvez le nombre magique entre {} et {}.", range.start(), range.end());

    println!("Le nombre magique est {}", number);
}

rand contient un raccourci random_range qui reçois une range en paramètre (ex: 1..=15) et qui fournit un nombre compris dans ce range en sortie.
ℹ️​ Notez l’utilisation de .clone() pour ne pas transférer la propriété de range et pouvoir l’utiliser sur la ligne suivante.

Récuperer l’entrée clavier de l’utilisateur

use std::io::stdin;

fn main() {
    let mut line = String::new();
    let input = stdin().read_line(&mut line);
    let guess: Option<u32> = input.ok().map_or(None, |_| line.trim().parse::<u32>().ok() );

    println!("vous avez choisi {:?}", guess);
}

La fonction read_line de stdin permet de lire une ligne entrée au clavier et la stocker dans une String (la variable mutable line dans notre cas)
La variable input resultante est un Result(Ok/Err).

La ligne qui suit est la plus compliquée du programme:
let guess: Option<u32> = input.ok().map_or(None, |_| line.trim().parse::<u32>().ok() ); 🤯​

Pas de panique, décortiquons-la:

  • input.ok(), transforme input qui est un Result(Ok/Err) en Option(Some/None)

  • Ensuite en appliquant .map_or() nous renvoyons

    • soit None si input est en Err (et donc input.ok() est None)

    • soit la valeur de la closure |_| line.trim().parse::<u32>().ok()

  • line.trim().parse::<u32>() trim la valeur de la String line et la parse en u32. Comme pour input, le resultat de parse est un Result<u32>(Ok/Err) que l’on transforme en Option<u32>(Some/None) avec la fonction .ok().

Voilà, guess est donc un Option<u32> (Some<u32>/None)

Comparer l’entrée clavier au nombre magique

Il ne reste plus qu’à regarder ce que vaut la variable guess en la comparant à la variable number. Et pour ça il suffit d’employer match en utilisant une condition (match guard).

use std::io::stdin;

fn main() {
    let range = 1..=15;
    let number = rand::random_range(range.clone());
    println!("Trouvez le nombre magique entre {} et {}.", range.start(), range.end());

    let mut line = String::new();
    let input = stdin().read_line(&mut line);
    let guess: Option<u32> = input.ok().map_or(None, |_| line.trim().parse::<u32>().ok() );

    match guess {
        None => println!("Entrez un nombre"),
        Some(n) if n == number => { println!("Bravo! Vous avez trouvé le nombre magique: {}", number); },
        Some(n) if n < number => println!("Trop petit"),
        Some(n) if n > number => println!("Trop grand"),
        Some(_) => panic!("Ce cas est impossible"),
    };
}

Les cas de figures traités dans le match sont donc les suivants:

  • None: On a juste fait “enter” au clavier (input=Err=>None) ou mis des lettres (line.trim().parse()=Err=>None)

  • Soit un nombre Some(n) égal ou inférieur ou supérieur à number

  • soit un dernier cas impossible qui n’arrivera jamais mais que le compilateur impose car les 3 cas au dessus sont impossible à determiné comme une complétude pour le compilateur.

Ajoutons les boucles pour laisser le joueur jouer indéfiniment

use std::io::stdin;

fn main() {
    loop {
        let range = 1..=15;
        let number = rand::random_range(range.clone());
        println!("Trouvez le nombre magique entre {} et {}. CTRL+c pour arreter.", range.start(), range.end());

        'inner : loop {
            let mut line = String::new();
            let input = stdin().read_line(&mut line);
            let guess: Option<u32> = input.ok().map_or(None, |_| line.trim().parse::<u32>().ok() );

            match guess {
                None => println!("Entrez un nombre"),
                Some(n) if n == number => { println!("Bravo! Vous avez trouvé le nombre magique: {}", number); break 'inner },
                Some(n) if n < number => println!("Trop petit"),
                Some(n) if n > number => println!("Trop grand"),
                Some(_) => panic!("Ce cas est impossible"),
            };
        }
    }

}

La boucle principale reboucle sur un nouveau jeu quand le jeu est fini.
La boucle nommée ‘inner laisse le joueur retenter un nombre jusqu’à ce que celui-ci trouve la solution.
ℹ️​ Notez l’utilisation de break ‘inner dans le match qui permet de sortir de la boucle secondaire quand le joueur a trouvé le nombre magique.

Conclusion

Ce programme est un classique (tellement classique que la doc rust en a fait un exemple ici), mais il concentre bon nombres de concepts différents que l’on retrouve dans tous les programmes en Rust.