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 --- Cargo.lock | 16 ++++++ Cargo.toml | 1 + src/game.rs | 115 ++++++++++++++++++++++++++++++++------ src/strategy.rs | 2 +- tests/official-runner-matching.rs | 3 +- 5 files changed, 118 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3474f51..aaf9d87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,11 @@ +[[package]] +name = "arrayvec" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "autocfg" version = "0.1.2" @@ -31,6 +39,11 @@ name = "libc" version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nodrop" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "num-traits" version = "0.2.6" @@ -193,6 +206,7 @@ dependencies = [ name = "steam-powered-wyrm" version = "0.1.0" dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", @@ -234,12 +248,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" diff --git a/Cargo.toml b/Cargo.toml index dd11915..0848b66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ serde = { version = "1.0.90", features = ["derive"] } serde_json = "1.0.39" rand = "0.6.5" num-traits = "0.2.6" +arrayvec = "0.4.10" \ No newline at end of file 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 { diff --git a/src/strategy.rs b/src/strategy.rs index 6ee0dce..37d2265 100644 --- a/src/strategy.rs +++ b/src/strategy.rs @@ -1,6 +1,6 @@ use crate::command::Command; use crate::game::GameBoard; -pub fn choose_move(state: &GameBoard) -> Command { +pub fn choose_move(_state: &GameBoard) -> Command { Command::DoNothing } diff --git a/tests/official-runner-matching.rs b/tests/official-runner-matching.rs index e5602a2..f4c2a83 100644 --- a/tests/official-runner-matching.rs +++ b/tests/official-runner-matching.rs @@ -8,7 +8,8 @@ use std::io::prelude::*; #[test] fn simulates_the_same_match() { - // TODO: Also assert map state + // TODO: Assert map state + // TODO: Assert end state let replays = Path::new("tests/replays/"); for replay in replays.read_dir().expect("read_dir failed") { -- cgit v1.2.3