From b5538fd3aacc1b9e6ed5e1d5b993d6e9330fdddc Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Sat, 14 Dec 2024 15:44:01 +0100 Subject: [PATCH] feat: added day 12 part 1 and 2 Signed-off-by: Louis Vallat --- .gitea/workflows/build_and_run.yml | 2 +- day12/Cargo.toml | 6 ++ day12/src/main.rs | 111 +++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 day12/Cargo.toml create mode 100644 day12/src/main.rs diff --git a/.gitea/workflows/build_and_run.yml b/.gitea/workflows/build_and_run.yml index db09ccd..43e3f1c 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, 6, 7, 8, 9, 10, 11] + day_number: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] runs-on: rust-bookworm steps: - name: Check out repository code diff --git a/day12/Cargo.toml b/day12/Cargo.toml new file mode 100644 index 0000000..be558ad --- /dev/null +++ b/day12/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day12" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day12/src/main.rs b/day12/src/main.rs new file mode 100644 index 0000000..0c1572a --- /dev/null +++ b/day12/src/main.rs @@ -0,0 +1,111 @@ +use std::collections::HashSet; +use std::{env, fs}; + +const DIRECTIONS: [(i32, i32); 4] = [(0, 1), (1, 0), (0, -1), (-1, 0)]; + +fn read_input(path: &str) -> String { + fs::read_to_string(path).expect("Cannot read file.") +} + +fn parse_input(input: &str) -> Vec> { + input + .lines() + .filter(|line| !line.is_empty()) + .map(|line| line.chars().collect()) + .collect() +} + +fn walk_garden( + map: &Vec>, + current: (i32, i32), + visited: &mut HashSet<(i32, i32)>, + borders: &mut HashSet<(i32, i32, usize)>, +) -> (usize, usize) { + visited.insert(current); + let mut perimeter = 0; + let mut area = 1; + for (i, direction) in DIRECTIONS.iter().enumerate() { + let target = (current.0 + direction.0, current.1 + direction.1); + if (0..map.len() as i32).contains(&target.0) + && (0..map[target.0 as usize].len() as i32).contains(&target.1) + { + if map[target.0 as usize][target.1 as usize] + == map[current.0 as usize][current.1 as usize] + { + if visited.contains(&target) { + continue; + } + let result = walk_garden(map, target, visited, borders); + area += result.0; + perimeter += result.1; + } else { + borders.insert((current.0, current.1, i)); + perimeter += 1; + } + } else { + borders.insert((current.0, current.1, i)); + perimeter += 1; + } + } + + (area, perimeter) +} + +fn count_sides(borders: &mut HashSet<(i32, i32, usize)>) -> usize { + let mut sides = 0; + while let Some((row, col, direction)) = borders.iter().last().cloned() { + sides += 1; + borders.remove(&(row, col, direction)); + for target_dir in [ + (direction + 1) % DIRECTIONS.len(), + (direction + DIRECTIONS.len() - 1) % DIRECTIONS.len(), + ] { + let mut target = (row, col); + loop { + target.0 += DIRECTIONS[target_dir].0; + target.1 += DIRECTIONS[target_dir].1; + if !borders.remove(&(target.0, target.1, direction)) { + break; + } + } + } + } + + sides +} + +fn compute_map_data(map: &Vec>) -> Vec<(usize, usize, usize)> { + let mut visited = HashSet::new(); + let mut data = Vec::new(); + for (row, line) in map.iter().enumerate() { + for (col, _) in line.iter().enumerate() { + if visited.contains(&(row as i32, col as i32)) { + continue; + } + let mut borders = HashSet::new(); + let (area, perimeter) = + walk_garden(map, (row as i32, col as i32), &mut visited, &mut borders); + data.push((area, perimeter, count_sides(&mut borders))); + } + } + + data +} + +fn main() { + let args: Vec = env::args().collect(); + for arg in args.iter().skip(1) { + let input = read_input(&arg); + let map = parse_input(&input); + let map_data = compute_map_data(&map); + println!("[{}]", &arg); + println!( + "\t[Part 1] => Answer is '{}'.", + map_data.iter().map(|data| data.0 * data.1).sum::() + ); + println!( + "\t[Part 2] => Answer is '{}'.", + map_data.iter().map(|data| data.0 * data.2).sum::() + ); + } +}