enqueue returns err on sanity checks, exec returns Turn for a series of turn results
parent
dcbdc29f78
commit
dfc99e97e3
|
|
@ -150,11 +150,44 @@ pub mod battle {
|
|||
self.teams.push(Team::new(third));
|
||||
}
|
||||
|
||||
pub fn enqueue(&mut self, m: Move) {
|
||||
pub fn enqueue(&mut self, m: Move) -> Result<(), String> {
|
||||
match m {
|
||||
Move::Attack(w_idx, r_idx) => {
|
||||
if self.teams()[w_idx].lost {
|
||||
return Err(format!("team[{w_idx}] lost"));
|
||||
}
|
||||
if !self.teams()[w_idx].any_can_attack() {
|
||||
return Err(format!("team[{w_idx}] cannot attack"));
|
||||
}
|
||||
if !self.teams()[r_idx].any_can_be_attacked() {
|
||||
return Err(format!("team[{r_idx}] cannot be attacked"));
|
||||
}
|
||||
}
|
||||
Move::Pass(team_idx) => {
|
||||
if self.teams()[team_idx].lost {
|
||||
return Err(format!("team[{team_idx}] lost"));
|
||||
}
|
||||
if !self.teams()[team_idx].any_can_attack() {
|
||||
return Err(format!("team[{team_idx}] cannot move"));
|
||||
}
|
||||
}
|
||||
Move::Swap(team_idx, mon_idx) => {
|
||||
if self.teams()[team_idx].lost {
|
||||
return Err(format!("team[{team_idx}] lost"));
|
||||
}
|
||||
if !self.teams()[team_idx].mons()[mon_idx].alive() {
|
||||
return Err(format!("team[{team_idx}].mons[{mon_idx}] cannot swap in"));
|
||||
}
|
||||
if self.teams()[team_idx].mons()[mon_idx].out {
|
||||
return Err(format!("team[{team_idx}].mons[{mon_idx}] already in"));
|
||||
}
|
||||
}
|
||||
};
|
||||
self.q.push(m);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exec(&mut self) {
|
||||
pub fn exec(&mut self) -> Vec<Turn> {
|
||||
self.turn_order().iter().for_each(|idx| {
|
||||
match self.q[*idx].clone() {
|
||||
Move::Pass(_) => {}
|
||||
|
|
@ -162,19 +195,21 @@ pub mod battle {
|
|||
let r_team = self.teams[r_team_idx].clone();
|
||||
let w_team = &mut self.teams[w_team_idx];
|
||||
|
||||
for r_mon_idx in 0..r_team.mons.len() {
|
||||
let r_mon = &r_team.mons[r_mon_idx];
|
||||
for r_mon_idx in 0..r_team.mons().len() {
|
||||
let r_mon = &r_team.mons()[r_mon_idx];
|
||||
if !r_mon.can_attack() {
|
||||
continue;
|
||||
}
|
||||
for w_mon_idx in 0..w_team.mons.len() {
|
||||
let w_mon = &w_team.mons[w_mon_idx];
|
||||
for w_mon_idx in 0..w_team.mons().len() {
|
||||
let w_mon = &w_team.mons()[w_mon_idx];
|
||||
if !w_mon.can_be_attacked() {
|
||||
continue;
|
||||
}
|
||||
let r_mon = &r_mon.mon;
|
||||
let w_mon = &w_mon.mon;
|
||||
w_team.mons[w_mon_idx].mon = Engine::attack(w_mon, r_mon);
|
||||
w_team.mons[w_mon_idx].out =
|
||||
w_team.mons[w_mon_idx].out && w_team.mons[w_mon_idx].alive()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +218,7 @@ pub mod battle {
|
|||
Move::Swap(team_idx, mon_idx) => {
|
||||
let mut team = self.teams[team_idx].clone();
|
||||
team.mons = team
|
||||
.mons
|
||||
.mons()
|
||||
.iter()
|
||||
.map(|m| {
|
||||
let mut m = m.clone();
|
||||
|
|
@ -196,15 +231,42 @@ pub mod battle {
|
|||
}
|
||||
};
|
||||
});
|
||||
self.new_turn();
|
||||
self.new_turn()
|
||||
}
|
||||
|
||||
pub fn teams(&self) -> Vec<Team> {
|
||||
self.teams.iter().map(|t| t.clone()).collect()
|
||||
}
|
||||
|
||||
fn new_turn(&mut self) {
|
||||
fn new_turn(&mut self) -> Vec<Turn> {
|
||||
self.q = vec![];
|
||||
|
||||
let mut result = vec![];
|
||||
|
||||
let teams = self.teams();
|
||||
let mut teams_not_lost = vec![];
|
||||
for i in 0..teams.len() {
|
||||
let has_mon_out = teams[i].mons().iter().filter(|m| m.out).count() > 0;
|
||||
let has_mon_alive = teams[i].mons().iter().filter(|m| m.alive()).count() > 0;
|
||||
let lost_before = teams[i].lost;
|
||||
|
||||
if !has_mon_out && has_mon_alive {
|
||||
result.push(Turn::MustSwap(i));
|
||||
} else if !has_mon_alive && !lost_before {
|
||||
self.teams[i].lost = true;
|
||||
result.push(Turn::Lose(i));
|
||||
}
|
||||
|
||||
if !self.teams()[i].lost {
|
||||
teams_not_lost.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
if teams_not_lost.len() == 1 {
|
||||
result.push(Turn::Win(teams_not_lost[0]));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn turn_order(&self) -> Vec<usize> {
|
||||
|
|
@ -281,15 +343,17 @@ pub mod battle {
|
|||
engine.join(team_b());
|
||||
|
||||
// player 0 mon 0 attacks team 1 mon 0
|
||||
engine.enqueue(Move::Attack(0, 1));
|
||||
engine
|
||||
.enqueue(Move::Attack(0, 1))
|
||||
.expect("failed to attack");
|
||||
// player 1 mon 0 passes
|
||||
engine.enqueue(Move::Pass(1));
|
||||
engine.enqueue(Move::Pass(1)).expect("failed to pass");
|
||||
// player 1 mon 1 not out
|
||||
engine.exec();
|
||||
assert_eq!(0, engine.exec().len());
|
||||
|
||||
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);
|
||||
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<mon::Instance> {
|
||||
|
|
@ -307,6 +371,7 @@ pub mod battle {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Team {
|
||||
mons: Vec<Instance>,
|
||||
lost: bool,
|
||||
}
|
||||
|
||||
impl Team {
|
||||
|
|
@ -320,18 +385,29 @@ pub mod battle {
|
|||
})
|
||||
.collect();
|
||||
mons[0].out = true;
|
||||
Self { mons: mons }
|
||||
Self {
|
||||
mons: mons.clone(),
|
||||
lost: mons.len() == 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mons(&self) -> Vec<Instance> {
|
||||
self.mons.iter().map(|i| i.clone()).collect()
|
||||
}
|
||||
|
||||
fn any_can_attack(&self) -> bool {
|
||||
self.mons().iter().filter(|m| m.can_attack()).count() > 0
|
||||
}
|
||||
|
||||
fn any_can_be_attacked(&self) -> bool {
|
||||
self.mons().iter().filter(|m| m.can_be_attacked()).count() > 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Instance {
|
||||
mon: mon::Instance,
|
||||
out: bool,
|
||||
pub mon: mon::Instance,
|
||||
pub out: bool,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
|
|
@ -378,6 +454,13 @@ pub mod battle {
|
|||
Attack(usize, usize),
|
||||
Swap(usize, usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Turn {
|
||||
MustSwap(usize),
|
||||
Lose(usize),
|
||||
Win(usize),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -400,9 +483,13 @@ mod mon_tests {
|
|||
let mut engine = battle::Engine::new(my_mons, they_mons);
|
||||
|
||||
// team0mon0 trades attacks with team1mon0
|
||||
engine.enqueue(battle::Move::Attack(1, 0));
|
||||
engine.enqueue(battle::Move::Attack(0, 1));
|
||||
engine.exec();
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(1, 0))
|
||||
.expect("failed to attack");
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(0, 1))
|
||||
.expect("failed to attack");
|
||||
assert_eq!(0, engine.exec().len());
|
||||
assert_eq!(6, engine.teams()[0].mons()[0].damage());
|
||||
assert_eq!(0, engine.teams()[0].mons()[1].damage());
|
||||
assert_eq!(1, engine.teams()[1].mons()[0].damage());
|
||||
|
|
@ -411,13 +498,65 @@ mod mon_tests {
|
|||
|
||||
// team0mon0 swaps with team0mon0
|
||||
// team1mon0 attacks team0
|
||||
engine.enqueue(battle::Move::Attack(1, 0));
|
||||
engine.enqueue(battle::Move::Swap(0, 1));
|
||||
engine.exec();
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(1, 0))
|
||||
.expect("failed to attack");
|
||||
engine
|
||||
.enqueue(battle::Move::Swap(0, 1))
|
||||
.expect("failed to swap");
|
||||
assert_eq!(0, engine.exec().len());
|
||||
assert_eq!(6, engine.teams()[0].mons()[0].damage());
|
||||
assert_eq!(1, engine.teams()[0].mons()[1].damage());
|
||||
assert_eq!(1, engine.teams()[1].mons()[0].damage());
|
||||
assert_eq!(0, engine.teams()[1].mons()[1].damage());
|
||||
assert_eq!(0, engine.teams()[1].mons()[2].damage());
|
||||
|
||||
for i in 2..engine.teams()[0].mons()[1].mon.dex.hp() {
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(1, 0))
|
||||
.expect("failed to attack");
|
||||
assert_eq!(0, engine.exec().len());
|
||||
assert_eq!(i, engine.teams()[0].mons()[1].damage());
|
||||
}
|
||||
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(1, 0))
|
||||
.expect("failed to kill");
|
||||
let turn_results = engine.exec();
|
||||
assert_eq!(1, turn_results.len());
|
||||
assert_eq!(battle::Turn::MustSwap(0), turn_results[0]);
|
||||
assert_eq!(
|
||||
engine.teams()[0].mons()[1].mon.dex.hp(),
|
||||
engine.teams()[0].mons()[1].damage()
|
||||
);
|
||||
assert!(!engine.teams()[0].mons()[1].out);
|
||||
|
||||
assert!(engine.enqueue(battle::Move::Attack(0, 1)).is_err());
|
||||
assert!(engine.enqueue(battle::Move::Pass(0)).is_err());
|
||||
engine
|
||||
.enqueue(battle::Move::Swap(0, 0))
|
||||
.expect("failed to swap on MustSwap");
|
||||
assert!(engine.enqueue(battle::Move::Attack(1, 0)).is_err());
|
||||
assert_eq!(0, engine.exec().len());
|
||||
|
||||
for i in 7..engine.teams()[0].mons()[0].mon.dex.hp() {
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(1, 0))
|
||||
.expect("failed to attack");
|
||||
assert_eq!(0, engine.exec().len());
|
||||
assert_eq!(i, engine.teams()[0].mons()[0].damage());
|
||||
}
|
||||
|
||||
engine
|
||||
.enqueue(battle::Move::Attack(1, 0))
|
||||
.expect("failed to kill");
|
||||
let turn_results = engine.exec();
|
||||
assert_eq!(2, turn_results.len(), "{:?}", turn_results);
|
||||
assert_eq!(battle::Turn::Lose(0), turn_results[0], "{:?}", turn_results);
|
||||
assert_eq!(battle::Turn::Win(1), turn_results[1], "{:?}", turn_results);
|
||||
assert_eq!(
|
||||
engine.teams()[0].mons()[0].mon.dex.hp(),
|
||||
engine.teams()[0].mons()[0].damage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue