-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathday_20.rs
88 lines (69 loc) · 2.37 KB
/
day_20.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use std::{collections::VecDeque, convert::identity};
use aoc_lib::{direction::cardinal::Direction, matrix::Grid};
use common::{solution, Answer};
use itertools::Itertools;
use nd_vec::{vector, Vec2};
solution!("Race Condition", 20);
fn part_a(input: &str) -> Answer {
Problem::parse(input).solve(2).into()
}
fn part_b(input: &str) -> Answer {
Problem::parse(input).solve(20).into()
}
struct Problem {
board: Grid<char>,
start: Vec2<usize>,
end: Vec2<usize>,
}
impl Problem {
fn parse(input: &str) -> Self {
let board = Grid::parse(input, identity);
let start = board.find('S').unwrap();
let end = board.find('E').unwrap();
Self { board, start, end }
}
fn solve(&self, max_skip: i32) -> u32 {
let (sc, ec) = (self.cost_map(self.start), self.cost_map(self.end));
let base_cost = sc[self.end];
let mut out = 0;
for (pos, tile) in self.board.iter() {
if *tile == '#' || sc[pos] == u32::MAX {
continue;
}
for (x, y) in (-max_skip..=max_skip).cartesian_product(-max_skip..=max_skip) {
let offset = vector!(x, y);
let dist = offset.manhattan_distance(&Vec2::zero());
if dist > max_skip {
continue;
}
let end = pos.try_cast::<i32>().unwrap() + offset;
if !self.board.contains(end) || self.board[end] == '#' || ec[end] == u32::MAX {
continue;
}
let cost = sc[pos] + ec[end] + dist as u32;
out += (cost + 100 <= base_cost) as u32;
}
}
out
}
fn cost_map(&self, start: Vec2<usize>) -> Grid<u32> {
let mut costs = Grid::new(self.board.size, u32::MAX);
let mut queue = VecDeque::new();
queue.push_back((start, 0));
while let Some((pos, dist)) = queue.pop_front() {
if costs[pos] != u32::MAX {
continue;
}
costs[pos] = dist;
for dir in Direction::ALL {
let next = dir.wrapping_advance(pos);
if let Some(tile) = self.board.get(next) {
if matches!(tile, '.' | 'E') {
queue.push_back((next, dist + 1));
}
}
}
}
costs
}
}