summaryrefslogtreecommitdiff
path: root/src/game.rs
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-05-21 13:27:25 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-05-21 13:27:25 +0200
commit63da94f7f1b25eddeb9ffd379f37c1a32e750fdb (patch)
tree83bd2670e948b16b37890e3ee9785f3672e0d32d /src/game.rs
parentb93a9c643485c720a0711ddaf90872b7c6f006c8 (diff)
More robust game logic and reasoning
Diffstat (limited to 'src/game.rs')
-rw-r--r--src/game.rs132
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()
}
}