diff --git a/src/bin/day14.rs b/src/bin/day14.rs index ce80891..ae94cab 100644 --- a/src/bin/day14.rs +++ b/src/bin/day14.rs @@ -21,36 +21,6 @@ struct Line { 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, @@ -83,19 +53,11 @@ impl FromStr for Multiline { } } -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 { +fn can_move(grain: &Coord, 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)) { + if !set.contains(&new_position) { return Some(new_position); } } @@ -104,18 +66,34 @@ fn can_move(grain: &Coord, lines: &[Line], set: &BTreeSet) -> Option(lines: &Input, terminate: F) -> usize -where - F: Fn(&Coord, &Input, &BTreeSet, bool) -> bool +where + F: Fn(&Coord, bool) -> bool, { let mut grains: VecDeque = VecDeque::new(); let mut set_grains: BTreeSet = BTreeSet::new(); + // convert lines to points + for line in lines { + let d = Coord::new( + (line.to.x() - line.from.x()).signum(), + (line.to.y() - line.from.y()).signum(), + ); + + let mut p = line.from; + while p != line.to { + set_grains.insert(p); + p = p + d; + } + set_grains.insert(p); + } + let lines_count = set_grains.len(); + loop { for i in (0..grains.len()).rev() { let grain = grains[i]; let mut is_set = false; - if let Some(new_grain) = can_move(&grain, lines, &set_grains) { + if let Some(new_grain) = can_move(&grain, &set_grains) { grains[i] = new_grain; } else { let grain = grains.pop_back().unwrap(); @@ -123,8 +101,8 @@ where is_set = true; } - if terminate(&grain, lines, &set_grains, is_set) { - return set_grains.len(); + if terminate(&grain, is_set) { + return set_grains.len() - lines_count; } } @@ -142,15 +120,19 @@ impl Solution for Day14 { } fn part_1(input: &Input) -> Output { - fn terminate(grain: &Coord, lines: &Input, set_grains: &BTreeSet, _is_set: bool) -> bool { - free_falling(grain, lines, set_grains) - } + let max_y = *input + .iter() + .map(|l| max(l.from.y(), l.to.y())) + .max() + .unwrap(); + + let terminate = |grain: &Coord, _is_set: bool| *grain.y() >= max_y; simulate_falling(input, terminate) } fn part_2(input: &Input) -> Output { - fn terminate(grain: &Coord, _lines: &Input, _set_grains: &BTreeSet, is_set: bool) -> bool { + fn terminate(grain: &Coord, is_set: bool) -> bool { is_set && grain == &Coord::new(500, 0) }