From 75c7b08449a42982fe888ae88bf773c566c8074d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=BCdiger=20Ludwig?= Date: Fri, 28 Jul 2023 23:19:14 +0200 Subject: [PATCH] day17 finished --- data/day17/blocks.txt | 17 ++ data/day17/example01.txt | 1 + data/day17/input.txt | 1 + src/days/day17/mod.rs | 424 +++++++++++++++++++++++++++++++++++++++ src/days/mod.rs | 4 +- 5 files changed, 446 insertions(+), 1 deletion(-) create mode 100644 data/day17/blocks.txt create mode 100644 data/day17/example01.txt create mode 100644 data/day17/input.txt create mode 100644 src/days/day17/mod.rs diff --git a/data/day17/blocks.txt b/data/day17/blocks.txt new file mode 100644 index 0000000..fbcc382 --- /dev/null +++ b/data/day17/blocks.txt @@ -0,0 +1,17 @@ +#### + +.#. +### +.#. + +..# +..# +### + +# +# +# +# + +## +## \ No newline at end of file diff --git a/data/day17/example01.txt b/data/day17/example01.txt new file mode 100644 index 0000000..fb5d89e --- /dev/null +++ b/data/day17/example01.txt @@ -0,0 +1 @@ +>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>> \ No newline at end of file diff --git a/data/day17/input.txt b/data/day17/input.txt new file mode 100644 index 0000000..9906efb --- /dev/null +++ b/data/day17/input.txt @@ -0,0 +1 @@ +><<>>>><>>><<>><<>><<<<>>><<<<>><<<>>>><<>><><<<><<<>><>>>><<<>><<<<><<><<<>>><>><<<<>><>>><<<<>>>><<>>>><<<>><><<<<><<>>><>>>><>>><>>>><<<<><<>>><<>>><<<<>>>><<<><>><<>><<<<>>><<<>>>><>><>>>><>>><<<<><<>><>>>><<<<><<>>>><<>>><>><<<>><<<<>>>><<<>>>><<<>>>><<<<>>><<<<>>><>><<>>>><<><<<<>><<>><<>>><<<>>>><>><<>>>><<>><<<<>>>><<>>>><>><>>><<<<><<>><<>>>><<><<<>>>><>>><<<<>>><<<>><<<><>>>><<<<><<<<>>><<<<><<<>><>><<<<>>>><<>>><<>><<>><<<><<<<><<<>>>><<<><<<<><<<<>><<<<>>><>>><<<<>>><<<><<<><>><<<<>><<<><<<<>>>><<>><<>>><<<<>>>><<><<<<>>>><<>>>><<<>>><<>><<><<<><<<<>>><<<<><><>>><<<<>>><<<>>><>>>><<<>>>><>>>><<<<>>><<<>><<>><<><<<<>>>><<<><<<<><<<<>><<<<>>>><>>>><<>>><<<<>><<<>>>><<><><<<<>>><>>>><<<>>><<<>>><>>>><>><<>>><>><<<><<<>>>><<<<>>>><><<>>><>><<<>>><<<>>><<<<>>>><<<<>>>><<>>><<<><<>><<<<><<<<>>>><><<>>>><<>>><<>>>><<<>>><<>><<>>><>>>><<<<>>>><<>>>><<>>>><<>>><<<>>><<<>>>><><<<<>>><>>>><<<>><<<>>>><<>>>><<<<>>><>>>><<<>>>><<>><>>>><<<>><<<><<<>>>><><<>>><<><<>>><<>>>><<>>>><<<>>><<<>><<><>><<<<>>><<<><<>>>><<<>>><<><<<<><<><<<<>>>><<<<>>>><<<><>>><>>><<<<><<<<><>><<<<>>>><<>>><>>><<<<>><><<<>>>><>>><<<>><>>>><<<<>>><<<>>>><<>><>><<<>><<<<>>>><<<<>>><<<<><<>>>><<>><<<>><<<>><<<><<><<<<>><<>>><<>><<<>>><<<<>><>>><<<>>>><<<<><<>>><<<<>><<<>>>><<<><<<><<>>><<>>>><<<<>>>><<<><<<>>>><<<<>>>><<<>><>>><<>>>><><<<<><<<<>><<><>>><>><<>>>><<<<><><<<<>>><<<>>>><<<>><<<>>><<<>>><<>><>>><<<><><>>>><>><>><<<>><<<<>><<<>>>><<<<>>><<<<>><>>>><>>>><<<>><>><<>>><<<<>><>>><<<>><>><<>>>><<<<>>><<>>>><<<><<>><<>>>><>><>>><<><>><<>>>><<<>><><<>>>><<<>>><<>><<<>>><>>>><>><<>><>><<<<>>><>>><<>><<>>>><<<><<<<><<>>>><<>><<>>><>>><<<>>><<>>>><<<<><<><><<<<>>>><<<<>>><<<><>>><<<<>>>><<<>><>>><<<>><<<>>>><<>><<<><<><<<<><><>>>><<>><>>>><>>>><<<<>>><<>><<<<>><<>>><<<>><<<<><<<>>><<<>>>><<><<><<<<>><>>><<><<<<>><<><><>><<<><<<<>><<<><<>><<>><>>><>>><<<<>>>><>>>><<<<><>><<<<>>>><<<<>>><<<<>>>><<<>>><<>><>><>>><<>><<<>>><<<>>><<<<>>>><<<<>>><<<>>>><>>>><><><<>>>><<>>>><<<<>>><<<<>>>><<>>><>><<<<>>><<<<>>>><<<><<<<>>>><<>>><<<><>><<>>>><<<>>><<<>>><>>><<<>>>><<<<>>>><<<><<>><<<><>>>><<>>><<>>>><>>><<>><<<>>>><<<<>>>><<<<>>><>>>><><<<<>><><<><><<<<><<<<>>>><><<><<<<>><<<<><<<<>>>><<<>>><<<<>>><>>><<>>><<<>><<<<>><>>><<<<><>><<<<>>>><>>>><<>>><>><>>><>>>><>><<<<>>><<<>><<<>>><<>>><<<>>>><<><<<<>>>><<<<>>><<<<>><<<<>>>><>>>><<<<>>>><<<>>>><><<<<>>>><<>><<<>><<<>>>><<>>><<<<>>><>>>><<<>><>>>><<>>>><<>>>><<><><<><<>><<<<>>><<<<>><<<>><>>><>>>><<<>>>><<>><><<<><<<<>>>><<><<<<><<<>>><<<><>>>><<>>>><<<<>><<<>><><<<>><<<<><<<>>>><<><><<<<>>><><>>>><>>>><<<<>><<><>>><<>>>><<>>><>>><<>><<<>><><><><>><<>>><>><<<>>>><<<>><>>><>>>><>><<>>><<>>>><<<<>>><<>><<<<>>><>><<<<>>><>>>><><<<>><<<>>><<<<>>><<><<<><<<<>>><>>><><<<<><<<>>><<>><><<<<>><<<><><<<<>>>><<<><><<>>><<<>><>>><<>><<<<><>><<><<<<>><<<>><>>><>>>><<<><<<>><<>><<<<>>>><<>><>>><>>><<<<>>><>>>><<<<>>>><<>>><<>>>><<<<><<<><<<>><<<<>><<<>>><<<<>>>><<<>>>><<>>>><<<<><<<<>>><<<<>><<><<<<><>><<<>><<<<><<>>>><<<><<<<>><<<<><><<<>>><>>>><<<<>>><<<<>><>><<<<><<<><<<>>><<<><<>>><<<><<<>>>><<<>>><<<><<<><<><<<>>><<<<>><>>>><<<<>>><<<>>><<<<><<>>><<<<>>><<<<>><>>><<><<<><<<<><<>><<<>>><<><<<>><<<<>><<>>><<<<><<<>>>><>>><<<<><>>>><<<<><<<<>><<>>><<><<<<>><<><>>><<<<>>><<<>><<<>>>><>><<>>><>>>><>>>><<<<>><<><<<>>>><<>><<<<>>><<>>><<>><<>><>>>><<><<<<>>><>>>><<>>>><<>><>>><<<<>>>><<<><>>><<>>><>>>><<<<><<>><<<>>>><<>><<<<>><<<><<>>>><>>>><<<<><><<<><<<<>><<<>>>><>>>><<<><><<<><<<>>><>>><<<>><<<>>>><<<><<<>>><<<<>>><<>>><<<>><>><>><<>>>><>><<<>>><<<><<<<>>><<<>>>><<>>><>><<>>>><<>>>><<>>><<><<>><<>>><<<<>>><<<<>><<<<>>>><<<>>>><<<<>><<>><<<<>><<<>>>><<>>>><>>><<<>><>>><>><<<><<<><<>>>><<<<>><><>><<<<>>>><<>>>><<>>>><<<>>><<<>>><<<<>>>><>>>><><<><<>>>><<>>>><>><<>><>><<>><<>><<<>>>><<>><<><<><<><<<<>>><<<<><><>>><<<>><><<<><<<<>><<>>>><<>><<<<>><<>><<>>><<><<>>>><>><<<<>>><><<>><>>>><<<<>>><<>>>><<><<<>>>><><<<>><>><<>><<><<<<>>><<><><<<<>>>><>>><><<>>>><<>>><>>>><<<<>>>><<>><<<>><<<>>>><>>>><<<><>><<>><<<<>>>><<<>>>><<<>>>><<<>><<<<>><<<<><<<<>>>><<>><<<<><<>><<<>>><<>>>><<<<>>>><>>>><<>>>><<<<>>><<<<>><<<>><<>>>><>><<>><<>><<>><<<<>>>><<<><<>>><<<><<<<>><<>><<<<>>><<<<>>><<<><>>>><<<<>>><<<<>>><>><<>>>><<<<><<<<>>>><<<<><<>>><<<<><<><<<>>><>><<<<>>>><<<><<<>>>><>>>><<<>><><<<<><<<>>><<>>><<<<>><<<<>>><<>>><<>>><<<<><<<>><>>>><>><<<<>>><>>><<>>><<>>><<><<>>>><<<>>>><<<>>><>>><<<>><>>>><<<>>><><<>>>><<<<>>><>>><<<<>>>><<<>><<<><<>>><<<>>><<<<>><<<<>><>><<<>><<<<>>>><<><>><>>>><<<<>>><>>><<<<>>><>>><>>><<<>>><<<><<<>><>><<<><<>>><>>><<<<>>><<<<>>><<>>>><>>><<<>>>><<<<><<>><<><><<<><<>><>><<<>>><<<<>>><>>><<<>>>><<<<>>>><<<>>><><<<<>>><<>>><<>>>><<>><<<<><>>><<<>>>><<><<><<>>><<><<<><<<>>>><>>><><><>>>><<>>><><<<>><><<>><><>><>>><><<<><<><>>><<<<>><<<<>>><>>><<>><<>>>><<<<><<<><<<<>>><<<<>><<<<>>><<>><<<>><<>><<>>>><<<<>>>><<>><<<><>><<<<>>>><<>><<><<<>><<>><>><<<>><<<>><<<>>>><<>>><<>><<<>><<>>><><>><<>>><>>><<<<>>><<<<>><<><>>><><<>>>><<><><<<>><>><<>>>><<<>>>><<<>><>>>><<<<>><<>>>><>>>><<<>><<>><>>><<<<><><><<<>>>><>>>><<><<<><><<<>>>><<<<>>>><><><<<>>><<<>><<<>>><<<<>>>><<<<>>><<>><><<<>>>><<>>><>>>><<<<>>><>>>><<<><<<>><<<>><<<><<<><<<>>>><<<<>>><<>>><>>><<>><>>>><<>>>><<>>>><<<>>>><<<>>><>>><<<<><><<>>><<<<>>><>><<<<>><>><<<><><<<<><>>><<<>>>><>>>><>>>><<<><<<<><<<<>>><<<>><<<>>><<>>>><<>>>><<<<>><<<>>><<<>>>><<<<><<>>>><<>><<<>><<<<><<><><<><>>><><<<><<<<>>><>>>><<><<<<>>>><>>>><>>><<>>><<<<>>><><<<<>>>><>>><<>><<<>>>><<>>>><<<>>>><<><<>>>><>><<<<><<><<<<>><<<><<<>>>><><>>><<>>>><<>><>><<<<>><<<>>>><>><<<>><<<>><<<><<<>><<<>><><<<<>>>><<>>><<<>>><<>>><>>>><>>>><<><<<>>>><<>>><>><<<<>><<><<<>>>><<>>>><>><<<<>>><<<<>>>><<<<><<<<>><<>>><>>><<<<>>>><<<>>><>><<<<>>><><<<<><<>>><<>>><><<<><<<<>><>>><<<<><<<><<><<<<><<<<>><<<<><<<<>>>><<<<>><>>>><>>><<<<><<>><>><<>>>><<<<>><<<<>>><>>><<<>>><<<>>><<<<>>><>>><<<<>>>><<<><<<><<<><<><<<>><<<>><<<>>><>><<>>><<>><<<<>>><<<<>><<<<><>>>><>>>><<<>>><<<>>><<><<<>>>><<<><<>><<>>><<><<<<><<<<>><<<>><>>><<<<>><<><<<<>>><<>><<<><>><<<<>><<<>><<>>><<<<><<>>>><<><<>>>><<<<>>>><<<><<<><<<<>>>><<<<><<>>>><<>>>><>>><<<>>>><<<<>>>><>>>><>>>><<<<><<><>><<><<>>>><>>>><><<<<>><<<<>><<>>><<>><>>>><<><<><<>><<<<>>><<<<>>>><<>><<>><<><><>><<<>>>><<<><<<>><<<<><<<<>>>><<<<>>>><<<>>><<><<>>>><<<><><<>>>><<<><<<><<<<>><>>>><<><<<><>>>><<>>>><<<<>><<<>><<>><<>><<>>>><<>>><>>>><>>><<>>>><<<>><<<>><<>><<<><<>><<<<>><<>>>><<<>>>><<<>>>><<><<<<><<<>>>><>>><<<><<<>>><<<<>>>><<><<<<>><<<>>><>>>><<<>>><<<<>>><<<>>>><>>>><<<<><<>><<<<>><><<<>><<>><<<>><<>>>><>>>><><<<>>>><<>>><<<>>>><<<<>><<>>>><<><<<<>><<<<>>><><<>><<<><<<>>>><<<><<<<>>><>>>><<<>><<>>><<>><><<><<<<><>>><>>>><>><>><<<>>>><<<<><<<<>>>><>>><<><<<>><<<>><>><>>>><><<>>>><>>><<<<><<<><>><><>>><<<><<<><<<<>>><<<>>><<<>><<<>>>><<<>>>><<<>>>><<<>>>><<<<>>><<<<><>>><><<<>>>><>>><<>><>><<<>>>><<<<><<>>>><>>><>>><<<>><<<<>>><<<><>>><>>><<><<><<<>>><<<<>>>><<<<>>>><>><<<<><<>>><<<<>>>><<>>><<<<><>><<>>><><<<<>>>><>>><<<<><<<<>>><<<<><<>>>><<<<>>>><<<<>>><<<>><>>><>>><<<<>>><>><<>><<><<<>>><<<><<<<>>>><>>>><<<<><<<<><<>>><<<>><>>><<<<>>>><<<><<<>><<>>>><><><<<<>><<<<><<<<><<<>>><<<>>><>><<<<>><<<<>><<<>>>><>><<>><<<<>>>><<<<>>>><<<>>><<<<>>><<<<>>><<<><<<<>>>><<>>>><<>>><<<>>>><><<<<><<<<><<>><>>>><>>><<<>><>>><<<<><<<<><<>>><>>><><<<><<>><<>>><<>>>><<<><>>>><<<>><<<><<>><<>>><<<<>>><<><<><><>>><><<>>><<<<><<<>>>><<<>>>><>>><<>>>><<<><<<>><<<>><<<<>>><<><<><<<<>>>><<>>>><>>><<>>>><><<<<>>><<<<>><<><<>>>><<<<>><<<<>>><<<<>>>><<<>>>><>>>><<<<>>>><>><<>>><<<><>>>><<>>>><<<>>><<>>>><<<>><<<>>>><<>><<<>>><<>><<><>><<><<<>>><<<>>><<<<>><<>>><>>>><<<<><<<><><>>><<<>><<<<>>><<>>>><>><>>>><<<<>>>><>>>><<<><<>>>><<<<><<<>>>><<<><><<<<>><<>>>><>>><<><>>><<>>>><>>><<>>><><<>>>><<<<>><>>><<<><<>><<<<>>><<<>><<<<>>><<>>><>><<<>><>>><<<<>>>><<<>><<>>><<<<><<>>>><<<<>>>><<><><<<<><<<>>>><<>>><<<<>>><<>>><<<<>><<<<><<>>>><<><>>><<<>><<<><<<>>><<<<>><<><>>>><<<><>><<>><>>>><<<<>><<<<>><><<<<><<<>>><<<<>><><>><<<<>>><>>>><<<<>>>><><<<<>>><<<<>>><<<<><><<<<>>>><<<>>>><<<<>><<>>><<<<>><<<><<>><><<<><<<>>><<<<>>>><<>>>><<>><<>>>><<<<>>><>><<><<>><<>><<<<><<<<>><<<>><<<>><<>>>><<<>>>><<<<>><>><>>>><<><<<<>><<<>><<<>><>>>><<<>>>><>>><<<<>>>><<<>>>><<><>>>><<<<><<>>>><<>>><<<<><<><<<<>>>><<>>><<><<<><<>><<<>><<>><<<><><<<<>>><<<<><<><<>><<>>><<<<>>><>>>><>>>><>>><><<<>><><<<>>>><<>>><<<<><<<><<<<><<<>>>><<>><>>><<<>>><>>><<>>>><>>>><>>>><<<>>>><>><<<><>><<<>>>><<>>><<>><>>>><>><<<<>>><<>><<><>>><><<<<>>>><<<><<<<>>><>>>><<><<<>>>><>>><<<<>><<>><<>>><>><>>><<<>>><>>>><<<><<<>>>><<<<>><<<>>><<<>>>><><<>><<><<<>>><><<<<><>>>><<<<>>><<><>>>><><<<<>>>><<<<>>>><><<><<>>><>><<<<>>><<<<><>>>><<>><<<<>>><>><<><<><<<>>>><<<<>><<<<>>>><>><<<<>>>><<<>>><<>>><><<<<><<<<>>><<>>><<<>>>><<<>>><><>><>>>><<<<><<>><<<<>>>><<<<>>>><<<<><<<><><>><><<<>>>><<<<>><<>><>>><<<>>><>>><<><<><>><>><<<>>>><<<<>>><><<<<><<<>>><<><<<>>><<<<><<<>>><<<>>>><<>>>><<>><>>><><<><<<>><>><<<<>><<>><>><<<<><<<<><>>>><<<<>>><<<><<<>><<><<<<>>><<<>>><>><><>>>><<<><>><<<<><>><<<<>>><><<><>><><<<<>>>><<><<<>>><<>>>><<<<>><<<>>><>>>><<>><>><<<<>>><<<>><><><>>>><<<>><<<<><<<>><<<<>><<<>>>><<>>><<<<>><><<>><<<>><<<<>>><<<>><>>>><<><<><>>>><<<><<>>>><><<<>><<>>><>><<<<>>><<<<><<<>>><<<<>>>><<>>>><<<>><<>><<<<>>><<<<>><>><<<>><<>>>><><<<>>>><<<>><>>>><<<>>>><<<<><<>><<<<>>><<>><><<>>><<<>><<>><<<>><<>>><<>>><<<>><<<>>><<>><<>><<<<>>>><<<<>>>><<<<><<>><<<>>><<<>>>><<<>><>>><><>><>>><><<>>>><>>><<<<>>>><<<>>>><>>><<<><<<>>><<<>><>>>><<<<>>><<<<><<<>>><<>>><<<>><<<<>><<<><<<>>>><><<<<>><<<<>>>><<<><<<>>><<<><<<<><>><>><<>>><<<>>><>>><><<<><<>>>><<<>>><>>><>>><>>>><<<><<<<>>>><<><<>><<<><<<><<<<><>><<><<<>>><<>>><<><<>>><<<<>><>><<<>>><<<<>>><>>><<<<>><>><<<<>><<>>>><<>>><>><<>><<<>><<<>>><<<<>><<<><<<>>>><<<>>><<>><<<<>><<<<>>><<<<>>>><<<>><<<<>>><>><<>>><<<>>><<<>>>><<<<>>>><<<><<<>>><<<>>><>>><>><<<<><><<<>><<<>>>><<<>>>><<<<>>>><<>><<>>><>><<<>>><<>><>><>><<<>>><>>><<<<>>><<<><<<>>><<<>><<<>><<<<>>><<<<>><<><><>>><<><<<<>>><<<<><<>><<<>><>><<<>>><<<>>>><<<<>>>><>>><<>>><<<<>><<<<>>>><<<>><<<>>><<<<><<<<><><<<>><>><<<<>><>>><<<><<<<>>><<<>><<<<><<<<>><>>><>><<><<>>>><<<<><<><<<<><<<<>>><<<><<<<>>><<<<><<<>>><<< \ No newline at end of file diff --git a/src/days/day17/mod.rs b/src/days/day17/mod.rs new file mode 100644 index 0000000..07f954a --- /dev/null +++ b/src/days/day17/mod.rs @@ -0,0 +1,424 @@ +use std::{cell::Cell, marker::PhantomData, ops::Index}; + +use crate::common::file::read_lines; + +use super::template::{DayTrait, ResultType}; +use itertools::Itertools; +use thiserror::Error; + +const DAY_NUMBER: usize = 17; +const STACK_WIDTH: usize = 7; + +pub struct Day; + +impl DayTrait for Day { + fn get_day_number(&self) -> usize { + DAY_NUMBER + } + + fn part1(&self, lines: &[String]) -> anyhow::Result { + let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + + let result = Day::run(pushes, 2022)?; + + Ok(ResultType::Integer(result as i64)) + } + + fn part2(&self, lines: &[String]) -> anyhow::Result { + let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + + let result = Day::run(pushes, 1000000000000)?; + + Ok(ResultType::Integer(result as i64)) + } +} + +impl Day { + fn run(push_cycle: Dispenser, max_cycles: i64) -> Result { + let raw = read_lines(DAY_NUMBER, "blocks.txt")?; + let rock_cycle = Dispenser::new(Rock::parse(&raw)?); + let mut cycle = 0; + let mut stack = Stack::new(); + + let mut last_drop = None; + let mut prev_bottom = 0; + let mut additional_height = None; + + while cycle < max_cycles { + let bottom = stack.one_rock(rock_cycle.next(), &push_cycle); + if bottom < prev_bottom { + let drop_distance = prev_bottom - bottom; + + match last_drop { + None => { + last_drop = Some((bottom, drop_distance, cycle, None)); + } + Some((_, last_distance, _, _)) if last_distance < drop_distance => { + last_drop = Some((bottom, drop_distance, cycle, None)); + } + Some((last_bottom, last_distance, last_cycle, None)) + if last_distance == drop_distance => + { + last_drop = Some(( + bottom, + drop_distance, + cycle, + Some((bottom - last_bottom, cycle - last_cycle)), + )); + } + Some((last_bottom, last_distance, last_cycle, Some((growth, period)))) + if last_distance == drop_distance => + { + if growth == bottom - last_bottom && period == cycle - last_cycle { + let first_bottom = last_bottom - growth; + let all_equal = (0..growth).all(|row| { + stack.0[first_bottom + row] == stack.0[last_bottom + row] + }); + if all_equal { + let iterations = (max_cycles - cycle) / period; + additional_height = Some(iterations * growth as i64); + cycle += iterations * period; + } + } else { + last_drop = Some(( + bottom, + drop_distance, + cycle, + Some((bottom - last_bottom, cycle - last_cycle)), + )) + } + } + _ => {} + } + } + prev_bottom = bottom; + cycle += 1; + } + match additional_height { + None => Ok(stack.height() as i64), + Some(height) => Ok(stack.height() as i64 + height), + } + } +} + +#[derive(Debug, Error)] +enum RockError { + #[error("IO Error")] + RockIOError(#[from] std::io::Error), + + #[error("A rock must not be zero length or hight")] + MustNotBeEmpty, + + #[error("All lines in a block must be the same length")] + AllLinesSameLength, + + #[error("Unknown direction: {0}")] + UnknownDirection(char), +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct Rock { + positions: Vec<(usize, usize)>, + width: usize, + height: usize, +} + +impl Rock { + pub fn new(blocks: Vec>) -> Result { + let height = blocks.len(); + if height == 0 { + return Err(RockError::MustNotBeEmpty); + } + + let width = blocks.iter().map(|line| line.len()).max().unwrap_or(0); + if width == 0 { + return Err(RockError::MustNotBeEmpty); + } + for line in blocks.iter() { + if line.len() != width { + return Err(RockError::AllLinesSameLength); + } + } + + let positions = blocks + .iter() + .enumerate() + .map(|(y, line)| { + line.iter() + .enumerate() + .filter_map(move |(x, block)| block.then_some((x, height - 1 - y))) + }) + .flatten() + .collect_vec(); + + Ok(Rock { + positions, + width, + height, + }) + } + + pub fn parse(lines: &[String]) -> Result, RockError> { + lines + .iter() + .batching(|it| { + let blocks = it + .skip_while(|line| line.is_empty()) + .take_while(|line| !line.is_empty()) + .map(|line| line.chars().map(|c| c == '#').collect_vec()) + .collect_vec(); + + if blocks.is_empty() { + None + } else { + Some(Rock::new(blocks)) + } + }) + .try_collect() + } +} + +#[derive(Debug, Clone, Copy)] +enum Push { + Left, + Right, +} + +impl Push { + pub fn parse(c: char) -> Result { + match c { + '<' => Ok(Push::Left), + '>' => Ok(Push::Right), + _ => Err(RockError::UnknownDirection(c)), + } + } +} + +struct FallingRock<'a> { + rock: &'a Rock, + bottom: usize, + left: usize, +} + +impl<'a> FallingRock<'a> { + pub fn new(rock: &'a Rock, stack_height: usize) -> FallingRock<'a> { + FallingRock { + rock, + bottom: stack_height + 3, + left: 2, + } + } + + pub fn try_push(mut self, push: &Push, stack: &Stack) -> Self { + let left = match push { + Push::Left => { + if self.left == 0 { + return self; + } + self.left - 1 + } + Push::Right => { + if self.left + self.rock.width >= STACK_WIDTH { + return self; + } + self.left + 1 + } + }; + if self.check_position(left, self.bottom, stack) { + self.left = left; + } + self + } + + pub fn try_drop(mut self, stack: &Stack) -> Result { + if self.bottom == 0 { + return Err(self); + } + let bottom = self.bottom - 1; + if self.check_position(self.left, bottom, stack) { + self.bottom = bottom; + Ok(self) + } else { + Err(self) + } + } + + fn check_position(&self, left: usize, bottom: usize, stack: &Stack) -> bool { + self.rock + .positions + .iter() + .all(|(x, y)| *y + bottom >= stack.height() || !stack[*y + bottom][*x + left]) + } + + fn reach(&self) -> usize { + self.bottom + self.rock.height + } + + fn positions(&'a self) -> impl Iterator + 'a { + self.rock + .positions + .iter() + .map(|(x, y)| (*x + self.left, *y + self.bottom)) + } +} + +struct Stack(Vec<[bool; STACK_WIDTH]>); + +impl Stack { + pub fn new() -> Stack { + Stack(vec![]) + } + + #[inline] + pub fn height(&self) -> usize { + self.0.len() + } + + pub fn settle_rock(&mut self, rock: FallingRock<'_>) { + for _ in self.0.len()..rock.reach() { + self.0.push([false; STACK_WIDTH]); + } + for (x, y) in rock.positions() { + self.0[y][x] = true + } + } + + fn one_rock(&mut self, rock: &Rock, push_cycle: &Dispenser) -> usize { + let mut rock = FallingRock::new(rock, self.height()); + loop { + let push = push_cycle.next(); + rock = rock.try_push(push, &self); + match rock.try_drop(&self) { + Ok(next_rock) => rock = next_rock, + Err(rock) => { + let bottom = rock.bottom; + self.settle_rock(rock); + return bottom; + } + } + } + } +} + +impl std::fmt::Display for Stack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let text = self + .0 + .iter() + .rev() + .map(|line| line.iter().map(|b| if *b { "#" } else { " " }).join("")) + .join("\n"); + write!(f, "{}", text) + } +} + +impl Index for Stack { + type Output = [bool; STACK_WIDTH]; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +struct Dispenser<'a, T> +where + T: 'a, +{ + data: Vec, + current: Cell, + _marker: PhantomData<&'a T>, +} + +impl<'a, T> Dispenser<'a, T> { + pub fn new(data: Vec) -> Self { + Dispenser { + data, + current: Cell::new(0), + _marker: PhantomData, + } + } + + fn next(&'a self) -> &'a T { + let idx = self.current.get(); + self.current.set(self.current.get() + 1); + if self.current.get() >= self.data.len() { + self.current.set(0); + } + self.data.get(idx).unwrap() + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::common::file::read_lines; + use anyhow::Result; + + #[test] + fn test_part1() -> Result<()> { + let day = Day {}; + let lines = read_lines(day.get_day_number(), "example01.txt")?; + let expected = ResultType::Integer(3068); + let result = day.part1(&lines)?; + assert_eq!(result, expected); + + Ok(()) + } + + #[test] + fn test_part2() -> Result<()> { + let day = Day {}; + let lines = read_lines(day.get_day_number(), "example01.txt")?; + let expected = ResultType::Integer(1514285714288); + let result = day.part2(&lines)?; + assert_eq!(result, expected); + + Ok(()) + } + + #[test] + fn read_blocks() -> Result<()> { + let day = Day {}; + let raw = read_lines(day.get_day_number(), "blocks.txt")?; + let rocks = Rock::parse(&raw)?; + let expected = Rock::new(vec![vec![true]; 4])?; + assert_eq!(rocks[3], expected); + + let angle_rock = &rocks[2]; + assert_eq!( + angle_rock.positions, + vec![(2, 2), (2, 1), (0, 0), (1, 0), (2, 0)] + ); + + Ok(()) + } + + #[test] + fn drop_one() -> Result<()> { + let day = Day {}; + let lines = read_lines(day.get_day_number(), "example01.txt")?; + let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + let raw = read_lines(day.get_day_number(), "blocks.txt")?; + let rocks = Dispenser::new(Rock::parse(&raw)?); + + let mut stack = Stack::new(); + stack.one_rock(rocks.next(), &pushes); + + assert_eq!(stack.0, vec![[false, false, true, true, true, true, false]]); + + Ok(()) + } + + #[test] + fn drop_some() -> Result<()> { + let day = Day {}; + let lines = read_lines(day.get_day_number(), "example01.txt")?; + let pushes = Dispenser::new(lines[0].chars().map(|c| Push::parse(c)).try_collect()?); + + let result = Day::run(pushes, 10)?; + + assert_eq!(result, 17); + + Ok(()) + } +} diff --git a/src/days/mod.rs b/src/days/mod.rs index aabc2dc..f639945 100644 --- a/src/days/mod.rs +++ b/src/days/mod.rs @@ -14,6 +14,7 @@ mod day13; mod day14; mod day15; mod day16; +mod day17; mod template; pub use template::DayTrait; @@ -23,7 +24,7 @@ pub mod day_provider { use super::*; use thiserror::Error; - const MAX_DAY: usize = 16; + const MAX_DAY: usize = 17; pub fn get_day(day_num: usize) -> Result, ProviderError> { match day_num { @@ -43,6 +44,7 @@ pub mod day_provider { 14 => Ok(Box::new(day14::Day)), 15 => Ok(Box::new(day15::Day)), 16 => Ok(Box::new(day16::Day)), + 17 => Ok(Box::new(day17::Day)), _ => Err(ProviderError::InvalidNumber(day_num)), } }