Skip to content

Commit 5415ef9

Browse files
committed
Day Douze Dilligently Done.
1 parent 588d912 commit 5415ef9

File tree

3 files changed

+154
-2
lines changed

3 files changed

+154
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ All solutions linked below:
2020
| [09](https://adventofcode.com/2023/day/9) | Mirage Maintenance | 150ms | 154ms | [day09.rs](./src/day09.rs) | :thinking: |
2121
| [10](https://adventofcode.com/2023/day/10) | Pipe Maze | 462µs | 698µs | [day10.rs](./src/day10.rs) | :confounded: |
2222
| [11](https://adventofcode.com/2023/day/11) | Cosmic Expansion | 1.63ms | 1.63ms | [day11.rs](./src/day11.rs) | :nerd_face: |
23-
<!--| [12](https://adventofcode.com/2023/day/12) | Hot Springs | | | [day12.rs](./src/day12.rs) | |-->
23+
| [12](https://adventofcode.com/2023/day/12) | Hot Springs | 1.60ms | 11.2ms | [day12.rs](./src/day12.rs) | :hot_face: |
2424
<!--| [13](https://adventofcode.com/2023/day/13) | Point of Incidence | | | [day13.rs](./src/day13.rs) | |-->
2525
<!--| [14](https://adventofcode.com/2023/day/14) | Parabolic Reflector Dish | | | [day14.rs](./src/day14.rs) | |-->
2626
<!--| [15](https://adventofcode.com/2023/day/15) | Lens Library | | | [day15.rs](./src/day15.rs) | |-->
@@ -42,4 +42,5 @@ All solutions linked below:
4242
4. It turns out part tests work better when calling the correct function. Who knew?
4343
5. It turns out functions work as intended when all branches are actually different. :man_facepalming:
4444
6. That's enough of a hiatus between days 9 and 10.
45-
7. I've got something similar to day 11 involving [star clusters](https://github.com/wrightdylan/cncalc).
45+
7. I've got something similar to day 11 involving [star clusters](https://github.com/wrightdylan/cncalc).
46+
8. Day 12 required learning more about non-deterministic finite automata and powerset construction. There is an excellent series by Neso Academy on YouTube called Theory of Computation & Automata Theory.

src/day12.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use rayon::prelude::*;
2+
use std::collections::HashMap;
3+
use std::iter::repeat;
4+
5+
type Record = (Vec<Symbol>, Vec<usize>);
6+
7+
// The alphabet of the NFA
8+
#[derive(Debug, Clone, PartialEq)]
9+
pub enum Symbol {
10+
Operational,
11+
Damaged,
12+
Unknown,
13+
}
14+
15+
trait NFA {
16+
fn permutations(&self) -> usize;
17+
fn unfold(&self) -> Record;
18+
}
19+
20+
impl NFA for Record {
21+
fn permutations(&self) -> usize {
22+
let symbols = &self.0;
23+
let groups = &self.1;
24+
let mut sequence = vec![Symbol::Operational];
25+
for &size in groups {
26+
for _ in 0..size {
27+
sequence.push(Symbol::Damaged);
28+
}
29+
sequence.push(Symbol::Operational);
30+
}
31+
32+
let mut powerset: HashMap<usize, usize> = HashMap::new();
33+
let mut subset: HashMap<usize, usize> = HashMap::new();
34+
powerset.insert(0, 1);
35+
36+
for symbol in symbols.iter() {
37+
for (&state, &count) in &powerset {
38+
match symbol {
39+
Symbol::Operational => {
40+
if state + 1 < sequence.len() && sequence[state + 1] == Symbol::Operational {
41+
*subset.entry(state + 1).or_default() += count;
42+
}
43+
if sequence[state] == Symbol::Operational {
44+
*subset.entry(state).or_default() += count;
45+
}
46+
},
47+
Symbol::Damaged => {
48+
if state + 1 < sequence.len() && sequence[state + 1] == Symbol::Damaged {
49+
*subset.entry(state + 1).or_default() += count;
50+
}
51+
},
52+
Symbol::Unknown => {
53+
if state + 1 < sequence.len() {
54+
*subset.entry(state + 1).or_default() += count;
55+
}
56+
if sequence[state] == Symbol::Operational {
57+
*subset.entry(state).or_default() += count;
58+
}
59+
},
60+
}
61+
}
62+
63+
powerset = subset;
64+
subset = HashMap::new();
65+
}
66+
67+
*powerset.get(&(sequence.len() - 1)).unwrap_or(&0)
68+
+ *powerset.get(&(sequence.len() - 2)).unwrap_or(&0)
69+
}
70+
71+
fn unfold(&self) -> Record {
72+
(
73+
(0..4).fold(self.0.clone(), |mut acc, _| {
74+
acc.push(Symbol::Unknown);
75+
acc.extend_from_slice(&self.0);
76+
acc
77+
}),
78+
repeat(&self.1)
79+
.take(5)
80+
.flatten()
81+
.cloned()
82+
.collect::<Vec<usize>>(),
83+
)
84+
}
85+
}
86+
87+
#[aoc_generator(day12)]
88+
pub fn input_generator(input: &str) -> Vec<Record> {
89+
input
90+
.lines()
91+
.map(|line| {
92+
line.trim()
93+
.split_once(' ')
94+
.map(|(springs, count)| (
95+
springs
96+
.chars()
97+
.map(|state| match state {
98+
'.' => Symbol::Operational,
99+
'#' => Symbol::Damaged,
100+
'?' => Symbol::Unknown,
101+
_ => panic!("Unknown state.")
102+
})
103+
.collect(),
104+
count
105+
.split(',')
106+
.map(|num| num.parse::<usize>().unwrap())
107+
.collect()
108+
))
109+
.unwrap()
110+
})
111+
.collect()
112+
}
113+
114+
#[aoc(day12, part1)]
115+
pub fn solve_part1(input: &Vec<Record>) -> usize {
116+
input
117+
.par_iter()
118+
.map(|line| line.permutations())
119+
.sum()
120+
}
121+
122+
#[aoc(day12, part2)]
123+
pub fn solve_part2(input: &Vec<Record>) -> usize {
124+
input
125+
.par_iter()
126+
.map(|line| line.unfold().permutations())
127+
.sum()
128+
}
129+
130+
#[cfg(test)]
131+
mod tests {
132+
use super::*;
133+
134+
const TEST: &str = "???.### 1,1,3
135+
.??..??...?##. 1,1,3
136+
?#?#?#?#?#?#?#? 1,3,1,6
137+
????.#...#... 4,1,1
138+
????.######..#####. 1,6,5
139+
?###???????? 3,2,1";
140+
141+
#[test]
142+
fn part1_test() {
143+
assert_eq!(solve_part1(&input_generator(TEST)), 21);
144+
}
145+
146+
#[test]
147+
fn part2_test() {
148+
assert_eq!(solve_part2(&input_generator(TEST)), 525_152);
149+
}
150+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ pub mod day08;
1414
pub mod day09;
1515
pub mod day10;
1616
pub mod day11;
17+
pub mod day12;
1718

1819
aoc_lib! { year = 2023 }

0 commit comments

Comments
 (0)