- added Debug for many structs
- added you_died to behavior
- modified FloorView to iterface better
- moved many traits impl to mod.rs
This commit is contained in:
2024-05-19 02:50:40 +02:00
parent 8862941152
commit ba7051ef07
8 changed files with 260 additions and 123 deletions

View File

@@ -1,41 +0,0 @@
/** Es.3
* Implementare una libreria che permetta di realizzare il seguente gioco.
* Il Campo di gioco e' una matrice n x n di celle le celle sui 4 lati sono dei muri e all'interno le celle possono essere
* - vuote
* - contenere cibo (un intero positivo)
* - contenere un veleno (un intero positivo)
*
* Un Giocatore si muove in questa matrice iniziando da una posizione casuale. Il giocatore ha
* - Direzione in cui si muove: Su, Giu', Destra, Sinistra
* - Posizione nella matrice
* - una forza (un intero positivo)
*
* Quando si muove avanza di una posizione nella direzione in cui il giocatore si muove. Una Configurazione e'
* un campo di gioco, e un giocatore in una posizione del campo per questa struttura implementate il trait Display
*
* Il gioco inizia con una configurazione in cui nella matrice ci sono m caselle con cibo e m con veleno (in posizioni casuali), un giocatore in una cella libera e un numero massimo di mosse.
* Ad ogni iterazione: Si lancia una moneta (Testa o Croce) se
* - Testa il giocatore si muove di una posizione nella direzione in cui si sta muovendo
* - altrimenti sceglie casualmente una dell 4 direzioni e fa un passo in quella direzione.
*
* Se la cella in cui si finisce
* contiene cibo, si aggiunge la quantita' di cibo alla forza
* contiene veleno, si decrementa la quantita' di veleno dalla forza
* e' un muro il giocatore rimbalza, cioe' resta nella stessa posizione ma cambia la sua direzione nella direzione opposta.
*
* Il gioco finisce quando
* - il giocatore finisce la forza (cioe' questa diventerebbe un valore <=0) e in questo caso PERDE
* - raggiunge il numero massimo di mosse nel qual caso VINCE
*
* Per n, m, le quantità iniziali dei vari elementi (elemento, cibo, forza) e il numero massimo di mosse usate variabili che possano essere inserite dall'utente.
* Se volete potete anche cambiare le regole del gioco.
* Mettere main e definizioni in files separati (le definizioni in uno o più files) e scrivete i test in una directory a parte.
*/
pub mod floor;
pub mod game;
pub mod generator;
pub mod cell;
pub mod config;
pub mod entities;

View File

@@ -8,7 +8,7 @@ use std::fmt::Display;
/// Essa ha diversi valori in base a cosa si può fare o meno su di essa.
/// Nel caso in cui passi sopra una entià esiste un metodo entity_over che
/// gestisce le varie casistiche.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Cell {
Entance,
Exit,
@@ -71,7 +71,7 @@ impl Display for Cell {
/// In questo modo si possono creare molteplici effetti che implementano
/// questo trait senza il bisogno di avere un Enum con essi
#[typetag::serde(tag = "type")]
pub trait Effect: DynClone {
pub trait Effect: DynClone + core::fmt::Debug {
/// Indica se l'effetto rimane nel terreno dopo la sua applicazione ad una entità.\
/// Nel caso di true, l'effetto non verrà rimosso dal terreno,
/// eltrimenti la cella dove si trova questo effetto diventerà Empty
@@ -89,7 +89,7 @@ clone_trait_object!(Effect);
/// Una volta utilizzato verrà rimosso dal piano.\
/// Nel caso in cui il danno sia negativo, l'entità verrà curata
/// (sempre che la sua vita sia un valore positivo e non negativo)
#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InstantDamage(pub i32);
#[typetag::serde]
impl Effect for InstantDamage {
@@ -105,7 +105,7 @@ impl Effect for InstantDamage {
/// Esso ignora il successivo comando che verrà impartito all'entità
/// con una probabilità del 50% e inserirà un movimento in una direzione casuale.\
/// Come parametro si può passare per quanti turni l'effetto dura
#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Confusion(pub u8);
#[typetag::serde]
impl Effect for Confusion {
@@ -128,7 +128,7 @@ impl Effect for Confusion {
/// Similmente a InstantDamage, se il danno è negativo allora il personaggio verrà curato,
/// sempre a patto che la sua vita sia un valore positivo.\
/// L'effetto dura un determinato numero di turni.
#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TurnBasedDamage {
time: u8,
damage: i32,

View File

@@ -8,7 +8,7 @@ use std::ops::Range;
/// Struttura di configurazione per la creazione di un dungeon.\
/// Ogni elemento indica un parametro per la generazione di un piano o di una entitità.\
/// Esiste una implementazione di default di questa struttura che genera un dungeon standard.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Config {
pub game_seed: u64,
pub maze_generation: ConfigMaze,
@@ -25,7 +25,7 @@ pub struct Config {
/// *straight_percentage* indica da 0 a 100 quanta percentuale c'è che un corridioio, quando viene generato
/// rimanga dritto o viri.\
/// *dead_ends* indica quanti corridoi che non portano a nulla devono esserci alla fine della generazione.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConfigMaze {
pub floor_size: Range<usize>,
pub room_size: Range<usize>,
@@ -40,16 +40,16 @@ pub struct ConfigMaze {
/// Es. effetto A priorità 1 ed effetto B con priorità 2\
/// Se in Config mettiamo 15 effetti per piano, allora avremo
/// in media 10 A e 5 B per ogni piano.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConfigEffect {
pub floors: Range<usize>,
pub effect: Box<dyn Effect>,
pub priority: usize,
pub priority: u32,
}
/// Valori di base per le statistiche di un giocatore.\
/// Esse verranno utilizzate quando un giocatore verrà creato.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConfigPlayer {
pub health: i32,
pub attack: i32,
@@ -61,14 +61,14 @@ pub struct ConfigPlayer {
/// Es. entità A priorità 1 ed entità B con priorità 2\
/// Se in Config mettiamo 15 entità per piano, allora avremo
/// in media 10 A e 5 B per ogni piano.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConfigEntity {
pub floors: Range<usize>,
pub name: String,
pub decider: Box<dyn Behavior>,
pub health: i32,
pub attack: i32,
pub priority: usize,
pub priority: u32,
}
impl Default for Config {
@@ -103,8 +103,8 @@ impl Default for Config {
entities: vec![],
entities_total: 0,
player_stats: ConfigPlayer {
health: 1000,
attack: 100,
health: 100,
attack: 10,
},
}
}

View File

@@ -6,7 +6,7 @@ use dyn_clone::{clone_trait_object, DynClone};
use rand::Rng;
use rand_pcg::Pcg32;
use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, fmt::Display, io::Write, mem};
use std::{collections::VecDeque, fmt::Display, mem};
/// Tupla nominata Position in modo che nel codice sia più chiaro a cosa serve.\
/// È molto più facile capire a colpo d'occhio Position rispetto a (usize, usize)\
@@ -17,7 +17,7 @@ pub struct Position(pub usize, pub usize);
/// Indica la direzione dove una entità sta guardando.\
/// È possibile anche non guardare in nessuna direzione tramite None.
#[derive(PartialEq, Eq, Hash, Clone, Copy, Deserialize, Serialize)]
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug, Deserialize, Serialize)]
pub enum Direction {
Up,
Down,
@@ -85,7 +85,7 @@ impl Display for Direction {
}
/// Rappresenta una entità all'interno del dungeon.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Entity {
name: String,
effects: VecDeque<Box<dyn Effect>>,
@@ -210,7 +210,7 @@ impl Display for Entity {
/// Azione che una qualsiasi entità può fare.
/// L'azione DoNothing permette all'entità di saltare il turno nel caso in cui sia utile.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Action {
Move(Direction),
//Attack(Direction),
@@ -253,12 +253,16 @@ impl Action {
/// In questo modo si possono creare molteplici comoprtamenti che implementano
/// questo trait senza il bisogno di avere un Enum con essi
#[typetag::serde(tag = "type")]
pub trait Behavior: DynClone {
pub trait Behavior: DynClone + core::fmt::Debug {
/// In questo metodo viene passata una struttura che contiene una rappresentazione del
/// piano semplice, avente solo delle informazioni parziali.\
/// Questo serve a mostrare eventualmente delle possibili informazioni all'utente
/// o di registrare dei valori per l'algoritmo di generazione delle azioni.
fn update(&self, floor: FloorView);
/// Funzione che viene richiamata quando l'entità muore.\
/// I parametri servono a far vedere un'ultima volta i dati del piano corrente all'entità
/// in modo che possa eventualmente fare ulteriori calcoli.
fn you_died(&self, floor: FloorView);
/// Genera una azione che poi verrà usata per l'entità associata.\
/// L'azione può essere generata in qualunque modo: casuale, sempre la stessa,
/// tramite interazione con console, o tramite una connessione ad un client.\
@@ -272,41 +276,13 @@ clone_trait_object!(Behavior);
/// Semplice implementazione di un possibile comportamento di una entità.\
/// In questo caso l'entità resterà immobile nel punto in cui si trova per sempre.
#[derive(Clone, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Immovable;
#[typetag::serde]
impl Behavior for Immovable {
fn update(&self, _floor: FloorView) {}
fn you_died(&self, _floor: FloorView) {}
fn get_next_action(&self) -> Option<Action> {
Some(Action::DoNothing)
}
}
/// Semplice implementazione di una possibile interfaccia console.
#[derive(Clone, Serialize, Deserialize)]
pub struct ConsoleInput;
#[typetag::serde]
impl Behavior for ConsoleInput {
fn update(&self, floor: FloorView) {
let mut term = console::Term::stdout();
let _ = term.clear_screen();
let _ = term.write_fmt(format_args!("{}\n", floor));
}
fn get_next_action(&self) -> Option<Action> {
let mut term = console::Term::stdout();
let _ = term.write("Insert your action [wasd or enter for nothing]: ".as_bytes());
loop {
if let Ok(ch) = term.read_char() {
match ch {
'\n' => return Some(Action::DoNothing),
'w' => return Some(Action::Move(Direction::Up)),
'a' => return Some(Action::Move(Direction::Left)),
's' => return Some(Action::Move(Direction::Down)),
'd' => return Some(Action::Move(Direction::Right)),
_ => (),
}
}
}
}
}

View File

@@ -4,12 +4,15 @@ use super::{
};
use rand_pcg::Pcg32;
use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, fmt::Display};
use std::{
collections::{HashMap, VecDeque},
fmt::Display,
};
/// Indica un piano del dungeon, in essa si possono trovare le celle in cui si
/// cammina e le entità che abitano il piano.\
/// Per poter accedere a questa struttura è necessario utilizzare FloorPtr e fare get()
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Floor {
level: usize,
grid: Vec<Vec<Cell>>,
@@ -43,6 +46,12 @@ impl Floor {
self.players.iter().any(|player| player.is_alive())
}
/// Restituisce la grandezza di un lato del piano.\
/// Per avere la quantità di celle basterà prendere il valore ed elevarlo a 2.
pub fn get_size(&self) -> usize {
self.grid.len()
}
/// Restituisce il livello di profondità del piano
pub fn get_level(&self) -> usize {
self.level
@@ -112,6 +121,21 @@ impl Floor {
pub fn get_limited_view_floor<'a>(&'a self, entity: &'a Entity) -> FloorView<'a> {
FloorView::new(self, entity)
}
/// Ritorna un iteratore a tutte le entità del piano.\
/// Le entità del piano si dividono in giocatori e entità, e questo iteratore le ritorna tutte,
/// passando prima dai giocatori e poi da tutto il resto.
pub fn get_all_entities<'a>(&'a self) -> impl Iterator<Item = &Entity> + 'a {
self.players.iter().chain(self.entities.iter())
}
/// Controlla che nella posizione indicata non ci siano altre entità e restituisce il numero di collisioni trovate.\
/// Questo metodo controlla TUTTE le entità e i giocatori del piano, quindi si svolge in O(n)
fn collisions(&self, pos: &Position) -> usize {
self.get_all_entities()
.filter(|entity| entity.position == *pos)
.fold(0, |count, _| count + 1)
}
}
/// Struttura di mezzo tra un piano e il gioco vero e proprio.\
@@ -122,6 +146,13 @@ pub struct FloorView<'a> {
pub floor: &'a Floor,
}
/// todo!() add docs
pub struct CellView<'a> {
pub position: Position,
pub entity: Option<&'a Entity>,
pub cell: &'a Cell,
}
impl<'a> FloorView<'a> {
/// Crea una vista del gioco corrente secondo la visione dell'entità passata in intput.\
/// Il SimpleFloor risultante avrà il piano, entità, livello e giocatori che si trovano
@@ -133,29 +164,80 @@ impl<'a> FloorView<'a> {
}
}
/// Ritorna un iteratore contenente gli iteratori di ogni riga del piano.
pub fn get_grid(&self, view: usize) -> impl Iterator<Item = impl Iterator<Item = CellView>> {
let grid = &self.floor.grid;
let entities = self
.floor
.get_all_entities()
.chain(std::iter::once(self.entity))
.map(|entity| (&entity.position, entity))
.collect::<HashMap<_, _>>();
let entities = std::rc::Rc::new(std::cell::RefCell::new(entities));
let temp_x = self.entity.position.0.saturating_sub(view);
let temp_y = self.entity.position.1.saturating_sub(view);
let size_x = temp_x.saturating_add(2 * view).min(grid.len());
let size_y = temp_y.saturating_add(2 * view).min(grid.len());
let view_x = size_x.saturating_sub(2 * view);
let view_y = size_y.saturating_sub(2 * view);
(view_y..size_y).rev().map(move |y| {
let entities = entities.clone();
(view_x..size_x)
.map(move |x| Position(x, y))
.map(move |position| {
let cell = &grid[position.0][position.1];
let entity = entities.borrow_mut().remove(&position);
CellView {
position,
entity,
cell,
}
})
})
}
/// Rappresentazione del piano come matrice di char
pub fn as_char_grid(&self) -> Vec<Vec<char>> {
let grid = &self.floor.grid;
let size = grid.len();
let mut grid = (0..size)
.map(|y| {
(0..size)
.flat_map(|x| {
let cell = &grid[x][y];
let ch = cell.as_char();
match cell {
Cell::Wall => [ch, ch, ch],
_ => [' ', ch, ' '],
}
})
.chain(std::iter::once('\n'))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
self.get_grid(self.floor.grid.len())
.map(|iter| {
iter.flat_map(|view| {
if let Some(e) = view.entity {
return [' ', e.direction.as_char(), ' '];
}
let pos = &self.entity.position;
grid[pos.1][pos.0 * 3 + 1] = self.entity.direction.as_char();
grid
let ch = view.cell.as_char();
match view.cell {
Cell::Wall => [ch, ch, ch],
_ => [' ', ch, ' '],
}
})
.chain(std::iter::once('\n'))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
}
/// todo!() add docs
pub fn box_of(size: usize, iter: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
std::iter::once('╔')
.chain(std::iter::repeat('═').take(size + 2))
.chain(['╗', '\n'].into_iter())
.chain(iter.enumerate().flat_map(move |(i, c)| {
let modulo = i % size;
if modulo == 0 {
vec!['║', ' ', c]
} else if modulo == size - 1 {
vec![c, ' ', '║', '\n']
} else {
vec![c]
}
.into_iter()
}))
.chain(std::iter::once('╚'))
.chain(std::iter::repeat('═').take(size + 2))
.chain(['╝', '\n'].into_iter())
}
}
@@ -164,7 +246,6 @@ impl<'a> Display for FloorView<'a> {
let grid: String = self
.as_char_grid()
.iter()
.rev()
.map(|row| row.iter().collect::<String>())
.collect();

View File

@@ -15,7 +15,7 @@ use std::{
/// Rappresenta un Dungeon in stile RogueLike.\
/// In esso possiamo trovare dei piani generati casualmente
/// e dei giocatori che esplorano.
#[derive(Clone, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Dungeon {
floors: Vec<Floor>,
config: Config,

View File

@@ -54,13 +54,13 @@ impl<'a> Generator<'a> {
/// Questo metodo creerà un piano avente delle stanze collegate tra di loro tramite dei
/// corridoi; inoltre in esse verranno inseriti degli effetti.
pub fn build_floor(mut self) -> Floor {
let attempts = self.config.maze_generation.room_placing_attempts;
let maze_gen = &self.config.maze_generation;
let room_size = self.config.maze_generation.room_size.clone();
let mut grid = MazeGenerator::new(self.size, room_size, &mut self.rng)
.generate_rooms(attempts)
.generate_labyrinth(80)
.generate_rooms(maze_gen.room_placing_attempts)
.generate_labyrinth(maze_gen.straight_percentage)
.connect_regions()
.remove_dead_ends(0)
.remove_dead_ends(maze_gen.dead_ends)
.finalize(Cell::Wall, Cell::Empty);
self.rand_place(&mut grid, Cell::Entance);
@@ -183,7 +183,7 @@ impl<'a> MazeGenerator<'a> {
/// Nel caso si può decidere di lasciare qualche zona che non va a collegarsi da nessuna parte
/// mettendo un numero > 0 nel cutoff.\
/// Questo indicherà che nel labirinto ci saranno al massimo N corridioi senza uscita.
pub fn remove_dead_ends(mut self, cutoff: usize) -> Self {
pub fn remove_dead_ends(mut self, cutoff: u32) -> Self {
let mut dead_ends = (0..self.size)
.into_iter()
.flat_map(|x| {
@@ -196,7 +196,7 @@ impl<'a> MazeGenerator<'a> {
.collect::<VecDeque<_>>();
while let Some(pos) = dead_ends.pop_front() {
if dead_ends.len() < cutoff {
if dead_ends.len() < cutoff as usize {
break;
}
@@ -272,7 +272,7 @@ impl<'a> MazeGenerator<'a> {
/// andare dritto quando crea il labirinto.\
/// Con percentuali alte si avranno molti corridoi lunghi, con percentuali basse si avranno
/// molte svolte.
pub fn generate_labyrinth(mut self, mut straight_percentage: usize) -> Self {
pub fn generate_labyrinth(mut self, mut straight_percentage: u32) -> Self {
straight_percentage = straight_percentage.min(100); // cap at 100
for x in (1..self.size).step_by(2) {
@@ -293,7 +293,7 @@ impl<'a> MazeGenerator<'a> {
/// https://en.wikipedia.org/wiki/Maze_generation_algorithm#Iterative_implementation_(with_stack)\
/// Il parametro straight_percentage indica quanto "scava" i corridoi del labirinto
/// senza girare, e quindi creando lunghi segmenti.
fn grow_maze(&mut self, start: Position, straight_percentage: usize) {
fn grow_maze(&mut self, start: Position, straight_percentage: u32) {
self.current_region += 1;
self.set(&start, Some(self.current_region));
@@ -401,7 +401,7 @@ impl<'a> MazeGenerator<'a> {
/// mentre i lati non hanno dimensione.\
/// I punti quindi salvati sono il minimo e il massimo di un rettangolo ed indicano il
/// punto più in basso da dove inizia l'area e quello più in alto.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
struct Room {
lo: Position,
hi: Position,

121
src/es03_game/mod.rs Normal file
View File

@@ -0,0 +1,121 @@
use self::{
cell::Cell,
config::Config,
entities::{Action, Behavior, Direction},
floor::FloorView,
game::Dungeon,
};
use serde::{Deserialize, Serialize};
use std::io::Write;
pub mod cell;
pub mod config;
pub mod entities;
pub mod floor;
pub mod game;
pub mod generator;
/** Es.3
* Implementare una libreria che permetta di realizzare il seguente gioco.
* Il Campo di gioco e' una matrice n x n di celle le celle sui 4 lati sono dei muri e all'interno le celle possono essere
* - vuote
* - contenere cibo (un intero positivo)
* - contenere un veleno (un intero positivo)
*
* Un Giocatore si muove in questa matrice iniziando da una posizione casuale. Il giocatore ha
* - Direzione in cui si muove: Su, Giu', Destra, Sinistra
* - Posizione nella matrice
* - una forza (un intero positivo)
*
* Quando si muove avanza di una posizione nella direzione in cui il giocatore si muove. Una Configurazione e'
* un campo di gioco, e un giocatore in una posizione del campo per questa struttura implementate il trait Display
*
* Il gioco inizia con una configurazione in cui nella matrice ci sono m caselle con cibo e m con veleno (in posizioni casuali), un giocatore in una cella libera e un numero massimo di mosse.
* Ad ogni iterazione: Si lancia una moneta (Testa o Croce) se
* - Testa il giocatore si muove di una posizione nella direzione in cui si sta muovendo
* - altrimenti sceglie casualmente una dell 4 direzioni e fa un passo in quella direzione.
*
* Se la cella in cui si finisce
* contiene cibo, si aggiunge la quantita' di cibo alla forza
* contiene veleno, si decrementa la quantita' di veleno dalla forza
* e' un muro il giocatore rimbalza, cioe' resta nella stessa posizione ma cambia la sua direzione nella direzione opposta.
*
* Il gioco finisce quando
* - il giocatore finisce la forza (cioe' questa diventerebbe un valore <=0) e in questo caso PERDE
* - raggiunge il numero massimo di mosse nel qual caso VINCE
*
* Per n, m, le quantità iniziali dei vari elementi (elemento, cibo, forza) e il numero massimo di mosse usate variabili che possano essere inserite dall'utente.
* Se volete potete anche cambiare le regole del gioco.
* Mettere main e definizioni in files separati (le definizioni in uno o più files) e scrivete i test in una directory a parte.
*/
pub fn run_console(player: String) {
let mut config = Config::default();
config.game_seed = rand::random();
let mut game = Dungeon::new_with(config);
game.add_player(player, Box::new(ConsoleInput));
while game.has_players() {
game.compute_turn();
}
}
/// Implementazione di una possibile interfaccia console.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ConsoleInput;
impl ConsoleInput {
fn floor_as_string(floor: &FloorView) -> String {
let view = 5;
let size = (2 * view) * 3;
let iter = floor.get_grid(view).flat_map(|iter| {
iter.flat_map(|view| {
if let Some(e) = view.entity {
return [' ', e.direction.as_char(), ' '].into_iter();
}
let ch = view.cell.as_char();
match view.cell {
Cell::Wall => [ch, ch, ch],
_ => [' ', ch, ' '],
}
.into_iter()
})
});
FloorView::box_of(size, iter).collect()
}
}
#[typetag::serde]
impl Behavior for ConsoleInput {
fn update(&self, floor: FloorView) {
let mut term = console::Term::stdout();
let _ = term.clear_screen();
let _ = term.write_fmt(format_args!(
"{}{}\n",
Self::floor_as_string(&floor),
floor.entity
));
}
fn you_died(&self, floor: FloorView) {
let mut term = console::Term::stdout();
let _ = term.clear_screen();
let _ = term.write_fmt(format_args!("{}\nYOU DIED!\n", floor));
}
fn get_next_action(&self) -> Option<Action> {
let mut term = console::Term::stdout();
let _ = term.write("Insert your action [wasd or space for nothing]: ".as_bytes());
loop {
if let Ok(ch) = term.read_char() {
match ch {
' ' => return Some(Action::DoNothing),
'w' => return Some(Action::Move(Direction::Up)),
'a' => return Some(Action::Move(Direction::Left)),
's' => return Some(Action::Move(Direction::Down)),
'd' => return Some(Action::Move(Direction::Right)),
_ => (),
}
}
}
}
}