diff --git a/2021/src/day5.rs b/2021/src/day5.rs index 53dbca1..9f60719 100644 --- a/2021/src/day5.rs +++ b/2021/src/day5.rs @@ -1,77 +1,150 @@ +use std::str::FromStr; + use color_eyre::eyre; -use num::{Integer, integer::gcd}; +use num::integer::gcd; + +use crate::lib::{ParseLines, IterExt}; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Point { x: isize, y: isize, } -#[derive(Copy, Clone)] -struct Line { - start: Point, - end: Point, -} - - -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] struct Slope { rise: isize, run: isize, } - -impl Line { - fn slope(&self) -> Slope { - let mut rise = self.end.y - self.start.y; - let mut run = self.end.x - self.start.x; - if rise != 0 && run != 0 { - let divisor = gcd(rise, run); - rise /= divisor; - run /= divisor; +impl Slope { + fn apply_to(&self, pt: Point) -> Point { + Point { + x: pt.x + self.run, + y: pt.y + self.rise, } - Slope {rise, run} } } +#[derive(Copy, Clone, Debug)] +struct Line { + start: Point, + end: Point, +} + +impl Line { + fn slope(&self) -> Slope { + let dx = self.end.y - self.start.y; + let dy = self.end.x - self.start.x; + + let divisor; + if dx == 0 || dy == 0 { + divisor = (dx + dy).abs(); + } + else { + divisor = gcd(dx, dy); + } + + Slope { + rise: dx / divisor, + run: dy / divisor, + } + } + + fn points(&self) -> PointsIter { + PointsIter { + line: self, + pos: self.start, + slope: self.slope(), + } + } +} + + +#[derive(Debug)] struct PointsIter<'a> { line: &'a Line, pos: Point, slope: Slope, } -impl PointsIter<'_> { - fn from(line: &Line) -> Self { - PointsIter { - line, - pos: line.start, - slope: line.slope(), - } - } -} - -impl Iterator for PointsIter { +impl Iterator for PointsIter<'_> { type Item = Point; fn next(&mut self) -> Option { - if self.pos.x = self.line.start.x && self.pos.y == self.line.start.y { - return Some(self.pos); - } - - if self.pos.x != self.line.end.x && self.pos.y != self.line.end.y { - self.pos.y += self.slope.rise; - self.pos.x += self.slope.run; - return Some(self.pos); - } - else { + // note, this approach will break if total dX and dY are not multiples of rise/run + if self.pos == self.slope.apply_to(self.line.end) { return None; } + + let orig = self.pos; + self.pos = self.slope.apply_to(self.pos); + Some(orig) } } -struct GridCounter { - rows: [[usize; W]; H], +impl FromStr for Line { + type Err = eyre::Report; + + fn from_str(s: &str) -> Result { + let (p1, p2) = s.split(" -> ").take_pair()?; + let (x1, y1) = p1.split(',').take_pair()?; + let (x2, y2) = p2.split(',').take_pair()?; + + let start = Point { + x: x1.parse::()?, + y: y1.parse::()?, + }; + let end = Point { + x: x2.parse::()?, + y: y2.parse::()?, + }; + + Ok(Line {start, end}) + } +} + + +fn part1(lines: &[Line]) -> usize { + let mut grid = vec![[0; 1000]; 1000]; + + let points = lines.iter() + .filter(|l| l.start.x == l.end.x || l.start.y == l.end.y) + .flat_map(|l| l.points()); + + for pt in points { + grid[pt.y as usize][pt.x as usize] += 1; + } + + grid.into_iter() + .flat_map(|row| row.into_iter()) + .filter(|n| *n > 1) + .count() +} + + +fn part2(lines: &[Line]) -> usize { + let mut grid = vec![[0; 1000]; 1000]; + + for pt in lines.iter().flat_map(|l| l.points()) { + grid[pt.y as usize][pt.x as usize] += 1; + } + + grid.into_iter() + .flat_map(|row| row.into_iter()) + .filter(|n| *n > 1) + .count() +} + + +pub fn run(data: &str) -> eyre::Result<(usize, usize)> { + let mut lines = Vec::new(); + for line in data.parse_lines::() { + lines.push(line?); + } + + Ok((part1(&lines), part2(&lines))) } diff --git a/2021/src/lib.rs b/2021/src/lib.rs index 1da8eda..f991069 100644 --- a/2021/src/lib.rs +++ b/2021/src/lib.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use std::fs::File; use color_eyre::{eyre}; +use eyre::eyre; pub fn load(filename: &str) -> eyre::Result { @@ -52,3 +53,19 @@ impl ParseLines for String { } } + +pub trait IterExt: Iterator { + fn take_pair(&mut self) -> eyre::Result<(Self::Item, Self::Item)> { + let a = match self.next() { + Some(v) => v, + None => return Err(eyre!("Not enough values to unpack")), + }; + let b = match self.next() { + Some(v) => v, + None => return Err(eyre!("Not enough values to unpack")), + }; + Ok((a, b)) + } +} + +impl IterExt for I {} diff --git a/2021/src/main.rs b/2021/src/main.rs index c104471..931307a 100644 --- a/2021/src/main.rs +++ b/2021/src/main.rs @@ -4,14 +4,14 @@ use color_eyre::eyre; mod lib; use lib::load; -mod day4; +mod day5; fn main() -> eyre::Result<()> { - let data = load("data/04.txt")?; + let data = load("data/05.txt")?; let start = Instant::now(); - let (one, two) = day4::run(&data)?; + let (one, two) = day5::run(&data)?; let (dur, unit) = format_ns(start.elapsed().as_nanos()); let precision = 2.0 - dur.log10().floor();