Compare commits

...

4 commits

Author SHA1 Message Date
acd627a008
feat: add titles for days 2023-12-09 01:53:48 +01:00
0b2475d2bf
improve day 5 2023-12-09 01:43:07 +01:00
fd056ae79e
update days macro 2023-12-08 22:07:58 +01:00
452b15fd03
add table output format 2023-12-08 18:09:18 +01:00
10 changed files with 172 additions and 73 deletions

View file

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

View file

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

View file

@ -1,8 +1,11 @@
//! Day 2
//! Day 2: Cube Conundrum
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,
@ -94,8 +97,6 @@ pub fn part2(input: Vec<String>) -> String {
mod tests {
use super::*;
const DAY: u8 = 2;
#[test]
fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "8");

View file

@ -1,4 +1,4 @@
//! Day 3
//! Day 3: Gear Ratios
use std::collections::{HashMap, HashSet};
@ -6,6 +6,9 @@ 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,
@ -181,8 +184,6 @@ pub fn part2(input: Vec<String>) -> String {
mod tests {
use super::*;
const DAY: u8 = 3;
#[test]
fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "4361");

View file

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

View file

@ -1,14 +1,17 @@
//! Day 5
//! Day 5: If You Give A Seed A Fertilizer
use std::collections::HashMap;
use std::{borrow::BorrowMut, collections::HashMap, ops::Range};
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<u64>,
maps: Vec<RangeMap<u64, u64>>,
maps: Vec<RangeMap<u64, Range<u64>>>,
}
impl Parsed {
@ -16,11 +19,45 @@ impl Parsed {
let mut s = seed;
for map in &self.maps {
if let Some((rng, dst)) = map.get_key_value(&s) {
s = dst + (s - rng.start);
s = dst.start + (s - rng.start);
}
}
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 {
@ -56,7 +93,10 @@ fn parse(input: Vec<String>) -> Parsed {
.collect_tuple()
.unwrap();
map.insert(mentry.1..(mentry.1 + mentry.2), mentry.0);
map.insert(
mentry.1..(mentry.1 + mentry.2),
mentry.0..(mentry.0 + mentry.2),
);
}
if !map.is_empty() {
maps.push(map);
@ -67,11 +107,15 @@ fn parse(input: Vec<String>) -> Parsed {
pub fn part1(input: Vec<String>) -> String {
let parsed = parse(input);
let tf = parsed.transform();
parsed
.seeds
.iter()
.map(|s| parsed.lookup(*s))
.map(|s| {
let (rng, dst) = tf.get_key_value(s).unwrap();
dst.start + (s - rng.start)
})
.min()
.unwrap()
.to_string()
@ -79,6 +123,7 @@ pub fn part1(input: Vec<String>) -> String {
pub fn part2(input: Vec<String>) -> String {
let parsed = parse(input);
let tf = parsed.transform();
let ranges = parsed
.seeds
@ -87,24 +132,40 @@ pub fn part2(input: Vec<String>) -> String {
.map(|(a, b)| (*a)..(*a + *b))
.collect::<RangeSet<_>>();
ranges
.into_iter()
.flat_map(|r| {
dbg!(&r);
r.into_iter()
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))
})
.map(|s| parsed.lookup(s))
.min()
.unwrap()
.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)]
mod tests {
use super::*;
const DAY: u8 = 5;
#[test]
fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "35");

View file

@ -1,9 +1,12 @@
//! Day 6
//! Day 6: Wait For It
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,
@ -67,8 +70,6 @@ pub fn part2(input: Vec<String>) -> String {
mod tests {
use super::*;
const DAY: u8 = 6;
#[test]
fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "288");

View file

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

View file

@ -1,9 +1,12 @@
//! Day 8
//! Day 8: Haunted Wasteland
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,
@ -90,8 +93,6 @@ pub fn part2(input: Vec<String>) -> String {
mod tests {
use super::*;
const DAY: u8 = 8;
#[test]
fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "2");

View file

@ -4,8 +4,10 @@ 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<String> {
@ -34,55 +36,83 @@ fn read_input_file<P: AsRef<Path>>(path: P) -> Vec<String> {
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 {
( $($n:expr, $module:ident,)* ) => {
( $($module:ident,)* ) => {
$(mod $module;)*
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),
}
fn days() -> Vec<Day> {
vec![
$(Day {
n: $module::DAY,
title: $module::TITLE,
p1: Box::new($module::part1),
p2: Box::new($module::part2),
},)*
]
}
};
}
days! {
0, day0,
1, day1,
2, day2,
3, day3,
4, day4,
5, day5,
6, day6,
7, day7,
8, day8,
day1,
day2,
day3,
day4,
day5,
day6,
day7,
day8,
}
fn main() {
let mut args = std::env::args();
args.next();
let days = days();
let day = args
.next()
.expect("no day")
.parse::<u8>()
.expect("invalid day");
if let Some(day) = args.next().and_then(|a| a.parse::<u8>().ok()) {
let part = args
.next()
.and_then(|arg| arg.parse::<u8>().ok().map(|p| p.clamp(1, 2)))
.unwrap_or(1);
let part = args
.next()
.and_then(|arg| arg.parse::<u8>().ok().map(|p| p.clamp(1, 2)))
.unwrap_or(1);
let dobj = days.iter().find(|d| d.n == day).expect("Day not found");
let res = get_result(dobj, part);
let res = get_result(day, part);
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)];
println!("✨The result for day {}/{:?} is✨", day, part);
println!("{}", res);
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}")
}
}
}