-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday21.rs
125 lines (117 loc) · 3.32 KB
/
day21.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::array;
use std::cmp::Ordering;
#[allow(clippy::erasing_op, clippy::identity_op)]
const fn key_pos(key: u8) -> u8 {
match key {
b'0' => 3 * 1 + 1,
b'1' => 3 * 2 + 0,
b'2' => 3 * 2 + 1,
b'3' => 3 * 2 + 2,
b'4' => 3 * 3 + 0,
b'5' => 3 * 3 + 1,
b'6' => 3 * 3 + 2,
b'7' => 3 * 4 + 0,
b'8' => 3 * 4 + 1,
b'9' => 3 * 4 + 2,
_ => 3 * 1 + 2,
}
}
#[allow(clippy::erasing_op, clippy::identity_op)]
const BLANK: u8 = 3 * 1 + 0;
#[allow(clippy::erasing_op, clippy::identity_op)]
const A: u8 = 3 * 1 + 2;
#[allow(clippy::erasing_op, clippy::identity_op)]
const UP: u8 = 3 * 1 + 1;
#[allow(clippy::erasing_op, clippy::identity_op)]
const LEFT: u8 = 3 * 0 + 0;
#[allow(clippy::erasing_op, clippy::identity_op)]
const DOWN: u8 = 3 * 0 + 1;
#[allow(clippy::erasing_op, clippy::identity_op)]
const RIGHT: u8 = 3 * 0 + 2;
fn search(table: &[Option<usize>; 225], p1: u8, p2: u8, pos: u8) -> Option<usize> {
if p1 == BLANK || p2 == BLANK {
None
} else if p1 == p2 {
table[(15 * pos + A) as usize]
} else {
let (y1, x1) = (p1 / 3, p1 % 3);
let (y2, x2) = (p2 / 3, p2 % 3);
[
match x1.cmp(&x2) {
Ordering::Less => Some(((x1 + 1, y1), RIGHT)),
Ordering::Greater => Some(((x1 - 1, y1), LEFT)),
_ => None,
},
match y1.cmp(&y2) {
Ordering::Less => Some(((x1, y1 + 1), UP)),
Ordering::Greater => Some(((x1, y1 - 1), DOWN)),
_ => None,
},
]
.into_iter()
.filter_map(|choice| {
let ((x, y), pos2) = choice?;
Some(table[(15 * pos + pos2) as usize]? + search(table, 3 * y + x, p2, pos2)?)
})
.min()
}
}
fn solve(data: &str, depth: usize) -> usize {
let mut table = array::from_fn::<_, 225, _>(|i| {
let (p1, p2) = (i as u8 / 15, i as u8 % 15);
if p1 == BLANK || p2 == BLANK {
None
} else {
let (x1, y1) = (p1 / 3, p1 % 3);
let (x2, y2) = (p2 / 3, p2 % 3);
Some((x1.abs_diff(x2) + y1.abs_diff(y2) + 1) as usize)
}
});
for _ in 0..depth {
table = array::from_fn(|i| {
let (p1, p2) = (i as u8 / 15, i as u8 % 15);
if p1 == BLANK || p2 == BLANK {
None
} else {
search(&table, p1, p2, A)
}
});
}
data.lines()
.filter_map(|line| {
let (mut num, mut len, mut pos) = (0, 0, A);
for b in line.bytes() {
if b.is_ascii_digit() {
num = 10 * num + (b - b'0') as usize;
}
let pos2 = key_pos(b);
len += table[(15 * pos + pos2) as usize]?;
pos = pos2;
}
Some(num * len)
})
.sum()
}
pub fn part1(data: &str) -> usize {
solve(data, 2)
}
pub fn part2(data: &str) -> usize {
solve(data, 25)
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
static EXAMPLE: &str = indoc! {"
029A
980A
179A
456A
379A
"};
#[test]
fn part1_examples() {
assert_eq!(126384, part1(EXAMPLE));
}
}