2021-12-05 11:59:48 +01:00
|
|
|
use std::cmp::Ordering;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use crate::Day;
|
|
|
|
|
|
|
|
pub struct Day05 {
|
|
|
|
vents: Vec<Path>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
|
|
|
|
struct Path {
|
|
|
|
start: Coordinate,
|
|
|
|
end: Coordinate,
|
|
|
|
covers: Vec<Coordinate>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
|
|
|
|
struct Coordinate {
|
|
|
|
x: i64,
|
|
|
|
y: i64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Day for Day05 {
|
|
|
|
fn init(content: String) -> Self {
|
|
|
|
let paths = content.lines().map(str_to_vent).collect::<Vec<Path>>();
|
|
|
|
Self {
|
|
|
|
vents: paths,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn part1(&self) -> String {
|
|
|
|
let new_vents = self.vents.iter().filter(|p| p.is_straight()).cloned().collect::<Vec<Path>>();
|
2021-12-05 14:27:26 +01:00
|
|
|
format!("{}", covered_twice(&new_vents))
|
2021-12-05 11:59:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn part2(&self) -> String {
|
2021-12-05 14:27:26 +01:00
|
|
|
format!("{}", covered_twice(&self.vents))
|
2021-12-05 11:59:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 09:12:49 +01:00
|
|
|
fn covered_twice(vents: &[Path]) -> usize {
|
2021-12-05 14:27:26 +01:00
|
|
|
let mut m = HashMap::new();
|
|
|
|
for vent in vents {
|
|
|
|
for cover in &vent.covers {
|
|
|
|
let m = m.entry(cover).or_insert(0);
|
|
|
|
*m += 1;
|
2021-12-05 11:59:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-05 14:27:26 +01:00
|
|
|
m.values().copied().filter(|x| *x >= 2).count()
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Path {
|
2021-12-05 11:59:48 +01:00
|
|
|
fn is_straight(&self) -> bool {
|
|
|
|
self.start.x == self.end.x || self.start.y == self.end.y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Coordinate {
|
|
|
|
fn dir_to(&self, other: &Coordinate) -> Direction {
|
|
|
|
return if self.x < other.x {
|
|
|
|
match self.y.cmp(&other.y) {
|
|
|
|
Ordering::Less => Direction::SE,
|
|
|
|
Ordering::Equal => Direction::E,
|
|
|
|
Ordering::Greater => Direction::NE,
|
|
|
|
}
|
|
|
|
} else if self.x == other.x {
|
|
|
|
match self.y.cmp(&other.y) {
|
|
|
|
Ordering::Less => Direction::S,
|
|
|
|
Ordering::Equal => panic!("why are the start and end equal??"),
|
|
|
|
Ordering::Greater => Direction::N,
|
|
|
|
}
|
|
|
|
} else if self.y < other.y {
|
|
|
|
Direction::SW
|
|
|
|
} else if self.y == other.y {
|
|
|
|
Direction::W
|
|
|
|
} else {
|
|
|
|
Direction::NW
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
fn new(x: i64, y: i64) -> Self {
|
|
|
|
Coordinate { x, y }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_vec(v: &[i64]) -> Self {
|
|
|
|
Coordinate { x: v[0], y: v[1] }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Eq, PartialEq, Debug)]
|
|
|
|
enum Direction {
|
|
|
|
NE,
|
|
|
|
NW,
|
|
|
|
SE,
|
|
|
|
SW,
|
|
|
|
N,
|
|
|
|
E,
|
|
|
|
W,
|
|
|
|
S,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Direction {
|
|
|
|
fn coord_diff(&self) -> (i64, i64) {
|
|
|
|
match self {
|
|
|
|
Direction::NE => (1, -1),
|
|
|
|
Direction::NW => (-1, -1),
|
|
|
|
Direction::SE => (1, 1),
|
|
|
|
Direction::SW => (-1, 1),
|
|
|
|
Direction::N => (0, -1),
|
|
|
|
Direction::E => (1, 0),
|
|
|
|
Direction::W => (-1, 0),
|
|
|
|
Direction::S => (0, 1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn str_to_vent(st: &str) -> Path {
|
|
|
|
let mut v = st.split(" -> ");
|
|
|
|
let a = v.next().unwrap();
|
|
|
|
let b = v.next().unwrap();
|
|
|
|
let start = a.split(',').map(|x| x.parse().unwrap()).collect::<Vec<i64>>();
|
|
|
|
let end = b.split(',').map(|x| x.parse().unwrap()).collect::<Vec<i64>>();
|
|
|
|
let start = Coordinate::from_vec(&start);
|
|
|
|
let end = Coordinate::from_vec(&end);
|
|
|
|
|
|
|
|
let dir = start.dir_to(&end).coord_diff();
|
|
|
|
let mut covers = vec![];
|
|
|
|
let mut c = start.clone();
|
|
|
|
|
|
|
|
while c != end {
|
|
|
|
covers.push(c.clone());
|
|
|
|
c.x += dir.0;
|
|
|
|
c.y += dir.1;
|
|
|
|
}
|
|
|
|
covers.push(end.clone());
|
|
|
|
|
|
|
|
Path {
|
|
|
|
start,
|
|
|
|
end,
|
|
|
|
covers,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::Day05;
|
2021-12-05 14:27:26 +01:00
|
|
|
use crate::day05::{Coordinate, Direction};
|
2021-12-05 11:59:48 +01:00
|
|
|
use crate::day::Day;
|
|
|
|
|
|
|
|
const INPUT: &str = r"0,9 -> 5,9
|
|
|
|
8,0 -> 0,8
|
|
|
|
9,4 -> 3,4
|
|
|
|
2,2 -> 2,1
|
|
|
|
7,0 -> 7,4
|
|
|
|
6,4 -> 2,0
|
|
|
|
0,9 -> 2,9
|
|
|
|
3,4 -> 1,4
|
|
|
|
0,0 -> 8,8
|
|
|
|
5,5 -> 8,2";
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn directions() {
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(0, 0)), Direction::NW);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(9, 9)), Direction::SE);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(9, 0)), Direction::NE);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(0, 9)), Direction::SW);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(0, 5)), Direction::W);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(5, 0)), Direction::N);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(9, 5)), Direction::E);
|
|
|
|
assert_eq!(Coordinate::new(5, 5).dir_to(&Coordinate::new(5, 9)), Direction::S);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_test() {
|
|
|
|
let d = Day05::init(INPUT.to_string());
|
|
|
|
assert_eq!("5", d.part1());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_test() {
|
|
|
|
let d = Day05::init(INPUT.to_string());
|
|
|
|
assert_eq!("12", d.part2());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part1_real() {
|
|
|
|
let d = Day05::init(crate::load_input("05"));
|
|
|
|
assert_eq!("6564", d.part1());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2_real() {
|
|
|
|
let d = Day05::init(crate::load_input("05"));
|
|
|
|
assert_eq!("19172", d.part2());
|
|
|
|
}
|
|
|
|
}
|