From a866bde485c7d8bc82820f2def70af7b6c70a066 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 21:25:36 +0200 Subject: Refile for merging repos --- 2017-battleships/src/math.rs | 338 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 2017-battleships/src/math.rs (limited to '2017-battleships/src/math.rs') diff --git a/2017-battleships/src/math.rs b/2017-battleships/src/math.rs new file mode 100644 index 0000000..3187829 --- /dev/null +++ b/2017-battleships/src/math.rs @@ -0,0 +1,338 @@ +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)); + } +} -- cgit v1.2.3