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

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

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

Εισαγωγή

Αναφορές null

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

Ο τύπος Option

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

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

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

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

Χρήση του Option

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

let nothing: Option<u32> = Option::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> = Option::Some(-4); // This will work
let another_right_way = Option::Some(-4); // Compiler infers that this is Option<i32>

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

let number = 47;
let option_number = Option::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 = Option::Some("choose something to say"); // some_words set to something

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

some_words = Option::None; // some_words now set to None

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

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

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

Οδηγίες

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

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

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

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

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

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

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

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

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

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

    let not_a_wizard_yet = Player { health: 79, mana: Option::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, Option::None);
    
  • Αν ο παίκτης έχει δεξαμενή mana αλλά ανεπαρκές mana, η μέθοδος δεν πρέπει να επηρεάζει τη δεξαμενή. Η ζημιά που επιστρέφεται πρέπει να είναι 0.

    let low_mana_wizard = Player { health: 93, mana: Option::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, Option::Some(3));
    
  • Διαφορετικά, το ξόρκι ρίχνεται με επιτυχία. Το mana_cost πρέπει να αφαιρείται από τη δεξαμενή mana του Player. Η ζημιά που επιστρέφεται πρέπει να είναι διπλάσια από το κόστος mana του ξόρκιου.

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

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

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

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

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