From 9edb4a224c072ffe88f25affcd735303036754d8 Mon Sep 17 00:00:00 2001 From: bel Date: Fri, 14 Mar 2025 00:11:11 -0600 Subject: [PATCH] impl Battle::Engine::exec() --- src/purerust/src/src.rs | 196 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 180 insertions(+), 16 deletions(-) diff --git a/src/purerust/src/src.rs b/src/purerust/src/src.rs index 657de99..0a18403 100755 --- a/src/purerust/src/src.rs +++ b/src/purerust/src/src.rs @@ -5,6 +5,7 @@ pub mod mon { pub hp: i32, pub atk: i32, pub def: i32, + pub spd: i32, } #[cfg(test)] @@ -18,6 +19,7 @@ pub mod mon { hp: 1, atk: 2, def: 3, + spd: 4, }; } } @@ -42,12 +44,14 @@ pub mod mon { hp: 10, atk: 11, def: 9, + spd: 11, }, Dex::Mari => Species{ name: "Mari".to_string(), hp: 10, atk: 8, def: 12, + spd: 9, }, } } @@ -67,6 +71,7 @@ pub mod mon { } } + #[derive(Clone, Debug)] pub struct Instance { pub species: Species, pub damage: i32, @@ -94,44 +99,203 @@ pub mod mon { } } -pub mod battler { +pub mod battle { use super::*; - pub fn attack(w: &mut mon::Instance, r: &mut mon::Instance) { - let atk_delta = r.species.atk - 10; - let def_delta = w.species.def - 10; + pub struct Engine { + teams: Vec, + q: Vec, + } - let power = match atk_delta - def_delta { - v if v < 1 => 1, - v => v, - }; + impl Engine { + pub fn new(first: Vec, second: Vec) -> Engine { + let mut result = Self{ + teams: vec![], + q: vec![], + }; + result.join(first); + result.join(second); + result + } - w.damage = match w.damage + power { - v if v < w.species.hp => v, - _ => w.species.hp, - }; + pub fn join(&mut self, third: Vec) { + self.teams.push(Team::new(third)); + } + + pub fn enqueue(&mut self, m: Move) { + self.q.push(m); + } + + pub fn exec(&mut self) { + self.turn_order().iter().for_each(|idx| { + match self.q[*idx].clone() { + Move::Pass(_) => {}, + Move::Attack(rIdx, wIdx) => { + let r = self.teams[rIdx.team].mons[rIdx.mon].clone(); + if r.can_attack() { + let w = self.teams[wIdx.team].mons[wIdx.mon].clone(); + if w.can_be_attacked() { + self.teams[wIdx.team].mons[wIdx.mon].mon = Engine::attack(&w.mon, &r.mon); + } + } + }, + }; + }); + self.new_turn(); + } + + fn new_turn(&mut self) { + self.q = vec![]; + } + + fn turn_order(&self) -> Vec { + let mut order = vec![]; + for i in 0..self.q.len() { + order.push((i, match &self.q[i] { + Move::Pass(_) => 0, + Move::Attack(w, _) => self.teams[w.team].mons[w.mon].mon.species.spd, + })); + } + order.sort_unstable_by(|a, b| a.1.cmp(&b.1)); + order.iter().map(|x| x.0).collect() + } + + fn attack(w: &mon::Instance, r: &mon::Instance) -> mon::Instance { + let mut w = w.clone(); + let atk_delta = r.species.atk - 10; + let def_delta = w.species.def - 10; + + let power = match atk_delta - def_delta { + v if v < 1 => 1, + v => v, + }; + + w.damage = match w.damage + power { + v if v < w.species.hp => v, + _ => w.species.hp, + }; + + w + } } #[cfg(test)] - mod species_tests { + mod engine_tests { use super::*; + #[test] + fn test_new() { + let mut engine = Engine::new(team_a(), team_b()); + engine.join(team_b()); + } + #[test] fn test_attack() { let mut pika = mon::Instance::roll(mon::Dex::Pika); let mut mari = mon::Instance::roll(mon::Dex::Mari); - attack(&mut pika, &mut mari); + pika = Engine::attack(&mut pika, &mut mari); assert_eq!(1, pika.damage); - attack(&mut mari, &mut pika); + mari = Engine::attack(&mut mari, &mut pika); assert_eq!(1, mari.damage); for _ in 0..pika.species.hp+5 { - attack(&mut pika, &mut mari); + pika = Engine::attack(&mut pika, &mut mari); } assert_eq!(pika.species.hp, pika.damage); assert_eq!(1, mari.damage); } + + #[test] + fn test_turn() { + let mut engine = Engine::new(team_a(), team_b()); + engine.join(team_b()); + + // player 0 mon 0 attacks team 1 mon 0 + engine.enqueue(Move::Attack(Idx{team: 0, mon: 0}, Idx{team: 1, mon: 0})); + // player 1 mon 0 passes + engine.enqueue(Move::Pass(Idx{team: 1, mon: 0})); + // player 1 mon 1 not out + engine.exec(); + + assert_eq!(0, engine.teams[0].mons[0].mon.damage); + assert_eq!(1, engine.teams[1].mons[0].mon.damage); + assert_eq!(0, engine.teams[1].mons[1].mon.damage); + } + + fn team_a() -> Vec { + vec![mon::Instance::roll(mon::Dex::Pika)] + } + + fn team_b() -> Vec { + vec![mon::Instance::roll(mon::Dex::Mari), mon::Instance::roll(mon::Dex::Pika)] + } + } + + #[derive(Debug)] + struct Team { + mons: Vec, + } + + impl Team { + fn new(mons: Vec) -> Self { + assert!(mons.len() > 0); + let mut mons: Vec<_> = mons.iter().map(|m| Instance{mon: m.clone(), out: false}).collect(); + mons[0].out = true; + Self{mons: mons} + } + } + + #[derive(Clone, Debug)] + struct Instance { + mon: mon::Instance, + out: bool, + } + + impl Instance { + fn alive(&self) -> bool { + self.mon.damage < self.mon.species.hp + } + + fn can_attack(&self) -> bool { + self.out && self.alive() + } + + fn can_be_attacked(&self) -> bool { + self.out && self.alive() + } + } + + #[cfg(test)] + mod instance_tests { + use super::*; + + #[test] + fn test_checks() { + let mut i = Instance{ + mon: mon::Instance::roll(mon::Dex::Pika), + out: true, + }; + assert_eq!(true, i.alive()); + assert_eq!(true, i.can_attack()); + assert_eq!(true, i.can_be_attacked()); + i.mon.damage = i.mon.species.hp; + assert_eq!(false, i.alive()); + assert_eq!(false, i.can_attack()); + assert_eq!(false, i.can_be_attacked()); + } + } + + #[derive(Clone, Debug)] + struct Idx { + pub team: usize, + pub mon: usize, + } + + #[derive(Clone, Debug)] + enum Move { + Pass(Idx), + Attack(Idx, Idx), } }