From acbb0123ac43786604cbbaeb6be63192c0616935 Mon Sep 17 00:00:00 2001 From: Bram Date: Fri, 13 Dec 2024 16:34:24 +0000 Subject: [PATCH] Refactor day 4 & 5 --- src/day_01/part_1.rs | 26 ----------- src/day_01/part_2.rs | 18 -------- src/day_02/part_1.rs | 26 ----------- src/day_02/part_2.rs | 43 ------------------ src/day_03/part_1.rs | 12 ----- src/day_03/part_2.rs | 30 ------------- src/day_04/mod.rs | 89 ++++++++++++++++++++++++++++++++++++- src/day_04/part_1.rs | 47 -------------------- src/day_04/part_2.rs | 36 --------------- src/day_05/mod.rs | 95 +++++++++++++++++++++++++++++++++++++++- src/day_05/part_1.rs | 55 ----------------------- src/day_05/part_2.rs | 72 ------------------------------ src/lib.rs | 16 +++++++ src/main.rs | 2 + src/utils/diagnostics.rs | 4 +- 15 files changed, 200 insertions(+), 371 deletions(-) delete mode 100644 src/day_01/part_1.rs delete mode 100644 src/day_01/part_2.rs delete mode 100644 src/day_02/part_1.rs delete mode 100644 src/day_02/part_2.rs delete mode 100644 src/day_03/part_1.rs delete mode 100644 src/day_03/part_2.rs delete mode 100644 src/day_04/part_1.rs delete mode 100644 src/day_04/part_2.rs delete mode 100644 src/day_05/part_1.rs delete mode 100644 src/day_05/part_2.rs diff --git a/src/day_01/part_1.rs b/src/day_01/part_1.rs deleted file mode 100644 index b33bc58..0000000 --- a/src/day_01/part_1.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::utils; - -pub fn answer(lines : &str) -> u32 { - let ( mut left, mut right ) : ( Vec, Vec ) = lines - .split("\n") - .filter_map(parse_line) - .unzip(); - - left.sort_unstable(); - right.sort_unstable(); - - left.iter() - .zip(right.iter()) - .map(|(x, y)| abs_diff(&x, &y)) - .sum() -} - -fn abs_diff(l : &u32, r : &u32) -> u32 { - if l >= r { l - r } else { r - l } -} - -fn parse_line(s : &str) -> Option<( u32, u32 )> { - let (left, right) = s.trim().split_once(" ")?; - - Some(( utils::str_to_u32(left)?, utils::str_to_u32(right)? )) -} diff --git a/src/day_01/part_2.rs b/src/day_01/part_2.rs deleted file mode 100644 index e723da1..0000000 --- a/src/day_01/part_2.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::utils; - -pub fn answer(s : &str) -> u32 { - let ( left, right ) : ( Vec, Vec ) = s - .split("\n") - .filter_map(parse_line) - .unzip(); - - left.iter() - .map(|x| right.iter().filter(|y| x == *y).count() as u32 * x) - .sum() -} - -fn parse_line(s : &str) -> Option<( u32, u32 )> { - let (left, right) = s.trim().split_once(" ")?; - - Some(( utils::str_to_u32(left)?, utils::str_to_u32(right)? )) -} diff --git a/src/day_02/part_1.rs b/src/day_02/part_1.rs deleted file mode 100644 index 3731009..0000000 --- a/src/day_02/part_1.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::utils; - -pub fn answer(text : &str) -> usize { - text.split("\n") - .map(parse_line) - .filter(is_safe_line) - .count() -} - -fn is_safe_line(v : &Vec) -> bool { - let diff : Vec = v - .windows(2) - .filter_map(|v| Some(v.get(0)? - v.get(1)?)) - .collect(); - - let all_increasing : bool = diff.iter().all(|&x| x < 0); - let all_decreasing : bool = diff.iter().all(|&x| x > 0); - let all_bounded_ok : bool = diff.iter().all(|&x| x.abs() <= 3); - - all_bounded_ok && (all_increasing || all_decreasing) -} - -fn parse_line(s : &str) -> Vec { - s.split(" ").filter_map(utils::str_to_i32).collect() -} - diff --git a/src/day_02/part_2.rs b/src/day_02/part_2.rs deleted file mode 100644 index 830b6bb..0000000 --- a/src/day_02/part_2.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::utils; - -pub fn answer(text : &str) -> usize { - text.split("\n") - .map(parse_line) - .filter(is_dampened_safe_line) - .count() -} - -fn is_dampened_safe_line(v : &Vec) -> bool { - if is_safe_line(v) { - return true; - } - - for i in 0..v.len() { - let mut cv = v.to_vec(); - cv.remove(i); - - if is_safe_line(&cv) { - return true; - } - } - - false -} - -fn is_safe_line(v : &Vec) -> bool { - let diff : Vec = v - .windows(2) - .filter_map(|v| Some(v.get(0)? - v.get(1)?)) - .collect(); - - let all_increasing : bool = diff.iter().all(|&x| x < 0); - let all_decreasing : bool = diff.iter().all(|&x| x > 0); - let all_bounded_ok : bool = diff.iter().all(|&x| x.abs() <= 3); - - all_bounded_ok && (all_increasing || all_decreasing) -} - -fn parse_line(s : &str) -> Vec { - s.split(" ").filter_map(utils::str_to_i32).collect() -} - diff --git a/src/day_03/part_1.rs b/src/day_03/part_1.rs deleted file mode 100644 index 5470c95..0000000 --- a/src/day_03/part_1.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::utils; -use regex::Regex; - -pub fn answer(text : &str) -> u32 { - Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)") - .unwrap() - .captures_iter(text) - .map(|c| c.extract()) - .filter_map(|(_, [l1, l2])| Some((utils::str_to_u32(l1)?, utils::str_to_u32(l2)?))) - .map(|(x, y)| x * y) - .sum() -} diff --git a/src/day_03/part_2.rs b/src/day_03/part_2.rs deleted file mode 100644 index 7ccd4ca..0000000 --- a/src/day_03/part_2.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::utils; -use regex::Regex; - -pub fn answer(text : &str) -> u32 { - match text.split_once("don't()") { - Some((head, tail)) => { - multiplication_sum(head) + tail.split("don't()").map(take_valid).sum::() - }, - None => { - multiplication_sum(text) - }, - } -} - -fn take_valid(s : &str) -> u32 { - match s.split_once("do()") { - Some((_, valid)) => multiplication_sum(valid), - None => 0, - } -} - -fn multiplication_sum(s : &str) -> u32 { - let re = Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)").unwrap(); - - re.captures_iter(s) - .map(|c| c.extract()) - .filter_map(|(_, [l1, l2])| Some((utils::str_to_u32(l1)?, utils::str_to_u32(l2)?))) - .map(|(x, y)| x * y) - .sum() -} diff --git a/src/day_04/mod.rs b/src/day_04/mod.rs index ec839a1..2f1e64b 100644 --- a/src/day_04/mod.rs +++ b/src/day_04/mod.rs @@ -1,2 +1,87 @@ -pub mod part_1; -pub mod part_2; \ No newline at end of file +use crate::utils; + +pub fn answer(text : String) -> ( u16, u16 ) { + let grid : Vec> = from_string(text); + + let rows = grid.len(); + let cols = grid[0].len(); + + ( find_xmas(&grid, rows as isize, cols as isize), find_x_mas(&grid, rows, cols) ) +} + +fn find_x_mas(grid : &Vec>, rows : usize, cols : usize) -> u16 { + let mut total : u16 = 0; + + for y in 1..rows-1 { + for x in 1..cols-1 { + if grid[y][x] == 'A' { + total = total + inspect_x_mas_location(&grid, x, y) + } + } + } + + total +} + +fn find_xmas(grid : &Vec>, rows : isize, cols : isize) -> u16 { + let mut total : u16 = 0; + + for y in 0..rows { + for x in 0..cols { + if grid[y as usize][x as usize] == 'X' { + total = total + inspect_xmas_location(&grid, x, y, rows, cols) + } + } + } + + total +} + +fn from_string(s : String) -> Vec> { + s.split("\n").map(|s| s.chars().collect()).collect() +} + +fn inspect_x_mas_location(grid : &Vec>, x : usize, y : usize) -> u16 { + let c1 = grid[y-1][x-1]; + let c2 = grid[y-1][x+1]; + let c3 = grid[y+1][x-1]; + let c4 = grid[y+1][x+1]; + + let horizontal = c1 == c2 && c3 == c4 && c1 != c3; + let vertical = c1 == c3 && c2 == c4 && c1 != c2; + let letters_match = (c1 == 'M' && c4 == 'S') || (c1 == 'S' && c4 == 'M'); + + if letters_match && (horizontal || vertical) { + 1 + } else { + 0 + } +} + +fn inspect_xmas_location(grid : &Vec>, x : isize, y : isize, rows : isize, cols : isize) -> u16 { + let mut total : u16 = 0; + + for dx in -1..=1 { + for dy in -1..=1 { + if dx == 0 && dy == 0 { + continue; + } + + let (s_x, s_y) = ( x + 3 * dx, y + 3 * dy ); + + if s_x < 0 || s_x >= rows || s_y < 0 || s_y >= cols { + continue; + } + + let m = grid[(y + 1 * dy) as usize][(x + 1 * dx) as usize]; + let a = grid[(y + 2 * dy) as usize][(x + 2 * dx) as usize]; + let s = grid[(y + 3 * dy) as usize][(x + 3 * dx) as usize]; + + if m == 'M' && a == 'A' && s == 'S' { + total = total + 1; + } + } + }; + + total +} diff --git a/src/day_04/part_1.rs b/src/day_04/part_1.rs deleted file mode 100644 index 8cba96c..0000000 --- a/src/day_04/part_1.rs +++ /dev/null @@ -1,47 +0,0 @@ -pub fn answer(s : &str) -> u32 { - let grid : Vec> = s.split("\n") - .map(|s| s.chars().collect()) - .collect(); - - let rows : i32 = grid.len().try_into().unwrap(); - let cols : i32 = grid[0].len().try_into().unwrap(); - let mut total : u32 = 0; - - for y in 0..rows { - for x in 0..cols { - if grid[y as usize][x as usize] == 'X' { - total = total + inspect_location(&grid, x, y, rows, cols) - } - } - }; - - total -} - -fn inspect_location(grid : &Vec>, x : i32, y : i32, rows : i32, cols : i32) -> u32 { - let mut total = 0; - - for dx in -1..=1 { - for dy in -1..=1 { - if dx == 0 && dy == 0 { - continue; - } - - let (s_x, s_y) = ( x + 3 * dx, y + 3 * dy ); - - if s_x < 0 || s_x >= rows || s_y < 0 || s_y >= cols { - continue; - } - - let m = grid[(y + 1 * dy) as usize][(x + 1 * dx) as usize]; - let a = grid[(y + 2 * dy) as usize][(x + 2 * dx) as usize]; - let s = grid[(y + 3 * dy) as usize][(x + 3 * dx) as usize]; - - if m == 'M' && a == 'A' && s == 'S' { - total = total + 1; - } - } - }; - - total -} diff --git a/src/day_04/part_2.rs b/src/day_04/part_2.rs deleted file mode 100644 index d38f448..0000000 --- a/src/day_04/part_2.rs +++ /dev/null @@ -1,36 +0,0 @@ -pub fn answer(s : &str) -> u32 { - let grid : Vec> = s.split("\n") - .map(|s| s.chars().collect()) - .collect(); - - let rows = grid.len(); - let cols = grid[0].len(); - let mut total : u32 = 0; - - for y in 1..rows-1 { - for x in 1..cols-1 { - if grid[y as usize][x as usize] == 'A' { - total = total + inspect_location(&grid, x, y) - } - } - }; - - total -} - -fn inspect_location(grid : &Vec>, x : usize, y : usize) -> u32 { - let c1 = grid[y-1][x-1]; - let c2 = grid[y-1][x+1]; - let c3 = grid[y+1][x-1]; - let c4 = grid[y+1][x+1]; - - let horizontal = c1 == c2 && c3 == c4 && c1 != c3; - let vertical = c1 == c3 && c2 == c4 && c1 != c2; - let letters_match = (c1 == 'M' && c4 == 'S') || (c1 == 'S' && c4 == 'M'); - - if letters_match && (horizontal || vertical) { - 1 - } else { - 0 - } -} diff --git a/src/day_05/mod.rs b/src/day_05/mod.rs index ec839a1..8c67c20 100644 --- a/src/day_05/mod.rs +++ b/src/day_05/mod.rs @@ -1,2 +1,93 @@ -pub mod part_1; -pub mod part_2; \ No newline at end of file +use crate::utils; +use std::collections::HashSet; + +type HandBook = Vec; +type RuleSet = HashSet<(u8, u8)>; + +pub fn answer(text : String) -> ( u16, u16 ) { + let ( rules, handbooks ) : ( RuleSet, Vec ) = parse_input(text); + let ( sorted, unsorted ) : ( Vec<_>, Vec<_> ) = handbooks + .iter() + .partition(|hb| follows_rules(hb, &rules)); + + ( sorted.iter().map(|hb| sorted_middle_value(hb) as u16).sum() + , unsorted.iter().map(|hb| unsorted_middle_value(hb, &rules)).sum() + ) +} + +fn follows_rules(handbook : &HandBook, rules : &RuleSet) -> bool { + handbook.windows(2).all(|rule| { + match (rule.get(0), rule.get(1)) { + (Some(a), Some(b)) => { + rules.contains(&(*a, *b)) + }, + // Invalid situations + _ => false, + } + }) +} + +fn parse_hand_line(s : &str) -> Option> { + let v : Vec = s.split(",").filter_map(utils::str_to_u8).collect(); + + if v.len() == 0 { None } else { Some(v) } +} + +fn parse_input(s : String) -> (RuleSet, Vec) { + let (r, h) = s.split_once("\n\n").unwrap_or(("", "")); + + ( r.split("\n").filter_map(parse_rule_line).collect() + , h.split("\n").filter_map(parse_hand_line).collect() + ) +} + +fn parse_rule_line(s : &str) -> Option<(u8, u8)> { + let (l, r) = s.split_once("|")?; + + Some(( utils::str_to_u8(l)?, utils::str_to_u8(r)? )) +} + +fn unsorted_middle_value(handbook : &HandBook, rules : &RuleSet) -> u16 { + let (head, tail) = handbook.split_first().unwrap(); + + let mut cursor : u8 = *head; + let mut num_lower : u8 = 0; + let mut num_higher : u8 = 0; + let mut lower : Vec = Vec::new(); + let mut higher : Vec = Vec::new(); + let mut stack : Vec = Vec::from(tail); + + loop { + // Classify numbers based on their order + while let Some(n) = stack.pop() { + if rules.contains(&( cursor, n )) { + higher.push(n); + } else { + lower.push(n); + } + } + + // Check if we've found the correct number + let low = num_lower + lower.len() as u8; + let high = num_higher + higher.len() as u8; + if low == high { + return cursor.into(); + } + + // Otherwise, rearrange the variables for the next step + if low < high { + (num_lower, lower) = (low + 1, Vec::new()); + (stack, higher) = (higher, Vec::new()); + } else { + (num_higher, higher) = (high + 1, Vec::new()); + (stack, lower) = (lower, Vec::new()); + } + + // Pick the next value + cursor = stack.pop().unwrap(); + } +} + +fn sorted_middle_value(handbook : &HandBook) -> u8 { + handbook[handbook.len() / 2] +} \ No newline at end of file diff --git a/src/day_05/part_1.rs b/src/day_05/part_1.rs deleted file mode 100644 index 2c31825..0000000 --- a/src/day_05/part_1.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::utils; - -type HandBook = Vec; -type RuleSet = Vec<(u32, u32)>; - -pub fn answer(s : &str) -> u32 { - let ( rules, handbooks ) = parse_input(s); - - handbooks.iter() - .filter(|hb| follows_rules(hb, &rules)) - .map(middle_value) - .sum() -} - -fn follows_rules(handbook : &HandBook, rules : &RuleSet) -> bool { - rules.iter().all(|rule| follows_rule(handbook, rule)) -} - -fn follows_rule(handbook : &HandBook, (first, second) : &(u32, u32)) -> bool { - let mut encountered_second : bool = false; - - for num in handbook.iter() { - if num == first { - return !encountered_second; - } else if num == second { - encountered_second = true; - } - } - - true -} - -fn middle_value(handbook : &HandBook) -> u32 { - handbook[handbook.len() / 2] -} - -fn parse_hand_line(s : &str) -> Option> { - let v : Vec = s.split(",").filter_map(utils::str_to_u32).collect(); - - if v.len() == 0 { None } else { Some(v) } -} - -fn parse_input(s : &str) -> (RuleSet, Vec) { - let (r, h) = s.split_once("\n\n").unwrap_or(("", "")); - - ( r.split("\n").filter_map(parse_rule_line).collect() - , h.split("\n").filter_map(parse_hand_line).collect() - ) -} - -fn parse_rule_line(s : &str) -> Option<(u32, u32)> { - let (l, r) = s.split_once("|")?; - - Some(( utils::str_to_u32(l)?, utils::str_to_u32(r)? )) -} diff --git a/src/day_05/part_2.rs b/src/day_05/part_2.rs deleted file mode 100644 index 3ba2fe0..0000000 --- a/src/day_05/part_2.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::utils; -use std::cmp::Ordering; - -type HandBook = Vec; -type RuleSet = Vec<(u32, u32)>; - -pub fn answer(s : &str) -> u32 { - let ( rules, mut handbooks ) = parse_input(s); - - handbooks.iter_mut() - .filter(|hb| !follows_rules(hb, &rules)) - .map(|hb| { reorder_pages(hb, &rules); middle_value(hb) }) - .sum() -} - -fn page_cmp(a : &u32, b : &u32, rules : &RuleSet) -> Ordering { - for (r1, r2) in rules.iter() { - if r1 == a && r2 == b { - return Ordering::Less; - } else if r1 == b && r2 == a { - return Ordering::Greater; - } - } - - Ordering::Equal -} - -fn follows_rules(handbook : &HandBook, rules : &RuleSet) -> bool { - rules.iter().all(|rule| follows_rule(handbook, rule)) -} - -fn follows_rule(handbook : &HandBook, (first, second) : &(u32, u32)) -> bool { - let mut encountered_second : bool = false; - - for num in handbook.iter() { - if num == first { - return !encountered_second; - } else if num == second { - encountered_second = true; - } - } - - true -} - -fn middle_value(handbook : &HandBook) -> u32 { - handbook[handbook.len() / 2] -} - -fn parse_hand_line(s : &str) -> Option> { - let v : Vec = s.split(",").filter_map(utils::str_to_u32).collect(); - - if v.len() == 0 { None } else { Some(v) } -} - -fn parse_input(s : &str) -> (RuleSet, Vec) { - let (r, h) = s.split_once("\n\n").unwrap_or(("", "")); - - ( r.split("\n").filter_map(parse_rule_line).collect() - , h.split("\n").filter_map(parse_hand_line).collect() - ) -} - -fn parse_rule_line(s : &str) -> Option<(u32, u32)> { - let (l, r) = s.split_once("|")?; - - Some(( utils::str_to_u32(l)?, utils::str_to_u32(r)? )) -} - -fn reorder_pages(handbook : &mut HandBook, rules : &RuleSet) { - handbook.sort_unstable_by(|a, b| page_cmp(a, b, rules)); -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5dd1fec..6454510 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ mod day_01; mod day_02; mod day_03; +mod day_04; +mod day_05; mod utils; use std::time::Duration; @@ -29,3 +31,17 @@ pub fn day_03() -> DailyOutput { ( p1 as u128, p2 as u128, d ) } + +pub fn day_04() -> DailyOutput { + let s : String = read_from_file("inputs/04.txt"); + let (p1, p2, d) = diagnostics::benchmark(s, day_04::answer); + + ( p1 as u128, p2 as u128, d ) +} + +pub fn day_05() -> DailyOutput { + let s : String = read_from_file("inputs/05.txt"); + let (p1, p2, d) = diagnostics::benchmark(s, day_05::answer); + + ( p1 as u128, p2 as u128, d ) +} diff --git a/src/main.rs b/src/main.rs index 1905c1b..1ee8a10 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ fn main() { aoc.insert(1, aoc_2024::day_01()); aoc.insert(2, aoc_2024::day_02()); aoc.insert(3, aoc_2024::day_03()); + aoc.insert(4, aoc_2024::day_04()); + aoc.insert(5, aoc_2024::day_05()); aoc.show_diagnostics(); } diff --git a/src/utils/diagnostics.rs b/src/utils/diagnostics.rs index 166e67d..7c16de7 100644 --- a/src/utils/diagnostics.rs +++ b/src/utils/diagnostics.rs @@ -39,11 +39,11 @@ impl AdventOfCode { .map(|(day, result)| { match result { Some(r) => { - println!("{}", r.to_string(day, MAX_MS_ON_SCREEN)); + println!("{}", r.to_string(day + 1, MAX_MS_ON_SCREEN)); r.duration.as_millis() }, None => { - println!("{}", empty_diagnostic(day)); + println!("{}", empty_diagnostic(day + 1)); 0 }, }