Add day 3 solution

This commit is contained in:
Wesley Moore 2017-12-06 22:54:15 +10:00
parent 23d9945b51
commit 2090b52c42
4 changed files with 193 additions and 0 deletions

4
2017/day/3/Cargo.lock generated Normal file
View file

@ -0,0 +1,4 @@
[[package]]
name = "day3"
version = "0.1.0"

6
2017/day/3/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "day3"
version = "0.1.0"
authors = ["Wesley Moore <wes@wezm.net>"]
[dependencies]

35
2017/day/3/problem.txt Normal file
View file

@ -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

148
2017/day/3/src/main.rs Normal file
View file

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