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 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 Slope {
fn apply_to(&self, pt: Point) -> Point {
Point {
x: pt.x + self.run,
y: pt.y + self.rise,
}
}
}
#[derive(Copy, Clone, Debug)]
struct Line {
start: Point,
end: Point,
}
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;
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(),
}
Slope {rise, run}
}
}
#[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<Self::Item> {
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<const W: usize, const H: usize> {
rows: [[usize; W]; H],
impl FromStr for Line {
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 color_eyre::{eyre};
use eyre::eyre;
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;
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();