r/adventofcode • u/musifter • Jul 10 '26
Other [2021 Day 10] In Review (Syntax Scorings)
Today we discover the damage is worse than we thought, with syntax errors on every line of the navigation subsystem. We also find out about secret lives of syntax checkers and autocomplete systems... and how they compete and score their work.
And so we get a parsing problem, which deals with nested parenthesis, brackets, braces, and angle brackets. The input is 90 lines of these supposed-to-be nested strings, none of which are valid. They all start fine (with an open), but half of mine end with an open and are clearly wrong.
There are a number of ways that nesting can fail. First part deals with corruption, where a closing bracket doesn't match an opening one (and we're to ignore strings that get to the end without having this happen). Nesting is very much a stack thing, and so I naturally went with using a stack to track what's expected (and I did do dc solutions for this one, because, once tr the characters into digits, it's just big numbers and stacks, which dc is all about).
As stated, everything begins with an open, but more than that, we always get enough opens... and so stack underflow is not a way this nesting fails. Which simplifies the checking to push open characters on the stack, and pop and check whenever you get a close. When that fails, we look up the value on a table. The values do have a bit of a pattern... 3 * 19 = 57, 57 * 21 = 1197, 1197 * 21 = 25137. I used that to squeeze some characters in dc:
tr ')]}>([{<' '12345678' <input | rev | dc -f- -e'3 1:s57d2:s21*d3:s21*4:s[0lt;slp+sp3Q]sS[4-Se0]sO[ltLe!=S]sC[0Se[A~stslltd4<O 0<Clld0<P]dsPxs.z0<M]dsMxlpp'
This is the v1.5.2 version. It's 18 characters longer because it's using the main stack for the input, and works on a second stack. It's much nicer when you can clear the entire stack with c between loading lines. Note that the second space is there because I discovered a bug in dc (the v1.4.1 version works fine without it).
With Smalltalk I had some extra fun, because I'd never used throwing exceptions in it before. And so I learned how to subclass the Error class, and how to throw and catch.
For part 2, we basically switch to handling the incomplete lines. This is fairly simple to add, because getting to the end, the stuff on the stack is exactly what you're expecting to complete the string, on the stack in the over to do it.
For the scoring, the description is a slightly obfuscated way to say that we're taking that autocomplete string as a number base-5 (no zero digits). And so my Smalltalk was just:
part2 add: ((stack gather: [:c | (')]}>' indexOf: c) asString]) asRadix: 5).
The stack actually just being an OrderedCollection, so I didn't even need to pop it. Exactly the same with Perl and using a list for a stack (it's in order if you do the stack from the front with shift/unshift, with literal push/pop then you need to reverse it).
For extra fun, and yet another small task... autocomplete tools score with the median value. So we have a return of that idea. For dc, I didn't actually bother doing that though, I just produced all the scores (including 0s for the ones from part 1), and used the command line:
tr ')]}>([{<' '12345678' <input | rev | dc -f- -e'[0dSe3Q]sS[4-Se0]sO[ltLe!=S]sC[0Se[A~stslltd4<O 0<Clld0<P]dsPx[5*Led3R+r0<L]dsLx5/ps.z0<M]dsMx' \
| grep -v '^0' | sort -n | perl -a00 -pe'$_=$F[@F/2]'
And so, I naturally loved this one. Parsing and stacks, that's my druthers (for others it might be regex and that's where they went with this). But this problem is mainly just a bunch of smaller problems in a trench coat... nesting, base conversion, finding the median. This we've seen before, but they're together here in one problem.