Skip to content

Commit b27f5e7

Browse files
committed
Year 2018 Day 12
1 parent 1ede750 commit b27f5e7

File tree

7 files changed

+123
-0
lines changed

7 files changed

+123
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
248248
| 9 | [Marble Mania](https://adventofcode.com/2018/day/9) | [Source](src/year2018/day09.rs) | 980 |
249249
| 10 | [The Stars Align](https://adventofcode.com/2018/day/10) | [Source](src/year2018/day10.rs) | 12 |
250250
| 11 | [Chronal Charge](https://adventofcode.com/2018/day/11) | [Source](src/year2018/day11.rs) | 1552 |
251+
| 12 | [Subterranean Sustainability](https://adventofcode.com/2018/day/12) | [Source](src/year2018/day12.rs) | 75 |
251252

252253
## 2017
253254

benches/benchmark.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ mod year2018 {
140140
benchmark!(year2018, day09);
141141
benchmark!(year2018, day10);
142142
benchmark!(year2018, day11);
143+
benchmark!(year2018, day12);
143144
}
144145

145146
mod year2019 {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ pub mod year2018 {
123123
pub mod day09;
124124
pub mod day10;
125125
pub mod day11;
126+
pub mod day12;
126127
}
127128

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

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ fn year2018() -> Vec<Solution> {
190190
solution!(year2018, day09),
191191
solution!(year2018, day10),
192192
solution!(year2018, day11),
193+
solution!(year2018, day12),
193194
]
194195
}
195196

src/year2018/day12.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//! # Subterranean Sustainability
2+
//!
3+
//! The problem is a one dimensional version of
4+
//! [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life).
5+
//!
6+
//! The trick for part two is that the plants will eventually stabilize into a repeating pattern
7+
//! that expands by the same amount each generation. Once 2 deltas between 3 subsequent
8+
//! generations are the same we extrapolate 50 billion generations into the future.
9+
use std::iter::repeat;
10+
11+
pub struct Input {
12+
rules: Vec<usize>,
13+
state: Tunnel,
14+
}
15+
16+
#[derive(Clone)]
17+
pub struct Tunnel {
18+
plants: Vec<usize>,
19+
start: i64,
20+
sum: i64,
21+
}
22+
23+
pub fn parse(input: &str) -> Input {
24+
let lines: Vec<_> = input.lines().map(str::as_bytes).collect();
25+
// Convert ASCII characters to `1` for a plant and `0` for an empty pot.
26+
let plants: Vec<_> = lines[0][15..].iter().map(|b| (b & 1) as usize).collect();
27+
// 5 plants gives 2⁵ = 32 possible combinations to consider.
28+
let mut rules = vec![0; 32];
29+
30+
// Convert each pattern into an index for fast lookup. For example `..#.#` becomes 5.
31+
for line in &lines[2..] {
32+
let binary = line.iter().fold(0, |acc, b| (acc << 1) | (b & 1) as usize);
33+
rules[binary >> 5] = binary & 1;
34+
}
35+
36+
Input { rules, state: Tunnel { plants, start: 0, sum: 0 } }
37+
}
38+
39+
pub fn part1(input: &Input) -> i64 {
40+
let mut current = input.state.clone();
41+
42+
for _ in 0..20 {
43+
current = step(&input.rules, &current);
44+
}
45+
46+
current.sum
47+
}
48+
49+
pub fn part2(input: &Input) -> i64 {
50+
let mut current = input.state.clone();
51+
let mut delta = 0;
52+
let mut generations = 0;
53+
54+
loop {
55+
let next = step(&input.rules, &current);
56+
let next_delta = next.sum - current.sum;
57+
58+
// Two identical deltas indicates that the pattern has stabilized.
59+
if delta == next_delta {
60+
break current.sum + delta * (50_000_000_000 - generations);
61+
}
62+
63+
current = next;
64+
delta = next_delta;
65+
generations += 1;
66+
}
67+
}
68+
69+
fn step(rules: &[usize], tunnel: &Tunnel) -> Tunnel {
70+
let mut index = 0;
71+
let mut sum = 0;
72+
let mut position = tunnel.start - 2;
73+
let mut plants = Vec::with_capacity(1_000);
74+
75+
// Add four extra empty pots to the end to make checking the last pattern easier.
76+
for plant in tunnel.plants.iter().chain(repeat(&0).take(4)) {
77+
index = ((index << 1) | plant) & 0b11111;
78+
79+
sum += position * rules[index] as i64;
80+
position += 1;
81+
82+
plants.push(rules[index]);
83+
}
84+
85+
// Tunnel expands by 2 pots at each end.
86+
Tunnel { plants, start: tunnel.start - 2, sum }
87+
}

tests/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ mod year2018 {
124124
mod day09_test;
125125
mod day10_test;
126126
mod day11_test;
127+
mod day12_test;
127128
}
128129

129130
mod year2019 {

tests/year2018/day12_test.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use aoc::year2018::day12::*;
2+
3+
const EXAMPLE: &str = "\
4+
initial state: #..#.#..##......###...###
5+
6+
...## => #
7+
..#.. => #
8+
.#... => #
9+
.#.#. => #
10+
.#.## => #
11+
.##.. => #
12+
.#### => #
13+
#.#.# => #
14+
#.### => #
15+
##.#. => #
16+
##.## => #
17+
###.. => #
18+
###.# => #
19+
####. => #";
20+
21+
#[test]
22+
fn part1_test() {
23+
let input = parse(EXAMPLE);
24+
assert_eq!(part1(&input), 325);
25+
}
26+
27+
#[test]
28+
fn part2_test() {
29+
let input = parse(EXAMPLE);
30+
assert_eq!(part2(&input), 50000000501);
31+
}

0 commit comments

Comments
 (0)