diff options
author | Justin Worthe <justin.worthe@gmail.com> | 2017-05-20 18:01:43 +0200 |
---|---|---|
committer | Justin Worthe <justin.worthe@gmail.com> | 2017-05-20 18:01:43 +0200 |
commit | 7dd0fe43fe7e72e5f56a8a61bbaec3a78399e6c8 (patch) | |
tree | 4b1cfd0375609019c0e1a07f34bf8e5574ffce84 | |
parent | 10c8ceb168e86a58e38086691ddd519bac63ff03 (diff) |
Moved ship placement knowledge out to be one per ship
-rw-r--r-- | src/actions.rs | 4 | ||||
-rw-r--r-- | src/knowledge.rs | 147 | ||||
-rw-r--r-- | src/lib.rs | 40 | ||||
-rw-r--r-- | src/math.rs | 70 | ||||
-rw-r--r-- | src/placement.rs | 1 | ||||
-rw-r--r-- | src/ships.rs | 12 |
6 files changed, 228 insertions, 46 deletions
diff --git a/src/actions.rs b/src/actions.rs index 9009099..2291de6 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -40,7 +40,7 @@ impl ShipPlacement { pub fn valid(&self, map_size: u16) -> bool { let start = self.point; - let end = start.move_point(self.direction, self.ship_type.length(), map_size); + let end = start.move_point(self.direction, self.ship_type.length() as i32, map_size); start.x < map_size && start.y < map_size && end.is_some() } pub fn valid_placements(placements: &Vec<ShipPlacement>, map_size: u16) -> bool { @@ -51,7 +51,7 @@ impl ShipPlacement { for placement in placements { individuals_valid = individuals_valid && placement.valid(map_size); - for i in 0..placement.ship_type.length() { + for i in 0..placement.ship_type.length() as i32 { match placement.point.move_point(placement.direction, i, map_size) { Some(block) => { no_overlaps = no_overlaps && !occupied.contains(&block); diff --git a/src/knowledge.rs b/src/knowledge.rs index 3b0c61a..20c982f 100644 --- a/src/knowledge.rs +++ b/src/knowledge.rs @@ -1,27 +1,42 @@ use actions::*; use ships::*; +use state::*; +use math::*; + use std::collections::HashMap; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Knowledge { - pub last_action: Option<Action>, + pub last_action: Action, pub opponent_map: OpponentMapKnowledge } impl Knowledge { - pub fn new() -> Knowledge { + pub fn new(map_size: u16, action: Action) -> Knowledge { Knowledge { - last_action: None, - opponent_map: OpponentMapKnowledge::new() + last_action: action, + opponent_map: OpponentMapKnowledge::new(map_size) } } pub fn with_action(&self, action: Action) -> Knowledge { Knowledge { - last_action: Some(action), + last_action: action, opponent_map: self.opponent_map.clone() } } + + pub fn resolve_last_action(&self, state: &State) -> Knowledge { + let mut new_knowledge = self.clone(); + match self.last_action { + Action::PlaceShips(_) => {}, + Action::Shoot(p) => { + new_knowledge.opponent_map.update_from_shot(p, &state); + } + }; + + new_knowledge + } } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -31,24 +46,132 @@ pub struct OpponentMapKnowledge { } impl OpponentMapKnowledge { - pub fn new() -> OpponentMapKnowledge { + pub fn new(map_size: u16) -> OpponentMapKnowledge { + let mut cells = Vec::with_capacity(map_size as usize); + for x in 0..map_size { + cells.push(Vec::with_capacity(map_size as usize)); + for y in 0..map_size { + cells[x as usize].push(KnowledgeCell::new(x, y)); + } + } + + let ships = Ship::all_types().iter() + .map(|s| (s.clone(), OpponentShipKnowledge::new(s.clone(), map_size))) + .collect::<HashMap<_, _>>(); + OpponentMapKnowledge { - ships: HashMap::new(), - cells: Vec::new() + ships: ships, + cells: cells } } + + fn update_from_shot(&mut self, p: Point, state: &State) { + let ref shot_cell = state.opponent_map.cells[p.x as usize][p.y as usize]; + let sunk_ship = self.ships.iter() + .filter(|&(_, x)| !x.destroyed) + .filter(|&(s, _)| state.opponent_map.ships.get(s).map(|x| x.destroyed) == Some(true)) + .map(|(s, _)| s.clone()) + .next(); //only one ship can be sunk at a time + + match sunk_ship { + None => {}, + Some(ship) => { + match self.ships.get_mut(&ship) { + Some(ref mut ship_knowledge) => {ship_knowledge.destroyed = true}, + None => {} + } + } + } + + let ref mut knowledge_cell = self.cells[p.x as usize][p.y as usize]; + if shot_cell.missed { + knowledge_cell.missed = true; + // knowledge_cell.possible_ship_uses.clear(); + } + else { + knowledge_cell.hit = true; + knowledge_cell.known_ship = sunk_ship; + // knowledge_cell.reduce_possibilities_to_known_ship(); + } + + } } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct OpponentShipKnowledge { - pub destroyed: bool + pub ship: Ship, + pub destroyed: bool, + pub possible_placements: Vec<PossibleShipPlacement> +} + +impl OpponentShipKnowledge { + pub fn new(ship: Ship, map_size: u16) -> OpponentShipKnowledge { + OpponentShipKnowledge { + ship: ship, + destroyed: false, + possible_placements: PossibleShipPlacement::enumerate(ship, map_size) + } + } } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct KnowledgeCell { - pub shot_attempted: bool, - pub possible_ship_uses: HashMap<Ship, u16>, - pub known_ship: Option<Ship> + pub missed: bool, + pub hit: bool, + pub known_ship: Option<Ship>, + pub position: Point +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct PossibleShipPlacement { + pub ship: Ship, + pub direction: Direction, + pub position: Point +} + +impl PossibleShipPlacement { + pub fn enumerate(ship: Ship, map_size: u16) -> Vec<PossibleShipPlacement> { + (0..(map_size-ship.length()+1)).flat_map(move |par| { + (0..map_size).flat_map(move |per| { + vec!( + PossibleShipPlacement { + ship: ship, + direction: Direction::East, + position: Point::new(par, per) + }, + PossibleShipPlacement { + ship: ship, + direction: Direction::South, + position: Point::new(per, par) + } + ) + }) + }).collect() + } + + pub fn touches_point(&self, p: Point) -> bool { + p.check_for_ship_collision(self.position, self.direction, self.ship.length()) + } +} + +impl KnowledgeCell { + pub fn new(x: u16, y: u16) -> KnowledgeCell { + KnowledgeCell { + missed: false, + hit: false, + position: Point::new(x, y), + known_ship: None + } + } + + pub fn shot_attempted(&self) -> bool { + self.missed || self.hit + } + + pub fn unknown_hit(&self) -> bool { + self.hit && self.known_ship.is_none() + } + } @@ -14,6 +14,7 @@ mod shooting; mod state; mod knowledge; +use actions::*; use math::*; use files::*; use ships::*; @@ -30,34 +31,37 @@ pub fn write_move(working_dir: PathBuf) -> Result<(), String> { let is_place_phase = state_json["Phase"] == 1; let map_size = State::map_size_from_json(&state_json)?; - let starting_knowledge = if is_place_phase { - Knowledge::new() - } - else { - read_knowledge()? - }; - - let action = if is_place_phase { - place_ships_randomly(map_size) + let (action, knowledge) = if is_place_phase { + placement(map_size) } else { let state = State::new(&state_json)?; - shoot_randomly(&state) + shoot(&state)? }; - - let ending_knowledge = starting_knowledge - .with_action(action.clone()); - - write_knowledge(&ending_knowledge) + + write_knowledge(&knowledge) .map_err(|e| format!("Failed to write knowledge to file. Error: {}", e))?; write_action(&working_dir, is_place_phase, action) .map_err(|e| format!("Failed to write action to file. Error: {}", e))?; - println!("Input state:\n{}\n\n", state_json); - println!("Existing knowledge:\n{}\n\n", serde_json::to_string(&starting_knowledge).unwrap_or(String::from(""))); - println!("End of turn knowledge:\n{}\n\n", serde_json::to_string(&ending_knowledge).unwrap_or(String::from(""))); + println!("Knowledge:\n{}\n\n", serde_json::to_string(&knowledge).unwrap_or(String::from(""))); Ok(()) } +fn placement(map_size: u16) -> (Action, Knowledge) { + let action = place_ships_randomly(map_size); + let knowledge = Knowledge::new(map_size, action.clone()); + + (action, knowledge) +} + +fn shoot(state: &State) -> Result<(Action, Knowledge), String> { + let old_knowledge = read_knowledge()?; + let knowledge = old_knowledge.resolve_last_action(&state); + let action = shoot_randomly(&state); + + Ok((action.clone(), knowledge.with_action(action))) +} + diff --git a/src/math.rs b/src/math.rs index 3d8a976..2ef1013 100644 --- a/src/math.rs +++ b/src/math.rs @@ -63,19 +63,63 @@ impl Point { } - pub fn move_point(&self, direction: Direction, distance: u16, map_size: u16) -> Option<Point> { - let x = self.x; - let y = self.y; - - match direction { - South if y < distance => None, - West if x < distance => None, - North if y + distance >= map_size => None, - East if x + distance >= map_size => None, - South => Some(Point::new(x, y-distance)), - West => Some(Point::new(x-distance, y)), - North => Some(Point::new(x, y+distance)), - East => Some(Point::new(x+distance, y)) + pub fn move_point(&self, direction: Direction, distance: i32, map_size: u16) -> Option<Point> { + let x = self.x as i32 + match direction { + West => -distance, + East => distance, + _ => 0 + }; + let y = self.y as i32 + match direction { + South => -distance, + North => distance, + _ => 0 + }; + let max = map_size as i32; + + if x >= max || y >= max || x < 0 || y < 0 { + None + } + else { + Some(Point::new(x as u16, y as u16)) } } + + pub fn move_point_no_bounds_check(&self, direction: Direction, distance: i32) -> Point { + let x = self.x as i32 + match direction { + West => -distance, + East => distance, + _ => 0 + }; + let y = self.y as i32 + match direction { + South => -distance, + North => distance, + _ => 0 + }; + + Point::new(x as u16, y as u16) + } + + pub fn check_for_ship_collision(&self, ship_start: Point, direction: Direction, length: u16) -> bool { + let reverse = match direction { + West | South => true, + East | North => false + }; + + let same_cross = match direction { + East | West => self.y == ship_start.y, + North | South => self.x == ship_start.x + }; + + let (parr_self, parr_ship) = match direction { + East | West => (self.x, ship_start.x), + North | South => (self.y, ship_start.y) + }; + + let corrected_parr_ship = match reverse { + true => parr_ship - length + 1, + false => parr_ship + }; + + same_cross && parr_self >= corrected_parr_ship && parr_self < corrected_parr_ship + length + } } diff --git a/src/placement.rs b/src/placement.rs index faa340e..4740d76 100644 --- a/src/placement.rs +++ b/src/placement.rs @@ -9,7 +9,6 @@ pub fn place_ships_randomly(map_size: u16) -> Action { current_placement = create_random_placement(map_size); !ShipPlacement::valid_placements(¤t_placement, map_size) } {} - Action::PlaceShips(current_placement) } diff --git a/src/ships.rs b/src/ships.rs index 344f9ed..ef29fe9 100644 --- a/src/ships.rs +++ b/src/ships.rs @@ -54,4 +54,16 @@ impl Ship { &Submarine => 3 } } + + pub fn all_types() -> Vec<Ship> { + use Ship::*; + + vec!( + Battleship, + Carrier, + Cruiser, + Destroyer, + Submarine + ) + } } |