use math::*; use ships::*; use std::fmt; use std::collections::HashSet; #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] pub enum Action { PlaceShips(Vec), Shoot(Weapon, Point) } impl fmt::Display for Action { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { &Action::Shoot(w, p) => writeln!(f, "{},{},{}", w, p.x, p.y), &Action::PlaceShips(ref ships) => ships.iter().map(|ref ship| { writeln!(f, "{} {} {} {}", ship.ship_type, ship.point.x, ship.point.y, ship.direction) }).fold(Ok(()), |acc, next| acc.and(next)) } } } #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] pub struct ShipPlacement { ship_type: Ship, point: Point, direction: Direction } impl ShipPlacement { pub fn new(ship_type: Ship, point: Point, direction: Direction) -> ShipPlacement { ShipPlacement { ship_type: ship_type, point: point, direction: direction } } pub fn valid(&self, map_size: u16) -> bool { let start = self.point; 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, map_size: u16) -> bool { let mut occupied = HashSet::new(); let individuals_valid = placements.iter().all(|p| p.valid(map_size)); let mut no_overlaps = true; for placement in placements { 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); occupied.insert(block); }, None => { //invalid case here is handled above } } } //block out the area around the current ship to prevent adjacent ships for i in 0..placement.ship_type.length() as i32 { match placement.point.move_point(placement.direction, i, map_size) { Some(current_block) => { if let Some(p) = current_block.move_point(Direction::North, 1, map_size) { occupied.insert(p); } if let Some(p) = current_block.move_point(Direction::South, 1, map_size) { occupied.insert(p); } if let Some(p) = current_block.move_point(Direction::East, 1, map_size) { occupied.insert(p); } if let Some(p) = current_block.move_point(Direction::West, 1, map_size) { occupied.insert(p); } }, None => { //invalid case here is handled above } } } } individuals_valid && no_overlaps } }