Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/advent-of-code-2021
Path: blob/main/src/day23.rs
97 views
1
use crate::dijkstra;
2
3
pub fn run() {
4
// #############
5
// #...........#
6
// ###D#B#D#B###
7
// #C#A#A#C#
8
// #########
9
let part1_start = State {
10
hallway: [None; 11],
11
rooms: [
12
[Some(D), Some(C)],
13
[Some(B), Some(A)],
14
[Some(D), Some(A)],
15
[Some(B), Some(C)],
16
],
17
};
18
19
// #############
20
// #...........#
21
// ###D#B#D#B###
22
// #D#C#B#A#
23
// #D#B#A#C#
24
// #C#A#A#C#
25
// #########
26
let part2_start = State {
27
hallway: [None; 11],
28
rooms: [
29
[Some(D), Some(D), Some(D), Some(C)],
30
[Some(B), Some(C), Some(B), Some(A)],
31
[Some(D), Some(B), Some(A), Some(A)],
32
[Some(B), Some(A), Some(C), Some(C)],
33
],
34
};
35
36
println!("{}", part1_start.min_organization_energy());
37
println!("{}", part2_start.min_organization_energy());
38
}
39
40
type Amphipod = u8;
41
const A: Amphipod = 0;
42
const B: Amphipod = 1;
43
const C: Amphipod = 2;
44
const D: Amphipod = 3;
45
46
#[derive(Clone, PartialEq, Eq, Hash)]
47
struct State<const ROOM_SIZE: usize> {
48
hallway: [Option<Amphipod>; 11],
49
rooms: [[Option<Amphipod>; ROOM_SIZE]; 4],
50
}
51
52
impl<const S: usize> State<S> {
53
fn min_organization_energy(&self) -> usize {
54
let goal = State {
55
hallway: [None; 11],
56
rooms: [[Some(A); S], [Some(B); S], [Some(C); S], [Some(D); S]],
57
};
58
59
dijkstra::shortest_path(self, &goal, Self::next_moves).unwrap()
60
}
61
62
fn next_moves(&self) -> Vec<(State<S>, usize)> {
63
[self.room_to_hallway_moves(), self.hallway_to_room_moves()].concat()
64
}
65
66
fn room_to_hallway_moves(&self) -> Vec<(State<S>, usize)> {
67
let mut moves = vec![];
68
for room_index in 0..self.rooms.len() {
69
if let Some((room_slot, amphipod)) = self.get_amphipod_to_leave_room(room_index) {
70
let hallway_from = room_index_to_hallway_index(room_index);
71
for hallway_to in 0..11 {
72
if self.can_move_to_hallway(hallway_from, hallway_to) {
73
let mut next_state = self.clone();
74
next_state.hallway[hallway_to] = Some(amphipod);
75
next_state.rooms[room_index][room_slot] = None;
76
77
let dx = usize_diff(hallway_from, hallway_to);
78
let dy = room_slot + 1;
79
let energy = (dx + dy) * amphipod_energy(amphipod);
80
81
moves.push((next_state, energy))
82
}
83
}
84
}
85
}
86
moves
87
}
88
89
fn hallway_to_room_moves(&self) -> Vec<(State<S>, usize)> {
90
let mut moves = vec![];
91
for (hallway_from, &a) in self.hallway.iter().enumerate() {
92
if let Some(amphipod) = a {
93
let room_index = amphipod as usize;
94
if self.can_move_to_room(room_index, hallway_from) {
95
let room_slot = get_empty_room_slot(&self.rooms[room_index]);
96
let hallway_to = room_index_to_hallway_index(room_index);
97
98
let mut next_state = self.clone();
99
next_state.hallway[hallway_from] = None;
100
next_state.rooms[room_index][room_slot] = Some(amphipod);
101
102
let dx = usize_diff(hallway_from, hallway_to);
103
let dy = room_slot + 1;
104
let energy = (dx + dy) * amphipod_energy(amphipod);
105
106
moves.push((next_state, energy))
107
}
108
}
109
}
110
moves
111
}
112
113
fn get_amphipod_to_leave_room(&self, room_index: usize) -> Option<(usize, Amphipod)> {
114
let room = self.rooms[room_index];
115
let all_amphipods_in_their_destination = room
116
.iter()
117
.filter_map(|a| *a)
118
.all(|a| a as usize == room_index);
119
if all_amphipods_in_their_destination {
120
return None;
121
}
122
room
123
.iter()
124
.enumerate()
125
.filter_map(|(i, a)| a.map(|a| (i, a)))
126
.next()
127
}
128
129
fn can_move_to_hallway(&self, from: usize, to: usize) -> bool {
130
let is_in_from_of_room = [2, 4, 6, 8].contains(&to);
131
if is_in_from_of_room {
132
return false;
133
}
134
self.amphipods_in_hallway(from, to) == 0
135
}
136
137
fn can_move_to_room(&self, room_index: usize, hallway_from: usize) -> bool {
138
let room_has_amphipod_of_other_type = self.rooms[room_index]
139
.iter()
140
.filter_map(|a| *a)
141
.any(|a| a as usize != room_index);
142
if room_has_amphipod_of_other_type {
143
return false;
144
}
145
let hallway_to = room_index_to_hallway_index(room_index);
146
self.amphipods_in_hallway(hallway_from, hallway_to) == 1
147
}
148
149
fn amphipods_in_hallway(&self, from: usize, to: usize) -> usize {
150
(from.min(to)..=from.max(to))
151
.filter(|i| self.hallway[*i].is_some())
152
.count()
153
}
154
}
155
156
fn get_empty_room_slot(room: &[Option<Amphipod>]) -> usize {
157
for (i, a) in room.iter().enumerate().rev() {
158
if a.is_none() {
159
return i;
160
}
161
}
162
unreachable!();
163
}
164
165
fn room_index_to_hallway_index(room_index: usize) -> usize {
166
2 + room_index * 2
167
}
168
169
fn amphipod_energy(a: Amphipod) -> usize {
170
10_usize.pow(a as u32)
171
}
172
173
fn usize_diff(a: usize, b: usize) -> usize {
174
a.max(b) - a.min(b)
175
}
176
177