Skip to content

Commit

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


;; # Day 7: Bridge Repair
;;
;; We're standing next to a bridge and we need to help a group of engineers
;; with their calibrations: some young elephants stole their operators, so
;; they are left with a list of numbers that looks like this:
;;
(def example "190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20")




;; ## Input parsing
;;
;; We'll extract all numbers from each line, knowing that the first value is
;; a result of an equation, and the remaining numbers are operands:
;;
(defn parse-data [input]
(aoc/parse-lines input :ints))

(def example-equations (parse-data example))
(def equations (parse-data (aoc/read-input 7)))




;; ## Solution
;;
;; Since both parts are basically the same, we will have one set of functions
;; to solve both.
;;
;; There are three possible operations:
;; - addition (`2 + 3 = 5`)
;; - multiplication (`2 * 3 = 6`)
;; - concatenation (`2 || 3 = 23`) (only for part 2)
;;
;; The first two are, of course, built-ins. We can define the third as:
;;
(def || (comp parse-long str))



;; For each equation we will recursively check if it is valid:
;; - If the equation contains just one operand, the equation is valid if
;; that operand is equal to the result.
;; - If the first operand is already higher than the result, the equation
;; can never be valid (each operation only increases the final value).
;; - Otherwise, we need to try all operations on the first two operands
;; and recurse with the result of that operation as a first operand.\
;; e.g. `[a b c d e] --> [(operation a b) c d e]`
;;
(defn valid-equation? [res [a b & tail] concat-op?]
(let [valid-op? (fn [op]
(valid-equation? res (conj tail (op a b)) concat-op?))]
(cond
(nil? b) (when (= res a) res)
(> a res) nil
:else (or (valid-op? +)
(valid-op? *)
(when concat-op? (valid-op? ||))))))


;; The only difference between part 1 and part 2 is if we use the concatenation
;; operator or not:
;;
(defn valid-1? [[res & ops]]
(valid-equation? res ops false))

(defn valid-2? [[res & ops]]
(valid-equation? res ops true))


;; Now we're ready to solve both parts.
;;
;; One thing to notice is that if an equation is valid in the first part, then
;; it will be also valid in the second part (we can use exactly the same operands
;; as in part 1) and there's no need to check it again,
;; especially since finding suitable operators in part 2 is significantly slower,
;; e.g. the longest equations have 11 operations:
;; - part 1: `2^11 = 2048` potential operations to check
;; - part 2: `3^11 = 177147` potential operations, 86 times more
;;
;; The [`group-by` function](https://clojuredocs.org/clojure.core/group-by)
;; will be useful once again: we'll split the equations in those that
;; satisfy the `valid-1?` functions and those that don't.\
;; To calculate the result for part 1, it is enough to sum all the results
;; (the first element of the list) from the first group.
;;
;; For the second part, we need to check the failing equations if they
;; satisfy the `valid-2?` function (once again we'll use the
;; [`pmap` function](https://clojuredocs.org/clojure.core/pmap) to do that
;; in parallel and gain some performance)
;; and calculate the sum of the results of those which do.
;; In the end, we need to add the result from part 1.
;;
(defn solve [equations]
(let [{pt1-eqs true
pt2-eqs false} (group-by (comp some? valid-1?) equations)
pt1 (aoc/sum-map first pt1-eqs)
pt2 (->> pt2-eqs
(pmap valid-2?)
(filter some?)
(reduce +)
(+ pt1))]
[pt1 pt2]))

(solve example-equations)
(solve equations)





;; ## Conclusion
;;
;; Today felt like a natural task to use recursion:
;; we have two simple exit conditions and we know how to get there (by trying
;; all possible combinations of operations).
;; A perfect job for the _Recursion Fairy_:
;;
;; > Your only task is to simplify the original problem, or to solve it directly
;; > when simplification is either unnecessary or impossible;
;; > the Recursion Fairy will magically take care of all the simpler
;; > subproblems for you.
;; >
;; > — Jeff Erickson, Algorithms
;;
;;
;; Today's highlights:
;; - `[a b & tail]`: unpack a seqence into its first two elements and all the rest









^{:nextjournal.clerk/visibility {:code :hide :result :hide}}
(defn -main [input]
(solve (parse-data input)))
3 changes: 2 additions & 1 deletion clojure/tests/solutions_tests.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(ns solutions-tests
(:require
day01 day02 day03 day04 day05
day06 ;day07 day08 day09 day10
day06 day07 ;day08 day09 day10
;; day11 day12 day13 day14 day15
;; day16 day17 day18 day19 day20
;; day21 day22 day23 day24 day25
Expand Down Expand Up @@ -29,6 +29,7 @@
(check-day 4 [18 9] [2534 1866])
(check-day 5 [143 123] [5091 4681])
(check-day 6 [41 6] [5131 1784])
(check-day 7 [3749 11387] [1985268524462 150077710195188])


(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 @@ -59,4 +59,5 @@ Task | Notebook | Comment
[Day 4: Ceres Search](https://adventofcode.com/2024/day/4) | [day04.clj](clojure/day04) | No Más!
[Day 5: Ceres Search](https://adventofcode.com/2024/day/5) | [day05.clj](clojure/day05) | Easier than it looked initially.
[Day 6: Ceres Search](https://adventofcode.com/2024/day/6) | [day06.clj](clojure/day06) | The guard is always right!
[Day 7: Ceres Search](https://adventofcode.com/2024/day/7) | [day07.clj](clojure/day07) | Elephants ate my homework!

Loading

0 comments on commit 6ecb020

Please sign in to comment.