Refactor day 4 & 5

main
Bram 2024-12-13 16:34:24 +00:00
parent 551a322cc2
commit acbb0123ac
15 changed files with 200 additions and 371 deletions

View File

@ -1,26 +0,0 @@
use crate::utils;
pub fn answer(lines : &str) -> u32 {
let ( mut left, mut right ) : ( Vec<u32>, Vec<u32> ) = 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)? ))
}

View File

@ -1,18 +0,0 @@
use crate::utils;
pub fn answer(s : &str) -> u32 {
let ( left, right ) : ( Vec<u32>, Vec<u32> ) = 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)? ))
}

View File

@ -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<i32>) -> bool {
let diff : Vec<i32> = 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<i32> {
s.split(" ").filter_map(utils::str_to_i32).collect()
}

View File

@ -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<i32>) -> 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<i32>) -> bool {
let diff : Vec<i32> = 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<i32> {
s.split(" ").filter_map(utils::str_to_i32).collect()
}

View File

@ -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()
}

View File

@ -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::<u32>()
},
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()
}

View File

@ -1,2 +1,87 @@
pub mod part_1; use crate::utils;
pub mod part_2;
pub fn answer(text : String) -> ( u16, u16 ) {
let grid : Vec<Vec<char>> = 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<Vec<char>>, 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<Vec<char>>, 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<Vec<char>> {
s.split("\n").map(|s| s.chars().collect()).collect()
}
fn inspect_x_mas_location(grid : &Vec<Vec<char>>, 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<Vec<char>>, 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
}

View File

@ -1,47 +0,0 @@
pub fn answer(s : &str) -> u32 {
let grid : Vec<Vec<char>> = 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<Vec<char>>, 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
}

View File

@ -1,36 +0,0 @@
pub fn answer(s : &str) -> u32 {
let grid : Vec<Vec<char>> = 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<Vec<char>>, 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
}
}

View File

@ -1,2 +1,93 @@
pub mod part_1; use crate::utils;
pub mod part_2; use std::collections::HashSet;
type HandBook = Vec<u8>;
type RuleSet = HashSet<(u8, u8)>;
pub fn answer(text : String) -> ( u16, u16 ) {
let ( rules, handbooks ) : ( RuleSet, Vec<HandBook> ) = 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<Vec<u8>> {
let v : Vec<u8> = s.split(",").filter_map(utils::str_to_u8).collect();
if v.len() == 0 { None } else { Some(v) }
}
fn parse_input(s : String) -> (RuleSet, Vec<HandBook>) {
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<u8> = Vec::new();
let mut higher : Vec<u8> = Vec::new();
let mut stack : Vec<u8> = 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]
}

View File

@ -1,55 +0,0 @@
use crate::utils;
type HandBook = Vec<u32>;
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<Vec<u32>> {
let v : Vec<u32> = s.split(",").filter_map(utils::str_to_u32).collect();
if v.len() == 0 { None } else { Some(v) }
}
fn parse_input(s : &str) -> (RuleSet, Vec<HandBook>) {
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)? ))
}

View File

@ -1,72 +0,0 @@
use crate::utils;
use std::cmp::Ordering;
type HandBook = Vec<u32>;
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<Vec<u32>> {
let v : Vec<u32> = s.split(",").filter_map(utils::str_to_u32).collect();
if v.len() == 0 { None } else { Some(v) }
}
fn parse_input(s : &str) -> (RuleSet, Vec<HandBook>) {
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));
}

View File

@ -1,6 +1,8 @@
mod day_01; mod day_01;
mod day_02; mod day_02;
mod day_03; mod day_03;
mod day_04;
mod day_05;
mod utils; mod utils;
use std::time::Duration; use std::time::Duration;
@ -29,3 +31,17 @@ pub fn day_03() -> DailyOutput {
( p1 as u128, p2 as u128, d ) ( 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 )
}

View File

@ -6,6 +6,8 @@ fn main() {
aoc.insert(1, aoc_2024::day_01()); aoc.insert(1, aoc_2024::day_01());
aoc.insert(2, aoc_2024::day_02()); aoc.insert(2, aoc_2024::day_02());
aoc.insert(3, aoc_2024::day_03()); aoc.insert(3, aoc_2024::day_03());
aoc.insert(4, aoc_2024::day_04());
aoc.insert(5, aoc_2024::day_05());
aoc.show_diagnostics(); aoc.show_diagnostics();
} }

View File

@ -39,11 +39,11 @@ impl AdventOfCode {
.map(|(day, result)| { .map(|(day, result)| {
match result { match result {
Some(r) => { 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() r.duration.as_millis()
}, },
None => { None => {
println!("{}", empty_diagnostic(day)); println!("{}", empty_diagnostic(day + 1));
0 0
}, },
} }