programming puzzle solutions

add 2024 day6 in rust

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