From 2090b52c42824040d89f98f74f6059baf5075adb Mon Sep 17 00:00:00 2001 From: Wesley Moore Date: Wed, 6 Dec 2017 22:54:15 +1000 Subject: [PATCH] Add day 3 solution --- 2017/day/3/Cargo.lock | 4 ++ 2017/day/3/Cargo.toml | 6 ++ 2017/day/3/problem.txt | 35 ++++++++++ 2017/day/3/src/main.rs | 148 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 2017/day/3/Cargo.lock create mode 100644 2017/day/3/Cargo.toml create mode 100644 2017/day/3/problem.txt create mode 100644 2017/day/3/src/main.rs diff --git a/2017/day/3/Cargo.lock b/2017/day/3/Cargo.lock new file mode 100644 index 0000000..e8c4535 --- /dev/null +++ b/2017/day/3/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "day3" +version = "0.1.0" + diff --git a/2017/day/3/Cargo.toml b/2017/day/3/Cargo.toml new file mode 100644 index 0000000..4455448 --- /dev/null +++ b/2017/day/3/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day3" +version = "0.1.0" +authors = ["Wesley Moore "] + +[dependencies] diff --git a/2017/day/3/problem.txt b/2017/day/3/problem.txt new file mode 100644 index 0000000..17e8548 --- /dev/null +++ b/2017/day/3/problem.txt @@ -0,0 +1,35 @@ +--- Day 3: Spiral Memory --- + +You come across an experimental new kind of memory stored on an infinite two-dimensional grid. + +Each square on the grid is allocated in a spiral pattern starting at a location marked 1 and then counting up while spiraling outward. For example, the first few squares are allocated like this: + +17 16 15 14 13 +18 5 4 3 12 +19 6 1 2 11 +20 7 8 9 10 +21 22 23---> ... + +While this is very space-efficient (no squares are skipped), requested data must be carried back to square 1 (the location of the only access port for this memory system) by programs that can only move up, down, left, or right. They always take the shortest path: the Manhattan Distance between the location of the data and square 1. + +For example: + + Data from square 1 is carried 0 steps, since it's at the access port. + Data from square 12 is carried 3 steps, such as: down, left, left. + Data from square 23 is carried only 2 steps: up twice. + Data from square 1024 must be carried 31 steps. + +How many steps are required to carry the data from the square identified in your puzzle input all the way to the access port? + +Your puzzle input is 289326. + +--- + +37 36 35 34 33 32 31 +38 17 16 15 14 13 30 +39 18 5 4 3 12 29 +40 19 6 1 2 11 28 +41 20 7 8 9 10 27 +42 21 22 23 24 25 26 +43 44 45 46 47 48 49 + diff --git a/2017/day/3/src/main.rs b/2017/day/3/src/main.rs new file mode 100644 index 0000000..7adaf0a --- /dev/null +++ b/2017/day/3/src/main.rs @@ -0,0 +1,148 @@ +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); +} +