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
use crate::Input;
use md5::digest::generic_array::arr;
use md5::digest::FixedOutput;
use md5::{Digest, Md5};
use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap};
#[derive(Hash, Clone, Eq, Ord, PartialOrd, PartialEq)]
struct State {
position: (i32, i32),
path_so_far: Vec<u8>,
doors: [bool; 4],
}
fn check_doors(passcode: &[u8], path_so_far: &[u8]) -> [bool; 4] {
let mut hasher = Md5::new();
let mut output = arr![u8; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0];
hasher.update(passcode);
hasher.update(path_so_far);
hasher.finalize_into_reset(&mut output);
let is_open = |byte: u8| (11..=16).contains(&byte);
[
is_open((output[0] & 0xF0) >> 4),
is_open(output[0] & 0x0F),
is_open((output[1] & 0xF0) >> 4),
is_open(output[1] & 0x0F),
]
}
pub fn solve(input: &mut Input) -> Result<String, String> {
let passcode = input.text.as_bytes();
let mut to_visit = BinaryHeap::new();
let mut cost_of_states = HashMap::new();
let initial_state = State {
position: (0, 0),
path_so_far: Vec::new(),
doors: check_doors(passcode, &[]),
};
to_visit.push(Reverse((0, initial_state.clone())));
cost_of_states.insert(initial_state, 0);
let mut desired_path_length = None;
while let Some(Reverse((visited_state_cost, visited_state))) = to_visit.pop() {
if visited_state.position == (3, 3) {
if input.is_part_one() {
return Ok(visited_state
.path_so_far
.iter()
.map(|&byte| byte as char)
.collect::<String>());
} else {
desired_path_length = Some(visited_state_cost);
continue;
}
}
for (idx, direction) in [(0, -1), (0, 1), (-1, 0), (1, 0)].iter().enumerate() {
if visited_state.doors[idx] {
let new_position = (
visited_state.position.0 + direction.0,
visited_state.position.1 + direction.1,
);
if new_position.0 < 0
|| new_position.0 > 3
|| new_position.1 < 0
|| new_position.1 > 3
{
continue;
}
let direction_char = [b'U', b'D', b'L', b'R'];
let mut new_path = visited_state.path_so_far.clone();
new_path.push(direction_char[idx]);
let doors = check_doors(passcode, &new_path);
let new_state = State {
position: new_position,
path_so_far: new_path,
doors,
};
let new_cost = visited_state_cost + 1;
to_visit.push(Reverse((new_cost, new_state)));
}
}
}
desired_path_length
.map(|length| length.to_string())
.ok_or_else(|| "No path found".to_string())
}
#[test]
pub fn tests() {
use crate::{test_part_one, test_part_two};
let real_input = include_str!("day17_input.txt");
test_part_one!(real_input => "RDRDUDLRDR".to_string());
test_part_two!(real_input => "386".to_string());
}