144 lines
3.3 KiB
Rust
144 lines
3.3 KiB
Rust
|
|
use std::{
|
||
|
|
cmp::Ordering,
|
||
|
|
collections::{HashMap, HashSet},
|
||
|
|
fmt::Display,
|
||
|
|
io::{BufRead, BufReader},
|
||
|
|
};
|
||
|
|
|
||
|
|
use crate::types::TestMode;
|
||
|
|
|
||
|
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||
|
|
struct Point {
|
||
|
|
x: i64,
|
||
|
|
y: i64,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Display for Point {
|
||
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
|
|
f.write_fmt(format_args!("({}, {})", self.x, self.y))
|
||
|
|
.unwrap();
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||
|
|
struct Line {
|
||
|
|
p1: Point,
|
||
|
|
p2: Point,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Display for Line {
|
||
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
|
|
f.write_fmt(format_args!("[{} -> {}]", self.p1, self.p2))
|
||
|
|
.unwrap();
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Line {
|
||
|
|
fn points(&self) -> HashSet<Point> {
|
||
|
|
let mut res = HashSet::new();
|
||
|
|
|
||
|
|
let dx = Self::sign(self.p2.x - self.p1.x);
|
||
|
|
let dy = Self::sign(self.p2.y - self.p1.y);
|
||
|
|
|
||
|
|
let mut x = self.p1.x;
|
||
|
|
let mut y = self.p1.y;
|
||
|
|
loop {
|
||
|
|
let p = Point { x, y };
|
||
|
|
|
||
|
|
res.insert(p);
|
||
|
|
|
||
|
|
if x == self.p2.x && y == self.p2.y {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
x += dx;
|
||
|
|
y += dy;
|
||
|
|
}
|
||
|
|
|
||
|
|
res
|
||
|
|
}
|
||
|
|
|
||
|
|
fn sign(expr: i64) -> i64 {
|
||
|
|
match expr.cmp(&0) {
|
||
|
|
Ordering::Less => -1,
|
||
|
|
Ordering::Equal => 0,
|
||
|
|
Ordering::Greater => 1,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
struct Input {
|
||
|
|
lines: Vec<Line>,
|
||
|
|
}
|
||
|
|
|
||
|
|
fn parse_input(test_mode: TestMode) -> Input {
|
||
|
|
let f = test_mode.input_file(file!());
|
||
|
|
let br = BufReader::new(f);
|
||
|
|
let lines = br
|
||
|
|
.lines()
|
||
|
|
.map(|line| {
|
||
|
|
let line = line.unwrap();
|
||
|
|
let mut s = line.split_terminator(" -> ");
|
||
|
|
|
||
|
|
let mut m = s.next().unwrap().split_terminator(',');
|
||
|
|
let p1 = Point {
|
||
|
|
x: m.next().unwrap().parse().unwrap(),
|
||
|
|
y: m.next().unwrap().parse().unwrap(),
|
||
|
|
};
|
||
|
|
|
||
|
|
let mut m = s.next().unwrap().split_terminator(',');
|
||
|
|
let p2 = Point {
|
||
|
|
x: m.next().unwrap().parse().unwrap(),
|
||
|
|
y: m.next().unwrap().parse().unwrap(),
|
||
|
|
};
|
||
|
|
|
||
|
|
Line { p1, p2 }
|
||
|
|
})
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
Input { lines }
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn hydrothermal_venture_1(test_mode: TestMode) {
|
||
|
|
let input = parse_input(test_mode);
|
||
|
|
|
||
|
|
let orthogonal_lines: Vec<_> = input
|
||
|
|
.lines
|
||
|
|
.into_iter()
|
||
|
|
.filter(|line| line.p1.x == line.p2.x || line.p1.y == line.p2.y)
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
let mut point_count = HashMap::<Point, i64>::new();
|
||
|
|
for line in orthogonal_lines {
|
||
|
|
for point in line.points() {
|
||
|
|
*point_count.entry(point).or_insert(0) += 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let num_intersections = point_count
|
||
|
|
.iter()
|
||
|
|
.filter(|point_usages| point_usages.1 > &1)
|
||
|
|
.count();
|
||
|
|
|
||
|
|
println!("{}", num_intersections);
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn hydrothermal_venture_2(test_mode: TestMode) {
|
||
|
|
let lines = parse_input(test_mode).lines;
|
||
|
|
|
||
|
|
let mut point_count = HashMap::<Point, i64>::new();
|
||
|
|
|
||
|
|
for line in lines {
|
||
|
|
for point in line.points() {
|
||
|
|
*point_count.entry(point).or_insert(0) += 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let num_intersections = point_count
|
||
|
|
.iter()
|
||
|
|
.filter(|point_usages| point_usages.1 > &1)
|
||
|
|
.count();
|
||
|
|
|
||
|
|
println!("{}", num_intersections);
|
||
|
|
}
|