From 3f5492b2bb67326be43cd7c5ba02ccf0ba1ae0e3 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 21:27:56 +0200 Subject: Refile for merging repos --- 2019-worms/src/game/player.rs | 259 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 2019-worms/src/game/player.rs (limited to '2019-worms/src/game/player.rs') diff --git a/2019-worms/src/game/player.rs b/2019-worms/src/game/player.rs new file mode 100644 index 0000000..0874c76 --- /dev/null +++ b/2019-worms/src/game/player.rs @@ -0,0 +1,259 @@ +use crate::geometry::*; +use arrayvec::ArrayVec; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Player { + pub moves_score: i32, + pub active_worm: usize, + pub select_moves: u8, + pub worms: ArrayVec<[Worm; 3]>, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Worm { + pub id: i32, + pub health: i32, + pub position: Point2d, + pub bombs: u8, + pub snowballs: u8, + pub rounds_until_unfrozen: u8, +} + +impl Player { + pub fn find_worm_position(&self, id: i32) -> Option { + self.worms.iter().position(|w| w.id == id) + } + + pub fn find_worm(&self, id: i32) -> Option<&Worm> { + self.worms.iter().find(|w| w.id == id) + } + + pub fn find_worm_mut(&mut self, id: i32) -> Option<&mut Worm> { + self.worms.iter_mut().find(|w| w.id == id) + } + + pub fn active_worm(&self) -> Option<&Worm> { + self.worms.get(self.active_worm) + } + + pub fn active_worm_mut(&mut self) -> Option<&mut Worm> { + self.worms.get_mut(self.active_worm) + } + + pub fn health(&self) -> i32 { + self.worms.iter().map(|w| w.health).sum() + } + + pub fn max_worm_health(&self) -> i32 { + self.worms.iter().map(|w| w.health).max().unwrap_or(0) + } + + pub fn clear_dead_worms(&mut self) { + for worm_index in (0..self.worms.len()).rev() { + if self.worms[worm_index].health <= 0 { + self.worms.remove(worm_index); + if self.active_worm >= worm_index { + self.active_worm = if self.active_worm > 0 { + self.active_worm - 1 + } else if self.worms.len() > 0 { + self.worms.len() - 1 + } else { + 0 + }; + } + } + } + } + + pub fn next_active_worm(&mut self) { + self.active_worm = (self.active_worm + 1) + .checked_rem(self.worms.len()) + .unwrap_or(0); + } + + fn health_score(&self) -> i32 { + self.health() / 3 + } + + pub fn score(&self) -> i32 { + self.moves_score + self.health_score() + } + + pub fn active_worm_is_frozen(&self) -> bool { + self.active_worm() + .map(|worm| worm.rounds_until_unfrozen > 0) + .unwrap_or(false) + } + + pub fn active_worm_is_frozen_after_tick(&self) -> bool { + self.active_worm() + .map(|worm| worm.rounds_until_unfrozen > 1) + .unwrap_or(false) + } + + pub fn bombs(&self) -> u8 { + self.worms.iter().map(|w| w.bombs).sum() + } + + pub fn snowballs(&self) -> u8 { + self.worms.iter().map(|w| w.snowballs).sum() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn clear_dead_worms_after_active_worm() { + let mut worms = ArrayVec::new(); + worms.push(Worm { + id: 1, + health: 50, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + worms.push(Worm { + id: 2, + health: 10, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + worms.push(Worm { + id: 3, + health: -2, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + let mut player = Player { + active_worm: 1, + moves_score: 0, + select_moves: 0, + worms, + }; + + player.clear_dead_worms(); + + assert_eq!(2, player.worms.len()); + assert_eq!(1, player.active_worm); + + assert_eq!(1, player.worms[0].id); + assert_eq!(2, player.worms[1].id); + } + + #[test] + fn clear_dead_worms_before_active_worm() { + let mut worms = ArrayVec::new(); + worms.push(Worm { + id: 1, + health: 0, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + worms.push(Worm { + id: 2, + health: 10, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + worms.push(Worm { + id: 3, + health: 2, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + let mut player = Player { + active_worm: 1, + moves_score: 0, + worms, + select_moves: 0, + }; + + player.clear_dead_worms(); + + assert_eq!(2, player.worms.len()); + assert_eq!(0, player.active_worm); + + assert_eq!(2, player.worms[0].id); + assert_eq!(3, player.worms[1].id); + } + + #[test] + fn clear_dead_worms_before_active_worm_wrapping() { + let mut worms = ArrayVec::new(); + worms.push(Worm { + id: 1, + health: 0, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + worms.push(Worm { + id: 2, + health: 10, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + worms.push(Worm { + id: 3, + health: 2, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + let mut player = Player { + active_worm: 0, + moves_score: 0, + worms, + select_moves: 0, + }; + + player.clear_dead_worms(); + + assert_eq!(2, player.worms.len()); + assert_eq!(1, player.active_worm); + + assert_eq!(2, player.worms[0].id); + assert_eq!(3, player.worms[1].id); + } + + #[test] + fn clear_last_dead_worm() { + let mut worms = ArrayVec::new(); + worms.push(Worm { + id: 1, + health: -10, + position: Point2d::new(0, 0), + rounds_until_unfrozen: 0, + bombs: 0, + snowballs: 0, + }); + let mut player = Player { + active_worm: 0, + moves_score: 0, + worms, + select_moves: 0, + }; + + player.clear_dead_worms(); + + assert_eq!(0, player.worms.len()); + // active worm is undefined in this case, but clearing the worms must not panic. + } +} -- cgit v1.2.3