Es 03 Rogue Game

- init of structures
- not done
- tests not done
This commit is contained in:
2024-04-30 19:09:54 +02:00
parent 03ccf0e733
commit a5a6045ee3
8 changed files with 429 additions and 0 deletions

42
src/es03_game.rs Normal file
View File

@@ -0,0 +1,42 @@
#![allow(unused)]
/** 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;

52
src/es03_game/cell.rs Normal file
View File

@@ -0,0 +1,52 @@
use rand::Rng;
use serde::{Deserialize, Serialize};
use super::{entities::{Direction, Entity}, game::Rogue};
#[derive(Clone, Copy, Deserialize, Serialize)]
pub enum Cell {
Staricase,
Special(Effect),
Wall,
Empty,
}
#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum Effect {
InstantDamage(i8),
TurnBasedDamage(u8, i8),
//DeBuff(i8, i8),
Confusion(u8),
//Custom(i16),
}
const DELTA: i32 = 5;
impl Effect {
pub fn effect(&self, game: &mut Rogue, entity: &mut Entity) {
let floor = game.current_floor();
let rng = floor.get_rng();
match *self {
Effect::InstantDamage(damage) => {
let damage = damage as i32;
let damage = damage + rng.gen_range(-DELTA..DELTA);
entity.health += damage;
}
Effect::Confusion(time) => {
if time > 0 {
entity.add_effect(Effect::Confusion(time - 1));
let coin_flip = rng.gen_range(0..=1);
if coin_flip == 1 {
let random_direction = Direction::generate_random(rng);
entity.direction = random_direction;
}
}
}
_ => todo!()
}
}
}
pub const POISON: Effect = Effect::InstantDamage(20);
pub const FOOD: Effect = Effect::InstantDamage(-20);
pub const CONFUSION: Effect = Effect::Confusion(u8::MAX);

35
src/es03_game/config.rs Normal file
View File

@@ -0,0 +1,35 @@
use super::cell::{self, Effect};
use serde::{Deserialize, Serialize};
/**
* Struttura Config usata per definire il gioco, ha alcune cose utili
* TODO sarebbe bello poterle prendere da file.
*/
#[derive(Clone, Deserialize, Serialize)]
pub struct Config {
pub game_seed: u64,
pub floor_size_range: (usize, usize),
pub effects: Vec<ConfigEffect>,
pub effects_total: usize,
}
#[derive(Clone, Copy, Deserialize, Serialize)]
pub struct ConfigEffect {
pub effect: Effect,
pub first_floor: usize,
pub last_floor: usize,
pub priority: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
game_seed: 0,
floor_size_range: (20, 30),
effects: vec![ConfigEffect { effect: cell::POISON, first_floor: 0, last_floor: 255, priority: 1 }],
effects_total: 45,
}
}
}

143
src/es03_game/entities.rs Normal file
View File

@@ -0,0 +1,143 @@
use std::{collections::VecDeque, fmt::Display};
use rand::Rng;
use rand_pcg::Pcg32;
use serde::{Deserialize, Serialize};
use super::{
cell::{Cell, Effect},
game::Rogue,
};
#[derive(Clone, Copy, Deserialize, Serialize)]
pub enum Direction {
UP,
DOWN,
LEFT,
RIGHT,
NONE,
}
impl Direction {
pub fn invert(&self) -> Self {
match *self {
Direction::UP => Direction::DOWN,
Direction::DOWN => Direction::UP,
Direction::RIGHT => Direction::LEFT,
Direction::LEFT => Direction::RIGHT,
_ => Direction::NONE,
}
}
pub fn move_from(&self, pos: (usize, usize)) -> (usize, usize) {
match *self {
Direction::UP => (pos.0, pos.1 + 1),
Direction::DOWN => (pos.0, pos.1 - 1),
Direction::RIGHT => (pos.0 + 1, pos.1),
Direction::LEFT => (pos.0 - 1, pos.1),
Direction::NONE => (pos.0, pos.1),
}
}
pub fn generate_random(rng: &mut Pcg32) -> Self {
match rng.gen_range(0..4) {
0 => Direction::UP,
1 => Direction::DOWN,
2 => Direction::LEFT,
_ => Direction::RIGHT,
}
}
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c = match self {
Self::UP => '▲',
Self::DOWN => '▼',
Self::LEFT => '◄',
Self::RIGHT => '►',
Self::NONE => '■',
};
write!(f, "{}", c)
}
}
#[derive(Clone, Copy, Deserialize, Serialize)]
pub enum Action {
Move(Direction),
// attack
DoNothing,
}
#[derive(Deserialize, Serialize)]
pub struct Entity {
name: String,
floor: usize,
effected_by: VecDeque<Effect>,
player: bool,
pub position: (usize, usize),
pub direction: Direction,
pub health: i32,
pub attack: i32,
}
impl Entity {
pub fn new(name: String) -> Self {
Self {
name,
floor: 0,
effected_by: VecDeque::new(),
position: (0, 0),
player: false,
direction: Direction::UP,
attack: 100,
health: 100,
}
}
pub fn new_player(name: String) -> Self {
let mut player = Self::new(name);
player.player = true;
player
}
pub fn add_effect(&mut self, effect: Effect) {
self.effected_by.push_back(effect);
}
pub fn compute_effects(&mut self, game: &mut Rogue) {
let total = self.effected_by.len(); // len could change
for _ in 0..total {
if let Some(effect) = self.effected_by.pop_front() {
effect.effect(game, self);
}
}
}
pub fn do_action(&mut self, game: &mut Rogue, action: Action) {
match action {
Action::Move(direction) => {
self.direction = direction;
self.do_action_move(game);
}
_ => todo!(),
}
}
fn do_action_move(&mut self, game: &mut Rogue) {
let direction = self.direction;
let pos = direction.move_from(self.position);
let floor = game.current_floor();
match floor.get_cell(pos) {
Cell::Empty => {
self.position = pos;
}
Cell::Special(effect) => {
self.position = pos;
self.add_effect(effect)
}
Cell::Staricase => {
if self.player {
game.build_new_floor()
}
}
Cell::Wall => self.direction = direction.invert(),
}
}
}

42
src/es03_game/floor.rs Normal file
View File

@@ -0,0 +1,42 @@
use rand_pcg::Pcg32;
use serde::{Deserialize, Serialize};
use super::cell::Cell;
use super::entities::Entity;
use super::game::Rogue;
use super::generator::Generator;
#[derive(Deserialize, Serialize)]
pub struct Floor {
level: usize,
grid: Vec<Vec<Cell>>,
entities: Vec<Entity>,
rng: Pcg32,
}
impl Floor {
pub fn new(generator: Generator) -> Self {
Self {
entities: vec![],
level: generator.level,
grid: generator.build_empty_matrix(),
rng: generator.rng,
}
}
pub fn get_rng(&mut self) -> &mut Pcg32 {
&mut self.rng
}
pub fn get_level(&self) -> usize {
self.level
}
pub fn get_cell(&self, pos: (usize, usize)) -> Cell {
self.grid[pos.0][pos.1]
}
pub fn compute_entities(&mut self, game: &mut Rogue) {
for entity in &mut self.entities {
entity.compute_effects(game);
entity.do_action(game, game.input_action(entity));
}
}
}

53
src/es03_game/game.rs Normal file
View File

@@ -0,0 +1,53 @@
use std::fmt::Display;
use rand::{RngCore, SeedableRng};
use rand_pcg::Pcg32;
use serde::{Deserialize, Serialize};
use super::{config::Config, entities::{Action, Entity}, floor::Floor, generator::Generator};
/**
* Struttura del gioco generico che implementa un RogueLike.
*/
#[derive(Deserialize, Serialize)]
pub struct Rogue {
floor: Floor,
rng: Pcg32,
config: Config,
}
impl Display for Rogue {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
impl Rogue {
pub fn new() -> Self {
let config = Config::default();
let mut rng = Pcg32::seed_from_u64(config.game_seed);
let floor = Floor::new(Generator::new(rng.next_u64(), 0, &config));
Self { rng, config, floor }
}
pub fn current_floor(&mut self) -> &mut Floor {
&mut self.floor
}
pub fn build_new_floor(&mut self) {
let level = self.floor.get_level();
let floor_seed = self.rng.next_u64();
let generator = Generator::new(floor_seed, level + 1, &self.config);
self.floor = Floor::new(generator);
}
pub fn input_action(&self, entity: &Entity) -> Action {
todo!()
}
pub fn compute_turn(&mut self) {
todo!();
}
}

View File

@@ -0,0 +1,61 @@
use rand::Rng;
use rand::SeedableRng;
use rand_pcg::Pcg32;
use super::cell::Cell;
use super::config::Config;
use super::config::ConfigEffect;
pub struct Generator<'a> {
pub rng: Pcg32,
pub level: usize,
size: usize,
effects: Vec<&'a ConfigEffect>,
effects_total: usize,
// enemies vec configuration?
}
impl<'a> Generator<'a> {
pub fn new(floor_seed: u64, floor_level: usize, config: &'a Config) -> Self {
let mut rand_pcg = Pcg32::seed_from_u64(floor_seed);
let range = config.floor_size_range.0..config.floor_size_range.1;
let floor_size = rand_pcg.gen_range(range);
let effects_list = &config.effects;
let effects_list = effects_list.into_iter();
let effects_list = effects_list.filter_map(|val| {
if floor_level >= val.first_floor && floor_level <= val.last_floor {
Some(val)
} else {
None
}
});
let effects_list = effects_list.collect();
Self {
rng: rand_pcg,
level: floor_level,
size: floor_size,
effects_total: config.effects_total,
effects: effects_list,
}
}
pub fn build_empty_matrix(&self) -> Vec<Vec<Cell>> {
self.build_matrix_with(Cell::Empty)
}
pub fn build_labyrinth(&mut self) -> Vec<Vec<Cell>> {
todo!()
}
pub fn build_rooms(&mut self) -> Vec<Vec<Cell>> {
todo!()
}
fn place_staircase(&mut self, grid: Vec<Vec<Cell>>) -> Vec<Vec<Cell>> {
todo!()
}
fn build_matrix_with(&self, cell: Cell) -> Vec<Vec<Cell>> {
vec![vec![cell; self.size]; self.size]
}
}

View File

@@ -1,2 +1,3 @@
pub mod es01_anagram;
pub mod es02_rational;
pub mod es03_game;