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. } }