diff options
author | Justin Worthe <justin@worthe-it.co.za> | 2019-05-21 13:27:25 +0200 |
---|---|---|
committer | Justin Worthe <justin@worthe-it.co.za> | 2019-05-21 13:27:25 +0200 |
commit | 63da94f7f1b25eddeb9ffd379f37c1a32e750fdb (patch) | |
tree | 83bd2670e948b16b37890e3ee9785f3672e0d32d /src/game.rs | |
parent | b93a9c643485c720a0711ddaf90872b7c6f006c8 (diff) |
More robust game logic and reasoning
Diffstat (limited to 'src/game.rs')
-rw-r--r-- | src/game.rs | 132 |
1 files changed, 78 insertions, 54 deletions
diff --git a/src/game.rs b/src/game.rs index 5ddbdfd..e3fb5c0 100644 --- a/src/game.rs +++ b/src/game.rs @@ -112,47 +112,65 @@ impl GameBoard { // Remove dead worms and update active worm for player in &mut self.players { player.clear_dead_worms(); - // Update the active worm - if player.worms.len() > 0 { - player.active_worm = (player.active_worm + 1).checked_rem(player.worms.len()).unwrap_or(0); - } + player.next_active_worm(); } } - pub fn simulate(&mut self, moves: [Command; 2]) -> SimulationOutcome { + pub fn simulate(&mut self, moves: [Command; 2]) { + self.simulate_moves(moves); + self.simulate_digs(moves); + self.simulate_shoots(moves); + + for player in &mut self.players { + player.clear_dead_worms(); + player.next_active_worm(); + } + + self.round += 1; + + self.outcome = match (self.players[0].worms.len(), self.players[1].worms.len(), self.round > self.max_rounds) { + (0, 0, _) => SimulationOutcome::Draw, + (_, 0, _) => SimulationOutcome::PlayerWon(0), + (0, _, _) => SimulationOutcome::PlayerWon(1), + (_, _, true) => SimulationOutcome::Draw, + _ => SimulationOutcome::Continue + }; + } + + fn simulate_moves(&mut self, moves: [Command; 2]) { match moves { - [Command::Move(x1, y1), Command::Move(x2, y2)] if x1 == x2 && y1 == y2 => { + [Command::Move(p1), Command::Move(p2)] if p1.x == p2.x && p1.y == p2.y => { // TODO: Get this from some sort of config rather let damage = 20; - debug_assert_eq!(Some(false), self.map.at(Point2d::new(x1, y1)), "Movement target wasn't empty, ({}, {})", x1, y1); + debug_assert_eq!(Some(false), self.map.at(Point2d::new(p1.x, p1.y)), "Movement target wasn't empty, ({}, {})", p1.x, p1.y); // Worms have a 50% chance of swapping places // here. I'm treating that as an edge case that I // don't need to handle for now. for player in &mut self.players { - player.active_worm_mut().health -= damage; + let worm = player.active_worm_mut(); + worm.health = worm.health.saturating_sub(damage); } }, _ => { for player_index in 0..moves.len() { - if let Command::Move(x, y) = moves[player_index] { - debug_assert_eq!(Some(false), self.map.at(Point2d::new(x, y)), "Movement target wasn't empty, ({}, {})", x, y); + if let Command::Move(p) = moves[player_index] { + debug_assert_eq!(Some(false), self.map.at(p), "Movement target wasn't empty, ({}, {})", p.x, p.y); let worm = self.players[player_index].active_worm_mut(); debug_assert!( - (worm.position.x - x).abs() <= 1 && - (worm.position.y - y).abs() <= 1, - "Tried to move too far away, ({}, {})", x, y + (worm.position.x - p.x).abs() <= 1 && + (worm.position.y - p.y).abs() <= 1, + "Tried to move too far away, ({}, {})", p.x, p.y ); - worm.position.x = x; - worm.position.y = y; - - self.powerups.retain(|p| { - if p.position == worm.position { - worm.health += p.value; + worm.position = p; + + self.powerups.retain(|power| { + if power.position == worm.position { + worm.health += power.value; false } else { true @@ -162,24 +180,28 @@ impl GameBoard { } } } - + } + + fn simulate_digs(&mut self, moves: [Command; 2]) { for player_index in 0..moves.len() { - if let Command::Dig(x, y) = moves[player_index] { + if let Command::Dig(p) = moves[player_index] { debug_assert!( - Some(true) == self.map.at(Point2d::new(x, y)) || - (player_index == 1 && moves[0] == Command::Dig(x, y)), - "Tried to dig through air, ({}, {})", x, y + Some(true) == self.map.at(p) || + (player_index == 1 && moves[0] == Command::Dig(p)), + "Tried to dig through air, ({}, {})", p.x, p.y ); debug_assert!{ - (self.players[player_index].active_worm().position.x - x).abs() <= 1 && - (self.players[player_index].active_worm().position.y - y).abs() <= 1, - "Tried to dig too far away, ({}, {})", x, y + (self.players[player_index].active_worm().position.x - p.x).abs() <= 1 && + (self.players[player_index].active_worm().position.y - p.y).abs() <= 1, + "Tried to dig too far away, ({}, {})", p.x, p.y }; - self.map.clear(Point2d::new(x, y)); + self.map.clear(p); } } - + } + + fn simulate_shoots(&mut self, moves: [Command; 2]) { for player_index in 0..moves.len() { if let Command::Shoot(dir) = moves[player_index] { let (center, weapon_range, weapon_damage) = { @@ -211,33 +233,38 @@ impl GameBoard { } } } + } - for player in &mut self.players { - player.clear_dead_worms(); - // Update the active worm - if player.worms.len() > 0 { - player.active_worm = (player.active_worm + 1).checked_rem(player.worms.len()).unwrap_or(0); - } - } + pub fn opponent(player_index: usize) -> usize { + (player_index + 1)%2 + } - self.round += 1; - self.outcome = match (self.players[0].worms.len(), self.players[1].worms.len(), self.round > self.max_rounds) { - (0, 0, _) => SimulationOutcome::Draw, - (_, 0, _) => SimulationOutcome::PlayerWon(0), - (0, _, _) => SimulationOutcome::PlayerWon(1), - (_, _, true) => SimulationOutcome::Draw, - _ => SimulationOutcome::Continue - }; + pub fn valid_move_commands(&self, player_index: usize) -> ArrayVec<[Command;8]> { + let worm = self.players[player_index].active_worm(); + let taken_positions = self.players.iter() + .flat_map(|p| p.worms.iter()) + .map(|w| w.position) + .collect::<ArrayVec<[Point2d<i8>; 6]>>(); - self.outcome + Direction::all() + .iter() + .map(Direction::as_vec) + .map(|d| worm.position + d) + .filter(|p| !taken_positions.contains(p)) + .filter_map(|p| match self.map.at(p) { + Some(false) => Some(Command::Move(p)), + Some(true) => Some(Command::Dig(p)), + _ => None, + }) + .collect() } - - pub fn find_targets(&self, player_index: usize, center: Point2d<i8>, weapon_range: u8) -> Vec<Direction> { + + pub fn valid_shoot_commands(&self, player_index: usize, center: Point2d<i8>, weapon_range: u8) -> ArrayVec<[Command;8]> { let range = weapon_range as i8; let dir_range = ((weapon_range as f32 + 1.) / 2f32.sqrt()).floor() as i8; - let mut directions: Vec<Direction> = self.players[(player_index + 1)%2].worms + self.players[GameBoard::opponent(player_index)].worms .iter() .filter_map(|w| { let diff = w.position - center; @@ -265,17 +292,14 @@ impl GameBoard { } }) .filter(|(dir, range)| { + // TODO if this filtered all players, I don't need to dedup let diff = dir.as_vec(); !(1..*range).any(|distance| self.map.at(center + diff * distance) != Some(false) && !self.players[player_index].worms.iter().any(|w| w.position == center + diff * distance)) }) - .map(|(dir, _range)| dir) - .collect(); - - directions.sort(); - directions.dedup(); - directions + .map(|(dir, _range)| Command::Shoot(dir)) + .collect() } } |