---
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??

<div class="notCompiling">
```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!"),
}
```
</div>

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".
