From 677ac73e3d238300ff8fcdc5383811902d7f85e2 Mon Sep 17 00:00:00 2001 From: Timothy Quilling Date: Fri, 6 Dec 2024 18:36:26 -0500 Subject: [PATCH] add 2024 day6 in rust --- Cargo.toml | 4 + src/2024/day6/rust/mod.rs | 286 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 src/2024/day6/rust/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 44b9289..d29df06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,10 @@ path = "src/2024/day4/rust/mod.rs" name = "2024_day5" path = "src/2024/day5/rust/mod.rs" +[[bin]] +name = "2024_day6" +path = "src/2024/day6/rust/mod.rs" + [lints.rust] unsafe_code = "forbid" diff --git a/src/2024/day6/rust/mod.rs b/src/2024/day6/rust/mod.rs new file mode 100644 index 0000000..2634408 --- /dev/null +++ b/src/2024/day6/rust/mod.rs @@ -0,0 +1,286 @@ +fn process_input(input: &str) -> Vec> { + input + .lines() + .map(|line| { + line.chars() + .map(|c| match c { + '.' => 0, + '#' => 1, + 'X' => 2, + '^' => 3, + _ => panic!("Unexpected character in input"), + }) + .collect::>() + }) + .collect::>>() +} + +fn wheel_direction(direction: i32) -> i32 { + match direction { + 3 => 4, + 4 => 5, + 5 => 6, + 6 => 3, + _ => panic!("Unexpected direction"), + } +} + +fn moving_into_boundaries(x: usize, y: usize, map_state: &mut [Vec], guard: i32) -> bool { + if (x == 0 && guard == 6) + || (y == 0 && guard == 3) + || (x == map_state[0].len() - 1 && guard == 4) + || (y == map_state.len() - 1 && guard == 5) + { + map_state[y][x] = 2; + true + } else { + false + } +} + +pub fn part_a(input: &str) -> i32 { + let mut map = process_input(input); + + fn move_guard(map_state: &mut Vec>, x: usize, y: usize) -> i32 { + let guard = map_state[y][x]; + let direction_wheel = [3, 4, 5, 6]; + let direction_index = direction_wheel + .iter() + .position(|&x| x == guard) + .expect("guard should face valid direction"); + let direction_wheel_vectors: Vec<(i32, i32)> = vec![(0, -1), (1, 0), (0, 1), (-1, 0)]; + + if moving_into_boundaries(x, y, map_state, guard) { + return 0; + } + + let new_y: usize = (y as i32 + direction_wheel_vectors[direction_index].1) + .try_into() + .expect("y should be valid"); + let new_x: usize = (x as i32 + direction_wheel_vectors[direction_index].0) + .try_into() + .expect("x should be valid"); + + match map_state[new_y][new_x] { + 0 => { + map_state[y][x] = 2; + map_state[new_y][new_x] = guard; + 1 + } + 1 => { + map_state[y][x] = wheel_direction(map_state[y][x]); + move_guard(map_state, x, y) + } + 2 => { + map_state[y][x] = 2; + map_state[new_y][new_x] = guard; + 0 + } + _ => panic!("Unexpected character in input"), + } + } + + let mut toggled_tile_count = map + .iter() + .flat_map(|row| row.iter()) + .filter(|&&tile| (3..=6).contains(&tile)) + .count() as i32; + + let mut found_guard = true; + while found_guard { + found_guard = false; + for y in 0..map.len() { + for x in 0..map[y].len() { + if let 3..=6 = map[y][x] { + found_guard = true; + toggled_tile_count += move_guard(&mut map, x, y); + } + } + } + } + toggled_tile_count +} + +pub fn part_b(input: &str) -> i32 { + let mut possible_loops = 0; + let mut map = process_input(input); + + fn check_guard_loop( + map_state: &mut Vec>, + x: usize, + y: usize, + origin_x: usize, + origin_y: usize, + origin_guard: i32, + mut recursion_depth: i32, + ) -> bool { + if recursion_depth > 10000 { + return true; + } + recursion_depth += 1; + let guard = map_state[y][x]; + let direction_wheel = [3, 4, 5, 6]; + let direction_index = direction_wheel + .iter() + .position(|&x| x == guard) + .expect("guard should face valid direction"); + let direction_wheel_vectors: Vec<(i32, i32)> = vec![(0, -1), (1, 0), (0, 1), (-1, 0)]; + if moving_into_boundaries(x, y, map_state, guard) { + return false; + } + let new_y: usize = (y as i32 + direction_wheel_vectors[direction_index].1) + .try_into() + .expect("y should be valid"); + let new_x: usize = (x as i32 + direction_wheel_vectors[direction_index].0) + .try_into() + .expect("x should be valid"); + + match map_state[new_y][new_x] { + 0 => { + map_state[y][x] = 2; + map_state[new_y][new_x] = guard; + check_guard_loop( + map_state, + new_x, + new_y, + origin_x, + origin_y, + origin_guard, + recursion_depth, + ) + } + 1 => { + map_state[y][x] = wheel_direction(map_state[y][x]); + check_guard_loop( + map_state, + x, + y, + origin_x, + origin_y, + origin_guard, + recursion_depth, + ) + } + 2 => { + map_state[y][x] = 2; + map_state[new_y][new_x] = guard; + if new_x == origin_x && new_y == origin_y && guard == origin_guard { + return true; + } + check_guard_loop( + map_state, + new_x, + new_y, + origin_x, + origin_y, + origin_guard, + recursion_depth, + ) + } + _ => panic!("Unexpected character in input"), + } + } + + fn move_guard(map_state: &mut Vec>, x: usize, y: usize) -> i32 { + let guard = map_state[y][x]; + let direction_wheel = [3, 4, 5, 6]; + let direction_index = direction_wheel + .iter() + .position(|&x| x == guard) + .expect("guard should face valid direction"); + let direction_wheel_vectors: Vec<(i32, i32)> = vec![(0, -1), (1, 0), (0, 1), (-1, 0)]; + + if (x == 0 && guard == 6) + || (y == 0 && guard == 3) + || (x == map_state[0].len() - 1 && guard == 4) + || (y == map_state.len() - 1 && guard == 5) + { + map_state[y][x] = 2; + return 0; + } + + let new_y: usize = (y as i32 + direction_wheel_vectors[direction_index].1) + .try_into() + .expect("y should be valid"); + let new_x: usize = (x as i32 + direction_wheel_vectors[direction_index].0) + .try_into() + .expect("x should be valid"); + + match map_state[new_y][new_x] { + 0 => { + let mut map_state_clone = map_state.clone(); + map_state_clone[new_y][new_x] = 1; + map_state_clone[y][x] = wheel_direction(map_state[y][x]); + let possible_loop = check_guard_loop(&mut map_state_clone, x, y, x, y, guard, 0); + map_state[y][x] = 2; + map_state[new_y][new_x] = guard; + if possible_loop { + 1 + } else { + 0 + } + } + 1 => { + map_state[y][x] = wheel_direction(map_state[y][x]); + move_guard(map_state, x, y) + } + 2 => { + map_state[y][x] = 2; + map_state[new_y][new_x] = guard; + 0 + } + _ => panic!("Unexpected character in input"), + } + } + + let mut found_guard = true; + while found_guard { + found_guard = false; + for y in 0..map.len() { + for x in 0..map[y].len() { + if let 3..=6 = map[y][x] { + found_guard = true; + possible_loops += move_guard(&mut map, x, y); + } + } + } + } + possible_loops +} + +pub fn main() { + let input = include_str!("../input.txt"); + let example_input = include_str!("../input_example.txt"); + // let example_input_b = include_str!("../input_example_b.txt"); + let day: u8 = 6; + println!("Day {} Example Part A: {}", day, part_a(example_input)); + println!("Day {} Part A: {}", day, part_a(input)); + println!("Day {} Example Part B: {}", day, part_b(example_input)); + println!("Day {} Part B: {}", day, part_b(input)); +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_part_a_example() { + let example_input = include_str!("../input_example.txt"); + assert_eq!(part_a(example_input), 41); + } + #[test] + fn test_part_a() { + let input = include_str!("../input.txt"); + assert_eq!(part_a(input), 4656); + } + #[test] + fn test_part_b_example() { + let example_input_b = include_str!("../input_example.txt"); + assert_eq!(part_b(example_input_b), 6); + } + // Rust test threads have a smaller stack size than the main thread, so this test will fail with a stack overflow. + // #[test] + // fn test_part_b() { + // let input = include_str!("../input.txt"); + // assert_eq!(part_b(input), 1575); + // } +} -- 2.51.2