diff --git a/src/day0.rs b/src/day0.rs index 77be5c1..c1b81ce 100644 --- a/src/day0.rs +++ b/src/day0.rs @@ -1,7 +1,4 @@ -//! Day 0: Dummy challenge for testing - -pub const DAY: u8 = 0; -pub const TITLE: &str = "Dummy challenge for testing"; +//! Dummy challenge for testing pub fn part1(input: Vec) -> String { input[0].to_owned() @@ -15,6 +12,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 0; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "123"); diff --git a/src/day1.rs b/src/day1.rs index 053ea65..739bc93 100644 --- a/src/day1.rs +++ b/src/day1.rs @@ -1,10 +1,7 @@ -//! Day 1: Trebuchet?! +//! Day 1 use aho_corasick::AhoCorasick; -pub const DAY: u8 = 1; -pub const TITLE: &str = "Trebuchet?!"; - pub fn part1(input: Vec) -> String { input .iter() @@ -50,6 +47,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 1; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "142"); diff --git a/src/day2.rs b/src/day2.rs index a08eb7c..3121160 100644 --- a/src/day2.rs +++ b/src/day2.rs @@ -1,11 +1,8 @@ -//! Day 2: Cube Conundrum +//! Day 2 use once_cell::sync::Lazy; use regex::Regex; -pub const DAY: u8 = 2; -pub const TITLE: &str = "Cube Conundrum"; - #[derive(Debug)] struct Game { id: u32, @@ -97,6 +94,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 2; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "8"); diff --git a/src/day3.rs b/src/day3.rs index 4e92b38..deff3bf 100644 --- a/src/day3.rs +++ b/src/day3.rs @@ -1,4 +1,4 @@ -//! Day 3: Gear Ratios +//! Day 3 use std::collections::{HashMap, HashSet}; @@ -6,9 +6,6 @@ use itertools::Itertools; use once_cell::sync::Lazy; use regex::Regex; -pub const DAY: u8 = 3; -pub const TITLE: &str = "Gear Ratios"; - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] struct Pos { x: i64, @@ -184,6 +181,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 3; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "4361"); diff --git a/src/day4.rs b/src/day4.rs index d66c436..272fba0 100644 --- a/src/day4.rs +++ b/src/day4.rs @@ -1,12 +1,9 @@ -//! Day 4: Scratchcards +//! Day 4 use std::collections::HashSet; use itertools::Itertools; -pub const DAY: u8 = 4; -pub const TITLE: &str = "Scratchcards"; - fn parse(line: &str) -> (HashSet, HashSet) { let (_, tmp) = line.split_once(": ").unwrap(); let (win, got) = tmp.split_once(" | ").unwrap(); @@ -65,6 +62,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 4; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "13"); diff --git a/src/day5.rs b/src/day5.rs index d23f8e0..1d1679f 100644 --- a/src/day5.rs +++ b/src/day5.rs @@ -1,17 +1,14 @@ -//! Day 5: If You Give A Seed A Fertilizer +//! Day 5 -use std::{borrow::BorrowMut, collections::HashMap, ops::Range}; +use std::collections::HashMap; use itertools::Itertools; use rangemap::{RangeMap, RangeSet}; -pub const DAY: u8 = 5; -pub const TITLE: &str = "If You Give A Seed A Fertilizer"; - #[derive(Debug)] struct Parsed { seeds: Vec, - maps: Vec>>, + maps: Vec>, } impl Parsed { @@ -19,45 +16,11 @@ impl Parsed { let mut s = seed; for map in &self.maps { if let Some((rng, dst)) = map.get_key_value(&s) { - s = dst.start + (s - rng.start); + s = dst + (s - rng.start); } } s } - - /// Transform the list of seed maps into a single map - fn transform(&self) -> RangeMap> { - self.maps - .clone() - .into_iter() - .reduce(|acc, mut m| { - // have dangling keys from next map as fallback - let mut res = m.clone(); - for (k, v) in acc.iter() { - // lookup value range in next map - for (olk, olv) in m.overlapping(v) { - let corr = olk.start.max(v.start)..olk.end.min(v.end); - let offset = corr.start - v.start; - let voffset = corr.start - olk.start; - let s = k.start + offset; - let l = rlen(&corr); - let olvs = olv.start + voffset; - res.insert(s..(s + l), olvs..(olvs + l)); - } - - // ranges in acc map not covered by next map - for g in m.gaps(v) { - let offset = g.start - v.start; - let s = k.start + offset; - let vs = v.start + offset; - let l = rlen(&g); - res.insert(s..(s + l), vs..(vs + l)); - } - } - res - }) - .unwrap() - } } fn parse(input: Vec) -> Parsed { @@ -93,10 +56,7 @@ fn parse(input: Vec) -> Parsed { .collect_tuple() .unwrap(); - map.insert( - mentry.1..(mentry.1 + mentry.2), - mentry.0..(mentry.0 + mentry.2), - ); + map.insert(mentry.1..(mentry.1 + mentry.2), mentry.0); } if !map.is_empty() { maps.push(map); @@ -107,15 +67,11 @@ fn parse(input: Vec) -> Parsed { pub fn part1(input: Vec) -> String { let parsed = parse(input); - let tf = parsed.transform(); parsed .seeds .iter() - .map(|s| { - let (rng, dst) = tf.get_key_value(s).unwrap(); - dst.start + (s - rng.start) - }) + .map(|s| parsed.lookup(*s)) .min() .unwrap() .to_string() @@ -123,7 +79,6 @@ pub fn part1(input: Vec) -> String { pub fn part2(input: Vec) -> String { let parsed = parse(input); - let tf = parsed.transform(); let ranges = parsed .seeds @@ -132,40 +87,24 @@ pub fn part2(input: Vec) -> String { .map(|(a, b)| (*a)..(*a + *b)) .collect::>(); - tf.into_iter() - .sorted_by_key(|(_, soils)| soils.start) - .find_map(|(seeds, soils)| { - ranges - .iter() - .find_map(|r| lowest_intersection(r, &seeds)) - .map(|seed| soils.start + (seed - seeds.start)) + ranges + .into_iter() + .flat_map(|r| { + dbg!(&r); + r.into_iter() }) + .map(|s| parsed.lookup(s)) + .min() .unwrap() .to_string() } -fn rlen(range: &Range) -> u64 { - range.end - range.start -} - -fn lowest_intersection(r1: &Range, r2: &Range) -> Option { - let (l, h) = if r1.start < r2.start { - (r1, r2) - } else { - (r2, r1) - }; - - if h.start <= l.end { - Some(h.start) - } else { - None - } -} - #[cfg(test)] mod tests { use super::*; + const DAY: u8 = 5; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "35"); diff --git a/src/day6.rs b/src/day6.rs index e0825dd..add2f59 100644 --- a/src/day6.rs +++ b/src/day6.rs @@ -1,12 +1,9 @@ -//! Day 6: Wait For It +//! Day 6 use std::ops::RangeInclusive; use itertools::Itertools; -pub const DAY: u8 = 6; -pub const TITLE: &str = "Wait For It"; - #[derive(Debug)] struct Race { t: u64, @@ -70,6 +67,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 6; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "288"); diff --git a/src/day7.rs b/src/day7.rs index e46b3a4..6d4450c 100644 --- a/src/day7.rs +++ b/src/day7.rs @@ -1,4 +1,4 @@ -//! Day 7: Camel Cards +//! Day 7 use std::{ cmp::Ordering, @@ -7,9 +7,6 @@ use std::{ use itertools::Itertools; -pub const DAY: u8 = 7; -pub const TITLE: &str = "Camel Cards"; - #[derive(Debug, PartialEq, Eq)] struct Hand(String); @@ -136,6 +133,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 7; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "6440"); diff --git a/src/day8.rs b/src/day8.rs index b4d8950..1fdacb4 100644 --- a/src/day8.rs +++ b/src/day8.rs @@ -1,12 +1,9 @@ -//! Day 8: Haunted Wasteland +//! Day 8 use std::collections::{HashMap, HashSet}; use itertools::Itertools; -pub const DAY: u8 = 8; -pub const TITLE: &str = "Haunted Wasteland"; - #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Direction { Right, @@ -93,6 +90,8 @@ pub fn part2(input: Vec) -> String { mod tests { use super::*; + const DAY: u8 = 8; + #[test] fn t_example1() { assert_eq!(part1(crate::read_example(DAY, 1)), "2"); diff --git a/src/main.rs b/src/main.rs index 20870da..9e52d02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,8 @@ use std::{ fs::File, io::{BufRead, BufReader}, path::Path, - time::Instant, }; -use once_cell::sync::Lazy; use path_macro::path; pub(crate) fn read_input(day: u8) -> Vec { @@ -36,83 +34,55 @@ fn read_input_file>(path: P) -> Vec { input } -fn get_result(day: &Day, part: u8) -> String { - let input = read_input(day.n); - if part == 2 { - (day.p2)(input) - } else { - (day.p1)(input) - } -} - -type SolveFn = Box) -> String>; - -struct Day { - n: u8, - title: &'static str, - p1: SolveFn, - p2: SolveFn, -} - macro_rules! days { - ( $($module:ident,)* ) => { + ( $($n:expr, $module:ident,)* ) => { $(mod $module;)* - fn days() -> Vec { - vec![ - $(Day { - n: $module::DAY, - title: $module::TITLE, - p1: Box::new($module::part1), - p2: Box::new($module::part2), - },)* - ] + fn get_result(day: u8, part: u8) -> String { + let input = read_input(day); + + match day { + $( + $n => match part { + 2 => $module::part2(input), + _ => $module::part1(input), + } + )* + _ => panic!("day {} missing", day), + } } }; } days! { - day1, - day2, - day3, - day4, - day5, - day6, - day7, - day8, + 0, day0, + 1, day1, + 2, day2, + 3, day3, + 4, day4, + 5, day5, + 6, day6, + 7, day7, + 8, day8, } fn main() { let mut args = std::env::args(); args.next(); - let days = days(); - if let Some(day) = args.next().and_then(|a| a.parse::().ok()) { - let part = args - .next() - .and_then(|arg| arg.parse::().ok().map(|p| p.clamp(1, 2))) - .unwrap_or(1); + let day = args + .next() + .expect("no day") + .parse::() + .expect("invalid day"); - let dobj = days.iter().find(|d| d.n == day).expect("Day not found"); - let res = get_result(dobj, part); + let part = args + .next() + .and_then(|arg| arg.parse::().ok().map(|p| p.clamp(1, 2))) + .unwrap_or(1); - println!("✨The result for day {}/{:?} is✨", day, part); - println!("{}", res); - } else { - println!("Advent of Code 2023 | Part one | Part two"); - println!("=============================|======================|======================"); - for day in days { - let title = &day.title[..day.title.len().min(20)]; + let res = get_result(day, part); - let t = Instant::now(); - let res_a = get_result(&day, 1); - let res_a = format!("{res_a} ({}ms)", t.elapsed().as_millis()); - print!("Day {:2}: {title:20} | {res_a:20}", day.n); - - let t = Instant::now(); - let res_b = get_result(&day, 2); - let res_b = format!("{res_b} ({}ms)", t.elapsed().as_millis()); - println!(" | {res_b:20}") - } - } + println!("✨The result for day {}/{:?} is✨", day, part); + println!("{}", res); }