2021-12-05 11:51:33 +01:00
|
|
|
#![feature(int_abs_diff)]
|
2021-12-05 10:01:53 +01:00
|
|
|
#![warn(clippy::pedantic)]
|
|
|
|
use std::{
|
2021-12-05 11:51:33 +01:00
|
|
|
cmp::Ordering,
|
2021-12-05 10:01:53 +01:00
|
|
|
convert::Infallible,
|
|
|
|
io::{stdin, BufRead},
|
|
|
|
str::FromStr,
|
|
|
|
};
|
|
|
|
use vector2d::Vector2D;
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
struct Line {
|
|
|
|
start: Vector2D<usize>,
|
|
|
|
end: Vector2D<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Line {
|
|
|
|
pub fn is_straight(&self) -> bool {
|
|
|
|
self.start.x == self.end.x || self.start.y == self.end.y
|
|
|
|
}
|
|
|
|
|
2021-12-05 11:51:33 +01:00
|
|
|
pub fn expand(&self) -> impl Iterator<Item = Vector2D<usize>> {
|
|
|
|
LinePoints::new(self)
|
2021-12-05 10:01:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for Line {
|
|
|
|
type Err = Infallible;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
let parts: Vec<_> = s.split(' ').collect();
|
|
|
|
let start: Vec<_> = parts[0].split(',').map(|n| n.parse().unwrap()).collect();
|
|
|
|
let end: Vec<_> = parts[2].split(',').map(|n| n.parse().unwrap()).collect();
|
|
|
|
|
|
|
|
Ok(Line {
|
|
|
|
start: Vector2D::new(start[0], start[1]),
|
|
|
|
end: Vector2D::new(end[0], end[1]),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-05 11:51:33 +01:00
|
|
|
struct LinePoints {
|
|
|
|
pos: Vector2D<usize>,
|
|
|
|
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<usize>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-05 10:01:53 +01:00
|
|
|
fn count_overlaps<'a>(it: impl IntoIterator<Item = &'a Line>) -> usize {
|
2021-12-05 11:44:09 +01:00
|
|
|
let points: Vec<(usize, usize)> = it
|
|
|
|
.into_iter()
|
|
|
|
.flat_map(Line::expand)
|
|
|
|
.map(Into::into)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let (max_x, max_y) = points.iter().fold((0, 0), |(acc_x, acc_y), &(x, y)| {
|
|
|
|
(usize::max(acc_x, x), usize::max(acc_y, y))
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut field = vec![vec![0_usize; max_y + 1]; max_x + 1];
|
|
|
|
|
|
|
|
let mut hits = 0;
|
|
|
|
for (x, y) in points {
|
|
|
|
let point = &mut field[x][y];
|
|
|
|
if *point == 1 {
|
|
|
|
hits += 1;
|
|
|
|
}
|
|
|
|
*point += 1;
|
|
|
|
}
|
|
|
|
hits
|
2021-12-05 10:01:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let lines: Vec<Line> = stdin()
|
|
|
|
.lock()
|
|
|
|
.lines()
|
|
|
|
.map(|s| s.unwrap().parse().unwrap())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
count_overlaps(lines.iter().filter(|line| line.is_straight()))
|
|
|
|
);
|
|
|
|
println!("{}", count_overlaps(&lines));
|
|
|
|
}
|