summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/day_09.rs8
-rw-r--r--src/day_18.rs178
-rw-r--r--src/lib.rs48
-rw-r--r--src/main.rs59
-rw-r--r--src/printer.rs117
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)?))
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 51f906c..54ce211 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
+ }
+}