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 { 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, } 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::::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::::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); }