From b9a480c97aa597dbd345eef96de4097f4742ca0f Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Tue, 10 Dec 2024 00:21:01 +0100 Subject: [PATCH] feat: added day 9 part 1 and 2 Signed-off-by: Louis Vallat --- .gitea/workflows/build_and_run.yml | 2 +- day9/Cargo.toml | 6 ++ day9/src/main.rs | 118 +++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 day9/Cargo.toml create mode 100644 day9/src/main.rs diff --git a/.gitea/workflows/build_and_run.yml b/.gitea/workflows/build_and_run.yml index a70ba0c..97ca62c 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] + day_number: [1, 2, 3, 4, 5, 6, 7, 8, 9] runs-on: rust-bookworm steps: - name: Check out repository code diff --git a/day9/Cargo.toml b/day9/Cargo.toml new file mode 100644 index 0000000..48c42be --- /dev/null +++ b/day9/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day9" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day9/src/main.rs b/day9/src/main.rs new file mode 100644 index 0000000..4d070e6 --- /dev/null +++ b/day9/src/main.rs @@ -0,0 +1,118 @@ +use std::collections::{HashSet, LinkedList}; +use std::{env, fs}; + +fn read_input(path: &str) -> String { + fs::read_to_string(path).expect("Cannot read file.") +} + +fn parse_input(input: &str) -> LinkedList<(Option, usize)> { + let mut nodes = LinkedList::new(); + let mut is_file = true; + let mut id = 0; + + for x in input.chars().filter_map(|c| c.to_digit(10)) { + if x != 0 { + if is_file { + nodes.push_back((Some(id), x as usize)); + id += 1; + } else { + nodes.push_back((None, x as usize)); + } + } + is_file = !is_file; + } + + nodes +} + +fn checksum<'a, I>(nodes: I) -> usize +where + I: IntoIterator, usize)>, +{ + let mut index = 0; + let mut sum = 0; + for node in nodes { + if let Some(id) = node.0 { + sum += (node.1 * ((id * index) + (id * (index + node.1 - 1)))) / 2; + } + index += node.1; + } + sum +} + +fn part_1(disk_map: &LinkedList<(Option, usize)>) -> usize { + let mut disk_map = disk_map.clone(); + let mut defragmented = LinkedList::new(); + let mut free_len = 0; + while let Some(node) = disk_map.pop_front() { + if node.0.is_some() { + defragmented.push_back(node); + } else { + let to_place = node.1; + free_len += to_place; + while let Some(node) = disk_map.pop_back() { + if let Some(id) = node.0 { + if node.1 > to_place { + defragmented.push_back((Some(id), to_place)); + disk_map.push_back((Some(id), node.1 - to_place)); + } else { + if node.1 < to_place { + disk_map.push_front((None, to_place - node.1)); + free_len -= to_place - node.1; + } + defragmented.push_back(node); + } + break; + } else { + free_len += node.1; + } + } + } + } + + defragmented.push_back((None, free_len)); + checksum(&defragmented) +} + +fn part_2(disk_map: &LinkedList<(Option, usize)>) -> usize { + let mut disk_map = Vec::from_iter(disk_map.clone()); + let mut defragmented_ids = HashSet::new(); + let mut i = disk_map.len() - 1; + + while i > 0 { + let id = disk_map[i].0.unwrap_or_default(); + if disk_map[i].0.is_some() && !defragmented_ids.contains(&id) { + defragmented_ids.insert(id); + let needed_space = disk_map[i].1; + for j in 0..i { + if disk_map[j].0.is_none() { + let available_space = disk_map[j].1; + if available_space >= needed_space { + disk_map.swap(i, j); + if available_space > needed_space { + disk_map.insert(j + 1, (None, available_space - needed_space)); + disk_map[j].1 = needed_space; + disk_map[i + 1].1 = needed_space; + i += 1; + } + break; + } + } + } + } + i -= 1; + } + + checksum(&disk_map) +} + +fn main() { + let args: Vec = env::args().collect(); + for arg in args.iter().skip(1) { + let input = read_input(&arg); + let disk_map = parse_input(&input); + println!("[{}]", &arg); + println!("\t[Part 1] => Answer is '{}'.", part_1(&disk_map)); + println!("\t[Part 2] => Answer is '{}'.", part_2(&disk_map)); + } +}