Compare commits
4 commits
8575784a7c
...
acd627a008
Author | SHA1 | Date | |
---|---|---|---|
acd627a008 | |||
0b2475d2bf | |||
fd056ae79e | |||
452b15fd03 |
10 changed files with 172 additions and 73 deletions
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
91
src/day5.rs
91
src/day5.rs
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue