summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2019-04-25 20:28:46 +0200
committerJustin Worthe <justin@worthe-it.co.za>2019-04-25 20:28:46 +0200
commitec9041a9526b52910aafac1f7c0acfc8215ac107 (patch)
treee87541ff0f442f137f4e5c797014bfa8311c6f3f
parent4978b4f27d7a0e6058d04e7184600bf835070a8b (diff)
Made the state object avoid any heap allocations
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml1
-rw-r--r--src/game.rs115
-rw-r--r--src/strategy.rs2
-rw-r--r--tests/official-runner-matching.rs3
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,4 +1,12 @@
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -32,6 +40,11 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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<Powerup>,
+ pub powerups: ArrayVec<[Powerup; 2]>,
pub map: Map,
}
+#[derive(Debug, PartialEq, Eq)]
pub struct Player {
pub active_worm: usize,
- pub worms: Vec<Worm>
+ 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<i8>, i32)
}
+#[derive(Debug, PartialEq, Eq)]
pub struct Map {
pub size: u8,
/// This is 2d, each row is size long
- pub cells: Vec<CellType>
+ 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<i8>) -> Option<&mut CellType> {
+ fn at(&self, p: Point2d<i8>) -> Option<CellType> {
+ 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<i8>) -> 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") {