summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin.worthe@gmail.com>2017-05-20 18:01:43 +0200
committerJustin Worthe <justin.worthe@gmail.com>2017-05-20 18:01:43 +0200
commit7dd0fe43fe7e72e5f56a8a61bbaec3a78399e6c8 (patch)
tree4b1cfd0375609019c0e1a07f34bf8e5574ffce84
parent10c8ceb168e86a58e38086691ddd519bac63ff03 (diff)
Moved ship placement knowledge out to be one per ship
-rw-r--r--src/actions.rs4
-rw-r--r--src/knowledge.rs147
-rw-r--r--src/lib.rs40
-rw-r--r--src/math.rs70
-rw-r--r--src/placement.rs1
-rw-r--r--src/ships.rs12
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()
+ }
+
}
diff --git a/src/lib.rs b/src/lib.rs
index 81dd845..f84c172 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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(&current_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
+ )
+ }
}