use std::num::ParseIntError;
fn main() {
use std::io::BufRead;
let filename = std::env::args().nth(1).expect("Expected filename");
let file = std::io::BufReader::new(
std::fs::File::open(<String as AsRef<std::path::Path>>::as_ref(
&filename,
))
.unwrap(),
);
let mut lines: Vec<VentLine> = file
.lines()
.flat_map(Result::ok)
.map(|s| s.parse())
.flat_map(Result::ok)
.collect();
let full_overlapping = count_intersections(&lines);
lines.retain(VentLine::orthogonal);
let orth_overlapping = count_intersections(&lines);
println!("Overlapping points (orthogonal): {}", orth_overlapping);
println!("Overlapping points (all): {}", full_overlapping);
}
#[derive(Clone, Copy)]
struct VentLine {
x1: i64,
y1: i64,
x2: i64,
y2: i64,
}
fn count_intersections(lines: &[VentLine]) -> usize {
use std::collections::HashMap;
let mut points: HashMap<_, usize> = HashMap::new();
for point in lines.iter().flat_map(VentLine::points) {
points.entry(point).and_modify(|c| *c += 1).or_insert(1);
}
points.values().filter(|c| *c > &1).count()
}
impl VentLine {
fn orthogonal(&self) -> bool {
self.x1 == self.x2 || self.y1 == self.y2
}
fn points(&self) -> Box<dyn Iterator<Item = (i64, i64)>> {
if self.x1 == self.x2 {
let x = self.x1;
let (y1, y2) = if self.y1 <= self.y2 {
(self.y1, self.y2)
} else {
(self.y2, self.y1)
};
Box::new((y1..=y2).map(move |y| (x, y)))
} else if self.y1 == self.y2 {
let y = self.y1;
let (x1, x2) = if self.x1 <= self.x2 {
(self.x1, self.x2)
} else {
(self.x2, self.x1)
};
Box::new((x1..=x2).map(move |x| (x, y)))
} else if (self.x2 - self.x1).abs() == (self.y2 - self.y1).abs() {
let xs: Box<dyn Iterator<Item = i64>> = if self.x1 <= self.x2 {
Box::new(self.x1..=self.x2)
} else {
Box::new((self.x2..=self.x1).rev())
};
let ys: Box<dyn Iterator<Item = i64>> = if self.y1 <= self.y2 {
Box::new(self.y1..=self.y2)
} else {
Box::new((self.y2..=self.y1).rev())
};
Box::new(xs.zip(ys))
} else {
Box::new(std::iter::empty())
}
}
}
#[derive(Debug)]
enum LineParseError {
Numeric(u8, ParseIntError),
General,
}
impl std::str::FromStr for VentLine {
type Err = LineParseError;
fn from_str(src: &str) -> Result<Self, Self::Err> {
let mut halves =
src.split("->").map(|h| h.trim().split(',').map(str::parse));
let mut half = halves.next().ok_or(LineParseError::General)?;
let x1 = half
.next()
.ok_or(LineParseError::General)?
.map_err(|n| LineParseError::Numeric(0, n))?;
let y1 = half
.next()
.ok_or(LineParseError::General)?
.map_err(|n| LineParseError::Numeric(1, n))?;
match half.next() {
None => Ok(()),
Some(_) => Err(LineParseError::General),
}?;
half = halves.next().ok_or(LineParseError::General)?;
let x2 = half
.next()
.ok_or(LineParseError::General)?
.map_err(|n| LineParseError::Numeric(2, n))?;
let y2 = half
.next()
.ok_or(LineParseError::General)?
.map_err(|n| LineParseError::Numeric(3, n))?;
Ok(Self { x1, y1, x2, y2 })
}
}
impl std::fmt::Debug for VentLine {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
write!(f, "{},{} -> {},{}", self.x1, self.y1, self.x2, self.y2)
}
}
impl std::fmt::Display for VentLine {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
<VentLine as std::fmt::Debug>::fmt(self, f)
}
}