Skip to content

Commit a24749e

Browse files
committed
AoC 2024 Day 20 - rust
1 parent 93f92db commit a24749e

File tree

6 files changed

+204
-1
lines changed

6 files changed

+204
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
| ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
1111
| python3 | [](src/main/python/AoC2024_01.py) | [](src/main/python/AoC2024_02.py) | [](src/main/python/AoC2024_03.py) | [](src/main/python/AoC2024_04.py) | [](src/main/python/AoC2024_05.py) | [](src/main/python/AoC2024_06.py) | [](src/main/python/AoC2024_07.py) | [](src/main/python/AoC2024_08.py) | [](src/main/python/AoC2024_09.py) | [](src/main/python/AoC2024_10.py) | [](src/main/python/AoC2024_11.py) | [](src/main/python/AoC2024_12.py) | [](src/main/python/AoC2024_13.py) | [](src/main/python/AoC2024_14.py) | [](src/main/python/AoC2024_15.py) | [](src/main/python/AoC2024_16.py) | [](src/main/python/AoC2024_17.py) | [](src/main/python/AoC2024_18.py) | [](src/main/python/AoC2024_19.py) | [](src/main/python/AoC2024_20.py) | [](src/main/python/AoC2024_21.py) | | | | |
1212
| java | [](src/main/java/AoC2024_01.java) | [](src/main/java/AoC2024_02.java) | [](src/main/java/AoC2024_03.java) | [](src/main/java/AoC2024_04.java) | [](src/main/java/AoC2024_05.java) | [](src/main/java/AoC2024_06.java) | [](src/main/java/AoC2024_07.java) | [](src/main/java/AoC2024_08.java) | | [](src/main/java/AoC2024_10.java) | [](src/main/java/AoC2024_11.java) | [](src/main/java/AoC2024_12.java) | | [](src/main/java/AoC2024_14.java) | [](src/main/java/AoC2024_15.java) | | | | | | | | | | |
13-
| rust | [](src/main/rust/AoC2024_01/src/main.rs) | [](src/main/rust/AoC2024_02/src/main.rs) | [](src/main/rust/AoC2024_03/src/main.rs) | [](src/main/rust/AoC2024_04/src/main.rs) | [](src/main/rust/AoC2024_05/src/main.rs) | [](src/main/rust/AoC2024_06/src/main.rs) | [](src/main/rust/AoC2024_07/src/main.rs) | [](src/main/rust/AoC2024_08/src/main.rs) | | [](src/main/rust/AoC2024_10/src/main.rs) | [](src/main/rust/AoC2024_11/src/main.rs) | [](src/main/rust/AoC2024_12/src/main.rs) | [](src/main/rust/AoC2024_13/src/main.rs) | [](src/main/rust/AoC2024_14/src/main.rs) | [](src/main/rust/AoC2024_15/src/main.rs) | [](src/main/rust/AoC2024_16/src/main.rs) | [](src/main/rust/AoC2024_17/src/main.rs) | [](src/main/rust/AoC2024_18/src/main.rs) | [](src/main/rust/AoC2024_19/src/main.rs) | | | | | | |
13+
| rust | [](src/main/rust/AoC2024_01/src/main.rs) | [](src/main/rust/AoC2024_02/src/main.rs) | [](src/main/rust/AoC2024_03/src/main.rs) | [](src/main/rust/AoC2024_04/src/main.rs) | [](src/main/rust/AoC2024_05/src/main.rs) | [](src/main/rust/AoC2024_06/src/main.rs) | [](src/main/rust/AoC2024_07/src/main.rs) | [](src/main/rust/AoC2024_08/src/main.rs) | | [](src/main/rust/AoC2024_10/src/main.rs) | [](src/main/rust/AoC2024_11/src/main.rs) | [](src/main/rust/AoC2024_12/src/main.rs) | [](src/main/rust/AoC2024_13/src/main.rs) | [](src/main/rust/AoC2024_14/src/main.rs) | [](src/main/rust/AoC2024_15/src/main.rs) | [](src/main/rust/AoC2024_16/src/main.rs) | [](src/main/rust/AoC2024_17/src/main.rs) | [](src/main/rust/AoC2024_18/src/main.rs) | [](src/main/rust/AoC2024_19/src/main.rs) | [](src/main/rust/AoC2024_20/src/main.rs) | | | | | |
1414
<!-- @END:ImplementationsTable:2024@ -->
1515

1616
## 2023
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "AoC2024_20"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../aoc" }
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#![allow(non_snake_case)]
2+
3+
use aoc::graph::BFS;
4+
use aoc::grid::{CharGrid, Grid};
5+
use aoc::Puzzle;
6+
7+
struct AoC2024_20;
8+
9+
impl AoC2024_20 {
10+
fn solve(&self, grid: &CharGrid, cheat_len: usize, target: usize) -> usize {
11+
let start = grid.find_first_matching(|ch| ch == 'S').unwrap();
12+
let distances = BFS::execute_full(
13+
start,
14+
|cell| grid.get(&cell) != '#',
15+
|cell| {
16+
grid.capital_neighbours(&cell)
17+
.into_iter()
18+
.filter(|n| grid.get(n) != '#')
19+
.collect()
20+
},
21+
);
22+
let mut ans = 0;
23+
for cell in distances.keys() {
24+
for md in 2..cheat_len + 1 {
25+
for n in cell.get_all_at_manhattan_distance(md) {
26+
if !distances.contains_key(&n) {
27+
continue;
28+
}
29+
if distances[&n] < distances[cell] {
30+
continue;
31+
}
32+
if distances[&n] - distances[cell] >= target + md {
33+
ans += 1;
34+
}
35+
}
36+
}
37+
}
38+
ans
39+
}
40+
fn sample_part_1(&self, grid: &CharGrid) -> usize {
41+
self.solve(grid, 2, 2)
42+
}
43+
44+
fn sample_part_2(&self, grid: &CharGrid) -> usize {
45+
self.solve(grid, 20, 50)
46+
}
47+
}
48+
49+
impl aoc::Puzzle for AoC2024_20 {
50+
type Input = CharGrid;
51+
type Output1 = usize;
52+
type Output2 = usize;
53+
54+
aoc::puzzle_year_day!(2024, 20);
55+
56+
fn parse_input(&self, lines: Vec<String>) -> Self::Input {
57+
CharGrid::from(&lines.iter().map(AsRef::as_ref).collect::<Vec<_>>())
58+
}
59+
60+
fn part_1(&self, grid: &Self::Input) -> Self::Output1 {
61+
self.solve(grid, 2, 100)
62+
}
63+
64+
fn part_2(&self, grid: &Self::Input) -> Self::Output2 {
65+
self.solve(grid, 20, 100)
66+
}
67+
68+
fn samples(&self) {
69+
aoc::puzzle_samples! {
70+
self, sample_part_1, TEST, 44,
71+
self, sample_part_2, TEST, 285
72+
};
73+
}
74+
}
75+
76+
fn main() {
77+
AoC2024_20 {}.run(std::env::args());
78+
}
79+
80+
const TEST: &str = "\
81+
###############
82+
#...#...#.....#
83+
#.#.#.#.#.###.#
84+
#S#...#.#.#...#
85+
#######.#.#.###
86+
#######.#.#...#
87+
#######.#.###.#
88+
###..E#...#...#
89+
###.#######.###
90+
#...###...#...#
91+
#.#####.#.###.#
92+
#.#...#.#.#...#
93+
#.#.#.#.#.#.###
94+
#...#...#...###
95+
###############
96+
";
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use super::*;
101+
102+
#[test]
103+
pub fn samples() {
104+
AoC2024_20 {}.samples();
105+
}
106+
}

src/main/rust/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/rust/aoc/src/graph.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,36 @@ where
6363
unreachable!();
6464
}
6565

66+
pub fn execute_full(
67+
start: T,
68+
is_end: impl Fn(T) -> bool,
69+
adjacent: impl Fn(T) -> Vec<T>,
70+
) -> HashMap<T, usize> {
71+
let mut q: VecDeque<State<T>> = VecDeque::new();
72+
q.push_back(State {
73+
node: start,
74+
distance: 0,
75+
});
76+
let mut seen: HashSet<T> = HashSet::new();
77+
seen.insert(start);
78+
let mut distances: HashMap<T, usize> = HashMap::new();
79+
while let Some(state) = q.pop_front() {
80+
if is_end(state.node) {
81+
distances.insert(state.node, state.distance);
82+
}
83+
adjacent(state.node).iter().for_each(|n| {
84+
if !seen.contains(n) {
85+
seen.insert(*n);
86+
q.push_back(State {
87+
node: *n,
88+
distance: state.distance + 1,
89+
});
90+
}
91+
});
92+
}
93+
distances
94+
}
95+
6696
pub fn flood_fill(start: T, adjacent: impl Fn(T) -> Vec<T>) -> HashSet<T> {
6797
let mut q: VecDeque<T> = VecDeque::new();
6898
q.push_back(start);
@@ -333,6 +363,39 @@ mod tests {
333363
bfs_execute(Cell::at(0, 0), Cell::at(4, 0));
334364
}
335365

366+
fn bfs_execute_full(start: Cell) -> HashMap<Cell, usize> {
367+
#[rustfmt::skip]
368+
let v = &vec![
369+
".###",
370+
"..##",
371+
"#..#",
372+
"##..",
373+
"###."];
374+
let grid = CharGrid::from(&v);
375+
let adjacent = |cell| {
376+
grid.capital_neighbours(&cell)
377+
.iter()
378+
.filter(|n| grid.get(&n) != '#')
379+
.cloned()
380+
.collect()
381+
};
382+
BFS::execute_full(start, |cell| grid.get(&cell) == '.', adjacent)
383+
}
384+
385+
#[test]
386+
pub fn bfs_execute_full_ok() {
387+
let dist = bfs_execute_full(Cell::at(0, 0));
388+
assert_eq!(dist.len(), 8);
389+
assert_eq!(dist[&Cell::at(0, 0)], 0);
390+
assert_eq!(dist[&Cell::at(1, 0)], 1);
391+
assert_eq!(dist[&Cell::at(1, 1)], 2);
392+
assert_eq!(dist[&Cell::at(2, 1)], 3);
393+
assert_eq!(dist[&Cell::at(2, 2)], 4);
394+
assert_eq!(dist[&Cell::at(3, 2)], 5);
395+
assert_eq!(dist[&Cell::at(3, 3)], 6);
396+
assert_eq!(dist[&Cell::at(4, 3)], 7);
397+
}
398+
336399
#[test]
337400
pub fn bfs_flood_fill() {
338401
#[rustfmt::skip]

src/main/rust/aoc/src/grid.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::geometry::XY;
22
use itertools::Itertools;
33
use std::cmp::Ordering;
4+
use std::collections::HashSet;
45
use std::fmt::{Display, Error, Formatter};
56

67
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
@@ -61,6 +62,25 @@ impl Cell {
6162
}
6263
ans
6364
}
65+
66+
#[allow(clippy::needless_lifetimes)]
67+
pub fn get_all_at_manhattan_distance<'a>(
68+
&'a self,
69+
distance: usize,
70+
) -> impl Iterator<Item = Cell> + 'a {
71+
(0..=distance).flat_map(move |dr| {
72+
let dc = distance - dr;
73+
let set = HashSet::from([
74+
(self.row.checked_add(dr), self.col.checked_add(dc)),
75+
(self.row.checked_add(dr), self.col.checked_sub(dc)),
76+
(self.row.checked_sub(dr), self.col.checked_add(dc)),
77+
(self.row.checked_sub(dr), self.col.checked_sub(dc)),
78+
]);
79+
set.into_iter()
80+
.filter(|(rr, cc)| rr.is_some() && cc.is_some())
81+
.map(|(rr, cc)| Cell::at(rr.unwrap(), cc.unwrap()))
82+
})
83+
}
6484
}
6585

6686
impl PartialOrd for Cell {

0 commit comments

Comments
 (0)