diff --git a/.aoc_tiles/tiles/2024/20.png b/.aoc_tiles/tiles/2024/20.png
index 2b1c678..852cf09 100644
Binary files a/.aoc_tiles/tiles/2024/20.png and b/.aoc_tiles/tiles/2024/20.png differ
diff --git a/README.md b/README.md
index b2594e8..ee1f321 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
- 2024 - 38 ⭐ - Rust
+ 2024 - 39 ⭐ - Rust
@@ -59,4 +59,7 @@
+
+
+
diff --git a/src/day20.rs b/src/day20.rs
new file mode 100644
index 0000000..b680046
--- /dev/null
+++ b/src/day20.rs
@@ -0,0 +1,165 @@
+use std::{
+ cmp::Reverse,
+ collections::{BinaryHeap, VecDeque},
+};
+
+use aoc_runner_derive::aoc;
+use grid::Grid;
+use rustc_hash::{FxHashMap, FxHashSet};
+
+trait PathTrack {
+ const DOES_WORK: bool = true;
+ fn new() -> Self;
+ fn push(&mut self, pos: (i64, i64));
+ fn finalize(&mut self) {}
+}
+
+struct RaceTrack {
+ map: Grid,
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
+struct State {
+ pos: (i64, i64),
+ cost: usize,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+struct CheatState {
+ s: State,
+ p: Vec<(i64, i64)>,
+}
+
+const DIRECTIONS: [(i64, i64); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
+
+impl RaceTrack {
+ fn valid_moves<'a>(&'a self, CheatState { s: state, p }: &'a CheatState) -> impl Iterator- + 'a {
+ let mut new_path = p.clone();
+ new_path.push(state.pos);
+ DIRECTIONS
+ .iter()
+ .map(|dir| (state.pos.0 + dir.0, state.pos.1 + dir.1))
+ .filter_map(move |pos| match &self.map.get(&pos) {
+ Some(b'.') | Some(b'S') | Some(b'E') => Some(CheatState {
+ p: new_path.clone(),
+ s: State {
+ pos,
+ cost: state.cost + 1,
+ },
+ }),
+ _ => None,
+ })
+ }
+ fn path_costs(&self, start: (i64, i64), goal: (i64, i64)) -> (Vec<(i64, i64)>, Grid