use std::fmt; use rand; use rand::distributions::{IndependentSample, Range}; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub enum Direction { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest } use Direction::*; impl fmt::Display for Direction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str( match self { &North => "North", &East => "East", &South => "South", &West => "West", &NorthEast => "NorthEast", &SouthEast => "SouthEast", &NorthWest => "NorthWest", &SouthWest => "SouthWest" } ) } } impl Direction { pub fn random() -> Direction { let mut rng = rand::thread_rng(); let between = Range::new(0, 4); let dir = between.ind_sample(&mut rng); match dir { 0 => North, 1 => East, 2 => South, 3 => West, _ => panic!("Invalid number generated by random number generator") } } } #[cfg(test)] mod direction_tests { use super::*; #[test] fn random_direction_does_not_panic() { for _ in 0..10000 { Direction::random(); } } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub struct Point { pub x: u16, pub y: u16 } impl Point { pub fn new(x: u16, y: u16) -> Point { Point { x: x, y: y } } pub fn random(map_size: u16) -> Point { let mut rng = rand::thread_rng(); let between = Range::new(0, map_size); let x = between.ind_sample(&mut rng); let y = between.ind_sample(&mut rng); Point::new(x, y) } pub fn move_point(&self, direction: Direction, distance: i32, map_size: u16) -> Option { let x = self.x as i32 + match direction { West|NorthWest|SouthWest => -distance, East|NorthEast|SouthEast => distance, _ => 0 }; let y = self.y as i32 + match direction { South|SouthWest|SouthEast => -distance, North|NorthWest|NorthEast => 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, _ => false //ships cannot go diagonally }; let same_cross = match direction { East | West => self.y == ship_start.y, North | South => self.x == ship_start.x, _ => false //ships cannot go diagonally }; let (parr_self, parr_ship) = match direction { East | West => (self.x, ship_start.x), North | South => (self.y, ship_start.y), _ => (self.x, self.y) //ships cannot go diagonally }; let corrected_parr_ship = match reverse { true => 1 + parr_ship - length, false => parr_ship }; same_cross && parr_self >= corrected_parr_ship && parr_self < corrected_parr_ship + length } pub fn is_adjacent(&self, other: Point) -> bool { let dx = if self.x > other.x {self.x - other.x} else {other.x - self.x}; let dy = if self.y > other.y {self.y - other.y} else {other.y - self.y}; (dx == 0 && dy == 1) || (dx == 1 && dy == 0) } pub fn is_on_lattice(&self, lattice_size: u16) -> bool { (self.x + self.y) % lattice_size == 0 } } #[cfg(test)] mod point_tests { use super::*; #[test] fn random_point_is_in_correct_range() { for _ in 0..10000 { let point = Point::random(15); assert!(point.x < 15); assert!(point.y < 15); } } #[test] fn move_point_works_north_west() { assert_eq!(Some(Point::new(3,7)), Point::new(5,5).move_point(NorthWest, 2, 10)); assert_eq!(Some(Point::new(7,3)), Point::new(5,5).move_point(NorthWest, -2, 10)); assert_eq!(None, Point::new(5,5).move_point(NorthWest, 6, 10)); assert_eq!(None, Point::new(5,5).move_point(NorthWest, -5, 10)); } #[test] fn move_point_works_west() { assert_eq!(Some(Point::new(3,5)), Point::new(5,5).move_point(West, 2, 10)); assert_eq!(Some(Point::new(7,5)), Point::new(5,5).move_point(West, -2, 10)); assert_eq!(None, Point::new(5,5).move_point(West, 6, 10)); assert_eq!(None, Point::new(5,5).move_point(West, -5, 10)); } #[test] fn move_point_works_east() { assert_eq!(Some(Point::new(7,5)), Point::new(5,5).move_point(East, 2, 10)); assert_eq!(Some(Point::new(3,5)), Point::new(5,5).move_point(East, -2, 10)); assert_eq!(None, Point::new(5,5).move_point(East, 5, 10)); assert_eq!(None, Point::new(5,5).move_point(East, -6, 10)); } #[test] fn move_point_works_south() { assert_eq!(Some(Point::new(5,3)), Point::new(5,5).move_point(South, 2, 10)); assert_eq!(Some(Point::new(5,7)), Point::new(5,5).move_point(South, -2, 10)); assert_eq!(None, Point::new(5,5).move_point(South, 6, 10)); assert_eq!(None, Point::new(5,5).move_point(South, -5, 10)); } #[test] fn move_point_works_north() { assert_eq!(Some(Point::new(5,7)), Point::new(5,5).move_point(North, 2, 10)); assert_eq!(Some(Point::new(5,3)), Point::new(5,5).move_point(North, -2, 10)); assert_eq!(None, Point::new(5,5).move_point(North, 5, 10)); assert_eq!(None, Point::new(5,5).move_point(North, -6, 10)); } #[test] fn unrestricted_move_point_works_west() { assert_eq!(Point::new(3,5), Point::new(5,5).move_point_no_bounds_check(West, 2)); assert_eq!(Point::new(7,5), Point::new(5,5).move_point_no_bounds_check(West, -2)); } #[test] fn unrestricted_move_point_works_east() { assert_eq!(Point::new(7,5), Point::new(5,5).move_point_no_bounds_check(East, 2)); assert_eq!(Point::new(3,5), Point::new(5,5).move_point_no_bounds_check(East, -2)); } #[test] fn unrestricted_move_point_works_south() { assert_eq!(Point::new(5,3), Point::new(5,5).move_point_no_bounds_check(South, 2)); assert_eq!(Point::new(5,7), Point::new(5,5).move_point_no_bounds_check(South, -2)); } #[test] fn unrestricted_move_point_works_north() { assert_eq!(Point::new(5,7), Point::new(5,5).move_point_no_bounds_check(North, 2)); assert_eq!(Point::new(5,3), Point::new(5,5).move_point_no_bounds_check(North, -2)); } #[test] fn ship_collision_check_works_west() { assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), West, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(6,5), West, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(7,5), West, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(8,5), West, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(9,5), West, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(10,5), West, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,5), West, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,4), West, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,6), West, 5)); } #[test] fn ship_collision_check_works_east() { assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), East, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(4,5), East, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(3,5), East, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(2,5), East, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(1,5), East, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(0,5), East, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,5), East, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,4), East, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,6), East, 5)); } #[test] fn ship_collision_check_works_north() { assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), North, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,4), North, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,3), North, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,2), North, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,1), North, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,0), North, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,6), North, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,4), North, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,4), North, 5)); } #[test] fn ship_collision_check_works_south() { assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,5), South, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,6), South, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,7), South, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,8), South, 5)); assert_eq!(true, Point::new(5,5).check_for_ship_collision(Point::new(5,9), South, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,10), South, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(5,4), South, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(4,6), South, 5)); assert_eq!(false, Point::new(5,5).check_for_ship_collision(Point::new(6,6), South, 5)); } #[test] fn adjacency_check_works() { assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(4,5))); assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(6,5))); assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(5,4))); assert_eq!(true, Point::new(5,5).is_adjacent(Point::new(5,6))); assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(4,4))); assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(6,6))); assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(6,4))); assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(4,6))); assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(5,5))); assert_eq!(false, Point::new(5,5).is_adjacent(Point::new(10,5))); } #[test] fn point_on_4_lattice_works() { assert_eq!(true, Point::new(0,0).is_on_lattice(4)); assert_eq!(true, Point::new(4,0).is_on_lattice(4)); assert_eq!(true, Point::new(0,4).is_on_lattice(4)); assert_eq!(true, Point::new(4,4).is_on_lattice(4)); assert_eq!(true, Point::new(1,3).is_on_lattice(4)); assert_eq!(true, Point::new(3,1).is_on_lattice(4)); assert_eq!(false, Point::new(0,1).is_on_lattice(4)); assert_eq!(false, Point::new(0,2).is_on_lattice(4)); assert_eq!(false, Point::new(0,3).is_on_lattice(4)); assert_eq!(false, Point::new(1,0).is_on_lattice(4)); assert_eq!(false, Point::new(2,0).is_on_lattice(4)); assert_eq!(false, Point::new(3,0).is_on_lattice(4)); } #[test] fn point_on_2_lattice_works() { assert_eq!(true, Point::new(0,0).is_on_lattice(2)); assert_eq!(true, Point::new(2,0).is_on_lattice(2)); assert_eq!(true, Point::new(0,2).is_on_lattice(2)); assert_eq!(true, Point::new(2,2).is_on_lattice(2)); assert_eq!(true, Point::new(1,1).is_on_lattice(2)); assert_eq!(false, Point::new(0,1).is_on_lattice(2)); assert_eq!(false, Point::new(1,0).is_on_lattice(2)); } }