diff --git a/samples/day14.txt b/samples/day14.txt new file mode 100644 index 0000000..1926028 --- /dev/null +++ b/samples/day14.txt @@ -0,0 +1,2 @@ +498,4 -> 498,6 -> 496,6 +503,4 -> 502,4 -> 502,9 -> 494,9 \ No newline at end of file diff --git a/src/bin/day14.rs b/src/bin/day14.rs new file mode 100644 index 0000000..8c47a74 --- /dev/null +++ b/src/bin/day14.rs @@ -0,0 +1,183 @@ +use std::{ + cmp::max, + collections::{BTreeSet, VecDeque}, + str::FromStr, +}; + +use aoc_2022::*; + +use color_eyre::Report; +use itertools::Itertools; +// use tracing::debug; + +type Input = Vec; +type Output = usize; + +type Coord = Vector2D; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +struct Line { + from: Coord, + to: Coord, +} + +impl Line { + fn direction(&self) -> Coord { + self.to - self.from + } + + fn extend_to_x(&self, x: i32) -> Option { + let d = self.direction(); + + match d.x() { + 0 => None, + _ => { + let k = (self.from.x() - x) / d.x(); + let y = self.from.y() + k * d.y(); + + Some(Coord::new(x, y)) + } + } + } + + fn contains(&self, coord: &Coord) -> bool { + let mut xs = [self.from.x(), self.to.x()]; + xs.sort(); + + let mut ys = [self.from.y(), self.to.y()]; + ys.sort(); + + (xs[0]..=xs[1]).contains(&coord.x()) && (ys[0]..=ys[1]).contains(&coord.y()) + } +} + +#[derive(Debug)] +struct Multiline { + lines: BTreeSet, +} + +impl FromStr for Multiline { + type Err = Report; + + fn from_str(s: &str) -> Result { + fn parse_coord(s: &str) -> Result { + let (x, y) = s.split(',').collect_tuple().unwrap(); + + Ok(Coord::new(x.parse()?, y.parse()?)) + } + + let segs: Vec = s + .split(" -> ") + .map(|c| parse_coord(c).unwrap()) + .collect_vec(); + let mut lines: BTreeSet = BTreeSet::new(); + + for i in 0..segs.len() - 1 { + lines.insert(Line { + from: segs[i], + to: segs[i + 1], + }); + } + + Ok(Multiline { lines }) + } +} + +fn free_falling(grain: &Coord, lines: &[Line], set: &BTreeSet) -> bool { + set.iter().all(|g| g.x() != grain.x() || g.y() < grain.y()) + && lines + .iter() + .filter_map(|l| l.extend_to_x(*grain.x())) + .all(|g| g.y() < grain.y()) +} + +fn can_move(grain: &Coord, lines: &[Line], set: &BTreeSet) -> Option { + for direction in [Coord::new(0, 1), Coord::new(-1, 1), Coord::new(1, 1)] { + let new_position = *grain + direction; + + if !set.contains(&new_position) && lines.iter().all(|l| !l.contains(&new_position)) { + return Some(new_position); + } + } + + None +} + +struct Day14; +impl Solution for Day14 { + fn parse_input>(pathname: P) -> Input { + file_to_structs::(pathname) + .into_iter() + .flat_map(|m| m.lines) + .collect_vec() + } + + fn part_1(input: &Input) -> Output { + let mut grains: VecDeque = VecDeque::new(); + let mut set_grains: BTreeSet = BTreeSet::new(); + + loop { + for i in (0..grains.len()).rev() { + let grain = &mut grains[i]; + + if free_falling(grain, input, &set_grains) { + return set_grains.len(); + } + + if let Some(new_grain) = can_move(grain, input, &set_grains) { + *grain = new_grain; + } else { + let grain = grains.pop_back().unwrap(); + set_grains.insert(grain); + } + } + + grains.push_front(Coord::new(500, 0)); + } + } + + fn part_2(input: &Input) -> Output { + let mut lines = input.clone(); + let y = lines + .iter() + .map(|l| max(l.from.y(), l.to.y())) + .max() + .unwrap(); + + lines.push(Line { + from: Coord::new(500 - 2 * (*y + 2), *y + 2), + to: Coord::new(500 + 2 * (*y + 2), *y + 2), + }); + + let mut grains: VecDeque = VecDeque::new(); + let mut set_grains: BTreeSet = BTreeSet::new(); + + loop { + // debug!("Grains setting: {:?}, set grains: {:?}", grains.len(), set_grains.len()); + for i in (0..grains.len()).rev() { + let grain = &mut grains[i]; + + if let Some(new_grain) = can_move(grain, &lines, &set_grains) { + *grain = new_grain; + } else { + let grain = grains.pop_back().unwrap(); + set_grains.insert(grain); + + // debug!("Set grain {:?}", grain); + if grain == Coord::new(500, 0) { + return set_grains.len(); + } + } + } + + grains.push_front(Coord::new(500, 0)); + } + } +} + +fn main() -> Result<()> { + // Day14::run("sample") + Day14::main() +} + +test_sample!(day_14, Day14, 24, 93);