From f2ee9efbb4855a8ab4bf939896f5fb76da435465 Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Sat, 7 Dec 2024 23:47:05 +0100 Subject: [PATCH] feat: added day 6 part 1 and 2 Signed-off-by: Louis Vallat --- .gitea/workflows/build_and_run.yml | 2 +- day6/Cargo.toml | 6 ++ day6/src/main.rs | 140 +++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 day6/Cargo.toml create mode 100644 day6/src/main.rs diff --git a/.gitea/workflows/build_and_run.yml b/.gitea/workflows/build_and_run.yml index e0c67b5..b050cac 100644 --- a/.gitea/workflows/build_and_run.yml +++ b/.gitea/workflows/build_and_run.yml @@ -11,7 +11,7 @@ jobs: name: Challenge for day strategy: matrix: - day_number: [1, 2, 3, 4, 5] + day_number: [1, 2, 3, 4, 5, 6] runs-on: rust-bookworm steps: - name: Check out repository code diff --git a/day6/Cargo.toml b/day6/Cargo.toml new file mode 100644 index 0000000..46c0229 --- /dev/null +++ b/day6/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day6" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day6/src/main.rs b/day6/src/main.rs new file mode 100644 index 0000000..09e286f --- /dev/null +++ b/day6/src/main.rs @@ -0,0 +1,140 @@ +use std::collections::HashSet; +use std::{env, fs}; + +const DIRECTIONS: [(i32, i32); 4] = [(-1, 0), (0, 1), (1, 0), (0, -1)]; + +#[derive(Clone, Eq, PartialEq)] +enum Position { + Obstacle, + Visited(HashSet), + Free, +} + +fn read_input(path: &str) -> String { + fs::read_to_string(path).expect("Cannot read file.") +} + +fn parse_input(input: &str) -> (Vec>, (i32, i32)) { + let mut position = (0, 0); + let map = input + .lines() + .filter(|line| !line.is_empty()) + .enumerate() + .map(|(row, line)| { + line.chars() + .enumerate() + .map(|(col, chr)| match chr { + '#' => Position::Obstacle, + '.' => Position::Free, + '^' => { + position = (row as i32, col as i32); + Position::Visited(HashSet::from_iter(vec![0])) + } + _ => unreachable!("Invalid character in input"), + }) + .collect() + }) + .collect(); + + (map, position) +} + +fn is_in_map(map: &Vec>, pos: (i32, i32)) -> bool { + (0..map.len() as i32).contains(&pos.0) && (0..map[pos.0 as usize].len() as i32).contains(&pos.1) +} + +fn will_loop(map: &Vec>, mut pos: (i32, i32), mut current_direction: usize) -> bool { + let mut map = map.clone(); + loop { + let direction = DIRECTIONS[current_direction]; + let next_pos = (pos.0 + direction.0, pos.1 + direction.1); + + if !is_in_map(&map, next_pos) { + break; + } + + match &map[next_pos.0 as usize][next_pos.1 as usize] { + Position::Obstacle => { + current_direction = (current_direction + 1) % DIRECTIONS.len(); + } + Position::Free => { + pos = next_pos; + map[pos.0 as usize][pos.1 as usize] = + Position::Visited(HashSet::from_iter(vec![current_direction])); + } + Position::Visited(visits) => { + pos = next_pos; + + if visits.contains(¤t_direction) { + return true; + } + let mut visits = visits.clone(); + visits.insert(current_direction); + map[pos.0 as usize][pos.1 as usize] = Position::Visited(visits); + } + } + } + false +} + +fn walk_the_map(map: &Vec>, mut pos: (i32, i32), compute_obstacles: bool) -> u32 { + let mut map = map.clone(); + let mut current_direction = 0; + let mut visited = 1; + let mut obstacles = 0; + loop { + let direction = DIRECTIONS[current_direction]; + let next_pos = (pos.0 + direction.0, pos.1 + direction.1); + + if !is_in_map(&map, next_pos) { + break; + } + + match &map[next_pos.0 as usize][next_pos.1 as usize] { + Position::Obstacle => { + current_direction = (current_direction + 1) % DIRECTIONS.len(); + } + Position::Free => { + map[next_pos.0 as usize][next_pos.1 as usize] = Position::Obstacle; + if compute_obstacles && will_loop(&map, pos, current_direction) { + obstacles += 1; + } + pos = next_pos; + map[pos.0 as usize][pos.1 as usize] = + Position::Visited(HashSet::from_iter(vec![current_direction])); + visited += 1; + } + Position::Visited(visits) => { + pos = next_pos; + let mut visits = visits.clone(); + visits.insert(current_direction); + map[pos.0 as usize][pos.1 as usize] = Position::Visited(visits); + } + } + } + + if compute_obstacles { + obstacles + } else { + visited + } +} + +fn part_1(map: &Vec>, pos: (i32, i32)) -> u32 { + walk_the_map(&map, pos, false) +} + +fn part_2(map: &Vec>, pos: (i32, i32)) -> u32 { + walk_the_map(&map, pos, true) +} + +fn main() { + let args: Vec = env::args().collect(); + for arg in args.iter().skip(1) { + let input = read_input(&arg); + let (map, start) = parse_input(&input); + println!("[{}]", &arg); + println!("\t[Part 1] => Answer is '{}'.", part_1(&map, start)); + println!("\t[Part 2] => Answer is '{}'.", part_2(&map, start)); + } +}