From ec9041a9526b52910aafac1f7c0acfc8215ac107 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Thu, 25 Apr 2019 20:28:46 +0200 Subject: Made the state object avoid any heap allocations --- src/game.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 17 deletions(-) (limited to 'src/game.rs') diff --git a/src/game.rs b/src/game.rs index 0d57028..434828a 100644 --- a/src/game.rs +++ b/src/game.rs @@ -2,17 +2,22 @@ use crate::geometry::*; use crate::command::Command; use crate::json; +use arrayvec::ArrayVec; + +#[derive(Debug, PartialEq, Eq)] pub struct GameBoard { pub players: [Player; 2], - pub powerups: Vec, + pub powerups: ArrayVec<[Powerup; 2]>, pub map: Map, } +#[derive(Debug, PartialEq, Eq)] pub struct Player { pub active_worm: usize, - pub worms: Vec + pub worms: ArrayVec<[Worm; 3]> } +#[derive(Debug, PartialEq, Eq)] pub struct Worm { pub id: i32, pub health: i32, @@ -21,23 +26,26 @@ pub struct Worm { pub weapon_range: u8 } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Powerup { Health(Point2d, i32) } +#[derive(Debug, PartialEq, Eq)] pub struct Map { pub size: u8, /// This is 2d, each row is size long - pub cells: Vec + pub cells: ArrayVec<[CellType; 2048]> } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum CellType { Air, Dirt, DeepSpace, } - +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum SimulationOutcome { PlayerWon(usize), Draw, @@ -98,23 +106,52 @@ impl GameBoard { } pub fn simulate(&mut self, moves: [Command; 2]) -> SimulationOutcome { - // TODO: Command validation - // TODO: Move collision case - /*if let [Command::Move(x1, y1), Command::Move(x2, y2)] = moves { + match moves { + [Command::Move(x1, y1), Command::Move(x2, y2)] if x1 == x2 && y1 == y2 => { + // TODO: Get this from some sort of config rather + let damage = 20; + + + debug_assert_eq!(Some(CellType::Air), self.map.at(Point2d::new(x1, y1))); + // 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; + } + }, + _ => { + for player_index in 0..moves.len() { + if let Command::Move(x, y) = moves[player_index] { + debug_assert_eq!(Some(CellType::Air), self.map.at(Point2d::new(x, y))); - }*/ + let worm = self.players[player_index].active_worm_mut(); - for player_index in 0..moves.len() { - if let Command::Move(x, y) = moves[player_index] { - let worm = self.players[player_index].active_worm_mut(); - worm.position.x = x; - worm.position.y = y; + debug_assert!( + (worm.position.x - x).abs() <= 1 && + (worm.position.y - y).abs() <= 1 + ); + + worm.position.x = x; + worm.position.y = y; + } + } } } for player_index in 0..moves.len() { if let Command::Dig(x, y) = moves[player_index] { - if let Some(c) = self.map.at(Point2d::new(x, y)) { + println!("Player {} is digging {}, {}", player_index, x, y); + debug_assert!( + Some(CellType::Dirt) == self.map.at(Point2d::new(x, y)) || + Some(CellType::Air) == self.map.at(Point2d::new(x, 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 + }; + + if let Some(c) = self.map.at_mut(Point2d::new(x, y)) { *c = CellType::Air; } } @@ -122,7 +159,33 @@ impl GameBoard { for player_index in 0..moves.len() { if let Command::Shoot(dir) = moves[player_index] { - // TODO: Shoot + let (center, weapon_range, weapon_damage) = { + let worm = self.players[player_index].active_worm(); + (worm.position, worm.weapon_range, worm.weapon_damage) + }; + let diff = dir.as_vec(); + + let range = if dir.is_diagonal() { + ((weapon_range as f32 + 1.) / 2f32.sqrt()).floor() as i8 + } else { + weapon_range as i8 + }; + + for distance in 1..=range { + let target = center + diff * distance; + match self.map.at(target) { + Some(CellType::Air) => { + let target_worm: Option<&mut Worm> = self.players.iter_mut() + .flat_map(|p| p.worms.iter_mut()) + .find(|w| w.position == target); + if let Some(target_worm) = target_worm { + target_worm.health -= weapon_damage; + break; + } + }, + _ => break + } + } } } @@ -143,7 +206,13 @@ impl GameBoard { // Update the active worm player.active_worm = (player.active_worm + 1) % player.worms.len(); } - SimulationOutcome::Continue + + match (self.players[0].worms.len(), self.players[1].worms.len()) { + (0, 0) => SimulationOutcome::Draw, + (_, 0) => SimulationOutcome::PlayerWon(0), + (0, _) => SimulationOutcome::PlayerWon(1), + _ => SimulationOutcome::Continue + } } } @@ -154,13 +223,25 @@ impl Player { .find(|w| w.id == id) } + fn active_worm(&self) -> &Worm { + &self.worms[self.active_worm] + } + fn active_worm_mut(&mut self) -> &mut Worm { &mut self.worms[self.active_worm] } } impl Map { - fn at(&mut self, p: Point2d) -> Option<&mut CellType> { + fn at(&self, p: Point2d) -> Option { + if p.y < 0 || p.x < 0 || p.y as u8 >= self.size || p.x as u8 >= self.size { + None + } else { + Some(self.cells[p.y as usize * self.size as usize + p.x as usize]) + } + } + + fn at_mut(&mut self, p: Point2d) -> Option<&mut CellType> { if p.y < 0 || p.x < 0 || p.y as u8 >= self.size || p.x as u8 >= self.size { None } else { -- cgit v1.2.3