Advent of Code 2022 part 3
Day 3
[2022-12-03]#development #adventofcode #lisp #apl
Previous: Advent of Code 2022 part 2
Next: Advent of Code 2022 part 4
note: I’m labeling these posts as “parts” instead of “days” since I may combine days, but so far each days discussion is long enough to warrant its own post.
GO BOILERMAKERS! lol as if they have a chance of beating Michigan, but still gotta say it. And no way I was paying a couple hundred bucks to watch what will surely be a rout, sorry guys.
post-game update: alright I have to give them credit, that game was much better than I expected, just a couple mistakes really, with some really stellar moments. Still glad I didn’t go :p
Day 3
For part 1 we need to split each line into two equal parts, find the common character between the parts, and then map that character to a value. Pretty straighforward, but it took me a bit of research to figure out how to map some concepts into APL. Inputs are passed to april as a vector of strings, one per line.
shared←{
l←⊃(⍴ ⍵) ÷ 2 ⍝ length of compartment
p←(l l / 1 2) ⊆ ⍵ ⍝ split compartments
⊃⊃∩/p ⍝ find common character
}
prio←'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ans1 ← +/ prio ⍳ shared ¨ in
There’s a few things that could probably be improved here, or that I don’t fully understand. In particular I’m still trying to wrap my head around tacit functions, and have been just been building functions or applying in steps instead.
- First we find the length of the string (
⍴ ⍵
) and divide by two. This actually returns a rank 1 vector so we just pick⊃
the first element. There’s probably a better way to do this. update: indeed there is a better way, with tally (≢
), which returns a scalar, so⊃(⍴ ⍵)
becomes≢⍵
. - Then we split the string into two parts, using partition (
⊆
). We build the partition key using replicate (/
1, e.g.2 2 / 1 2
would yield1 1 2 2
, which would parition the first two and last two elements together. - To find the common character we use intersection (
∩
), which in my initial solution I applied straighforward ly to the two parts as(⊃p)∩(2⊃p)
. I then realized I could just reduce over the parts, although I don’t totally understand the result of that, but I found if I use pick twice then I’ll get just the character (e.g."∩/ '123' '145'
yields#0A"1"
, while⊃∩/ '123' '145'
yields"1"
, which is what I’d expect from reduce). The second pick is to yield a single character, since if the common character repeats intersection will also repeat it. - We then run that function for every line, getting a vector of the common characters, which we get the index/value for by looking up in an array of the characters (which I was hoping I could more easily construct with ASCII character codes, but I didn’t find a way to do that in APL). Finally we reduce to get the sum.
Part 2 has us instead finding the common character between each group
of three lines. I struggled a bit at first to figure out how to
partition the input vector into groups of three, first seeing if there
was a way to do it with parition or enclose, then looking at windowed
reduce (e.g. 2+/ 1 2 3 4
calculates the sum of each pair, 3 5 7
). Then I realized with a facepalm that I could use reshape to turn
it into a 3xn matrix, and operate on the rows.
win←{↓(⊃(⍴⍵)÷⍺) ⍺ ⍴ ⍵} ⍝ split into ⍺-length arrays
com←{⊃⊃∩/⍵} ⍝ find common character
prio←'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
ans2 ← +/ prio ⍳ com ¨ 3 win in
- The
win
function is generalized to split a vector into parts of length ⍺. I’m not sure if there’s a way to say “reshape this into a n x ?, where you figure out how many rows we need to fit all the elements, so we just manually calculated the size ((⊃(⍴⍵)÷⍺)
). We then use split (↓
) to break it into sub-vectors. - The
com
function is the same algorithm we used to find the common characters in part 1. Here we needed to reduce, since we’re looking for the common character from three strings. - The rest is essentially the same as part 1.
-
yes replicate is also reduce, depending on whether it’s used with one or two arguments. This is common in vector languages, and often the two forms make sense together, but this is a case where the two forms don’t really make sense together (to me) ↩︎
Next: Advent of Code 2022 part 4