AoC2021/src/day05.rs

197 lines
4.8 KiB
Rust
Raw Normal View History

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());
}
}