//! Day 05 of 2021. use crate::prelude::*; /// Get the solution for day 05 of 2021. pub fn solution() -> Solution { Solution::new(Day::new(5, 2021), part_1, part_2).with_expected(6687, 19851) } /// A vector of two sets of coordinates. #[derive(Debug)] struct Vector { /// The starting coordinate. a: (isize, isize), /// The ending coordinate. b: (isize, isize), } impl Vector { /// Calculate the covered diagonal coordinates of the [`Vector`]. fn diagonal_coordinates(&self) -> Vec<(isize, isize)> { let x_coordinates = { let x_amount = (self.a.0 - self.b.0).abs(); if self.a.0 < self.b.0 { (0..=x_amount).map(|x| self.a.0 + x).collect::>() } else { (0..=x_amount).map(|x| self.a.0 - x).collect::>() } }; let y_coordinates = { let y_amount = (self.a.1 - self.b.1).abs(); if self.a.1 < self.b.1 { (0..=y_amount).map(|x| self.a.1 + x).collect::>() } else { (0..=y_amount).map(|x| self.a.1 - x).collect::>() } }; x_coordinates.into_iter().zip(y_coordinates).collect() } /// Calculate the covered horizontal coordinates of the [`Vector`]. fn horizontal_coordinates(&self) -> Vec<(isize, isize)> { let y = self.a.1; let (x1, x2) = if self.a.0 < self.b.0 { (self.a.0, self.b.0) } else { (self.b.0, self.a.0) }; (x1..=x2).map(|x| (x, y)).collect() } /// Calculate the covered vertical coordinates of the [`Vector`]. fn vertical_coordinates(&self) -> Vec<(isize, isize)> { let x = self.a.0; let (y1, y2) = if self.a.1 < self.b.1 { (self.a.1, self.b.1) } else { (self.b.1, self.a.1) }; (y1..=y2).map(|y| (x, y)).collect() } } impl FromStr for Vector { type Err = Error; fn from_str(s: &str) -> Result { let numbers = s .replace(" -> ", ",") .split(',') .map(str::parse) .collect::, _>>()?; if numbers.len() == 4 { Ok(Self { a: (numbers[0], numbers[1]), b: (numbers[2], numbers[3]), }) } else { Err(eyre!("Invalid input: {}", s)) } } } /// The logic to solve part one. fn part_1(input: &str) -> Result { let vectors = input .lines() .filter_map(|line| { if let Ok(vector) = Vector::from_str(line) { if vector.a.0 == vector.b.0 || vector.a.1 == vector.b.1 { return Some(vector); } } None }) .collect::>(); let mut travelled_coordinates = HashMap::<(isize, isize), isize>::new(); for vector in vectors { let coordinates = if vector.a.0 == vector.b.0 { vector.vertical_coordinates() } else { vector.horizontal_coordinates() }; for coordinate in coordinates { let amount = travelled_coordinates.entry(coordinate).or_default(); *amount += 1; } } Ok(count_result(travelled_coordinates).to_string()) } /// The logic to solve part two. fn part_2(input: &str) -> Result { let vectors = input .lines() .map(str::parse) .collect::>>()?; let mut travelled_coordinates = HashMap::<(isize, isize), isize>::new(); for vector in vectors { let coordinates = if vector.a.0 == vector.b.0 { vector.vertical_coordinates() } else if vector.a.1 == vector.b.1 { vector.horizontal_coordinates() } else { vector.diagonal_coordinates() }; for coordinate in coordinates { let amount = travelled_coordinates.entry(coordinate).or_default(); *amount += 1; } } Ok(count_result(travelled_coordinates).to_string()) } /// Count the results of the coordinates that overlap at least two lines. fn count_result(coordinates: HashMap<(isize, isize), isize>) -> usize { coordinates .into_iter() .filter(|(_, amount)| amount >= &2) .count() }