Le enumerazioni sono strutture di dati organizzati come una lista di valori costanti. Per ogni istanza possiamo scegliere una sola opzione, un solo valore da usare. La stessa cosa vale per Rust ma con questo linguaggio abbiamo anche il vantaggio di combinarle con le strutture per creare una logica complessa e ben organizzata.
Per capire come si usano pensiamo ad videogioco di ruolo. Quando inizia la battaglia il programma deve sapere gestire i turni, e passare da uno stato all’altro: quando selezioniamo un’attività, una mossa o uno strumento. A seconda dello stato deve mostrare la schermata adatta. Perciò creiamo innanzitutto un’enum per gestire gli stati della battaglia.
pub enum BattleState {
Start,
ActionSelect,
MoveSelect,
ItemSelect,
End{ win: bool},
}
Poi creiamo una funzione che per ogni stato richiama la funzione appropriata. Noi usiamo match sulla variabile che ha la proprietà dell’enumerazione in modo da gestire ogni caso.
pub fn use_state(&mut self, state: BattleState) {
match state {
BattleState::Start => self.start_battle(),
BattleState::ActionSelect => self.select_action(),
BattleState::MoveSelect => self.select_move(),
BattleState::ItemSelect => self.select_item(),
BattleState::End { win } => self.end_battle(win),
}
}
Oppure, possiamo creare un trait, un’interfaccia che implementa i metodi al suo interno all’interno delle struct.
pub trait HandleState {
fn set_end(&mut self, win: bool);
fn use_state(&mut self, state: BattleState);
fn start_battle(&mut self);
fn select_action(&mut self);
fn select_move(&mut self);
fn select_item(&mut self);
fn end_battle(&mut self, win: bool);
}
//Dobbiamo avere già creato la struct Battle anche vuota al momento
//struct Battle;
impl HandleState for Battle {
fn use_state(&mut self, state: BattleState) {
match state {
BattleState::Start => self.start_battle(),
BattleState::ActionSelect => self.select_action(),
BattleState::MoveSelect => self.select_move(),
BattleState::ItemSelect => self.select_item(),
BattleState::End { win } => self.end_battle(win),
}
}
fn set_end(&mut self, win: bool) {
self.use_state(BattleState::End { win });
}
fn start_battle(&mut self) {
todo!() //todo! è una macro che indica che la funzione deve ancora essere creata.
}
fn select_action(&mut self) {
todo!()
}
fn select_move(&mut self) {
todo!()
}
fn end_battle(&mut self, win: bool) {
if win {
println!("You win!");
} else {
println!("You lose...");
}
}
fn select_item(&mut self) {
todo!()
}
}
A questo punto creiamo la struttura per il sistema delle battaglie. Prima di tutto creiamo la base per i personaggi con un campo per la salute e una funzione per ricevere danni:
pub struct BattleUnit {
hp: i32,
}
impl BattleUnit {
pub fn new(hp:i32) -> Self {
Self {hp}
}
pub fn hp(&self) -> i32 {
self.hp
}
pub fn damage(&mut self, amount:i32) {
self.hp -= amount;
}
}
A questo punto creiamo un tratto per il gestire la battaglia e la implementiamo in Battle.
pub(crate) trait BattleSystem {
fn handle_game_over(&mut self);
fn handle_game(&mut self);
fn player_turn(&mut self);
fn enemy_turn(&mut self);
}
impl BattleSystem for Battle {
fn handle_game(&mut self) {
while self.player.hp() > 0 && self.enemy.hp() > 0 {
self.player_turn();
self.enemy_turn();
}
}
fn handle_game_over(&mut self) {
if self.player.hp() <= 0 {
self.set_end(false);
} else {
self.set_end(true);
}
}
fn player_turn(&mut self) {
self.enemy.damage(5);
println!("HP Enemy: {}", self.enemy.hp());
thread::sleep(time::Duration::from_secs(1));
}
fn enemy_turn(&mut self) {
self.player.damage(5);
println!("HP Player: {}", self.player.hp());
thread::sleep(time::Duration::from_secs(1));
}
}
A questo punto inseriamo le proprietà e i metodi di Battle:
pub struct Battle {
pub player: BattleUnit,
pub enemy: BattleUnit,
}
impl Battle {
pub fn start(&mut self) {
self.handle_game();
self.handle_game_over();
}
}
A questo punto, in main ci basterà creare le unità per la battaglia e richiamare il metodo start() di battle:
fn main() {
Battle {
player: BattleUnit::new(50),
enemy: BattleUnit::new(30),
}.start();
}