tangled
alpha
login
or
join now
quilling.dev
/
advent-of-code
2
fork
atom
programming puzzle solutions
2
fork
atom
overview
issues
pulls
pipelines
add 2024 day6 in rust
quilling.dev
1 year ago
677ac73e
1b6cd3f1
+290
2 changed files
expand all
collapse all
unified
split
Cargo.toml
src
2024
day6
rust
mod.rs
+4
Cargo.toml
reviewed
···
31
31
name = "2024_day5"
32
32
path = "src/2024/day5/rust/mod.rs"
33
33
34
34
+
[[bin]]
35
35
+
name = "2024_day6"
36
36
+
path = "src/2024/day6/rust/mod.rs"
37
37
+
34
38
[lints.rust]
35
39
unsafe_code = "forbid"
36
40
+286
src/2024/day6/rust/mod.rs
reviewed
···
1
1
+
fn process_input(input: &str) -> Vec<Vec<i32>> {
2
2
+
input
3
3
+
.lines()
4
4
+
.map(|line| {
5
5
+
line.chars()
6
6
+
.map(|c| match c {
7
7
+
'.' => 0,
8
8
+
'#' => 1,
9
9
+
'X' => 2,
10
10
+
'^' => 3,
11
11
+
_ => panic!("Unexpected character in input"),
12
12
+
})
13
13
+
.collect::<Vec<i32>>()
14
14
+
})
15
15
+
.collect::<Vec<Vec<i32>>>()
16
16
+
}
17
17
+
18
18
+
fn wheel_direction(direction: i32) -> i32 {
19
19
+
match direction {
20
20
+
3 => 4,
21
21
+
4 => 5,
22
22
+
5 => 6,
23
23
+
6 => 3,
24
24
+
_ => panic!("Unexpected direction"),
25
25
+
}
26
26
+
}
27
27
+
28
28
+
fn moving_into_boundaries(x: usize, y: usize, map_state: &mut [Vec<i32>], guard: i32) -> bool {
29
29
+
if (x == 0 && guard == 6)
30
30
+
|| (y == 0 && guard == 3)
31
31
+
|| (x == map_state[0].len() - 1 && guard == 4)
32
32
+
|| (y == map_state.len() - 1 && guard == 5)
33
33
+
{
34
34
+
map_state[y][x] = 2;
35
35
+
true
36
36
+
} else {
37
37
+
false
38
38
+
}
39
39
+
}
40
40
+
41
41
+
pub fn part_a(input: &str) -> i32 {
42
42
+
let mut map = process_input(input);
43
43
+
44
44
+
fn move_guard(map_state: &mut Vec<Vec<i32>>, x: usize, y: usize) -> i32 {
45
45
+
let guard = map_state[y][x];
46
46
+
let direction_wheel = [3, 4, 5, 6];
47
47
+
let direction_index = direction_wheel
48
48
+
.iter()
49
49
+
.position(|&x| x == guard)
50
50
+
.expect("guard should face valid direction");
51
51
+
let direction_wheel_vectors: Vec<(i32, i32)> = vec![(0, -1), (1, 0), (0, 1), (-1, 0)];
52
52
+
53
53
+
if moving_into_boundaries(x, y, map_state, guard) {
54
54
+
return 0;
55
55
+
}
56
56
+
57
57
+
let new_y: usize = (y as i32 + direction_wheel_vectors[direction_index].1)
58
58
+
.try_into()
59
59
+
.expect("y should be valid");
60
60
+
let new_x: usize = (x as i32 + direction_wheel_vectors[direction_index].0)
61
61
+
.try_into()
62
62
+
.expect("x should be valid");
63
63
+
64
64
+
match map_state[new_y][new_x] {
65
65
+
0 => {
66
66
+
map_state[y][x] = 2;
67
67
+
map_state[new_y][new_x] = guard;
68
68
+
1
69
69
+
}
70
70
+
1 => {
71
71
+
map_state[y][x] = wheel_direction(map_state[y][x]);
72
72
+
move_guard(map_state, x, y)
73
73
+
}
74
74
+
2 => {
75
75
+
map_state[y][x] = 2;
76
76
+
map_state[new_y][new_x] = guard;
77
77
+
0
78
78
+
}
79
79
+
_ => panic!("Unexpected character in input"),
80
80
+
}
81
81
+
}
82
82
+
83
83
+
let mut toggled_tile_count = map
84
84
+
.iter()
85
85
+
.flat_map(|row| row.iter())
86
86
+
.filter(|&&tile| (3..=6).contains(&tile))
87
87
+
.count() as i32;
88
88
+
89
89
+
let mut found_guard = true;
90
90
+
while found_guard {
91
91
+
found_guard = false;
92
92
+
for y in 0..map.len() {
93
93
+
for x in 0..map[y].len() {
94
94
+
if let 3..=6 = map[y][x] {
95
95
+
found_guard = true;
96
96
+
toggled_tile_count += move_guard(&mut map, x, y);
97
97
+
}
98
98
+
}
99
99
+
}
100
100
+
}
101
101
+
toggled_tile_count
102
102
+
}
103
103
+
104
104
+
pub fn part_b(input: &str) -> i32 {
105
105
+
let mut possible_loops = 0;
106
106
+
let mut map = process_input(input);
107
107
+
108
108
+
fn check_guard_loop(
109
109
+
map_state: &mut Vec<Vec<i32>>,
110
110
+
x: usize,
111
111
+
y: usize,
112
112
+
origin_x: usize,
113
113
+
origin_y: usize,
114
114
+
origin_guard: i32,
115
115
+
mut recursion_depth: i32,
116
116
+
) -> bool {
117
117
+
if recursion_depth > 10000 {
118
118
+
return true;
119
119
+
}
120
120
+
recursion_depth += 1;
121
121
+
let guard = map_state[y][x];
122
122
+
let direction_wheel = [3, 4, 5, 6];
123
123
+
let direction_index = direction_wheel
124
124
+
.iter()
125
125
+
.position(|&x| x == guard)
126
126
+
.expect("guard should face valid direction");
127
127
+
let direction_wheel_vectors: Vec<(i32, i32)> = vec![(0, -1), (1, 0), (0, 1), (-1, 0)];
128
128
+
if moving_into_boundaries(x, y, map_state, guard) {
129
129
+
return false;
130
130
+
}
131
131
+
let new_y: usize = (y as i32 + direction_wheel_vectors[direction_index].1)
132
132
+
.try_into()
133
133
+
.expect("y should be valid");
134
134
+
let new_x: usize = (x as i32 + direction_wheel_vectors[direction_index].0)
135
135
+
.try_into()
136
136
+
.expect("x should be valid");
137
137
+
138
138
+
match map_state[new_y][new_x] {
139
139
+
0 => {
140
140
+
map_state[y][x] = 2;
141
141
+
map_state[new_y][new_x] = guard;
142
142
+
check_guard_loop(
143
143
+
map_state,
144
144
+
new_x,
145
145
+
new_y,
146
146
+
origin_x,
147
147
+
origin_y,
148
148
+
origin_guard,
149
149
+
recursion_depth,
150
150
+
)
151
151
+
}
152
152
+
1 => {
153
153
+
map_state[y][x] = wheel_direction(map_state[y][x]);
154
154
+
check_guard_loop(
155
155
+
map_state,
156
156
+
x,
157
157
+
y,
158
158
+
origin_x,
159
159
+
origin_y,
160
160
+
origin_guard,
161
161
+
recursion_depth,
162
162
+
)
163
163
+
}
164
164
+
2 => {
165
165
+
map_state[y][x] = 2;
166
166
+
map_state[new_y][new_x] = guard;
167
167
+
if new_x == origin_x && new_y == origin_y && guard == origin_guard {
168
168
+
return true;
169
169
+
}
170
170
+
check_guard_loop(
171
171
+
map_state,
172
172
+
new_x,
173
173
+
new_y,
174
174
+
origin_x,
175
175
+
origin_y,
176
176
+
origin_guard,
177
177
+
recursion_depth,
178
178
+
)
179
179
+
}
180
180
+
_ => panic!("Unexpected character in input"),
181
181
+
}
182
182
+
}
183
183
+
184
184
+
fn move_guard(map_state: &mut Vec<Vec<i32>>, x: usize, y: usize) -> i32 {
185
185
+
let guard = map_state[y][x];
186
186
+
let direction_wheel = [3, 4, 5, 6];
187
187
+
let direction_index = direction_wheel
188
188
+
.iter()
189
189
+
.position(|&x| x == guard)
190
190
+
.expect("guard should face valid direction");
191
191
+
let direction_wheel_vectors: Vec<(i32, i32)> = vec![(0, -1), (1, 0), (0, 1), (-1, 0)];
192
192
+
193
193
+
if (x == 0 && guard == 6)
194
194
+
|| (y == 0 && guard == 3)
195
195
+
|| (x == map_state[0].len() - 1 && guard == 4)
196
196
+
|| (y == map_state.len() - 1 && guard == 5)
197
197
+
{
198
198
+
map_state[y][x] = 2;
199
199
+
return 0;
200
200
+
}
201
201
+
202
202
+
let new_y: usize = (y as i32 + direction_wheel_vectors[direction_index].1)
203
203
+
.try_into()
204
204
+
.expect("y should be valid");
205
205
+
let new_x: usize = (x as i32 + direction_wheel_vectors[direction_index].0)
206
206
+
.try_into()
207
207
+
.expect("x should be valid");
208
208
+
209
209
+
match map_state[new_y][new_x] {
210
210
+
0 => {
211
211
+
let mut map_state_clone = map_state.clone();
212
212
+
map_state_clone[new_y][new_x] = 1;
213
213
+
map_state_clone[y][x] = wheel_direction(map_state[y][x]);
214
214
+
let possible_loop = check_guard_loop(&mut map_state_clone, x, y, x, y, guard, 0);
215
215
+
map_state[y][x] = 2;
216
216
+
map_state[new_y][new_x] = guard;
217
217
+
if possible_loop {
218
218
+
1
219
219
+
} else {
220
220
+
0
221
221
+
}
222
222
+
}
223
223
+
1 => {
224
224
+
map_state[y][x] = wheel_direction(map_state[y][x]);
225
225
+
move_guard(map_state, x, y)
226
226
+
}
227
227
+
2 => {
228
228
+
map_state[y][x] = 2;
229
229
+
map_state[new_y][new_x] = guard;
230
230
+
0
231
231
+
}
232
232
+
_ => panic!("Unexpected character in input"),
233
233
+
}
234
234
+
}
235
235
+
236
236
+
let mut found_guard = true;
237
237
+
while found_guard {
238
238
+
found_guard = false;
239
239
+
for y in 0..map.len() {
240
240
+
for x in 0..map[y].len() {
241
241
+
if let 3..=6 = map[y][x] {
242
242
+
found_guard = true;
243
243
+
possible_loops += move_guard(&mut map, x, y);
244
244
+
}
245
245
+
}
246
246
+
}
247
247
+
}
248
248
+
possible_loops
249
249
+
}
250
250
+
251
251
+
pub fn main() {
252
252
+
let input = include_str!("../input.txt");
253
253
+
let example_input = include_str!("../input_example.txt");
254
254
+
// let example_input_b = include_str!("../input_example_b.txt");
255
255
+
let day: u8 = 6;
256
256
+
println!("Day {} Example Part A: {}", day, part_a(example_input));
257
257
+
println!("Day {} Part A: {}", day, part_a(input));
258
258
+
println!("Day {} Example Part B: {}", day, part_b(example_input));
259
259
+
println!("Day {} Part B: {}", day, part_b(input));
260
260
+
}
261
261
+
262
262
+
#[cfg(test)]
263
263
+
mod tests {
264
264
+
use super::*;
265
265
+
#[test]
266
266
+
fn test_part_a_example() {
267
267
+
let example_input = include_str!("../input_example.txt");
268
268
+
assert_eq!(part_a(example_input), 41);
269
269
+
}
270
270
+
#[test]
271
271
+
fn test_part_a() {
272
272
+
let input = include_str!("../input.txt");
273
273
+
assert_eq!(part_a(input), 4656);
274
274
+
}
275
275
+
#[test]
276
276
+
fn test_part_b_example() {
277
277
+
let example_input_b = include_str!("../input_example.txt");
278
278
+
assert_eq!(part_b(example_input_b), 6);
279
279
+
}
280
280
+
// Rust test threads have a smaller stack size than the main thread, so this test will fail with a stack overflow.
281
281
+
// #[test]
282
282
+
// fn test_part_b() {
283
283
+
// let input = include_str!("../input.txt");
284
284
+
// assert_eq!(part_b(input), 1575);
285
285
+
// }
286
286
+
}