AoC/aoc_2024/src/day21.rs

377 lines
9.6 KiB
Rust
Raw Normal View History

2024-12-21 12:03:49 +00:00
use aoc_runner_derive::{aoc, aoc_generator};
2024-12-22 00:47:51 +00:00
#[derive(Debug, PartialEq, Eq)]
2024-12-21 21:45:13 +00:00
struct Path {
steps: usize,
2024-12-21 21:45:13 +00:00
path: [Option<DirButton>; 6],
}
2024-12-21 21:45:13 +00:00
impl Path {
2024-12-22 00:47:51 +00:00
const fn from(buttons: &[DirButton]) -> Option<Self> {
let steps = buttons.len();
let mut path = [None, None, None, None, None, None];
let mut i = 0;
while i < steps {
2024-12-22 01:30:41 +00:00
path[i] = Some(buttons[i]);
2024-12-22 00:47:51 +00:00
i += 1;
}
2024-12-22 00:47:51 +00:00
let steps = steps + 1;
path[i] = Some(DirButton::A);
Some(Self { steps, path })
}
}
2024-12-21 12:03:49 +00:00
2024-12-21 20:16:07 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DirButton {
2024-12-21 12:03:49 +00:00
Up,
Down,
Left,
Right,
A,
}
2024-12-22 00:47:51 +00:00
const DIRBUTTON_PATHS: [[[Option<Path>; 2]; 5]; 5] = [
// Up
[
// Up
[Path::from(&[]), None],
// Down
[Path::from(&[DirButton::Down]), None],
// Left
[Path::from(&[DirButton::Down, DirButton::Left]), None],
// Right
[
Path::from(&[DirButton::Down, DirButton::Right]),
Path::from(&[DirButton::Right, DirButton::Down]),
],
// A
[Path::from(&[DirButton::Right]), None],
],
// Down
[
// Up
[Path::from(&[DirButton::Up]), None],
// Down
[Path::from(&[]), None],
// Left
[Path::from(&[DirButton::Left]), None],
// Right
[Path::from(&[DirButton::Right]), None],
// A
[
Path::from(&[DirButton::Up, DirButton::Right]),
Path::from(&[DirButton::Right, DirButton::Up]),
],
],
// Left
[
// Up
[Path::from(&[DirButton::Right, DirButton::Up]), None],
// Down
[Path::from(&[DirButton::Right]), None],
// Left
[Path::from(&[]), None],
// Right
[Path::from(&[DirButton::Right, DirButton::Right]), None],
// A
[
Path::from(&[DirButton::Right, DirButton::Right, DirButton::Up]),
None,
],
],
// Right
[
// Up
[
Path::from(&[DirButton::Up, DirButton::Left]),
Path::from(&[DirButton::Left, DirButton::Up]),
],
// Down
[Path::from(&[DirButton::Left]), None],
// Left
[Path::from(&[DirButton::Left, DirButton::Left]), None],
// Right
[Path::from(&[]), None],
// A
[Path::from(&[DirButton::Up]), None],
],
// A
[
// Up
[Path::from(&[DirButton::Left]), None],
// Down
[
Path::from(&[DirButton::Down, DirButton::Left]),
Path::from(&[DirButton::Left, DirButton::Down]),
],
// Left
[
Path::from(&[DirButton::Down, DirButton::Left, DirButton::Left]),
None,
],
// Right
[Path::from(&[DirButton::Down]), None],
// A
[Path::from(&[]), None],
],
];
2024-12-21 12:03:49 +00:00
2024-12-22 00:47:51 +00:00
impl DirButton {
2024-12-22 01:30:41 +00:00
const fn index(&self) -> usize {
2024-12-21 20:16:07 +00:00
match self {
DirButton::Up => 0,
DirButton::Down => 1,
DirButton::Left => 2,
DirButton::Right => 3,
DirButton::A => 4,
}
}
2024-12-22 01:30:41 +00:00
const fn paths_to(&self, other: &DirButton) -> &[Option<Path>; 2] {
2024-12-22 00:47:51 +00:00
&DIRBUTTON_PATHS[self.index()][other.index()]
2024-12-21 12:03:49 +00:00
}
}
2024-12-22 00:50:48 +00:00
#[derive(Debug, PartialEq, Eq)]
2024-12-21 20:16:07 +00:00
enum NumButton {
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Zero,
2024-12-21 12:03:49 +00:00
A,
}
2024-12-21 20:16:07 +00:00
impl NumButton {
2024-12-22 01:30:41 +00:00
const fn pos(&self) -> (usize, usize) {
2024-12-21 12:03:49 +00:00
match self {
2024-12-21 20:16:07 +00:00
NumButton::One => (0, 2),
NumButton::Two => (1, 2),
NumButton::Three => (2, 2),
NumButton::Four => (0, 1),
NumButton::Five => (1, 1),
NumButton::Six => (2, 1),
NumButton::Seven => (0, 0),
NumButton::Eight => (1, 0),
NumButton::Nine => (2, 0),
NumButton::Zero => (1, 3),
NumButton::A => (2, 3),
2024-12-21 12:03:49 +00:00
}
}
2024-12-22 01:30:41 +00:00
fn paths_to(&self, other: &NumButton) -> [Option<Path>; 2] {
2024-12-21 12:03:49 +00:00
let start = self.pos();
let end = other.pos();
2024-12-22 00:47:51 +00:00
let bx = if start.0 < end.0 {
DirButton::Right
2024-12-21 12:03:49 +00:00
} else {
2024-12-22 00:47:51 +00:00
DirButton::Left
2024-12-21 12:03:49 +00:00
};
2024-12-22 00:47:51 +00:00
let dx = start.0.abs_diff(end.0);
let by = if start.1 < end.1 {
DirButton::Down
2024-12-21 12:03:49 +00:00
} else {
2024-12-22 00:47:51 +00:00
DirButton::Up
2024-12-21 12:03:49 +00:00
};
2024-12-22 00:47:51 +00:00
let dy = start.1.abs_diff(end.1);
if start.0 == 0 && end.1 == 3 {
2024-12-22 00:47:51 +00:00
let mut x = [None, None, None, None, None, None];
(0..dx).for_each(|i| {
2024-12-22 00:50:48 +00:00
x[i] = Some(bx);
2024-12-22 00:47:51 +00:00
});
(0..dy).for_each(|i| {
2024-12-22 00:50:48 +00:00
x[i + dx] = Some(by);
2024-12-22 00:47:51 +00:00
});
x[dx + dy] = Some(DirButton::A);
[
Some(Path {
steps: dx + dy + 1,
path: x,
}),
None,
]
} else if end.0 == 0 && start.1 == 3 {
2024-12-22 00:47:51 +00:00
let mut y = [None, None, None, None, None, None];
(0..dx).for_each(|i| {
2024-12-22 00:50:48 +00:00
y[i + dy] = Some(bx);
2024-12-22 00:47:51 +00:00
});
(0..dy).for_each(|i| {
2024-12-22 00:50:48 +00:00
y[i] = Some(by);
2024-12-22 00:47:51 +00:00
});
y[dx + dy] = Some(DirButton::A);
[
Some(Path {
steps: dx + dy + 1,
path: y,
}),
None,
]
2024-12-21 12:03:49 +00:00
} else {
2024-12-22 00:47:51 +00:00
let mut x = [None, None, None, None, None, None];
let mut y = [None, None, None, None, None, None];
(0..dx).for_each(|i| {
2024-12-22 00:50:48 +00:00
x[i] = Some(bx);
y[i + dy] = Some(bx);
2024-12-22 00:47:51 +00:00
});
(0..dy).for_each(|i| {
2024-12-22 00:50:48 +00:00
x[i + dx] = Some(by);
y[i] = Some(by);
2024-12-22 00:47:51 +00:00
});
x[dx + dy] = Some(DirButton::A);
y[dx + dy] = Some(DirButton::A);
[
Some(Path {
steps: dx + dy + 1,
path: x,
}),
Some(Path {
steps: dx + dy + 1,
path: y,
}),
]
2024-12-21 12:03:49 +00:00
}
}
}
2024-12-22 01:30:41 +00:00
type Input = Vec<(DistanceSize, Vec<NumButton>)>;
2024-12-21 12:03:49 +00:00
#[aoc_generator(day21)]
fn parse(input: &str) -> Input {
input
.lines()
.map(|line| {
2024-12-21 20:16:07 +00:00
(
line[0..3].parse().unwrap(),
line.chars()
.map(|c| match c {
'1' => NumButton::One,
'2' => NumButton::Two,
'3' => NumButton::Three,
'4' => NumButton::Four,
'5' => NumButton::Five,
'6' => NumButton::Six,
'7' => NumButton::Seven,
'8' => NumButton::Eight,
'9' => NumButton::Nine,
'0' => NumButton::Zero,
'A' => NumButton::A,
_ => unreachable!(),
})
.collect(),
)
2024-12-21 12:03:49 +00:00
})
.collect()
}
2024-12-22 01:30:41 +00:00
type DistanceSize = u64;
type DistanceMatrix = [[DistanceSize; 5]; 5];
2024-12-21 21:45:13 +00:00
fn precompute_steps(steps: &mut [DistanceMatrix]) {
if steps.len() == 1 {
return;
2024-12-21 20:16:07 +00:00
}
2024-12-21 21:45:13 +00:00
precompute_steps(&mut steps[1..]);
2024-12-21 21:45:13 +00:00
let buttons = [
DirButton::Up,
DirButton::Down,
DirButton::Left,
DirButton::Right,
DirButton::A,
];
2024-12-22 00:47:51 +00:00
buttons.iter().for_each(|start| {
buttons.iter().for_each(|end| {
steps[0][start.index()][end.index()] = start
2024-12-22 01:30:41 +00:00
.paths_to(end)
2024-12-22 00:47:51 +00:00
.iter()
2024-12-21 21:45:13 +00:00
.flatten()
2024-12-22 00:47:51 +00:00
.map(|path| {
path.path
.iter()
.flatten()
.fold((0, &DirButton::A), |acc, next| {
(acc.0 + steps[1][acc.1.index()][next.index()], next)
})
.0
2024-12-21 21:45:13 +00:00
})
2024-12-22 01:30:41 +00:00
.fold(DistanceSize::MAX, |acc, next| acc.min(next));
2024-12-21 21:45:13 +00:00
});
});
2024-12-21 20:16:07 +00:00
}
2024-12-22 01:30:41 +00:00
fn shortest_dir_path(path: Path, steps: &mut [DistanceMatrix]) -> DistanceSize {
2024-12-21 20:16:07 +00:00
path.path
2024-12-22 00:50:48 +00:00
.iter()
2024-12-21 20:16:07 +00:00
.flatten()
2024-12-22 00:50:48 +00:00
.fold((0, &DirButton::A), |acc, next| {
2024-12-22 01:30:41 +00:00
(acc.0 + steps[0][acc.1.index()][next.index()], next)
2024-12-21 20:16:07 +00:00
})
.0
}
2024-12-22 01:30:41 +00:00
fn shortest_path(path: &[NumButton], steps: &mut [DistanceMatrix]) -> DistanceSize {
2024-12-22 00:50:48 +00:00
path.iter()
.fold((0, &NumButton::A), |acc, end| {
2024-12-22 01:30:41 +00:00
(
acc.0
+ acc
.1
.paths_to(end)
.into_iter()
.flatten()
.map(|path| shortest_dir_path(path, steps))
.fold(DistanceSize::MAX, |acc, next| acc.min(next)),
end,
)
2024-12-21 20:16:07 +00:00
})
.0
}
2024-12-21 12:03:49 +00:00
#[aoc(day21, part1)]
2024-12-22 01:30:41 +00:00
fn part1(input: &Input) -> DistanceSize {
2024-12-21 21:45:13 +00:00
let mut steps = vec![[[1; 5]; 5]; 3];
precompute_steps(&mut steps);
2024-12-21 20:16:07 +00:00
2024-12-21 12:03:49 +00:00
input
.iter()
2024-12-21 20:16:07 +00:00
.map(|(num, path)| num * shortest_path(path, &mut steps))
2024-12-21 12:03:49 +00:00
.sum()
}
#[aoc(day21, part2)]
2024-12-22 01:30:41 +00:00
fn part2(input: &Input) -> DistanceSize {
2024-12-21 21:45:13 +00:00
let mut steps = vec![[[1; 5]; 5]; 26];
precompute_steps(&mut steps);
2024-12-21 20:16:07 +00:00
input
.iter()
2024-12-21 20:16:07 +00:00
.map(|(num, path)| num * shortest_path(path, &mut steps))
.sum()
}
2024-12-21 12:03:49 +00:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_example() {
assert_eq!(part1(&parse("179A")), 68 * 179);
2024-12-21 12:03:49 +00:00
assert_eq!(part1(&parse("379A")), 64 * 379);
assert_eq!(part1(&parse("456A")), 64 * 456);
assert_eq!(part1(&parse("980A")), 60 * 980);
assert_eq!(part1(&parse("029A")), 68 * 29);
}
}