diff options
author | Justin Worthe <justin@worthe-it.co.za> | 2017-07-15 19:17:49 +0200 |
---|---|---|
committer | Justin Worthe <justin@worthe-it.co.za> | 2017-07-15 19:17:49 +0200 |
commit | 6379c1252e8ec51e607865638ff7d5ae79bd9c96 (patch) | |
tree | ae9dab9d7e9fa557c60835fc933e5b34e66a718a /src | |
parent | c14a74190d72ced3e6fdbf718fce25b4a0e8cc8f (diff) |
Adding shooting of new weapons
Diffstat (limited to 'src')
-rw-r--r-- | src/knowledge.rs | 134 | ||||
-rw-r--r-- | src/ships.rs | 71 | ||||
-rw-r--r-- | src/shooting.rs | 19 |
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]) } } |