mirror of
https://github.com/wezm/advent-of-code.git
synced 2024-11-13 19:18:01 +00:00
148 lines
3.6 KiB
Rust
148 lines
3.6 KiB
Rust
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 }
|
|
}
|
|
|
|
// 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 {
|
|
if index == 1 { return 0 }
|
|
|
|
let angle = angle(index);
|
|
let radius = (spiral_diameter(index) / 2) as f64;
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Data from square 1 is carried 0 steps, since it's at the access port.
|
|
#[test]
|
|
fn test_example1() {
|
|
assert_eq!(manhattan_distance(1), 0);
|
|
}
|
|
|
|
// Data from square 12 is carried 3 steps, such as: down, left, left.
|
|
#[test]
|
|
fn test_example2() {
|
|
assert_eq!(manhattan_distance(12), 3);
|
|
}
|
|
|
|
// Data from square 23 is carried only 2 steps: up twice.
|
|
#[test]
|
|
fn test_example3() {
|
|
assert_eq!(manhattan_distance(23), 2);
|
|
}
|
|
|
|
// Data from square 1024 must be carried 31 steps.
|
|
#[test]
|
|
fn test_example4() {
|
|
assert_eq!(manhattan_distance(1024), 31);
|
|
}
|
|
|
|
/*---*/
|
|
|
|
#[test]
|
|
fn test_example5() {
|
|
assert_eq!(manhattan_distance(46), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example6() {
|
|
assert_eq!(manhattan_distance(11), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example7() {
|
|
assert_eq!(manhattan_distance(10), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example8() {
|
|
assert_eq!(manhattan_distance(9), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example9() {
|
|
assert_eq!(manhattan_distance(49), 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example10() {
|
|
assert_eq!(manhattan_distance(28), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example11() {
|
|
assert_eq!(manhattan_distance(1089), 32);
|
|
}
|
|
|
|
#[test]
|
|
fn test_example12() {
|
|
assert_eq!(manhattan_distance(37), 6);
|
|
}
|
|
|