Add diagnostics + optimizations
parent
4dba9ee6bf
commit
551a322cc2
|
@ -4,4 +4,5 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
colored="2.1.0"
|
||||||
regex="1.11.1"
|
regex="1.11.1"
|
||||||
|
|
|
@ -1,2 +1,32 @@
|
||||||
pub mod part_1;
|
use crate::utils;
|
||||||
pub mod part_2;
|
|
||||||
|
fn abs_diff(l : &u32, r : &u32) -> u32 {
|
||||||
|
if l >= r { l - r } else { r - l }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn answer(text : String) -> ( u32, u32 ) {
|
||||||
|
let ( mut left, mut right ) = from_string(text);
|
||||||
|
|
||||||
|
left.sort_unstable();
|
||||||
|
right.sort_unstable();
|
||||||
|
|
||||||
|
( left.iter()
|
||||||
|
.zip(right.iter())
|
||||||
|
.map(|(x, y)| abs_diff(&x, &y))
|
||||||
|
.sum()
|
||||||
|
// Part 2 can probably be optimized further but it might not matter
|
||||||
|
, left.iter()
|
||||||
|
.map(|x| right.iter().filter(|y| x == *y).count() as u32 * x)
|
||||||
|
.sum()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_string(s : String) -> ( Vec<u32>, Vec<u32> ) {
|
||||||
|
s.split("\n").filter_map(parse_line).unzip()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)? ))
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,47 @@
|
||||||
pub mod part_1;
|
use crate::utils;
|
||||||
pub mod part_2;
|
|
||||||
|
pub fn answer(text : String) -> ( usize, usize ) {
|
||||||
|
let (safe, danger) : (Vec<_>, Vec<_>) = text
|
||||||
|
.split("\n")
|
||||||
|
.map(parse_line)
|
||||||
|
.partition(is_safe_line);
|
||||||
|
|
||||||
|
let total_safe = safe.len();
|
||||||
|
let total_damp : usize = danger.iter().filter(is_dampened_safe_line).count();
|
||||||
|
|
||||||
|
( total_safe, total_safe + total_damp )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dampened_safe_line(v : &&Vec<u8>) -> 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<u8>) -> bool {
|
||||||
|
let diff : Vec<i8> = v
|
||||||
|
.windows(2)
|
||||||
|
.filter_map(|v| Some(*v.get(0)? as i8 - *v.get(1)? as i8))
|
||||||
|
.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<u8> {
|
||||||
|
s.split(" ").filter_map(utils::str_to_u8).collect()
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,27 @@
|
||||||
pub mod part_1;
|
use crate::utils;
|
||||||
pub mod part_2;
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub fn answer(text : String) -> ( u32, u32 ) {
|
||||||
|
let re : Regex = Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)").unwrap();
|
||||||
|
|
||||||
|
let (always, ignore) : (Vec<_>, Vec<_>) = text
|
||||||
|
.split("do()")
|
||||||
|
.map(|s| s.split_once("don't()").unwrap_or((s, "")))
|
||||||
|
.map(|(a, i)| ( mulsum(&re, a), mulsum(&re, i) ))
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
let a : u32 = always.iter().sum();
|
||||||
|
let i : u32 = ignore.iter().sum();
|
||||||
|
|
||||||
|
( a + i, a )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mulsum(regx : &Regex, s : &str) -> u32 {
|
||||||
|
regx.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()
|
||||||
|
}
|
||||||
|
|
47
src/lib.rs
47
src/lib.rs
|
@ -1,36 +1,31 @@
|
||||||
mod utils;
|
|
||||||
mod day_01;
|
mod day_01;
|
||||||
mod day_02;
|
mod day_02;
|
||||||
mod day_03;
|
mod day_03;
|
||||||
mod day_04;
|
mod utils;
|
||||||
mod day_05;
|
|
||||||
|
|
||||||
pub fn day_01() {
|
use std::time::Duration;
|
||||||
let s : String = utils::read_from_file("inputs/01.txt");
|
use crate::utils::read_from_file;
|
||||||
println!("Day 01 part 1: {}", day_01::part_1::answer(s.as_str()));
|
use crate::utils::diagnostics;
|
||||||
println!("Day 01 part 2: {}", day_01::part_2::answer(s.as_str()));
|
|
||||||
|
type DailyOutput = ( u128, u128, Duration );
|
||||||
|
|
||||||
|
pub fn day_01() -> DailyOutput {
|
||||||
|
let s : String = read_from_file("inputs/01.txt");
|
||||||
|
let ( p1, p2, d ) = diagnostics::benchmark(s, day_01::answer);
|
||||||
|
|
||||||
|
( p1 as u128, p2 as u128, d )
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn day_02() {
|
pub fn day_02() -> DailyOutput {
|
||||||
let s : String = utils::read_from_file("inputs/02.txt");
|
let s : String = read_from_file("inputs/02.txt");
|
||||||
println!("Day 02 part 1: {}", day_02::part_1::answer(s.as_str()));
|
let (p1, p2, d) = diagnostics::benchmark(s, day_02::answer);
|
||||||
println!("Day 02 part 2: {}", day_02::part_2::answer(s.as_str()));
|
|
||||||
|
( p1 as u128, p2 as u128, d )
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn day_03() {
|
pub fn day_03() -> DailyOutput {
|
||||||
let s : String = utils::read_from_file("inputs/03.txt");
|
let s : String = read_from_file("inputs/03.txt");
|
||||||
println!("Day 03 part 1: {}", day_03::part_1::answer(s.as_str()));
|
let (p1, p2, d) = diagnostics::benchmark(s, day_03::answer);
|
||||||
println!("Day 03 part 2: {}", day_03::part_2::answer(s.as_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn day_04() {
|
( p1 as u128, p2 as u128, d )
|
||||||
let s : String = utils::read_from_file("inputs/04.txt");
|
|
||||||
println!("Day 04 part 1: {}", day_04::part_1::answer(s.as_str()));
|
|
||||||
println!("Day 04 part 2: {}", day_04::part_2::answer(s.as_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn day_05() {
|
|
||||||
let s : String = utils::read_from_file("inputs/05.txt");
|
|
||||||
println!("Day 05 part 1: {}", day_05::part_1::answer(s.as_str()));
|
|
||||||
println!("Day 05 part 2: {}", day_05::part_2::answer(s.as_str()));
|
|
||||||
}
|
}
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -1,7 +1,11 @@
|
||||||
|
mod utils;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
aoc_2024::day_01();
|
let mut aoc = crate::utils::diagnostics::AdventOfCode::new();
|
||||||
aoc_2024::day_02();
|
|
||||||
aoc_2024::day_03();
|
aoc.insert(1, aoc_2024::day_01());
|
||||||
aoc_2024::day_04();
|
aoc.insert(2, aoc_2024::day_02());
|
||||||
aoc_2024::day_05();
|
aoc.insert(3, aoc_2024::day_03());
|
||||||
|
|
||||||
|
aoc.show_diagnostics();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
use colored::Colorize;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
const MAX_MS_ON_SCREEN : u128 = 100;
|
||||||
|
const BAR_WIDTH_UNIT : f32 = 10.0;
|
||||||
|
|
||||||
|
pub struct AdventOfCode {
|
||||||
|
days : [ Option<DayResults>; 25 ],
|
||||||
|
}
|
||||||
|
impl AdventOfCode {
|
||||||
|
pub fn insert(&mut self, i : usize, (p1, p2, d ) : ( u128, u128, Duration )) {
|
||||||
|
self.days[i - 1] = Some(
|
||||||
|
DayResults{ part_1 : p1, part_2 : p2, duration : d }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> AdventOfCode {
|
||||||
|
AdventOfCode{
|
||||||
|
// If I don't do it like this, the compiler wants me to define
|
||||||
|
// the Copy trait for the DayResults struct.
|
||||||
|
days : [
|
||||||
|
None, None, None, None, None,
|
||||||
|
None, None, None, None, None,
|
||||||
|
None, None, None, None, None,
|
||||||
|
None, None, None, None, None,
|
||||||
|
None, None, None, None, None,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_diagnostics(&self) {
|
||||||
|
println!("| | {: ^12} | {: ^12} | Duration |",
|
||||||
|
"Part 1", "Part 2"
|
||||||
|
);
|
||||||
|
|
||||||
|
let total : u128 = self.days
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(day, result)| {
|
||||||
|
match result {
|
||||||
|
Some(r) => {
|
||||||
|
println!("{}", r.to_string(day, MAX_MS_ON_SCREEN));
|
||||||
|
r.duration.as_millis()
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
println!("{}", empty_diagnostic(day));
|
||||||
|
0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
println!("");
|
||||||
|
println!("Total duration: {total} ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DayResults {
|
||||||
|
part_1 : u128,
|
||||||
|
part_2 : u128,
|
||||||
|
duration : Duration,
|
||||||
|
}
|
||||||
|
impl DayResults {
|
||||||
|
fn to_string(&self, day : usize, max_duration : u128) -> String {
|
||||||
|
let duration = self.duration.as_millis();
|
||||||
|
let fraction = duration as f32 / max_duration as f32;
|
||||||
|
|
||||||
|
let g = horizontal_bar('#', ' ', 2.0 * BAR_WIDTH_UNIT * fraction, 2.0 * BAR_WIDTH_UNIT);
|
||||||
|
let o = horizontal_bar('#', ' ', BAR_WIDTH_UNIT * (fraction - 0.50), BAR_WIDTH_UNIT);
|
||||||
|
let r = horizontal_bar('#', ' ', BAR_WIDTH_UNIT * (fraction - 0.75), BAR_WIDTH_UNIT);
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"| Day {: >2} | {: >12} | {: >12} | {: >6}ms | [{}{}{}",
|
||||||
|
day, self.part_1, self.part_2, self.duration.as_millis(),
|
||||||
|
g.as_str().green(), o.as_str().yellow(), r.as_str().red()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn benchmark<A,B>(s : String, f : fn(String) -> ( A, B )) -> ( A, B, Duration ) {
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
let ( a, b ) = f(s);
|
||||||
|
|
||||||
|
( a, b, now.elapsed() )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn empty_diagnostic(day : usize) -> String {
|
||||||
|
format!("| Day {: >2} | {:->12} | {:->12} | {:->8} | [", day, "", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn horizontal_bar(full : char, empty : char, width : f32, max_width : f32) -> String {
|
||||||
|
(0..(max_width.floor() as u32))
|
||||||
|
.map(|w| {
|
||||||
|
if (w as f32) < width {
|
||||||
|
full
|
||||||
|
} else {
|
||||||
|
empty
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
pub mod diagnostics;
|
||||||
|
|
||||||
pub fn read_from_file(name : &str) -> String {
|
pub fn read_from_file(name : &str) -> String {
|
||||||
match fs::read_to_string(name) {
|
match fs::read_to_string(name) {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
@ -14,6 +16,10 @@ pub fn str_to_i32(s : &str) -> Option<i32> {
|
||||||
s.trim().to_string().parse::<i32>().ok()
|
s.trim().to_string().parse::<i32>().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn str_to_u8(s : &str) -> Option<u8> {
|
||||||
|
s.trim().to_string().parse::<u8>().ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn str_to_u32(s : &str) -> Option<u32> {
|
pub fn str_to_u32(s : &str) -> Option<u32> {
|
||||||
s.trim().to_string().parse::<u32>().ok()
|
s.trim().to_string().parse::<u32>().ok()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue