summaryrefslogtreecommitdiff
path: root/2017-battleships/src/math.rs
diff options
context:
space:
mode:
Diffstat (limited to '2017-battleships/src/math.rs')
-rw-r--r--2017-battleships/src/math.rs338
1 files changed, 338 insertions, 0 deletions
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<Point> {
+ 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));
+ }
+}