at least part 2 was easy

This commit is contained in:
Joseph Montanaro 2021-12-08 21:52:32 -08:00
parent 444a7f6e47
commit 2269a7e086
3 changed files with 136 additions and 46 deletions

View File

@ -1,77 +1,150 @@
use std::str::FromStr;
use color_eyre::eyre; 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 { struct Point {
x: isize, x: isize,
y: isize, y: isize,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
struct Line {
start: Point,
end: Point,
}
#[derive(Copy, Clone)]
struct Slope { struct Slope {
rise: isize, rise: isize,
run: isize, run: isize,
} }
impl Slope {
impl Line { fn apply_to(&self, pt: Point) -> Point {
fn slope(&self) -> Slope { Point {
let mut rise = self.end.y - self.start.y; x: pt.x + self.run,
let mut run = self.end.x - self.start.x; y: pt.y + self.rise,
if rise != 0 && run != 0 {
let divisor = gcd(rise, run);
rise /= divisor;
run /= divisor;
} }
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> { struct PointsIter<'a> {
line: &'a Line, line: &'a Line,
pos: Point, pos: Point,
slope: Slope, slope: Slope,
} }
impl PointsIter<'_> { impl Iterator for PointsIter<'_> {
fn from(line: &Line) -> Self {
PointsIter {
line,
pos: line.start,
slope: line.slope(),
}
}
}
impl Iterator for PointsIter {
type Item = Point; type Item = Point;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
if self.pos.x = self.line.start.x && self.pos.y == self.line.start.y { // note, this approach will break if total dX and dY are not multiples of rise/run
return Some(self.pos); if self.pos == self.slope.apply_to(self.line.end) {
}
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 {
return None; return None;
} }
let orig = self.pos;
self.pos = self.slope.apply_to(self.pos);
Some(orig)
} }
} }
struct GridCounter<const W: usize, const H: usize> { impl FromStr for Line {
rows: [[usize; W]; H], type Err = eyre::Report;
fn from_str(s: &str) -> Result<Self, Self::Err> {
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::<isize>()?,
y: y1.parse::<isize>()?,
};
let end = Point {
x: x2.parse::<isize>()?,
y: y2.parse::<isize>()?,
};
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::<Line>() {
lines.push(line?);
}
Ok((part1(&lines), part2(&lines)))
} }

View File

@ -3,6 +3,7 @@ use std::str::FromStr;
use std::fs::File; use std::fs::File;
use color_eyre::{eyre}; use color_eyre::{eyre};
use eyre::eyre;
pub fn load(filename: &str) -> eyre::Result<String> { pub fn load(filename: &str) -> eyre::Result<String> {
@ -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<I: Iterator> IterExt for I {}

View File

@ -4,14 +4,14 @@ use color_eyre::eyre;
mod lib; mod lib;
use lib::load; use lib::load;
mod day4; mod day5;
fn main() -> eyre::Result<()> { fn main() -> eyre::Result<()> {
let data = load("data/04.txt")?; let data = load("data/05.txt")?;
let start = Instant::now(); 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 (dur, unit) = format_ns(start.elapsed().as_nanos());
let precision = 2.0 - dur.log10().floor(); let precision = 2.0 - dur.log10().floor();