1use crate::util::parse::*;
19use std::array::from_fn;
20
21pub struct Square {
22 size: usize,
23 bytes: Vec<u8>,
24}
25
26pub fn parse(input: &str) -> Square {
27 let raw: Vec<_> = input.lines().map(str::as_bytes).collect();
28 let size = raw.len();
29 let bytes = raw.into_iter().flatten().map(|b| b.to_decimal()).collect();
30 Square { size, bytes }
31}
32
33pub fn part1(input: &Square) -> usize {
35 dijkstra(input)
36}
37
38pub fn part2(input: &Square) -> usize {
40 let Square { size, bytes } = input;
41
42 let mut expanded = Square { size: 5 * size, bytes: vec![0; 25 * size * size] };
43
44 for (i, &b) in bytes.iter().enumerate() {
45 let x1 = i % size;
46 let y1 = i / size;
47 let base = b as usize;
48
49 for x2 in 0..5 {
50 for y2 in 0..5 {
51 let index = (5 * size) * (y2 * size + y1) + (x2 * size + x1);
52 expanded.bytes[index] = (1 + (base - 1 + x2 + y2) % 9) as u8;
53 }
54 }
55 }
56
57 dijkstra(&expanded)
58}
59
60fn dijkstra(square: &Square) -> usize {
63 let Square { size, bytes } = square;
64 let edge = size - 1;
65 let end = size * size - 1;
66
67 let mut todo: [Vec<u32>; 10] = from_fn(|_| Vec::with_capacity(1_000));
69 let mut cost = vec![u16::MAX; size * size];
70 let mut risk = 0;
71
72 todo[0].push(0);
74 cost[0] = 0;
75
76 loop {
77 let i = risk % 10;
78
79 for j in 0..todo[i].len() {
80 let current = todo[i][j] as usize;
81 if current == end {
82 return risk;
83 }
84
85 let mut check = |next: usize| {
86 let next_cost = risk as u16 + bytes[next] as u16;
87 if next_cost < cost[next] {
88 todo[(next_cost % 10) as usize].push(next as u32);
89 cost[next] = next_cost;
90 }
91 };
92 let x = current % size;
93 let y = current / size;
94
95 if x > 0 {
96 check(current - 1);
97 }
98 if x < edge {
99 check(current + 1);
100 }
101 if y > 0 {
102 check(current - size);
103 }
104 if y < edge {
105 check(current + size);
106 }
107 }
108
109 todo[i].clear();
110 risk += 1;
111 }
112}