-
Notifications
You must be signed in to change notification settings - Fork 0
/
quest15.rs
93 lines (76 loc) · 2.32 KB
/
quest15.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::util::grid::*;
use crate::util::point::*;
use std::collections::{HashSet, VecDeque};
const OPEN: u32 = 0;
const WALL: u32 = u32::MAX;
pub fn part1(notes: &str) -> u32 {
solve(notes)
}
pub fn part2(notes: &str) -> u32 {
solve(notes)
}
pub fn part3(notes: &str) -> u32 {
solve(notes)
}
fn solve(notes: &str) -> u32 {
let ascii = Grid::parse(notes);
let start = Point::new(ascii.width / 2, 0);
let mut grid = ascii.same_size_with(0);
grid.bytes = ascii.bytes.into_iter().map(to_mask).collect();
let (total, _) = collect(&mut grid, &mut HashSet::from([start]), start);
total
}
fn collect(grid: &mut Grid<u32>, seen: &mut HashSet<Point>, start: Point) -> (u32, u32) {
// Flood fill
let mut todo = VecDeque::from([start]);
let mut children = Vec::new();
while let Some(point) = todo.pop_front() {
for next in neighbours(grid, point) {
if seen.insert(next) {
if grid[next] == OPEN {
todo.push_back(next);
} else {
children.push(next);
}
}
}
}
// Process sub-graphs
let mut total = 0;
let mut letters = 0;
for child in children {
let (child_total, child_letters) = collect(grid, seen, child);
total += child_total;
letters |= child_letters & !grid[start];
}
// Process current graph
let mut todo = VecDeque::from([(start, letters, 0)]);
let mut seen = HashSet::from([(start, letters)]);
while let Some((point, remaining, cost)) = todo.pop_front() {
if point == start && remaining == 0 {
total += cost;
break;
}
for next in neighbours(grid, point) {
let remaining = remaining & !grid[next];
if seen.insert((next, remaining)) {
todo.push_back((next, remaining, cost + 1));
}
}
}
grid[start] |= letters;
(total, grid[start])
}
fn neighbours(grid: &Grid<u32>, point: Point) -> impl Iterator<Item = Point> + '_ {
ORTHOGONAL
.iter()
.map(move |&offset| point + offset)
.filter(|&next| grid.contains(next) && grid[next] != WALL)
}
fn to_mask(ascii: u8) -> u32 {
match ascii {
b'.' => OPEN,
b'#' | b'~' => WALL,
_ => 1 << (ascii - b'A'),
}
}