Advent of Code 2024 part 2

Dec 3, 2024
Day 1
Commented code below. Some things I'm really liking about uiua here:
- The editor does static analysis to determine how many stack arguments are used and how many values are returned, and colors the function names accordingly.
- The code is whitespace-insensitive and the formatter aligns comments, really helps break up larger functions in digestible pieces.
- Simple tests with
assert
, and test modules.
Stack manipulation/management is certainly one of the trickier parts of uiua, and as in so many areas there can be multiple ways to get the stack arranged as you need it, but sometimes one way is the clear "correct" way, although that often depends not only on what you need now but what you also need next.
Day 2
I had a bit of fun with this one, coming up with unicode symbols to use for each function.
Day 3
Day 4
Day 5
Days 6-13
I tried some of these initially in uiua, but fell back to clojure when I found myself struggling to correctly frame the problem. Having earlier fallen in love with parser-combinators for parsing the problem inputs, I settled on using the instaparse library, which allows one to define the parser as a context-free grammar, then transform it into a readily-usable data structure. For example (day 7):
(def inp
"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")
(def parse
(insta/parser
"S = eq+
eq = value <':'> values <'\\n'?>
values = (<' '> value)+
value = #'\\d+'
"))
(defn parse-inp [inp]
(->> (parse inp)
(insta/transform
{:value #(Long/parseLong %)
:values (fn [& rest] (vec rest))
:eq (fn [r v] {:res r :vals v})
:S (fn [& rest] rest)})))
(comment
(parse-inp inp)
; ({:res 190, :vals [10 19]}
; {:res 3267, :vals [81 40 27]}
; {:res 83, :vals [17 5]}
; {:res 156, :vals [15 6]}
; {:res 7290, :vals [6 8 6 15]}
; {:res 161011, :vals [16 10 13]}
; {:res 192, :vals [17 8 14]}
; {:res 21037, :vals [9 7 18 13]}
; {:res 292, :vals [11 6 16 20]})
)
Or a more involved example that lends itself well to this sort of parsing (day 13):
(def test1 "Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279")
(def parse
(insta/parser
"S = mach (<blank> mach)+
blank = #'\\n\\n'
mach = A B prize
A = <'Button A: X+'> d <', Y+'> d <'\\n'>
B = <'Button B: X+'> d <', Y+'> d <'\\n'>
d = #'\\d+'
prize = <'Prize: X='> d <', Y='> d <'\\n'>?"))
(defn parse-inp [inp]
(->> (parse inp)
(insta/transform
{:d #(Integer/parseInt %)
:mach (fn [a b p] {:a (subvec a 1) :b (subvec b 1) :prize (subvec p 1)})
:S (fn [& rest] rest)})))
(comment
(parse-inp test1)
; ({:a [94 34], :b [22 67], :prize [8400 5400]}
; {:a [26 66], :b [67 21], :prize [12748 12176]}
; {:a [17 86], :b [84 37], :prize [7870 6450]}
; {:a [69 23], :b [27 71], :prize [18641 10279]})
)