diff --git a/rogue_lib/src/floor.rs b/rogue_lib/src/floor.rs index a46ed2f..84e8410 100644 --- a/rogue_lib/src/floor.rs +++ b/rogue_lib/src/floor.rs @@ -206,7 +206,10 @@ pub struct FloorView<'a> { pub floor: &'a Floor, } -/// todo!() add docs +/// Struttura di mezzo usata per far visualizzare una cella e +/// l'eventuale entità che si trova sopra.\ +/// Questa truttura viene usata solamente per prendere i valori in modo read-only +/// e viene utilizzata da FloorView nel metodo get_grid. pub struct CellView<'a> { pub position: Position, pub entity: Option<&'a Entity>, diff --git a/rogue_lib/src/game.rs b/rogue_lib/src/game.rs index ad79da3..9160589 100644 --- a/rogue_lib/src/game.rs +++ b/rogue_lib/src/game.rs @@ -92,7 +92,7 @@ impl Dungeon { pub fn compute_turn(&mut self) { let moved = self.floors.iter_mut().fold(None, |moved, floor| { if floor.has_players() { - let _ = floor.update_players(); //todo!() evantually return the dead players? idk + let _ = floor.update_players(); floor.update_entities(); } diff --git a/rogue_lib/src/generator.rs b/rogue_lib/src/generator.rs index 061a9b0..48d4196 100644 --- a/rogue_lib/src/generator.rs +++ b/rogue_lib/src/generator.rs @@ -16,8 +16,6 @@ use std::{ ops::Range, }; -// todo!() enemies vec configuration? - /// Generatore del gioco che può creare dei piani del dungeon. /// Idealmente questo generatore si comporta come il pattern Factory. /// Per far si che funzioni ha bisongo di un seed per la generazione del piano @@ -81,15 +79,18 @@ impl<'a> Generator<'a> { Floor::new(self.level, self.rng, entities, grid) } - /// todo!() docs + /// Permette di piazzare delle entità in modo casuale nell piano passato.\ + /// Le entità verranno messe solamente sopra celle Empty e non sopvrapposte fra di loro.\ + /// Alla fine verrà restituito un vettore contenente tutte le entità che dovrà poi essere associato + /// al piano in fase di creazione. fn rand_place_entities(&mut self, grid: &mut Vec>) -> Vec { - let entities = vec_filter(&self.config.entities, |e| { + let entities = ProbVec::new(&self.config.entities, |e| { e.floors.contains(&self.level).then(|| (e.priority, e)) }); let mut result: Vec = vec![]; for _ in 0..self.config.entities_total { - let config = vec_get_sample(&entities, &mut self.rng).clone(); + let config = entities.sample(&mut self.rng).clone(); let mut entity = Entity::new( config.name.clone(), config.health, @@ -111,20 +112,19 @@ impl<'a> Generator<'a> { /// piazza gli effetti della confgurazione in modo casuale su tutto il piano.\ /// essi vengono piazzati solamente sulle celle Empty fn rand_place_effects(&mut self, grid: &mut Vec>) { - let effects = vec_filter(&self.config.effects, |e| { + let effects = ProbVec::new(&self.config.effects, |e| { e.floors.contains(&self.level).then(|| (e.priority, e)) }); for _ in 0..self.config.effects_total { - let effect = vec_get_sample(&effects, &mut self.rng).effect.clone(); + let effect = effects.sample(&mut self.rng).effect.clone(); let cell = Cell::Special(effect); let pos = self.rand_empty_cell_pos(grid, 0..self.size, 0..self.size); grid[pos.0][pos.1] = cell; } } - /// piazza una cella in un punto casuale del piano.\ - /// il metodo contiuna a provare a piazzare la cella finche non trova una cella Empty. - /// todo!() docs + /// piazza una cella in un punto casuale tra i range inseriti.\ + /// il metodo continua a provare a piazzare la cella finche non trova una cella Empty. fn rand_empty_cell_pos( &mut self, grid: &mut Vec>, @@ -141,27 +141,49 @@ impl<'a> Generator<'a> { } } -/// crea una vista del vettore passato in input dopo aver applicato la funzione di filtro -pub fn vec_filter(original: &Vec, filter: F) -> Vec<(f32, &T)> -where - F: FnMut(&T) -> Option<(u32, &T)>, -{ - let temp = original.iter().filter_map(filter).collect::>(); - let max = temp.iter().fold(0, |a, b| a.max(b.0)) + 1; - let total = temp.iter().map(|(p, _)| (max - *p) as f32).sum::(); - let mut accum = 0.0; - temp.into_iter() - .map(|(p, item)| { - accum += (max - p) as f32 / total; - (accum, item) - }) - .collect() +pub struct ProbVec<'a, T> { + prob: Vec<(f32, &'a T)>, } -/// todo!() docs -pub fn vec_get_sample<'a, T>(vec: &Vec<(f32, &'a T)>, rng: &mut impl Rng) -> &'a T { - let sample = rng.gen_range(0.0..1.0); - vec.iter().filter(|(p, _)| *p >= sample).next().unwrap().1 +impl<'a, T> ProbVec<'a, T> { + /// Crea una vista del vettore passato in input dopo aver applicato la funzione di filtro.\ + /// Il vettore risultante avrà una tupla contenente l'elemento T e la sua probabilità + /// di essere scelto fra tutti gli elementi del vettore.\ + /// Quindi la somma di tutte le probabilità sarà 1.0 (floating arithmetic permettendo).\ + /// La funzione passata in input deve restituire un valore che più vicino a 0 è, maggiore la priorità + /// dell'elemento di essere selezionato.\ + /// L'algoritmo poi penserà a trasformare le priorità in probabilità secondo questa logica:\ + /// A, priorità 1 e B, priorità 2 => A, 0.66 e B, 0.33\ + /// Ciò significa che A ha probabilità doppia rispetto a B di essere scelta. + pub fn new(original: &'a Vec, filter: F) -> Self + where + F: FnMut(&T) -> Option<(u32, &T)>, + { + let temp = original.iter().filter_map(filter).collect::>(); + let max = temp.iter().fold(0, |a, b| a.max(b.0)) + 1; + let total = temp.iter().map(|(p, _)| (max - *p) as f32).sum::(); + let mut accum = 0.0; + let prob = temp + .into_iter() + .map(|(p, item)| { + accum += (max - p) as f32 / total; + (accum, item) + }) + .collect(); + Self { prob } + } + + /// Dato un vettore generato secondo la funzione vec_filter, essa ne prende un valore casuale + /// utilizzando le probabilità interne del vettore.\ + pub fn sample(&self, rng: &mut impl Rng) -> &'a T { + let sample = rng.gen_range(0.0..1.0); + self.prob + .iter() + .filter(|(p, _)| *p >= sample) + .next() + .unwrap() + .1 + } } /// Utile per la generazione del labirinto.\ diff --git a/rogue_lib/src/lib.rs b/rogue_lib/src/lib.rs index 0d1e4c7..bfe6e56 100644 --- a/rogue_lib/src/lib.rs +++ b/rogue_lib/src/lib.rs @@ -61,7 +61,12 @@ pub fn run_console(player: String, seed: u64) { } } -/// todo!() add docs +/// Permette di aggiungere all'iteratore passato in input una box +/// intesa come una cornice attorno alle stringhe passate.\ +/// Questa funzione è utile nel casoin cui le stringhe generate dall'iteratore +/// abbiano tutte la stessa lunghezza.\ +/// La cornice generata sarà composta dai seguenti caratteri: ║ ═ ╔ ╗ ╚ ╝.\ +/// Eventualmente si può passare un titolo da aggiungere in cima alla cornice. pub fn box_of( size: usize, title: String, @@ -91,10 +96,13 @@ pub fn box_of( .chain(std::iter::once("╝\n".to_string())) } +// list of colors and other formatting thigy +// https://misc.flogisoft.com/bash/tip_colors_and_formatting +// https://gist.github.com/JBlond/2fea43a3049b38287e5e9cefc87b2124 const COLOR_RESET: &str = "\x1b[0m"; const COLOR_EFFECT: &str = "\x1b[95m"; const COLOR_ENEMY: &str = "\x1b[38;5;1m"; -const COLOR_PLAYER: &str = "\x1b[38;5;166m"; +const COLOR_PLAYER: &str = "\x1b[38;5;208m"; const COLOR_PLAYER_HEALTH: &str = "\x1b[31m"; /// Implementazione di una possibile interfaccia console.\ @@ -103,7 +111,8 @@ const COLOR_PLAYER_HEALTH: &str = "\x1b[31m"; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ConsoleInput; impl ConsoleInput { - /// todo!() add docs + /// Stampa il piano passato in input.\ + /// Verranno usate altre funzioni di appoggio per formattare al meglio gli oggetti passati. fn print_floor(&self, floor: FloorView, other: String) { let mut term = console::Term::stdout(); let _ = term.clear_screen(); @@ -113,7 +122,8 @@ impl ConsoleInput { Self::entity_as_string(floor.entity), )); } - /// todo!() add docs + /// Permette di prendere una stringa con le informazioni dell'entità.\ + /// Alcune di esse sono il nome, la vita massima e quanto ne rimane sottoforma di HP bar. fn entity_as_string(entity: &Entity) -> String { let times = 20; let health_bar = (entity.get_health() * times) / entity.get_health_max(); @@ -127,7 +137,9 @@ impl ConsoleInput { entity.get_health_max() ) } - /// todo!() add docs + /// Permette di prendere una stringa con le informazioni del piano.\ + /// Il risultato sarà una vista del piano con raggio 5 (scelto arbitrariamente), + /// e che quindi restituirà una porzione di campo 10x10 evidenziando eventuali celle o entità. fn floor_as_string(floor: &FloorView) -> String { let view = 5; let size = (2 * view) * 3; @@ -186,11 +198,11 @@ impl Behavior for ConsoleInput { let _ = term.write_line("[z] => for doing nothing"); let _ = term.write_line("[q] => for exit the game"); let _ = term.write("Press ANY button to continue...".as_bytes()); - let _ = term.read_char(); - let _ = term.clear_line(); + let _ = term.read_char(); // waiting for user acknowledgment + let _ = term.clear_line(); // clear line "press button..." let _ = term.clear_last_lines(4); // this number is from the previous message (4 total lines of help) - let _ = term.move_cursor_up(1); - let _ = term.move_cursor_right(prompt.len()); + let _ = term.move_cursor_up(1); // moving up since the first write_line put me down by one + let _ = term.move_cursor_right(prompt.len()); // moving at the end of the prompt } _ => (), } diff --git a/rogue_lib/src/main.rs b/rogue_lib/src/main.rs new file mode 100644 index 0000000..79dabe8 --- /dev/null +++ b/rogue_lib/src/main.rs @@ -0,0 +1,4 @@ +fn main() { + let seed = rand::random(); + rogue_lib::run_console("Jack".to_string(), seed); +} diff --git a/rogue_lib/tests/tests.rs b/rogue_lib/tests/tests.rs index e11ab88..5d1ba8a 100644 --- a/rogue_lib/tests/tests.rs +++ b/rogue_lib/tests/tests.rs @@ -346,12 +346,12 @@ fn test_game_initial_config() { fn test_generator_priority() { let mut vec = vec![(1_u32, &"a"), (3, &"b"), (2, &"c")].into_iter(); let vec1 = vec!["", "", ""]; - let vec = rogue_lib::generator::vec_filter(&vec1, |_| vec.next()); + let prob = rogue_lib::generator::ProbVec::new(&vec1, |_| vec.next()); let mut sum: std::collections::HashMap<&str, u32> = std::collections::HashMap::new(); let mut rng = ::seed_from_u64(0); let tot = 600000; for _ in 0..tot { - let sample = rogue_lib::generator::vec_get_sample(&vec, &mut rng); + let sample = prob.sample(&mut rng); let val = sum.entry(*sample).or_default(); *val += 1; }