--- title: Erstes Spiel published: 2022-10-18T17:56:26+02:00 sorting: 2 slug: higher-lower-game --- [Link zum Buch](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html) ## Projekt erstellen Das Projekt wird wie in den vorherigen Einträgen beschrieben erstellt. ## Einen Input aufnehmen ```rust use std::io; println!("Guess the number!"); println!("Please input your guess."); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("Failed to read line"); println!("You guessed: {}", guess); ``` Ausgabe: > Guess the number! > Please input your guess. > Number? 42 > You guessed: 42 ## Was haben wir gemacht? * `use std::io;` bindet die Standard-IO Bibliothek ein * `let mut guess` legt eine Variable `guess` an - `mut` bedeutet, dass sie ``mutable'' also veränderbar ist * `String::new()` erstellt eine neue Instanz der `String`-Klasse * `io::stdin()` legt ein `Stdin`-Objekt an - ein Handler für die CLI-Eingabe - ohne die ``use'' Anweisung oben, müsste es `std::io::stdin()` sein * `.read_line(&mut guess)` ließt eine Zeile und speichert sie in guess - `&` erstellt dabei eine Referenz (wie in C) - Referenzen sind standardmäßig immutable - deshalb `&mut` - `read_line()` gibt ein `Result`-Objekt zurück, dieser kann `Ok` oder `Err` enthalten * `.expect("Fehlermeldung")` entpackt das `Result`-Objekt - Theoretisch ist das unnötig, sonst gibt es aber eine Warnung - Sollte ein `Err` im Result sein, wird durch `expect()` eine Exception auftreten * `println!("Eingabe: {}", guess)` ist ein formatiertes print ## Eine random Zahl erstellen Für eine random Zahl brauchen wir die erste Dependency. + Also `Cargo.toml` bearbeiten: ```toml [dependencies] rand = "0.3.14" ``` Dependencies findet man auch auf [crates.io](https://crates.io). Die crate `rand` kann jetzt im Code verwendet werden. ```rust extern crate rand; use rand::Rng; let secret_number: u32 = rand::thread_rng().gen_range(1, 101); println!("{}", secret_number); ``` Und schwubbs wird eine zufälle Zahl ausgegeben. ## Höher oder tiefer? Vergleichen wir doch einfach mal… Aber was ist das? Ein Fehler??
```rust use std::cmp::Ordering; match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } ```
Der Compiler sagt uns dann Folgendes: ```rust match guess.cmp(&secret_number) { ^^^^^^^^^^^^^^ expected struct `String`, found `u32` mismatched types ``` Unser `guess` ist ja ein `String`! Den kann man nicht einfach mit einem `int` vergleichen (anscheinend). Wir müssen unser guess also umwandeln: ```rust let guess: u32 = guess.trim().parse().expect("Please type a number!"); ``` `.strip()` entfernt Whitespace von beiden Seiten und `parse()` macht eine Zahl draus. `guess` als Variable ist schon vorhanden? Kein Problem! Rust erlaubt ``Shadowing'', damit man nicht mehrere Variablen unterschiedlicher Datentypen für den selben Wert anlegen muss. Jetzt sollte das Vergleichen auch klappen! ```rust use std::cmp::Ordering; match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } ``` > Too big! Wuuh! ## Nicht nur ein Versuch Damit wir mehrmals raten können, brauchen wir eine Schleife. ```rust let secret_number: u32 = rand::thread_rng().gen_range(1, 101); loop { let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("Failed to read line"); let guess: u32 = guess.trim().parse().expect("Please type a number!"); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => println!("You win!"), } } ``` > Number? 100 > Too big! > Number? 50 > Too big! > Number? 25 > Too small! > Number? 30 > Too small! > Number? 40 > Too small! > Number? 42 > Too small! > Number? 45 > You win! > Number? 45 > You win! > Number? 100 > Too big! > Number? 45 > You win! > Number? > ... Funktioniert, aber selbst nach dem Erraten passiert nichts und wir sollen weiter raten. Offensichtlich müssen wir die Schleife noch abbrechen. ```rust let secret_number: u32 = rand::thread_rng().gen_range(1, 101); loop { let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("Failed to read line"); let guess: u32 = guess.trim().parse().expect("Please type a number!"); match guess.cmp(&secret_number) { Ordering::Less => println!("Too small!"), Ordering::Greater => println!("Too big!"), Ordering::Equal => { println!("You win!"); break; }, } } ``` > Number? 100 > Too big! > Number? 50 > Too big! > Number? 25 > Too small! > Number? 42 > Too big! > Number? 39 > Too big! > Number? 37 > Too big! > Number? 36 > Too big! > Number? 33 > Too big! > Number? 30 > Too big! > Number? 29 > You win! ## Error handling Derzeit stirbt das Programm einfach mit einem Fehler, wenn man keine Zahl eingibt. Das können wir auch relativ einfach fixen: ```rust loop { let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("Failed to read line"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; // Wenn wir hier her kommen, haben wir eine gültige Zahl und beenden einfach. break; } ``` > Number? a > Number? b > Number? 🦀 > Number? 5 Statt einem `expect()` haben wir nun eine `match`-Expression. Die Syntax ist relativ einfach zu verstehen. Man kann auch mehrere `Ok(value)` nutzen, wobei dann das richtige aufgerufen wird. `Err(_)` nutzt den Unterstrich, um alle Fehler zu catchen, nicht nur einen speziellen. Das `num` nach dem Pfeil ist ein implizites Return. Wenn eine Variable am Ende eines Blocks steht, wird sie zurückgegeben. ## Fertig Wir haben nun alle Elemente für das "Higher-Lower-Game".