From d03ed5fca14fcd53d940cb79d0d881854c5d4440 Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Fri, 6 Dec 2024 01:31:44 +0100 Subject: [PATCH] feat: added day 5 part 1 and 2 Signed-off-by: Louis Vallat --- .gitea/workflows/build_and_run.yml | 2 +- day5/Cargo.toml | 6 ++ day5/src/main.rs | 114 +++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 day5/Cargo.toml create mode 100644 day5/src/main.rs diff --git a/.gitea/workflows/build_and_run.yml b/.gitea/workflows/build_and_run.yml index dd96060..e0c67b5 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] + day_number: [1, 2, 3, 4, 5] runs-on: rust-bookworm steps: - name: Check out repository code diff --git a/day5/Cargo.toml b/day5/Cargo.toml new file mode 100644 index 0000000..49bdb49 --- /dev/null +++ b/day5/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day5" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day5/src/main.rs b/day5/src/main.rs new file mode 100644 index 0000000..5015bef --- /dev/null +++ b/day5/src/main.rs @@ -0,0 +1,114 @@ +use std::collections::{HashMap, HashSet}; +use std::{env, fs}; + +fn read_input(path: &str) -> String { + fs::read_to_string(path).expect("Cannot read file.") +} + +fn parse_input(input: &str) -> (HashMap>, Vec>) { + let mut rules: HashMap> = HashMap::new(); + let mut updates = vec![]; + let mut lines = input.lines(); + + while let Some(line) = lines.next() { + if line.is_empty() { + break; + } + + let rule = line + .split_once("|") + .map(|l| (l.0.parse().unwrap(), l.1.parse().unwrap())) + .unwrap(); + if rules.contains_key(&rule.0) { + rules.get_mut(&rule.0).unwrap().insert(rule.1); + } else { + rules.insert(rule.0, HashSet::from_iter(vec![rule.1])); + } + } + + while let Some(line) = lines.next() { + if line.is_empty() { + break; + } + updates.push(line.split(",").map(|l| l.parse().unwrap()).collect()); + } + + (rules, updates) +} + +fn middle_if_complies(rules: &HashMap>, update: &Vec) -> Option { + let mut already_met = HashSet::new(); + for x in update { + if let Some(rules_for_x) = rules.get(&x) { + for must_be_after in rules_for_x { + if already_met.contains(must_be_after) { + return None; + } + } + } + already_met.insert(x); + } + Some(update[update.len() / 2]) +} + +fn part_1(rules: &HashMap>, updates: &Vec>) -> u32 { + updates + .iter() + .filter_map(|update| middle_if_complies(rules, update)) + .sum() +} + +fn middle_if_reorders(rules: &HashMap>, update: &Vec) -> Option { + let mut update = update.clone(); + let mut already_met = HashMap::new(); + let (mut pos, mut has_been_reordered) = (0, false); + while pos < update.len() { + let x = update[pos]; + let mut lowest_pos = None; + if let Some(rule) = rules.get(&x) { + for must_be_after in rule { + if already_met.contains_key(must_be_after) { + let old_pos = *already_met.get(must_be_after).unwrap(); + if lowest_pos.is_none() + || (lowest_pos.is_some() && lowest_pos.unwrap() > old_pos) + { + lowest_pos = Some(old_pos); + } + } + } + if let Some(lowest_pos) = lowest_pos { + update.swap(lowest_pos, pos); + already_met.insert(x, lowest_pos); + has_been_reordered = true; + } + } + if lowest_pos.is_none() { + already_met.insert(x, pos); + pos += 1; + } + } + + if has_been_reordered { + Some(update[update.len() / 2]) + } else { + None + } +} + +fn part_2(rules: &HashMap>, updates: &Vec>) -> u32 { + updates + .iter() + .filter_map(|update| middle_if_reorders(rules, update)) + .sum() +} + +fn main() { + let args: Vec = env::args().collect(); + for arg in args.iter().skip(1) { + let input = read_input(&arg); + let (rules, updates) = parse_input(&input); + println!("[{}]", &arg); + println!("\t[Part 1] => Answer is '{}'.", part_1(&rules, &updates)); + println!("\t[Part 2] => Answer is '{}'.", part_2(&rules, &updates)); + } +}