Διαδρομές
/
Rust
Rust
/
Ασκήσεις
/
Παιχνίδι ρόλων
Παιχνίδι ρόλων

Παιχνίδι ρόλων

Άσκηση εκμάθησης

Εισαγωγή

Αναφορές null

Αν έχεις χρησιμοποιήσει ποτέ κάποια άλλη γλώσσα προγραμματισμού (C/C++, Python, Java, Ruby, Lisp κ.λπ.), είναι πιθανό να έχεις συναντήσει το null ή το nil. Η χρήση του null ή του nil είναι ο τρόπος με τον οποίο αυτές οι γλώσσες δηλώνουν ότι μια συγκεκριμένη μεταβλητή δεν έχει τιμή. Ωστόσο, αυτό κάνει το να χρησιμοποιήσεις κατά λάθος μια μεταβλητή που δείχνει στο null ένα εύκολο (και συχνό) λάθος. Όπως μπορείς να φανταστείς, το να προσπαθείς να καλέσεις μια συνάρτηση που δεν υπάρχει ή να προσπελάσεις μια τιμή που δεν υπάρχει μπορεί να οδηγήσει σε κάθε λογής bugs και καταρρεύσεις. Ο δημιουργός του null έφτασε στο σημείο να το αποκαλέσει 'το λάθος του ενός δισεκατομμυρίου δολαρίων'.

Ο τύπος Option

Για να αποφύγει αυτά τα προβλήματα, η Rust δεν χρησιμοποιεί αναφορές null. Ωστόσο, χρειάζεται ακόμα έναν ασφαλή τρόπο να δηλώνει ότι μια συγκεκριμένη μεταβλητή δεν έχει τιμή. Εδώ έρχεται το Option. Αντί να έχει μια μεταβλητή που στερείται τιμής, οι μεταβλητές της Rust μπορούν να χρησιμοποιούν το enum Option. Αυτό το enum έχει δύο παραλλαγές: το None, το αντίστοιχο του null στη Rust, και το Some(T), όπου το T είναι μια τιμή οποιουδήποτε τύπου.

Μοιάζει κάπως έτσι:

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

Μπορείς να σκεφτείς το Option ως ένα στρώμα ασφάλειας ανάμεσα σε εσένα και στα προβλήματα που μπορούν να προκαλέσουν οι αναφορές null, διατηρώντας παράλληλα τη χρησιμότητά τους σε εννοιολογικό επίπεδο.

Χρήση του Option

Το να θέσεις μια μεταβλητή σε None είναι αρκετά απλό:

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

Ωστόσο, αν θέλεις ο τύπος Option να μεταφέρει μια τιμή, δεν μπορείς να αναθέσεις αυτή την τιμή απευθείας. Μια μεταβλητή τύπου Option και, ας πούμε, μια μεταβλητή τύπου i32 δεν είναι ισοδύναμες. Θα χρειαστεί να χρησιμοποιήσεις το Some:

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>

Για τον ίδιο λόγο, το παρακάτω δεν θα δουλέψει:

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

Αν θέλεις να πάρεις την τιμή που περιέχει το Some, θα πρέπει πρώτα να ελέγξεις ότι υπάρχει:

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"

Εκτός από το match, η Rust έχει κι άλλα εργαλεία για τον έλεγχο και την πρόσβαση σε τιμές που περιέχονται σε ένα Option, αλλά το match θα πρέπει να σου είναι πλέον οικείο.

Επιπλέον, δες το ως μια επίδειξη του γιατί η Rust χρησιμοποιεί το Option αντί για αναφορά null. Το θέμα είναι ότι πρέπει οπωσδήποτε να ελέγξεις αν η μεταβλητή Option είναι Some (οπότε μπορείς να προχωρήσεις και να εξαγάγεις και να χρησιμοποιήσεις την τιμή που περιέχει) ή None. Οτιδήποτε άλλο και το πρόγραμμά σου δεν θα μεταγλωττιστεί. Ο μεταγλωττιστής σε προστατεύει από το null.

Οδηγίες

Δουλεύεις πάνω στην υλοποίηση ενός παιχνιδιού ρόλων. Ο χαρακτήρας του παίκτη αναπαρίσταται από το εξής:

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

Οι παίκτες σε αυτό το παιχνίδι πρέπει να φτάσουν στο level 10 για να ξεκλειδώσουν ένα mana pool και να αρχίσουν να ρίχνουν ξόρκια. Πριν από αυτό το σημείο, το mana του Player είναι None.

Δουλεύεις πάνω σε δύο λειτουργίες σε αυτό το παιχνίδι: τη μηχανική revive και τη μηχανική ρίψης ξόρκιων.

Η μέθοδος revive πρέπει να ελέγχει αν ο Player είναι πράγματι νεκρός (το health του έχει φτάσει στο 0) και, αν είναι, η μέθοδος πρέπει να επιστρέφει ένα νέο στιγμιότυπο Player με 100 health. Αν το level του Player είναι 10 ή παραπάνω, πρέπει επίσης να αναβιώνει με 100 mana. Αν το level του Player είναι κάτω από 10, το mana του πρέπει να είναι None. Η μέθοδος revive πρέπει να διατηρεί το level του Player.

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

Αν η μέθοδος revive κληθεί σε έναν Player του οποίου το health είναι 1 ή παραπάνω, τότε η μέθοδος πρέπει να επιστρέφει None.

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

Η μέθοδος cast_spell παίρνει μια μεταβλητή αναφορά σε έναν Player καθώς και μια παράμετρο mana_cost που δείχνει πόσο mana κοστίζει το ξόρκι. Επιστρέφει την ποσότητα ζημιάς που προκαλεί το ξόρκι, η οποία θα είναι πάντα το διπλάσιο του κόστους mana του ξόρκι, αν το ξόρκι εκτελεστεί με επιτυχία.

  • Αν ο παίκτης δεν έχει πρόσβαση σε mana pool, η προσπάθεια να ρίξει το ξόρκι πρέπει να μειώσει το health του κατά το κόστος mana του ξόρκι. Η ζημιά που επιστρέφεται πρέπει να είναι 0.

    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);
    
  • Αν ο παίκτης έχει mana pool αλλά ανεπαρκές mana, η μέθοδος δεν πρέπει να επηρεάζει το pool, αλλά να επιστρέφει 0

    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));
    
  • Διαφορετικά, το mana_cost πρέπει να αφαιρείται από το mana pool του Player και να επιστρέφεται η κατάλληλη ποσότητα ζημιάς.

    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));
    

Καλή διασκέδαση!

Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
Rust Exercism

Έτοιμος να ξεκινήσεις την άσκηση Παιχνίδι ρόλων;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις Rust με 99 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.