Es 03 Rogue Game
- init of structures - not done - tests not done
This commit is contained in:
42
src/es03_game.rs
Normal file
42
src/es03_game.rs
Normal 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
52
src/es03_game/cell.rs
Normal 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
35
src/es03_game/config.rs
Normal 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
143
src/es03_game/entities.rs
Normal 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
42
src/es03_game/floor.rs
Normal 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
53
src/es03_game/game.rs
Normal 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!();
|
||||
}
|
||||
}
|
||||
61
src/es03_game/generator.rs
Normal file
61
src/es03_game/generator.rs
Normal 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]
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod es01_anagram;
|
||||
pub mod es02_rational;
|
||||
pub mod es03_game;
|
||||
|
||||
Reference in New Issue
Block a user