diff --git a/2024/day6/.gitignore b/2024/day6/.gitignore new file mode 100644 index 0000000..2ac37a0 --- /dev/null +++ b/2024/day6/.gitignore @@ -0,0 +1,2 @@ +target +input.txt diff --git a/2024/day6/Cargo.lock b/2024/day6/Cargo.lock new file mode 100644 index 0000000..a16c0bf --- /dev/null +++ b/2024/day6/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "day6" +version = "0.1.0" diff --git a/2024/day6/Cargo.toml b/2024/day6/Cargo.toml new file mode 100644 index 0000000..46c0229 --- /dev/null +++ b/2024/day6/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day6" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/2024/day6/src/main.rs b/2024/day6/src/main.rs new file mode 100644 index 0000000..c7ab69b --- /dev/null +++ b/2024/day6/src/main.rs @@ -0,0 +1,150 @@ +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::time::Instant; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct Point { + row: i32, + col: i32, +} + +impl Point { + fn new(row: i32, col: i32) -> Self { + Self { row, col } + } + + fn from(point: &Point, dir: &Point) -> Self { + Self { + row: point.row + dir.row, + col: point.col + dir.col, + } + } + + fn is_in_map(&self, map: &[&[u8]]) -> bool { + !(self.col >= map[0].len() as i32 + || self.row >= map.len() as i32 + || self.col < 0 + || self.row < 0) + } + + fn is_in_map_str(&self, map: &[String]) -> bool { + !(self.col >= map[0].len() as i32 + || self.row >= map.len() as i32 + || self.col < 0 + || self.row < 0) + } +} + +const DIRECTION: [Point; 4] = [ + Point { row: 1, col: 0 }, + Point { row: 0, col: -1 }, + Point { row: -1, col: 0 }, + Point { row: 0, col: 1 }, +]; + +fn main() { + let file = fs::read_to_string("./input.txt").unwrap(); + + let mut map: Vec<&[u8]> = Vec::new(); + let mut map2: Vec = Vec::new(); + let mut start = Point::new(0, 0); + + for (line_idx, line) in file.lines().enumerate() { + map.push(line.as_bytes()); + map2.push(line.to_string()); + for (idx, c) in line.chars().enumerate() { + if c == '^' { + start.row = line_idx as i32; + start.col = idx as i32; + } + } + } + + let start_time = Instant::now(); + println!("Solution 1: {}", puzzle1(map.clone(), start)); + let end = Instant::now() - start_time; + println!("Solution 1 time: {}", end.as_micros()); + + let start_time = Instant::now(); + println!("Solution 2: {}", puzzle2(map2, start)); + let end = Instant::now() - start_time; + println!("Solution 2 time: {}", end.as_micros()); +} + +fn puzzle1(map: Vec<&[u8]>, start: Point) -> usize { + let mut visited = HashSet::new(); + visited.insert(start); + let mut current = start; + let mut dir = 2; + + loop { + let next = Point::from(¤t, &DIRECTION[dir]); + if !next.is_in_map(&map) { + break; + } + if map[next.row as usize][next.col as usize] != b'#' { + visited.insert(next); + current = next; + } else { + dir = (dir + 1) % 4; + } + } + visited.len() +} + +fn puzzle2(map: Vec, start: Point) -> usize { + let mut map = map; + let visited = HashMap::new(); + let mut positions = HashSet::new(); + puzzle2_rec(start, 2, &mut map, visited, &mut positions, false); + + positions.len() +} + +fn puzzle2_rec( + mut current: Point, + mut dir: usize, + map: &mut Vec, + mut visited: HashMap, + positions: &mut HashSet, + placed: bool, +) -> bool { + while current.is_in_map_str(map) { + visited + .entry(current) + .or_insert([false, false, false, false]); + if visited[¤t][dir] && placed { + return true; + } + let next = Point::from(¤t, &DIRECTION[dir]); + if !next.is_in_map_str(map) { + return false; + } + if map[next.row as usize].as_bytes()[next.col as usize] == b'#' { + dir = (dir + 1) % 4; + } else { + if !placed && !visited.contains_key(&next) { + let c = map[next.row as usize] + .chars() + .nth(next.col as usize) + .unwrap(); + map[next.row as usize].replace_range(next.col as usize..=next.col as usize, "#"); + if puzzle2_rec( + current, + (dir + 1) % 4, + map, + visited.clone(), + positions, + true, + ) { + positions.insert(next); + } + map[next.row as usize] + .replace_range(next.col as usize..=next.col as usize, &c.to_string()); + } + visited.get_mut(¤t).unwrap()[dir] = true; + current = next; + } + } + false +}