diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/day_09.rs | 8 | ||||
-rw-r--r-- | src/day_18.rs | 178 | ||||
-rw-r--r-- | src/lib.rs | 48 | ||||
-rw-r--r-- | src/main.rs | 59 | ||||
-rw-r--r-- | src/printer.rs | 117 |
5 files changed, 382 insertions, 28 deletions
diff --git a/src/day_09.rs b/src/day_09.rs index 78cf69a..8c13228 100644 --- a/src/day_09.rs +++ b/src/day_09.rs @@ -11,18 +11,18 @@ impl Problem for Day09 { } impl Solution for Day09 { - type Answer1 = isize; + type Answer1 = usize; - type Answer2 = isize; + type Answer2 = usize; fn part_1(input: &str) -> anyhow::Result<Self::Answer1> { let data = parse_input(input)?; - Ok(data.into_iter().map(extrapolate_forward).sum()) + Ok(data.into_iter().map(extrapolate_forward).sum::<isize>() as usize) } fn part_2(input: &str) -> anyhow::Result<Self::Answer2> { let data = parse_input(input)?; - Ok(data.into_iter().map(extrapolate_reverse).sum()) + Ok(data.into_iter().map(extrapolate_reverse).sum::<isize>() as usize) } } diff --git a/src/day_18.rs b/src/day_18.rs new file mode 100644 index 0000000..1ed5100 --- /dev/null +++ b/src/day_18.rs @@ -0,0 +1,178 @@ +use crate::{Problem, Solution}; + +pub struct Day18; + +impl Problem for Day18 { + const DAY: u8 = 18; + + const INPUT: &'static str = include_str!("../input/day_18.txt"); +} + +impl Solution for Day18 { + type Answer1 = usize; + + type Answer2 = usize; + + fn part_1(input: &str) -> anyhow::Result<Self::Answer1> { + let mut pos = (0_isize, 0_isize); + let mut trenches = vec![(pos, 'S')]; + + let mut b = 0; + let mut dir = 'S'; + + for line in input.lines() { + let mut iter = line.split_whitespace(); + let c = iter.next().unwrap().chars().next().unwrap(); + let len = iter.next().unwrap().parse()?; + b += len; + + if "DU".contains(c) && dir != c { + dir = c; + if let Some((_, prev)) = trenches.last_mut() { + *prev = dir + } + } + + for _ in 0..len { + match c { + 'U' => pos.0 -= 1, + 'D' => pos.0 += 1, + 'L' => pos.1 -= 1, + 'R' => pos.1 += 1, + c => return Err(anyhow::format_err!("Invalid character: {c}")), + }; + + trenches.push((pos, dir)); + } + } + + trenches.sort(); + + let n = trenches + .group_by(|a, b| a.0 .0 == b.0 .0) + .fold(b, |acc, row| { + acc + row + .iter() + .map_windows(|[((_, start), c1), ((_, end), c2)]| { + if *c1 == 'U' && *c2 == 'D' { + start.abs_diff(*end) - 1 + } else { + 0 + } + }) + .sum::<usize>() + }); + + Ok(n) + + // 58396 > + // 58396 > + // 49897 == + } + + fn part_2(input: &str) -> anyhow::Result<Self::Answer2> { + let mut pos = (0_isize, 0_isize); + let mut trenches = vec![(pos, 'S')]; + + let mut b = 0; + let mut dir = 'S'; + + for line in input.lines() { + let (c, len) = parse_code(line); + b += len; + + if "DU".contains(c) && dir != c { + dir = c; + if let Some((_, prev)) = trenches.last_mut() { + *prev = dir + } + } + + for _ in 0..len { + match c { + 'U' => pos.0 -= 1, + 'D' => pos.0 += 1, + 'L' => pos.1 -= 1, + 'R' => pos.1 += 1, + c => return Err(anyhow::format_err!("Invalid character: {c}")), + }; + + trenches.push((pos, dir)); + } + } + + trenches.sort(); + + let n = trenches + .group_by(|a, b| a.0 .0 == b.0 .0) + .fold(b, |acc, row| { + acc + row + .iter() + .map_windows(|[((_, start), c1), ((_, end), c2)]| { + if *c1 == 'U' && *c2 == 'D' { + start.abs_diff(*end) - 1 + } else { + 0 + } + }) + .sum::<usize>() + }); + + Ok(n) + } +} + +fn parse_code(s: &str) -> (char, usize) { + let iter = &mut s + .split_whitespace() + .nth(2) + .unwrap() + .strip_prefix("(#") + .unwrap() + .strip_suffix(')') + .unwrap() + .chars(); + let dist = usize::from_str_radix(&iter.take(5).collect::<String>(), 16).unwrap(); + let code = iter.next().unwrap().to_digit(10).unwrap(); + let c = match code { + 0 => 'R', + 1 => 'D', + 2 => 'L', + 3 => 'U', + n => panic!("Invalid code: {n}"), + }; + + (c, dist) +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = indoc::indoc! {r#" + R 6 (#70c710) + D 5 (#0dc571) + L 2 (#5713f0) + D 2 (#d2c081) + R 2 (#59c680) + D 2 (#411b91) + L 5 (#8ceee2) + U 2 (#caa173) + L 1 (#1b58a2) + U 2 (#caa171) + R 2 (#7807d2) + U 3 (#a77fa3) + L 2 (#015232) + U 2 (#7a21e3) + "#}; + + #[test] + fn test_part_1() -> anyhow::Result<()> { + Ok(assert_eq!(62, Day18::part_1(INPUT)?)) + } + + #[test] + fn test_part_2() -> anyhow::Result<()> { + Ok(assert_eq!(952408144115, Day18::part_2(INPUT)?)) + } +} @@ -4,7 +4,9 @@ iter_array_chunks, array_windows, iter_intersperse, - impl_trait_in_assoc_type + impl_trait_in_assoc_type, + macro_metavar_expr, + slice_group_by )] pub mod day_01; @@ -24,6 +26,50 @@ pub mod day_14; pub mod day_15; pub mod day_16; pub mod day_17; +pub mod day_18; +pub mod printer; + +pub use printer::Printer; + +pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>; + +#[macro_export] +macro_rules! get_days { + // `()` indicates that the macro takes no argument. + ($($day:path),*) => { + pub fn get_days() -> [(&'static str, [Box<dyn Fn(&str) -> anyhow::Result<usize>>; 2]); ${count(day)}] { + [ + $((<$day>::INPUT, + [ + Box::new(<$day>::part_1), + Box::new(<$day>::part_2), + ]) + ,)* + ] + } + }; +} + +get_days!( + day_01::Day01, + day_02::Day02, + day_03::Day03, + day_04::Day04, + day_05::Day05, + day_06::Day06, + day_07::Day07, + day_08::Day08, + day_09::Day09, + day_10::Day10, + day_11::Day11, + day_12::Day12, + day_13::Day13, + day_14::Day14, + day_15::Day15, + day_16::Day16, + day_17::Day17, + day_18::Day18 +); pub trait Problem { const DAY: u8; diff --git a/src/main.rs b/src/main.rs index 33333c1..0883c66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,40 @@ -use aoc_2023::{ - day_01::Day01, day_02::Day02, day_03::Day03, day_04::Day04, day_05::Day05, day_06::Day06, - day_07::Day07, day_08::Day08, day_09::Day09, day_10::Day10, day_11::Day11, day_12::Day12, - day_13::Day13, day_14::Day14, day_15::Day15, day_16::Day16, day_17::Day17, Solution, -}; +fn main() -> Result<(), Box<dyn std::error::Error>> { + let mut args = std::env::args().skip(1); + let days = aoc_2023::get_days(); -fn main() -> anyhow::Result<()> { - Day01::solve()?; - Day02::solve()?; - Day03::solve()?; - Day04::solve()?; - Day05::solve()?; - Day06::solve()?; - Day07::solve()?; - Day08::solve()?; - Day09::solve()?; - Day10::solve()?; - Day11::solve()?; - Day12::solve()?; - Day13::solve()?; - Day14::solve()?; - Day15::solve()?; - Day16::solve()?; - Day17::solve()?; + let days_n = if let Some(first) = args.next() { + if "all".contains(first.as_str()) { + (1..days.len() + 1).collect::<Vec<_>>() + } else { + std::iter::once(first) + .chain(args) + .map(|d| d.parse::<usize>()) + .collect::<Result<Vec<_>, _>>()? + } + } else { + vec![days.len()] + }; + + for day in days_n { + if day == 0 || day > days.len() { + eprintln!("Error: Invalid day: {day}"); + continue; + } + + let (input, parts) = &days[day - 1]; + + for (part, f) in parts.iter().enumerate() { + let timer = std::time::SystemTime::now(); + let answer = f(input)?; + let duration = timer.elapsed()?; + println!( + "Day {day}.{}: {answer} ({}ms)", + part + 1, + duration.as_micros() as f64 / 100f64 + ); + } + println!() + } Ok(()) } diff --git a/src/printer.rs b/src/printer.rs new file mode 100644 index 0000000..7091124 --- /dev/null +++ b/src/printer.rs @@ -0,0 +1,117 @@ +pub struct Printer<W: std::io::Write> { + lines: usize, + buffer: String, + writer: W, +} + +impl<W> From<W> for Printer<W> +where + W: std::io::Write, +{ + fn from(value: W) -> Self { + Self { + lines: 0, + buffer: String::new(), + writer: value, + } + } +} + +impl<W: std::io::Write> Printer<W> { + pub fn write(&mut self) -> std::io::Result<&mut Self> { + writeln!(self.writer, "{}", self.buffer)?; + self.writer.flush()?; + + self.lines = self.buffer.lines().count() + 1; + self.buffer.clear(); + + Ok(self) + } + + pub fn write_pause(&mut self, prompt: &str) -> std::io::Result<&mut Self> { + use std::io::Read; + + self.buffer.push('\n'); + self.buffer.push_str(prompt); + + self.write()?; + let _ = std::io::stdin().read(&mut [0u8])?; + Ok(self) + } + + pub fn write_pause_condition( + &mut self, + prompt: &str, + cond: bool, + ) -> std::io::Result<&mut Self> { + if cond { + self.write_pause(prompt) + } else { + self.write() + } + } + + pub fn write_sleep(&mut self, time: u64) -> std::io::Result<&mut Self> { + self.write()?; + std::thread::sleep(std::time::Duration::from_millis(time)); + + Ok(self) + } + + pub fn clear(&mut self) -> &mut Self { + self.buffer + .insert_str(0, &"\x1b[1A\x1b[K".repeat(self.lines)); + self.lines = 0; + self + } + + pub fn with(&mut self, fmt: std::fmt::Arguments) -> &mut Self { + self.buffer.push('\n'); + self.buffer.push_str(format!("{}", fmt).as_str()); + self + } + + pub fn with_grid<T: std::fmt::Display>(&mut self, grid: &[Vec<T>]) -> &mut Self { + let s = grid + .iter() + .flat_map(|row| { + row.iter() + .map(|s| s.to_string()) + .chain(std::iter::once("\n".to_string())) + }) + .collect::<String>(); + + self.buffer.push('\n'); + self.buffer.push_str(s.as_str()); + + self + } + + pub fn with_grid_path<T: std::fmt::Display>( + &mut self, + grid: &[Vec<T>], + highlight: &[(usize, usize)], + ) -> &mut Self { + let s = grid + .iter() + .enumerate() + .flat_map(|(x, row)| { + row.iter() + .enumerate() + .map(move |(y, s)| { + if highlight.contains(&(x, y)) { + format!("\x1b[1;30;42m{s}\x1b[0m") + } else { + s.to_string() + } + }) + .chain(std::iter::once("\n".to_string())) + }) + .collect::<String>(); + + self.buffer.push('\n'); + self.buffer.push_str(s.as_str()); + + self + } +} |