1use std::collections::VecDeque;
2use utils::graph::explore_hamiltonian_paths;
3use utils::grid;
4use utils::prelude::*;
5
6#[derive(Clone, Debug)]
11pub struct Day24 {
12 part1: u32,
13 part2: u32,
14}
15
16impl Day24 {
17 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
18 let mut digit_counts = [0usize; 10];
19
20 let (_, cols, grid) = grid::from_str_padded(input, 1, b'#', |b| match b {
23 b'.' | b'#' => Some(b),
24 b'0'..=b'9' => {
25 digit_counts[(b - b'0') as usize] += 1;
26 Some(b)
27 }
28 _ => None,
29 })?;
30
31 let digits = digit_counts.iter().position(|&c| c == 0).unwrap_or(10);
32 if let Some(d) = digit_counts.iter().position(|&c| c > 1) {
33 return Err(InputError::new(input, 0, format!("duplicate {d} digit")));
34 }
35 if digits == 0 {
36 return Err(InputError::new(input, 0, "expected 0 in grid"));
37 }
38 if digit_counts[digits..].iter().any(|&c| c > 0) {
39 return Err(InputError::new(input, 0, format!("missing {digits} digit")));
40 }
41
42 let mut digit_positions = vec![0; digits];
43 for (i, &c) in grid.iter().enumerate() {
44 if c.is_ascii_digit() {
45 digit_positions[(c - b'0') as usize] = i;
46 }
47 }
48
49 let mut dist_matrix = vec![u32::MAX; digits * digits];
51 'digits: for (start_digit, &start_index) in digit_positions.iter().enumerate() {
52 let mut visited = vec![false; grid.len()];
53 visited[start_index] = true;
54
55 let mut queue = VecDeque::new();
56 queue.push_back((start_index, 0));
57
58 while let Some((index, dist)) = queue.pop_front() {
59 if grid[index].is_ascii_digit() {
60 let end_digit = (grid[index] - b'0') as usize;
61 dist_matrix[(start_digit * digits) + end_digit] = dist;
62 dist_matrix[(end_digit * digits) + start_digit] = dist;
63
64 if dist_matrix[start_digit * digits..(start_digit + 1) * digits]
66 .iter()
67 .all(|&c| c != u32::MAX)
68 {
69 continue 'digits;
70 }
71 }
72
73 for next in [index - 1, index + 1, index - cols, index + cols] {
74 if grid[next] != b'#' && !visited[next] {
75 queue.push_back((next, dist + 1));
76 visited[next] = true;
77 }
78 }
79 }
80
81 return Err(InputError::new(input, 0, "unreachable digit"));
82 }
83
84 let (mut part1, mut part2) = (u32::MAX, u32::MAX);
86 explore_hamiltonian_paths(
87 digits as u32,
88 0,
89 0,
90 |a, b| dist_matrix[(a as usize * digits) + b as usize],
91 |a, b| a + b,
92 |total, loop_edge| {
93 part1 = part1.min(total);
94 part2 = part2.min(total + loop_edge);
95 },
96 );
97
98 Ok(Self { part1, part2 })
99 }
100
101 #[must_use]
102 pub fn part1(&self) -> u32 {
103 self.part1
104 }
105
106 #[must_use]
107 pub fn part2(&self) -> u32 {
108 self.part2
109 }
110}
111
112examples!(Day24 -> (u32, u32) [
113 {file: "day24_example0.txt", part1: 14, part2: 20},
114]);