Compare commits
No commits in common. "acd627a0084083b05ee3b08eb06d5a5d013549ef" and "8575784a7c744376aa1dc09a3934343264de43d3" have entirely different histories.
acd627a008
...
8575784a7c
10 changed files with 73 additions and 172 deletions
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
89
src/day5.rs
89
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 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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
88
src/main.rs
88
src/main.rs
|
@ -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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue