Skip to content

Commit

Permalink
day 20
Browse files Browse the repository at this point in the history
  • Loading branch information
narimiran committed Dec 20, 2024
1 parent 4883d14 commit a56209b
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 1 deletion.
121 changes: 121 additions & 0 deletions clojure/day20.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
^{:nextjournal.clerk/visibility {:code :hide :result :hide}}
(ns day20
{:nextjournal.clerk/auto-expand-results? true
:nextjournal.clerk/toc :collapsed}
(:require aoc))


;; # Day 20: Race Condition
;;
;; Today we're "right outside the CPU" and it's time for some race condition
;; festival.
;;
;; We're given a map of the racetrack (another 2D grid task!).
;; This time, there's only one way from the start (`S`) to the end (`E`),
;; and our job is to... cheat.



;; ## Input parsing
;;
;; We already had the same parsing task in [Day 16 solution](./aoc),
;; but this time we'll create a hash map, as we can use it directly later.
;;
(defn parse-data [input]
(let [lines (aoc/parse-lines input)]
{:start (first (aoc/grid->point-set lines #{\S}))
:end (first (aoc/grid->point-set lines #{\E}))
:walls (aoc/grid->point-set lines #{\#})}))


(def data (parse-data (aoc/read-input 20)))



;; ## Solution
;;
;; Once again, it is possible to write a single solution that works for
;; both parts.
;;
;; Assuming we have a list of all visited points on our `path` (and we'll
;; see a bit later how easy this can be done), we need to find all
;; "shortcuts" which would save us _at least_ 100 steps.
;;
;; We'll try every pair of points in our path that are at least 100
;; steps appart (`old-dist`).\
;; A `new-dist` between those two points (using cheats to walk through
;; the walls) is a direct path between them: a `manhattan` distance.
;;
;; We have a `limit` of the maximum length of a shortcut:
;; 2 steps in Part 1 and 20 steps in Part 2.
;;
;; Putting it all together, we're only interested in a total number of
;; such shortcuts:
;;
(defn count-cheats [indexed-path limit]
(aoc/do-count [[i a] indexed-path
[j b] indexed-path
:let [old-dist (- i j)]
:while (> old-dist 100)
:let [new-dist (aoc/manhattan a b)]
:when (and (<= new-dist limit)
(<= new-dist (- old-dist 100)))]))


;; The only thing remaining is to find the original `path` between `start`
;; and `end`.\
;; We've parsed our data into a hashmap with `:start`, `:end` and `:walls`
;; keys.
;; These are exactly the keys that my `[pathfinding helper]`(./aoc#graph-traversal)
;; uses: we pass the hashmap to `aoc/dfs` and extract `:path` from the
;; results.
;; Done.
;;
;; We need `[index value]` pair for each point on the path, and we can get
;; it using the `(map-indexed vector ...)` idiom.
;;
;; Since the `count-cheats` function does a nested loop through the path,
;; and the path itself has almost 10.000 elements, it takes a significant
;; amount of time to run it.\
;; Instead of waiting for the calculation for Part 1 to finish, before running
;; the calculation for Part 2, we can run them concurrently.
;;
;; We use the [`future` macro](https://clojuredocs.org/clojure.core/future),
;; which invokes a function in a separate thread, not blocking the current
;; one:
;;
(defn solve [grid]
(let [path (:path (aoc/dfs grid))
indexed-path (vec (map-indexed vector path))
p1 (future (count-cheats indexed-path 2))
p2 (future (count-cheats indexed-path 20))]
[@p1 @p2]))


(solve data)




;; ## Conclusion
;;
;; A surprisingly short one.
;; Basically, it is just one short function that does all the work needed.
;;
;; Btw, having a maze where you can destroy some walls to reach the end
;; quicker?
;; Now I'm even more sure than on Day 18: Eric must have played my
;; [aMaze game](https://narimiran.github.io/amaze/)! :)
;;
;; Today's highlights:
;; - `future`: invoke a function in another thread, concurrently






^{:nextjournal.clerk/visibility {:code :hide :result :hide}}
(defn -main [input]
(let [data (parse-data input)]
(solve data)))
3 changes: 2 additions & 1 deletion clojure/tests/solutions_tests.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
day01 day02 day03 day04 day05
day06 day07 day08 day09 day10
day11 day12 day13 day14 day15
day16 day17 day18 day19 ;day20
day16 day17 day18 day19 day20
;; day21 day22 day23 day24 day25
[clojure.test :refer [deftest is run-tests successful?]]))

Expand Down Expand Up @@ -42,6 +42,7 @@
(check-day 17 nil ["7,4,2,0,5,0,5,3,7" 202991746427434])
(check-day 18 nil [384 "36,10"])
(check-day 19 [6 16] [240 848076019766013])
(check-day 20 nil [1445 1008040])


(let [summary (run-tests)]
Expand Down
1 change: 1 addition & 0 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ Task | Notebo
[Day 17: Chronospatial Computer](https://adventofcode.com/2024/day/17) | [day17.clj](clojure/day17) | bench | Intcode meets Assembunny.
[Day 18: RAM Run](https://adventofcode.com/2024/day/18) | [day18.clj](clojure/day18) | viz | [aMaze](https://narimiran.github.io/amaze/) :)
[Day 19: Linen Layout](https://adventofcode.com/2024/day/19) | [day19.clj](clojure/day19) | | Another easy one.
[Day 20: Race Condition](https://adventofcode.com/2024/day/20) | [day20.clj](clojure/day20) | | aMaze, again.

Loading

0 comments on commit a56209b

Please sign in to comment.