Another go at day 3 solution that works this time

This commit is contained in:
Wesley Moore 2017-12-24 15:44:52 +11:00
parent 54b3860012
commit 67dcde80c5
2 changed files with 30 additions and 72 deletions

1
2017/day/3/input Normal file
View file

@ -0,0 +1 @@
289326

View file

@ -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);
}