Compare commits

..

No commits in common. "acd627a0084083b05ee3b08eb06d5a5d013549ef" and "8575784a7c744376aa1dc09a3934343264de43d3" have entirely different histories.

10 changed files with 73 additions and 172 deletions

View file

@ -1,7 +1,4 @@
//! Day 0: Dummy challenge for testing //! Dummy challenge for testing
pub const DAY: u8 = 0;
pub const TITLE: &str = "Dummy challenge for testing";
pub fn part1(input: Vec<String>) -> String { pub fn part1(input: Vec<String>) -> String {
input[0].to_owned() input[0].to_owned()
@ -15,6 +12,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 0;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "123"); assert_eq!(part1(crate::read_example(DAY, 1)), "123");

View file

@ -1,10 +1,7 @@
//! Day 1: Trebuchet?! //! Day 1
use aho_corasick::AhoCorasick; use aho_corasick::AhoCorasick;
pub const DAY: u8 = 1;
pub const TITLE: &str = "Trebuchet?!";
pub fn part1(input: Vec<String>) -> String { pub fn part1(input: Vec<String>) -> String {
input input
.iter() .iter()
@ -50,6 +47,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 1;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "142"); assert_eq!(part1(crate::read_example(DAY, 1)), "142");

View file

@ -1,11 +1,8 @@
//! Day 2: Cube Conundrum //! Day 2
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
pub const DAY: u8 = 2;
pub const TITLE: &str = "Cube Conundrum";
#[derive(Debug)] #[derive(Debug)]
struct Game { struct Game {
id: u32, id: u32,
@ -97,6 +94,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 2;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "8"); assert_eq!(part1(crate::read_example(DAY, 1)), "8");

View file

@ -1,4 +1,4 @@
//! Day 3: Gear Ratios //! Day 3
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -6,9 +6,6 @@ use itertools::Itertools;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
pub const DAY: u8 = 3;
pub const TITLE: &str = "Gear Ratios";
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
struct Pos { struct Pos {
x: i64, x: i64,
@ -184,6 +181,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 3;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "4361"); assert_eq!(part1(crate::read_example(DAY, 1)), "4361");

View file

@ -1,12 +1,9 @@
//! Day 4: Scratchcards //! Day 4
use std::collections::HashSet; use std::collections::HashSet;
use itertools::Itertools; use itertools::Itertools;
pub const DAY: u8 = 4;
pub const TITLE: &str = "Scratchcards";
fn parse(line: &str) -> (HashSet<u32>, HashSet<u32>) { fn parse(line: &str) -> (HashSet<u32>, HashSet<u32>) {
let (_, tmp) = line.split_once(": ").unwrap(); let (_, tmp) = line.split_once(": ").unwrap();
let (win, got) = tmp.split_once(" | ").unwrap(); let (win, got) = tmp.split_once(" | ").unwrap();
@ -65,6 +62,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 4;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "13"); assert_eq!(part1(crate::read_example(DAY, 1)), "13");

View file

@ -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 itertools::Itertools;
use rangemap::{RangeMap, RangeSet}; use rangemap::{RangeMap, RangeSet};
pub const DAY: u8 = 5;
pub const TITLE: &str = "If You Give A Seed A Fertilizer";
#[derive(Debug)] #[derive(Debug)]
struct Parsed { struct Parsed {
seeds: Vec<u64>, seeds: Vec<u64>,
maps: Vec<RangeMap<u64, Range<u64>>>, maps: Vec<RangeMap<u64, u64>>,
} }
impl Parsed { impl Parsed {
@ -19,45 +16,11 @@ impl Parsed {
let mut s = seed; let mut s = seed;
for map in &self.maps { for map in &self.maps {
if let Some((rng, dst)) = map.get_key_value(&s) { if let Some((rng, dst)) = map.get_key_value(&s) {
s = dst.start + (s - rng.start); s = dst + (s - rng.start);
} }
} }
s s
} }
/// Transform the list of seed maps into a single map
fn transform(&self) -> RangeMap<u64, Range<u64>> {
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<String>) -> Parsed { fn parse(input: Vec<String>) -> Parsed {
@ -93,10 +56,7 @@ fn parse(input: Vec<String>) -> Parsed {
.collect_tuple() .collect_tuple()
.unwrap(); .unwrap();
map.insert( map.insert(mentry.1..(mentry.1 + mentry.2), mentry.0);
mentry.1..(mentry.1 + mentry.2),
mentry.0..(mentry.0 + mentry.2),
);
} }
if !map.is_empty() { if !map.is_empty() {
maps.push(map); maps.push(map);
@ -107,15 +67,11 @@ fn parse(input: Vec<String>) -> Parsed {
pub fn part1(input: Vec<String>) -> String { pub fn part1(input: Vec<String>) -> String {
let parsed = parse(input); let parsed = parse(input);
let tf = parsed.transform();
parsed parsed
.seeds .seeds
.iter() .iter()
.map(|s| { .map(|s| parsed.lookup(*s))
let (rng, dst) = tf.get_key_value(s).unwrap();
dst.start + (s - rng.start)
})
.min() .min()
.unwrap() .unwrap()
.to_string() .to_string()
@ -123,7 +79,6 @@ pub fn part1(input: Vec<String>) -> String {
pub fn part2(input: Vec<String>) -> String { pub fn part2(input: Vec<String>) -> String {
let parsed = parse(input); let parsed = parse(input);
let tf = parsed.transform();
let ranges = parsed let ranges = parsed
.seeds .seeds
@ -132,40 +87,24 @@ pub fn part2(input: Vec<String>) -> String {
.map(|(a, b)| (*a)..(*a + *b)) .map(|(a, b)| (*a)..(*a + *b))
.collect::<RangeSet<_>>(); .collect::<RangeSet<_>>();
tf.into_iter()
.sorted_by_key(|(_, soils)| soils.start)
.find_map(|(seeds, soils)| {
ranges ranges
.iter() .into_iter()
.find_map(|r| lowest_intersection(r, &seeds)) .flat_map(|r| {
.map(|seed| soils.start + (seed - seeds.start)) dbg!(&r);
r.into_iter()
}) })
.map(|s| parsed.lookup(s))
.min()
.unwrap() .unwrap()
.to_string() .to_string()
} }
fn rlen(range: &Range<u64>) -> u64 {
range.end - range.start
}
fn lowest_intersection(r1: &Range<u64>, r2: &Range<u64>) -> Option<u64> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 5;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "35"); assert_eq!(part1(crate::read_example(DAY, 1)), "35");

View file

@ -1,12 +1,9 @@
//! Day 6: Wait For It //! Day 6
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use itertools::Itertools; use itertools::Itertools;
pub const DAY: u8 = 6;
pub const TITLE: &str = "Wait For It";
#[derive(Debug)] #[derive(Debug)]
struct Race { struct Race {
t: u64, t: u64,
@ -70,6 +67,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 6;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "288"); assert_eq!(part1(crate::read_example(DAY, 1)), "288");

View file

@ -1,4 +1,4 @@
//! Day 7: Camel Cards //! Day 7
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
@ -7,9 +7,6 @@ use std::{
use itertools::Itertools; use itertools::Itertools;
pub const DAY: u8 = 7;
pub const TITLE: &str = "Camel Cards";
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
struct Hand(String); struct Hand(String);
@ -136,6 +133,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 7;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "6440"); assert_eq!(part1(crate::read_example(DAY, 1)), "6440");

View file

@ -1,12 +1,9 @@
//! Day 8: Haunted Wasteland //! Day 8
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use itertools::Itertools; use itertools::Itertools;
pub const DAY: u8 = 8;
pub const TITLE: &str = "Haunted Wasteland";
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction { enum Direction {
Right, Right,
@ -93,6 +90,8 @@ pub fn part2(input: Vec<String>) -> String {
mod tests { mod tests {
use super::*; use super::*;
const DAY: u8 = 8;
#[test] #[test]
fn t_example1() { fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "2"); assert_eq!(part1(crate::read_example(DAY, 1)), "2");

View file

@ -4,10 +4,8 @@ use std::{
fs::File, fs::File,
io::{BufRead, BufReader}, io::{BufRead, BufReader},
path::Path, path::Path,
time::Instant,
}; };
use once_cell::sync::Lazy;
use path_macro::path; use path_macro::path;
pub(crate) fn read_input(day: u8) -> Vec<String> { pub(crate) fn read_input(day: u8) -> Vec<String> {
@ -36,83 +34,55 @@ fn read_input_file<P: AsRef<Path>>(path: P) -> Vec<String> {
input 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<dyn Fn(Vec<String>) -> String>;
struct Day {
n: u8,
title: &'static str,
p1: SolveFn,
p2: SolveFn,
}
macro_rules! days { macro_rules! days {
( $($module:ident,)* ) => { ( $($n:expr, $module:ident,)* ) => {
$(mod $module;)* $(mod $module;)*
fn days() -> Vec<Day> { fn get_result(day: u8, part: u8) -> String {
vec![ let input = read_input(day);
$(Day {
n: $module::DAY, match day {
title: $module::TITLE, $(
p1: Box::new($module::part1), $n => match part {
p2: Box::new($module::part2), 2 => $module::part2(input),
},)* _ => $module::part1(input),
] }
)*
_ => panic!("day {} missing", day),
}
} }
}; };
} }
days! { days! {
day1, 0, day0,
day2, 1, day1,
day3, 2, day2,
day4, 3, day3,
day5, 4, day4,
day6, 5, day5,
day7, 6, day6,
day8, 7, day7,
8, day8,
} }
fn main() { fn main() {
let mut args = std::env::args(); let mut args = std::env::args();
args.next(); args.next();
let days = days();
if let Some(day) = args.next().and_then(|a| a.parse::<u8>().ok()) { let day = args
.next()
.expect("no day")
.parse::<u8>()
.expect("invalid day");
let part = args let part = args
.next() .next()
.and_then(|arg| arg.parse::<u8>().ok().map(|p| p.clamp(1, 2))) .and_then(|arg| arg.parse::<u8>().ok().map(|p| p.clamp(1, 2)))
.unwrap_or(1); .unwrap_or(1);
let dobj = days.iter().find(|d| d.n == day).expect("Day not found"); let res = get_result(day, part);
let res = get_result(dobj, part);
println!("✨The result for day {}/{:?} is✨", day, part); println!("✨The result for day {}/{:?} is✨", day, part);
println!("{}", res); 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 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}")
}
}
} }