-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathday24.rs
111 lines (102 loc) · 3.04 KB
/
day24.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashSet};
use std::ops::Range;
fn add_mod_range(a: usize, b: usize, range: Range<usize>) -> usize {
(a - range.start + b) % range.len() + range.start
}
fn sub_mod_range(a: usize, b: usize, range: Range<usize>) -> usize {
(a - range.start + range.len() - b % range.len()) % range.len() + range.start
}
fn search(
lines: &[&[u8]],
start: (usize, usize),
end: (usize, usize),
time: usize,
) -> Option<usize> {
let mut seen: HashSet<_> = [(start, time)].into();
let mut queue: BinaryHeap<_> = [(Reverse(0), (start, time))].into();
while let Some((_, (pos @ (x, y), time))) = queue.pop() {
if pos == end {
return Some(time);
}
for (dx, dy) in [(-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)] {
let Some(x) = x.checked_add_signed(dx) else { continue };
let Some(y) = y.checked_add_signed(dy) else { continue };
if y >= lines.len() || x == 0 {
continue;
}
let line = lines[y];
if x >= line.len() - 1 {
continue;
}
if y == 0 || y == lines.len() - 1 {
if lines[y][x] != b'.' {
continue;
}
} else if line[add_mod_range(x, time + 1, 1..line.len() - 1)] == b'<'
|| line[sub_mod_range(x, time + 1, 1..line.len() - 1)] == b'>'
|| lines[add_mod_range(y, time + 1, 1..lines.len() - 1)][x] == b'^'
|| lines[sub_mod_range(y, time + 1, 1..lines.len() - 1)][x] == b'v'
{
continue;
}
if seen.insert(((x, y), time + 1)) {
queue.push((
Reverse(time + x.abs_diff(end.0) + y.abs_diff(end.1)),
((x, y), time + 1),
));
}
}
}
None
}
pub fn part1<'a, I, S>(lines: I) -> Option<usize>
where
I: IntoIterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
let lines = lines
.into_iter()
.map(|line| line.as_ref().as_bytes())
.collect::<Vec<_>>();
search(
&lines,
(1, 0),
(lines.last()?.len() - 2, lines.len() - 1),
0,
)
}
pub fn part2<'a, I, S>(lines: I) -> Option<usize>
where
I: IntoIterator<Item = &'a S>,
S: AsRef<str> + 'a,
{
let lines = lines
.into_iter()
.map(|line| line.as_ref().as_bytes())
.collect::<Vec<_>>();
let start = (1, 0);
let end = (lines.last()?.len() - 2, lines.len() - 1);
search(
&lines,
start,
end,
search(&lines, end, start, search(&lines, start, end, 0)?)?,
)
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
static EXAMPLE: &[&str] = &[
"#.######", "#>>.<^<#", "#.<..<<#", "#>v.><>#", "#<^v^^>#", "######.#",
];
#[test]
fn part1_examples() {
assert_eq!(Some(18), part1(EXAMPLE));
}
#[test]
fn part2_examples() {
assert_eq!(Some(54), part2(EXAMPLE));
}
}