Tracks
/
Rust
Rust
/
Übungen
/
Rollenspiel
Rollenspiel

Rollenspiel

Lernübung

Einführung

Null-Referenzen

Wenn du schon einmal eine andere Programmiersprache verwendet hast (C/C++, Python, Java, Ruby, Lisp usw.), bist du wahrscheinlich schon einmal auf null oder nil gestoßen. Mit null oder nil zeigen diese Sprachen an, dass eine bestimmte Variable keinen Wert hat. Allerdings passiert es dadurch leicht (und häufig), dass du versehentlich eine Variable verwendest, die auf null zeigt. Wie du dir vorstellen kannst, kann der Versuch, eine Funktion aufzurufen, die es nicht gibt, oder auf einen Wert zuzugreifen, der nicht existiert, zu allerlei Bugs und Abstürzen führen. Der Erfinder von null nannte es sogar seinen 'Milliarden-Dollar-Fehler'.

Der Typ Option

Um diese Probleme zu vermeiden, verwendet Rust keine Null-Referenzen. Rust braucht aber trotzdem eine sichere Möglichkeit anzugeben, dass eine bestimmte Variable keinen Wert hat. Hier kommt Option ins Spiel. Statt eine Variable zu haben, der ein Wert fehlt, können Rust-Variablen das Enum Option verwenden. Dieses Enum hat zwei Varianten: None, das Äquivalent zu null in Rust; und Some(T), wobei T ein Wert beliebigen Typs ist.

Es sieht so aus:

enum Option<T> {
    None,
    Some(T),
}

Du kannst dir Option als eine Schutzschicht zwischen dir und den Problemen vorstellen, die Null-Referenzen verursachen können, wobei ihr konzeptioneller Nutzen erhalten bleibt.

Option verwenden

Eine Variable auf None zu setzen, ist ganz einfach:

let nothing: Option<u32> = None;  // Variable nothing is set to None

Wenn du aber möchtest, dass der Typ Option einen Wert trägt, kannst du diesen Wert nicht direkt zuweisen. Eine Variable vom Typ Option und zum Beispiel eine Variable vom Typ i32 sind nicht dasselbe. Du musst Some verwenden:

let wrong_way: Option<i32> = -4; // This will not work

let right_way: Option<i32> = Some(-4); // This will work
let another_right_way = Some(-4); // Compiler infers that this is Option<i32>

Aus demselben Grund funktioniert auch Folgendes nicht:

let number = 47;
let option_number = Some(15);

let compile_error = number + option_number; // Cannot add an i32 and an Option<i32> - they are of different types

Wenn du den Wert erhalten möchtest, den Some enthält, musst du zuerst prüfen, ob er existiert:

let mut some_words = Some("choose something to say"); // some_words set to something

match some_words {
    Some(str) => println!("Here, we will {}", str),
    None => println!("I've got nothing to say"),
} // Prints "Here, we will choose something to say"

some_words = None; // some_words now set to None

// exactly the same match block as above
match some_words {
    Some(str) => println!("Here, we will {}", str),
    None => println!("I've got nothing to say"),
} // Prints "I've got nothing to say"

Neben match hat Rust noch andere Werkzeuge, um Werte in Option zu prüfen und auf sie zuzugreifen, aber match dürfte dir inzwischen vertraut sein.

Sieh das außerdem als Demonstration dafür, warum Rust Option statt einer Null-Referenz verwendet. Der Punkt ist, dass du musst prüfen, ob die Option-Variable Some ist (in diesem Fall kannst du den enthaltenen Wert herausziehen und verwenden) oder None. Bei allem anderen lässt sich dein Programm nicht kompilieren; der Compiler bewahrt dich vor null.

Anleitung

Du arbeitest an der Umsetzung eines Rollenspiels. Die Spielfigur des Spielers wird durch Folgendes dargestellt:

pub struct Player {
    health: u32,
    mana: Option<u32>,
    level: u32,
}

Spieler in diesem Spiel müssen Level 10 erreichen, bevor sie einen Manapool freischalten, damit sie Zauber wirken können. Bis dahin ist das Mana des Spielers None.

Du arbeitest an zwei Mechaniken in diesem Spiel: der Wiederbelebung und dem Wirken von Zaubersprüchen.

Die Methode revive sollte prüfen, ob der Spieler wirklich tot ist (seine Gesundheit hat 0 erreicht), und wenn ja, eine neue Player-Instanz mit 100 Gesundheit zurückgeben. Wenn das Level des Spielers 10 oder höher ist, sollte er außerdem mit 100 Mana wiederbelebt werden. Ist das Level des Spielers niedriger als 10, sollte sein Mana None sein. Die Methode revive sollte das Level des Spielers beibehalten.

let dead_player = Player { health: 0, mana: None, level: 2 };
dead_player.revive()
// Returns Player { health: 100, mana: None, level: 2 }

Wird die Methode revive für einen Spieler aufgerufen, dessen Gesundheit 1 oder höher ist, sollte die Methode None zurückgeben.

let alive_player = Player { health: 1, mana: Some(15), level: 11 };
alive_player.revive()
// Returns None

Die Methode cast_spell nimmt eine veränderliche Referenz auf den Spieler sowie einen Parameter mana_cost entgegen, der angibt, wie viel Mana der Zauber kostet. Sie gibt den Schaden zurück, den der gewirkte Zauber verursacht. Wenn der Zauber erfolgreich gewirkt wird, ist dieser immer doppelt so hoch wie die Manakosten des Zaubers.

  • Hat der Spieler keinen Zugriff auf einen Manapool, muss der Versuch, den Zauber zu wirken, seine Gesundheit um die Manakosten des Zaubers verringern. Der zurückgegebene Schaden muss 0 sein.

    let not_a_wizard_yet = Player { health: 79, mana: None, level: 9 };
    assert_eq!(not_a_wizard_yet.cast_spell(5), 0)
    assert_eq!(not_a_wizard_yet.health, 74);
    assert_eq!(not_a_wizard_yet.mana, None);
    
  • Hat der Spieler einen Manapool, aber nicht genug Mana, sollte die Methode den Pool nicht verändern, sondern 0 zurückgeben.

    let low_mana_wizard = Player { health: 93, mana: Some(3), level: 12 };
    assert_eq!(low_mana_wizard.cast_spell(10), 0);
    assert_eq!(low_mana_wizard.health, 93);
    assert_eq!(low_mana_wizard.mana, Some(3));
    
  • Andernfalls sollte der Wert von mana_cost vom Manapool des Spielers abgezogen und der entsprechende Schaden zurückgegeben werden.

    let wizard = Player { health: 123, mana: Some(30), level: 18 };
    assert_eq!(wizard.cast_spell(10), 20);
    assert_eq!(wizard.health, 123);
    assert_eq!(wizard.mana, Some(20));
    

Viel Spaß!

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Rust Exercism

Bereit, mit Rollenspiel zu starten?

Melde dich bei Exercism an, um Rust mit 99 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.