Skip to content

Commit d80e555

Browse files
committed
Year 2017 Day 3
1 parent eb62792 commit d80e555

File tree

8 files changed

+120
-1
lines changed

8 files changed

+120
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ pie
243243
| --- | --- | --- | --: |
244244
| 1 | [Inverse Captcha](https://adventofcode.com/2017/day/1) | [Source](src/year2017/day01.rs) | 1 |
245245
| 2 | [Corruption Checksum](https://adventofcode.com/2017/day/2) | [Source](src/year2017/day02.rs) | 3 |
246+
| 3 | [Spiral Memory](https://adventofcode.com/2017/day/3) | [Source](src/year2017/day03.rs) | 2 |
246247

247248
## 2016
248249

benches/benchmark.rs

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ mod year2016 {
9494
mod year2017 {
9595
benchmark!(year2017, day01);
9696
benchmark!(year2017, day02);
97+
benchmark!(year2017, day03);
9798
}
9899

99100
mod year2019 {

input/year2017/day03.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
312051

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ pub mod year2016 {
222222
pub mod year2017 {
223223
pub mod day01;
224224
pub mod day02;
225+
pub mod day03;
225226
}
226227

227228
/// # Rescue Santa from deep space with a solar system adventure.

src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn year2016() -> Vec<Solution> {
141141
}
142142

143143
fn year2017() -> Vec<Solution> {
144-
vec![solution!(year2017, day01), solution!(year2017, day02)]
144+
vec![solution!(year2017, day01), solution!(year2017, day02), solution!(year2017, day03)]
145145
}
146146

147147
fn year2019() -> Vec<Solution> {

src/year2017/day03.rs

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//! # Spiral Memory
2+
//!
3+
//! ## Part One
4+
//!
5+
//! Consider the layout as a sequence of hollow donuts. We find the donut that contains the value
6+
//! which gives one component of the Manhattan value. The second component is the distance from
7+
//! the center of each edge.
8+
//!
9+
//! For example say the target value is 20. We find the donut then subtract the inner donuts
10+
//! to make the values relative then calculate the values modulo the edge size.
11+
//!
12+
//! ```none
13+
//! <------------
14+
//! 17 16 15 14 13 7 6 5 4 3 3 2 [1] 0 3 ^
15+
//! 18 5 4 3 12 8 2 | 0 2 |
16+
//! 19 6 1 2 11 => 9 1 => | [1] [1] |
17+
//! 20 7 8 9 10 10 0 | 2 0 |
18+
//! 21 22 23 24 25 11 12 13 14 15 v 3 0 [1] 2 3
19+
//! ------------>
20+
//! ```
21+
//!
22+
//! The first component is the horizonal or vertical distance from the centre to the ring,
23+
//! in this case 2 steps. The second component is the distance from the target number to the
24+
//! center of each edge, in this case 2 - 1 = 1.
25+
//!
26+
//! ## Part Two
27+
//!
28+
//! We use the [`Point`] utility to move in the spiral direction. Values are stored in a hashmap
29+
//! defaulting to zero if the value doesn't exist yet.
30+
use crate::util::hash::*;
31+
use crate::util::parse::*;
32+
use crate::util::point::*;
33+
34+
pub fn parse(input: &str) -> u32 {
35+
input.unsigned()
36+
}
37+
38+
pub fn part1(input: &u32) -> u32 {
39+
let target = *input;
40+
let mut a = 3;
41+
42+
// Find the donut that contains the value.
43+
while a * a < target {
44+
a += 2;
45+
}
46+
let b = a - 1;
47+
let c = a - 2;
48+
49+
// Distance to donut plus distance to center of edge.
50+
(b / 2) + (c / 2).abs_diff((target - c * c - 1) % b)
51+
}
52+
53+
pub fn part2(input: &u32) -> u32 {
54+
let target = *input;
55+
let mut size = 2;
56+
57+
let mut position = Point::new(1, 0);
58+
let mut direction = UP;
59+
let mut left = LEFT;
60+
61+
let mut values = FastMap::build([(ORIGIN, 1)]);
62+
63+
'outer: loop {
64+
// Fill in one donut at a time.
65+
for edge in 0..4 {
66+
for i in 0..size {
67+
// Default to zero if a value doesn't exist yet.
68+
let value = |point| values.get(&point).unwrap_or(&0);
69+
70+
// Values in front and to the right (relative to our current direction) are not
71+
// filled in yet, so we only need to consider values to the left and behind.
72+
let next = value(position - direction)
73+
+ value(position + left + direction)
74+
+ value(position + left)
75+
+ value(position + left - direction);
76+
77+
if next > target {
78+
break 'outer next;
79+
}
80+
values.insert(position, next);
81+
82+
// Turn left at the very end of each edge, unless this is the last edge of
83+
// the square.
84+
if i == size - 1 && edge < 3 {
85+
position += left;
86+
} else {
87+
position += direction;
88+
}
89+
}
90+
91+
direction = left;
92+
left = left.counter_clockwise();
93+
}
94+
95+
size += 2;
96+
}
97+
}

tests/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ mod year2016 {
8787
mod year2017 {
8888
mod day01_test;
8989
mod day02_test;
90+
mod day03_test;
9091
}
9192

9293
mod year2019 {

tests/year2017/day03_test.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use aoc::year2017::day03::*;
2+
3+
const FIRST_EXAMPLE: &str = "1024";
4+
5+
const SECOND_EXAMPLE: &str = "805";
6+
7+
#[test]
8+
fn part1_test() {
9+
let input = parse(FIRST_EXAMPLE);
10+
assert_eq!(part1(&input), 31);
11+
}
12+
13+
#[test]
14+
fn part2_test() {
15+
let input = parse(SECOND_EXAMPLE);
16+
assert_eq!(part2(&input), 806);
17+
}

0 commit comments

Comments
 (0)