diff --git a/2021/day5/day5_rs/src/main.rs b/2021/day5/day5_rs/src/main.rs index d8a07ea..63b52d0 100644 --- a/2021/day5/day5_rs/src/main.rs +++ b/2021/day5/day5_rs/src/main.rs @@ -1,5 +1,7 @@ +#![feature(int_abs_diff)] #![warn(clippy::pedantic)] use std::{ + cmp::Ordering, convert::Infallible, io::{stdin, BufRead}, str::FromStr, @@ -17,27 +19,8 @@ impl Line { self.start.x == self.end.x || self.start.y == self.end.y } - pub fn expand(&self) -> Vec> { - let delta = self.end.as_isizes() - self.start.as_isizes(); - - #[allow(clippy::if_not_else)] - let len = if delta.x != 0 { - delta.x.abs() - } else if delta.y != 0 { - delta.y.abs() - } else { - unreachable!() - }; - - let step = delta / len; - - (0..=len) - .scan(self.start, |acc, _| { - let prev = *acc; - *acc = (acc.as_isizes() + step).as_usizes(); - Some(prev) - }) - .collect() + pub fn expand(&self) -> impl Iterator> { + LinePoints::new(self) } } @@ -56,6 +39,54 @@ impl FromStr for Line { } } +struct LinePoints { + pos: Vector2D, + x_change: Ordering, + y_change: Ordering, + points_left: usize, +} + +impl LinePoints { + fn new(line: &Line) -> Self { + let x_change = line.end.x.cmp(&line.start.x); + let y_change = line.end.y.cmp(&line.start.y); + + let delta_x = line.end.x.abs_diff(line.start.x); + let delta_y = line.end.y.abs_diff(line.start.y); + + Self { + pos: line.start, + x_change, + y_change, + points_left: usize::max(delta_x, delta_y) + 1, + } + } +} + +impl Iterator for LinePoints { + type Item = Vector2D; + + fn next(&mut self) -> Option { + if self.points_left == 0 { + return None; + } + + let current = self.pos; + match self.x_change { + Ordering::Less => self.pos.x -= 1, + Ordering::Greater => self.pos.x += 1, + Ordering::Equal => {} + } + match self.y_change { + Ordering::Less => self.pos.y -= 1, + Ordering::Greater => self.pos.y += 1, + Ordering::Equal => {} + } + self.points_left -= 1; + Some(current) + } +} + fn count_overlaps<'a>(it: impl IntoIterator) -> usize { let points: Vec<(usize, usize)> = it .into_iter()