156 lines
3.8 KiB
Rust
156 lines
3.8 KiB
Rust
//! 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::<Vec<_>>()
|
|
} else {
|
|
(0..=x_amount).map(|x| self.a.0 - x).collect::<Vec<_>>()
|
|
}
|
|
};
|
|
|
|
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::<Vec<_>>()
|
|
} else {
|
|
(0..=y_amount).map(|x| self.a.1 - x).collect::<Vec<_>>()
|
|
}
|
|
};
|
|
|
|
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<Self, Self::Err> {
|
|
let numbers = s
|
|
.replace(" -> ", ",")
|
|
.split(',')
|
|
.map(str::parse)
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
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<String> {
|
|
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::<Vec<_>>();
|
|
|
|
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<String> {
|
|
let vectors = input
|
|
.lines()
|
|
.map(str::parse)
|
|
.collect::<Result<Vec<Vector>>>()?;
|
|
|
|
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()
|
|
}
|