From d385199c48d565d9ffc8948f137d716e534d5d0d Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Sun, 7 Jul 2019 12:03:44 +0200 Subject: Cleaning up TODOs --- src/constants.rs | 4 ++++ src/game.rs | 36 ++++++++++++++++++++++++------------ src/game/map.rs | 53 +++++++++++++++++++---------------------------------- src/json.rs | 15 ++++++++++++++- 4 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 1c34377..94be135 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -176,3 +176,7 @@ pub const KILL_SCORE: i32 = 40; pub const DIG_SCORE: i32 = 7; pub const MOVE_SCORE: i32 = 5; pub const INVALID_COMMAND_SCORE_PENALTY: i32 = 4; + +pub const STARTING_BOMBS: u8 = 3; + +pub const COLLISSION_DAMAGE: i32 = 20; diff --git a/src/game.rs b/src/game.rs index f5bcfa2..56c583b 100644 --- a/src/game.rs +++ b/src/game.rs @@ -15,7 +15,6 @@ use map::*; use arrayvec::ArrayVec; use fnv::FnvHashSet; -// TODO: How much sense does it actually make to split the worms between the players? #[derive(Debug, PartialEq, Eq, Clone)] pub struct GameBoard { pub round: u16, @@ -72,7 +71,11 @@ impl GameBoard { position: Point2d::new(w.position.x, w.position.y), weapon_damage: commando_damage, weapon_range: commando_range, - bombs: if w.health == 100 { 3 } else { 0 }, // TODO: parse and check worm type rather, move these out to constants + bombs: if w.profession == json::WormType::Agent { + STARTING_BOMBS + } else { + 0 + }, }) .collect(), }; @@ -126,7 +129,17 @@ impl GameBoard { if let Some(worm) = self.players[1].find_worm_mut(w.id) { worm.health = w.health; worm.position = Point2d::new(w.position.x, w.position.y); - // TODO: How to update opponent worm bombs? + // TODO: How to update opponent worm bombs? One idea: + // start the update by passing in the move that I did, + // and doing a simulation where the opponent did + // nothing. Then, looking at the points that the + // opponent actually got, determine if they did a + // simple walk / dig / shoot or if they hit multiple + // worms, hit worms with bomb levels of damage, or + // destroyed a dirt not next to them. I can further + // tell if they used a select (number of selects), and + // who their new active worm is, and if that worm + // could have bombed. } } self.players[0].moves_score = json.my_player.score - json.my_player.health_score(); @@ -218,8 +231,7 @@ impl GameBoard { fn simulate_moves(&mut self, actions: [Action; 2]) { match actions { [Action::Move(p1), Action::Move(p2)] if p1.x == p2.x && p1.y == p2.y => { - // TODO: Get this from some sort of config rather - let damage = 20; + let damage = COLLISSION_DAMAGE; debug_assert_eq!( Some(false), @@ -308,6 +320,7 @@ impl GameBoard { // TODO: Destroy health packs + // TODO: iter, filter_map, filter, filter_map... flatten this block down a smidge. for player_index in 0..actions.len() { if let Action::Bomb(p) = actions[player_index] { if self.map.at(p).is_some() { @@ -317,15 +330,12 @@ impl GameBoard { worm.bombs = worm.bombs.saturating_sub(1); - // damage as per https://forum.entelect.co.za/uploads/default/original/2X/8/89e6e6cf35791a0448b5a6bbeb63c558ce41804a.jpeg - for &(damage_offset, weapon_damage) in BOMB_DAMAGES.iter() { let target = p + damage_offset; if self.map.at(target) == Some(true) { self.map.clear(target); - // TODO: How does this score get assigned if both players lobbed a banana? - // (answer, currently all to A, but watch https://forum.entelect.co.za/t/scoring-with-simultaneous-banana-digs/766/1 + // TODO: Fix this so that simultaneous bomb throwing gives points to both players self.players[player_index].moves_score += DIG_SCORE; } @@ -566,13 +576,15 @@ impl GameBoard { } }) .filter(|(dir, range)| { - // TODO if this filtered all players, I don't need to dedup let diff = dir.as_vec(); + // NB: This is up to range EXCLUSIVE, so if there's + // anything in the way, even another good shot, skip. !(1..*range).any(|distance| { self.map.at(center + diff * distance) != Some(false) - && !self.players[player_index] - .worms + && !self + .players .iter() + .flat_map(|p| p.worms.iter()) .any(|w| w.position == center + diff * distance) }) }) diff --git a/src/game/map.rs b/src/game/map.rs index c062c8f..45d25fd 100644 --- a/src/game/map.rs +++ b/src/game/map.rs @@ -1,13 +1,13 @@ -use crate::geometry::*; use crate::constants::*; +use crate::geometry::*; #[derive(Default, Debug, PartialEq, Eq, Clone)] pub struct Map { - cells: [u64; MAP_U64S] + cells: [u64; MAP_U64S], } impl Map { - pub fn at(&self, p: Point2d) -> Option { + fn internal_index(p: Point2d) -> Option<(usize, usize)> { if p.y < 0 || p.y as usize >= MAP_SIZE { None } else { @@ -18,47 +18,32 @@ impl Map { let global_bit = row_data.start_bit + p.x as usize - row_data.x_offset; let integer = global_bit / 64; let bit = global_bit % 64; - let mask = 1 << bit; - Some(self.cells[integer] & mask != 0) + Some((integer, bit)) } } } + pub fn at(&self, p: Point2d) -> Option { + Map::internal_index(p).map(|(integer, bit)| { + let mask = 1 << bit; + self.cells[integer] & mask != 0 + }) + } + pub fn set(&mut self, p: Point2d) { - if p.y < 0 || p.y as usize >= MAP_SIZE { - debug_assert!(false, "Tried to set an out of bounds bit, {:?}", p); + if let Some((integer, bit)) = Map::internal_index(p) { + let mask = 1 << bit; + self.cells[integer] |= mask; } else { - let row_data = &MAP_ROW_SIZE[p.y as usize]; - if p.x < row_data.x_offset as i8 || p.x as usize >= row_data.x_offset + row_data.len() { - debug_assert!(false, "Tried to set an out of bounds bit, {:?}", p); - } else { - let global_bit = row_data.start_bit + p.x as usize - row_data.x_offset; - let integer = global_bit / 64; - let bit = global_bit % 64; - let mask = 1 << bit; - self.cells[integer] |= mask; - } + panic!("Tried to set an out of bounds bit, {:?}", p); } } pub fn clear(&mut self, p: Point2d) { - if p.y < 0 || p.y as usize >= MAP_SIZE { - debug_assert!(false, "Tried to set an out of bounds bit, {:?}", p); + if let Some((integer, bit)) = Map::internal_index(p) { + let mask = !(1 << bit); + self.cells[integer] &= mask; } else { - let row_data = &MAP_ROW_SIZE[p.y as usize]; - if p.x < row_data.x_offset as i8 || p.x as usize >= row_data.x_offset + row_data.len() { - debug_assert!(false, "Tried to set an out of bounds bit, {:?}", p); - } else { - let global_bit = row_data.start_bit + p.x as usize - row_data.x_offset; - let integer = global_bit / 64; - let bit = global_bit % 64; - let mask = !(1 << bit); - self.cells[integer] &= mask; - } + panic!("Tried to set an out of bounds bit, {:?}", p); } } } - -#[cfg(test)] -mod test { - // TODO: Property test for at, set and clear -} diff --git a/src/json.rs b/src/json.rs index 86b35ea..4ac274a 100644 --- a/src/json.rs +++ b/src/json.rs @@ -54,6 +54,7 @@ pub struct PlayerWorm { pub movement_range: u32, pub weapon: Weapon, pub banana_bombs: Option, + pub profession: WormType, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] @@ -80,6 +81,14 @@ pub struct OpponentWorm { pub position: Position, pub digging_range: u32, pub movement_range: u32, + pub profession: WormType, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "PascalCase")] +pub enum WormType { + Commando, + Agent, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] @@ -254,7 +263,8 @@ mod test { "y": 16 }, "diggingRange": 1, - "movementRange": 1 + "movementRange": 1, + "profession": "Commando" } ] } @@ -355,6 +365,7 @@ mod test { count: 3, damage_radius: 2, }), + profession: WormType::Agent, }, PlayerWorm { id: 2, @@ -367,6 +378,7 @@ mod test { digging_range: 1, movement_range: 1, banana_bombs: None, + profession: WormType::Commando, }, ], }, @@ -381,6 +393,7 @@ mod test { position: Position { x: 31, y: 16 }, digging_range: 1, movement_range: 1, + profession: WormType::Commando, }], }], map: vec![ -- cgit v1.2.3