From 67dcde80c5d7d07f993590d17de7744f525a71da Mon Sep 17 00:00:00 2001 From: Wesley Moore Date: Sun, 24 Dec 2017 15:44:52 +1100 Subject: [PATCH] Another go at day 3 solution that works this time --- 2017/day/3/input | 1 + 2017/day/3/src/main.rs | 101 ++++++++++++----------------------------- 2 files changed, 30 insertions(+), 72 deletions(-) create mode 100644 2017/day/3/input diff --git a/2017/day/3/input b/2017/day/3/input new file mode 100644 index 0000000..fa5db30 --- /dev/null +++ b/2017/day/3/input @@ -0,0 +1 @@ +289326 diff --git a/2017/day/3/src/main.rs b/2017/day/3/src/main.rs index 7adaf0a..a879b2f 100644 --- a/2017/day/3/src/main.rs +++ b/2017/day/3/src/main.rs @@ -1,83 +1,41 @@ -use std::f64::consts::PI; - -// There might me a more elegant solution to this problem that I've missed (it was completed -// without any Internet access). At least it runs in constant time and space. fn main() { println!("{}", manhattan_distance(289326)); } -// Determine the dimensions of the rectangle in the spiral that the given number must be on. Will -// always be an odd number as 1 is in the middle. -fn spiral_diameter(index: i32) -> i32 { - let sqrt = (index as f64).sqrt().ceil() as i32; - if sqrt % 2 == 0 { sqrt + 1 } else { sqrt } +fn spiral_to(index: usize) -> (isize, isize) { + let mut x = 0isize; + let mut y = 0isize; + let mut edge = 1; + let mut delta = 1isize; + let mut i = 1usize; + + loop { + // Horizontal + for _i in 0..edge { + x += delta; + i += 1; + if i == index { return (x, y) } + } + + // Vertical + for _i in 0..edge { + y += delta; + i += 1; + if i == index { return (x, y) } + } + + edge += 1; + delta *= -1; + } + } -// Returns angle in radians of number on spiral -fn angle(index: i32) -> f64 { - if index == 1 { return 0. } - - let diameter = spiral_diameter(index); - - let area_of_inner_rectangle = (diameter - 2).pow(2); - let squares_around_rectangle = diameter.pow(2) - area_of_inner_rectangle; - let squares_per_side = squares_around_rectangle / 4; - - // Divide the space around the rectangle into squares_around_rectangle segments - let angle_per_segment = 2. * PI / squares_around_rectangle as f64; - - // Determine how far around the rectangle this index is. - // Offset adjusts for the last number being at the bottom right corner of any - // given rectangle. - let offset = squares_per_side / 2; - let angle = (index - area_of_inner_rectangle - offset) as f64 * angle_per_segment; - - angle -} - -fn manhattan_distance(index: i32) -> i32 { +fn manhattan_distance(index: usize) -> usize { if index == 1 { return 0 } - let angle = angle(index); - let radius = (spiral_diameter(index) / 2) as f64; + let (x, y) = spiral_to(index); - // Calculate the x and y coordinates, scale by √2 so that sin/cos at corners is 1 - let horizontal_distance = 1f64.min((2f64.sqrt() * angle.cos()).abs()); - let vertical_distance = 1f64.min((2f64.sqrt() * angle.sin()).abs()); - - let distance = radius * (horizontal_distance + vertical_distance); - - distance.round() as i32 -} - -#[test] -fn test_angle_should_be_zero() { - assert_eq!(angle(1), 0.); - assert_eq!(angle(2), 0.); - assert_eq!(angle(11), 0.); - assert_eq!(angle(28), 0.); -} - -#[test] -fn test_angle_should_be_half_pi() { - use std::f64::consts::FRAC_PI_2; - - assert_eq!(angle(4), FRAC_PI_2); - assert_eq!(angle(15), FRAC_PI_2); - assert_eq!(angle(34), FRAC_PI_2); -} - -#[test] -fn test_angle_should_be_1_point_75_pi() { - assert_eq!(angle(9), 1.75 * PI); - assert_eq!(angle(25), 1.75 * PI); - assert_eq!(angle(49), 1.75 * PI); - assert_eq!(angle(1089), 1.75 * PI); -} - -#[test] -fn test_angle_37() { - assert_eq!(angle(37), 0.75 * PI); + (x.abs() + y.abs()) as usize } // Data from square 1 is carried 0 steps, since it's at the access port. @@ -145,4 +103,3 @@ fn test_example11() { fn test_example12() { assert_eq!(manhattan_distance(37), 6); } -