summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/knowledge.rs134
-rw-r--r--src/ships.rs71
-rw-r--r--src/shooting.rs19
3 files changed, 151 insertions, 73 deletions
diff --git a/src/knowledge.rs b/src/knowledge.rs
index 44693e5..4fe60da 100644
--- a/src/knowledge.rs
+++ b/src/knowledge.rs
@@ -11,6 +11,7 @@ pub struct Knowledge {
pub opponent_map: OpponentMapKnowledge,
pub map_size: u16,
pub available_weapons: Vec<Weapon>,
+ pub shootable_weapons: Vec<Weapon>,
pub charging_weapons: HashMap<Weapon, u16>
}
@@ -21,6 +22,7 @@ impl Knowledge {
opponent_map: OpponentMapKnowledge::new(map_size),
map_size: map_size,
available_weapons: Vec::new(),
+ shootable_weapons: Vec::new(),
charging_weapons: HashMap::new()
}
}
@@ -43,12 +45,14 @@ impl Knowledge {
available_weapons.sort_by_key(|weapon| format!("{}",weapon));
available_weapons.dedup();
- new_knowledge.available_weapons = available_weapons.iter()
+ new_knowledge.available_weapons = available_weapons;
+
+ new_knowledge.shootable_weapons = new_knowledge.available_weapons.iter()
.filter(|weapon| weapon.energy_cost(state.map_size) <= energy)
.cloned()
.collect();
- new_knowledge.charging_weapons = available_weapons.iter()
+ new_knowledge.charging_weapons = new_knowledge.available_weapons.iter()
.filter(|weapon| weapon.energy_cost(state.map_size) > energy)
.map(|weapon| (weapon.clone(), weapon.single_shot_rounds_to_ready(energy, state.map_size)))
.collect();
@@ -57,51 +61,13 @@ impl Knowledge {
Action::PlaceShips(_) => {
(vec!(), vec!(), vec!())
},
- Action::Shoot(Weapon::SingleShot, p) => {
- Knowledge::to_hits_and_misses(vec!(Some(p)), &state)
- },
- Action::Shoot(Weapon::DoubleShotVertical, p) => {
- Knowledge::to_hits_and_misses(vec!(
- p.move_point(Direction::North, 1, state.map_size),
- p.move_point(Direction::South, 1, state.map_size)
- ), &state)
- },
- Action::Shoot(Weapon::DoubleShotHorizontal, p) => {
- Knowledge::to_hits_and_misses(vec!(
- p.move_point(Direction::East, 1, state.map_size),
- p.move_point(Direction::West, 1, state.map_size)
- ), &state)
- },
- Action::Shoot(Weapon::CornerShot, p) => {
- Knowledge::to_hits_and_misses(vec!(
- p.move_point(Direction::NorthEast, 1, state.map_size),
- p.move_point(Direction::SouthEast, 1, state.map_size),
- p.move_point(Direction::NorthWest, 1, state.map_size),
- p.move_point(Direction::SouthWest, 1, state.map_size),
- ), &state)
- },
- Action::Shoot(Weapon::CrossShotDiagonal, p) => {
- Knowledge::to_hits_and_misses(vec!(
- p.move_point(Direction::NorthEast, 1, state.map_size),
- p.move_point(Direction::SouthEast, 1, state.map_size),
- p.move_point(Direction::NorthWest, 1, state.map_size),
- p.move_point(Direction::SouthWest, 1, state.map_size),
- Some(p)
- ), &state)
- },
- Action::Shoot(Weapon::CrossShotHorizontal, p) => {
- Knowledge::to_hits_and_misses(vec!(
- p.move_point(Direction::North, 1, state.map_size),
- p.move_point(Direction::East, 1, state.map_size),
- p.move_point(Direction::South, 1, state.map_size),
- p.move_point(Direction::West, 1, state.map_size),
- Some(p)
- ), &state)
- },
-
Action::Shoot(Weapon::SeekerMissle, p) => {
Knowledge::seeker_hits_and_misses(p, &state)
}
+
+ Action::Shoot(w, p) => {
+ Knowledge::to_hits_and_misses(w.affected_cells(p, state.map_size), &state)
+ }
};
let sunk_ships = new_knowledge.opponent_map.update_sunk_ships(&state);
@@ -111,9 +77,7 @@ impl Knowledge {
new_knowledge
}
- fn to_hits_and_misses(points: Vec<Option<Point>>, state: &State) -> (Vec<Point>, Vec<Point>, Vec<Point>) {
- let points = points.iter().filter_map(|&p| p).collect::<Vec<_>>();
-
+ fn to_hits_and_misses(points: Vec<Point>, state: &State) -> (Vec<Point>, Vec<Point>, Vec<Point>) {
let hits = points.iter().filter(|p| state.opponent_map.cells[p.x as usize][p.y as usize].damaged).cloned().collect();
let misses = points.iter().filter(|p| state.opponent_map.cells[p.x as usize][p.y as usize].missed).cloned().collect();
let unknown = points.iter().filter(|p| !state.opponent_map.cells[p.x as usize][p.y as usize].missed && !state.opponent_map.cells[p.x as usize][p.y as usize].damaged).cloned().collect();
@@ -157,7 +121,7 @@ impl Knowledge {
//don't add more after a hit is found
for ring in rings {
if hits.is_empty() {
- let (mut new_hits, mut new_misses, mut unknown) = Knowledge::to_hits_and_misses(ring, &state);
+ let (mut new_hits, mut new_misses, mut unknown) = Knowledge::to_hits_and_misses(ring.iter().filter_map(|&p| p).collect::<Vec<_>>(), &state);
misses.append(&mut new_misses);
if !new_hits.is_empty() {
hits.append(&mut new_hits);
@@ -201,26 +165,64 @@ impl Knowledge {
best_cells
}
- pub fn get_most_possibility_shots_on_lattice(&self) -> Vec<Point> {
- let on_lattice = self.opponent_map.cells_on_lattice(self.lattice_size());
+ pub fn get_best_seek_shots(&self) -> (Weapon, Vec<Point>) {
let possible_placements = self.opponent_map.possible_placements();
+ // let lattice = self.lattice_size(); //TODO use the lattice still?
- let mut max_possibilities = 1;
- let mut best_cells = Vec::new();
-
- for cell in on_lattice {
- let possibilities = possible_placements.iter()
- .filter(|placement| placement.touches_point(cell)).count();
- if possibilities > max_possibilities {
- max_possibilities = possibilities;
- best_cells = vec!(cell);
- }
- else if possibilities == max_possibilities {
- best_cells.push(cell);
+ let mut best_shots: HashMap<Weapon, (Vec<Point>, usize)> = HashMap::new();
+
+ for &weapon in self.available_weapons.iter() {
+ let mut current_best_score = 1;
+ let mut best_cells = Vec::new();
+
+ for target in self.opponent_map.flat_cell_position_list() {
+ let cells = if weapon == Weapon::SeekerMissle {
+ let full_range = weapon.affected_cells(target, self.map_size);
+ let has_hits = full_range.iter().any(|p| self.opponent_map.cells[p.x as usize][p.y as usize].hit);
+ if has_hits {
+ vec!()
+ }
+ else {
+ full_range
+ }
+ }
+ else {
+ weapon.affected_cells(target, self.map_size)
+ };
+
+ let possibilities = possible_placements.iter()
+ .filter(|placement| placement.touches_any_point(&cells))
+ .count();
+
+ if possibilities > current_best_score {
+ current_best_score = possibilities;
+ best_cells = vec!(target);
+ }
+ else if possibilities == current_best_score {
+ best_cells.push(target);
+ }
}
+
+ best_shots.insert(weapon, (best_cells, current_best_score));
}
- best_cells
+ let best_single: Option<(Weapon, (Vec<Point>, usize))> =
+ best_shots.get(&Weapon::SingleShot).map(|x| (Weapon::SingleShot, x.clone()));
+
+ let best: (Weapon, (Vec<Point>, usize)) =
+ best_shots.iter()
+ .max_by_key(|&(_, &(_, score))| score)
+ .and_then(|(&weapon, x)| {
+ if self.shootable_weapons.contains(&weapon) {
+ Some((weapon, x.clone()))
+ } else {
+ best_single
+ }
+ })
+ .unwrap_or((Weapon::SingleShot, (vec!(), 0)));
+
+
+ (best.0.clone(), (best.1).0)
}
fn lattice_size(&self) -> u16 {
@@ -344,6 +346,12 @@ impl OpponentMapKnowledge {
}).collect()
}
+ fn flat_cell_position_list(&self) -> Vec<Point> {
+ self.cells.iter().flat_map(|x| {
+ x.iter().map(|y| y.position)
+ }).collect()
+ }
+
fn cells_on_lattice(&self, lattice_size: u16) -> Vec<Point> {
self.cells.iter().flat_map(|x| {
x.iter()
diff --git a/src/ships.rs b/src/ships.rs
index 104e986..422f24e 100644
--- a/src/ships.rs
+++ b/src/ships.rs
@@ -1,6 +1,8 @@
use std::fmt;
use std::str;
+use math::*;
+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub enum Weapon {
SingleShot,
@@ -61,6 +63,75 @@ impl Weapon {
//weird plus is to make the integer rounding up instead of down
(required_energy + energy_per_round - 1) / energy_per_round
}
+
+ pub fn affected_cells(&self, target: Point, map_size: u16) -> Vec<Point> {
+ use Weapon::*;
+
+ let p = target;
+ match self {
+ &SingleShot => {
+ vec!(Some(p))
+ },
+ &DoubleShotVertical => {
+ vec!(
+ p.move_point(Direction::North, 1, map_size),
+ p.move_point(Direction::South, 1, map_size)
+ )
+ },
+ &DoubleShotHorizontal => {
+ vec!(
+ p.move_point(Direction::East, 1, map_size),
+ p.move_point(Direction::West, 1, map_size)
+ )
+ },
+ &CornerShot => {
+ vec!(
+ p.move_point(Direction::NorthEast, 1, map_size),
+ p.move_point(Direction::SouthEast, 1, map_size),
+ p.move_point(Direction::NorthWest, 1, map_size),
+ p.move_point(Direction::SouthWest, 1, map_size),
+ )
+ },
+ &CrossShotDiagonal => {
+ vec!(
+ p.move_point(Direction::NorthEast, 1, map_size),
+ p.move_point(Direction::SouthEast, 1, map_size),
+ p.move_point(Direction::NorthWest, 1, map_size),
+ p.move_point(Direction::SouthWest, 1, map_size),
+ Some(p)
+ )
+ },
+ &CrossShotHorizontal => {
+ vec!(
+ p.move_point(Direction::North, 1, map_size),
+ p.move_point(Direction::East, 1, map_size),
+ p.move_point(Direction::South, 1, map_size),
+ p.move_point(Direction::West, 1, map_size),
+ Some(p)
+ )
+ },
+ &SeekerMissle => {
+ vec!(
+ Some(p),
+
+ p.move_point(Direction::North, 1, map_size),
+ p.move_point(Direction::East, 1, map_size),
+ p.move_point(Direction::South, 1, map_size),
+ p.move_point(Direction::West, 1, map_size),
+
+ p.move_point(Direction::NorthEast, 1, map_size),
+ p.move_point(Direction::SouthEast, 1, map_size),
+ p.move_point(Direction::NorthWest, 1, map_size),
+ p.move_point(Direction::SouthWest, 1, map_size),
+
+ p.move_point(Direction::North, 2, map_size),
+ p.move_point(Direction::East, 2, map_size),
+ p.move_point(Direction::South, 2, map_size),
+ p.move_point(Direction::West, 2, map_size)
+ )
+ }
+ }.iter().filter_map(|&p| p).collect::<Vec<_>>()
+ }
}
diff --git a/src/shooting.rs b/src/shooting.rs
index bcdf783..e0358ee 100644
--- a/src/shooting.rs
+++ b/src/shooting.rs
@@ -7,33 +7,32 @@ use knowledge::*;
use ships::*;
pub fn shoot_smartly(knowledge: &Knowledge) -> Action {
- let shot = if knowledge.has_unknown_hits() {
+ let (weapon, target) = if knowledge.has_unknown_hits() {
destroy_shoot(&knowledge)
}
else {
seek_shoot(&knowledge)
};
-
- //TODO shoot other weapons
- Action::Shoot(Weapon::SingleShot, shot)
+
+ Action::Shoot(weapon, target)
}
-fn seek_shoot(knowledge: &Knowledge) -> Point {
- let possibilities = knowledge.get_most_possibility_shots_on_lattice();
+fn seek_shoot(knowledge: &Knowledge) -> (Weapon, Point) {
+ let (weapon, possibilities) = knowledge.get_best_seek_shots();
if possibilities.is_empty() {
println!("All possible shots on the current lattice have been tried!");
- Point::new(0,0)
+ (Weapon::SingleShot, Point::new(0,0))
}
else {
let mut rng = rand::thread_rng();
let between = Range::new(0, possibilities.len());
let i = between.ind_sample(&mut rng);
- possibilities[i as usize]
+ (weapon, possibilities[i as usize])
}
}
-fn destroy_shoot(knowledge: &Knowledge) -> Point {
+fn destroy_shoot(knowledge: &Knowledge) -> (Weapon, Point) {
let possibilities = knowledge.get_best_adjacent_shots();
if possibilities.is_empty() {
seek_shoot(&knowledge)
@@ -43,6 +42,6 @@ fn destroy_shoot(knowledge: &Knowledge) -> Point {
let between = Range::new(0, possibilities.len());
let i = between.ind_sample(&mut rng);
- possibilities[i as usize]
+ (Weapon::SingleShot, possibilities[i as usize])
}
}