|
https://adventofcode.com/2023/day/4#part2 |
|
|
|
My solution to Part 1 was BOOOORING: just a bunch of regular |
|
expressions. YAWN. |
|
|
|
This one, I hope, is anything but. Usually with this sort of thing, I |
|
recommend typing it in to see how it works, but you might not find it |
|
that INSTRUCTIVE for this set of keystrokes, because a lot of the |
|
commands don't do anything when you type them! (They only have effect |
|
when playing back the macro, when the buffer has different contents.) |
|
|
|
However, if you ARE interested in this sort of thing, I recommend trying |
|
typing it in first to see if you can figure it out before reading on: |
|
it's more fun that way! |
|
|
|
yypD-2W |
|
qamayiwjA <Esc>p |
|
okJDJDciw +1<C-V><Esc><CR>3BJDJDdiw<Esc> |
|
`a*++y$@0`awq |
|
:2d |
|
:%norm!I1<Space> |
|
ggP+ |
|
qbq |
|
qqqqq |
|
"dyiw |
|
kC<C-R>=<C-R>-+<C-R>d<CR><Esc> |
|
jo0<Esc>-3W10@a |
|
+C<C-R>=<C-R>-<CR>@b`a<Esc>Ima<Esc> |
|
:.g/^ma0/norm!D |
|
0y$ddk@0 |
|
kJD+ |
|
@qq |
|
10u |
|
qb+ciw<C-R>=<C-R>-+<C-R>d<CR><Esc>q |
|
uggO0<Esc>+@q |
|
|
|
Not sure if I'll ever get around to writing up a fully annotated |
|
explanation of how this one works, but a rough description that |
|
reasonably experienced Vimmers should be able to follow is below. |
|
|
|
Before I realised how many scratchcards we were going to end up with I |
|
wrote a version that actually made new lines for each copied card. I'm |
|
pretty sure this version works, but I'm way less certain that it will |
|
complete before the HEAT DEATH OF THE UNIVERSE. (I set it running |
|
yesterday and it's still going: 670k scratchcards and counting.) |
|
|
|
So the basic concept for this MARGINALLY more efficient version is to |
|
only keep a single line for each card, and add a counter at the start of |
|
that line to keep track of how many copies of that card we have. It also |
|
adds a running total at the top of how many scratchcards we currently |
|
have stuffed into our pockets, drawers, every room in the house, the |
|
garage, the other houses in the village, etc. |
|
|
|
We do the calculations with three macro recordings. The macro in "q |
|
does all the work. Those in "a and "b are little helpers. Like |
|
little macro elves. |
|
|
|
Macro a: Check for Matches |
|
-------------------------- |
|
|
|
This macro is to be run with the cursor over one of the winning numbers. |
|
If it is a matching number, then it writes the text "+1" into the line |
|
below. This task is a LITTLE bit tricky when you're not allowed to use |
|
any functions or Vimscript so it works like this: |
|
|
|
First it sets the 'a mark to the current location, then it copies |
|
the current number into the line below. Next it writes two more lines of |
|
apparent GIBBERISH into the next two lines below that. Then it jumps |
|
back to the 'a mark to return to the current number and searches for |
|
the number with the * command. This will leave the cursor either on |
|
the matching number, if there is one, or on the copy we just made in the |
|
line below, if not. |
|
|
|
Next it moves two lines down, placing the cursor on one of the two lines |
|
we wrote before. Then it yanks that line - putting it in the yank register |
|
"0 - and immediately executes it as a macro with @0. |
|
|
|
The two lines we wrote contain the normal mode editing commands to |
|
either remove the number we added (for no match) or replace it with "+1" |
|
(for a match). |
|
|
|
Finally, macro "a moves the cursor one word forwards to the next |
|
winning number. |
|
|
|
We will run this macro 10 times: once for each winning number. |
|
|
|
Macro b: Make Copies for 1 Scratchcard |
|
--------------------------------------- |
|
|
|
Let's say you have 12 copies of scratchcard A, and it has two matching |
|
numbers. This macro moves down a line, and then adds 12 copies to the |
|
scratchcard the cursor is currently over. When processing scratchcard A. |
|
We'll run it twice: once for each winning number. |
|
|
|
It's not super complicated: it expects the number of copies to be stored |
|
in register "d and it adds that to the number the cursor is over |
|
by using the expression register. |
|
|
|
Recursive Macro q: Process All The Scratchcards |
|
----------------------------------------------- |
|
|
|
So now we have our "a and "b macros stuffed in our utility belt, |
|
doing the rest of the calculations will surely be TRIVIAL. Right? |
|
...Right? |
|
|
|
Macro "q starts with the cursor on the copies-counter for the |
|
current scratchcard, which is near the top of the buffer on line 2. |
|
First, it yanks the count into register "d. Then it moves up a line (to |
|
the total and adds the current scratchcard's count with the expression |
|
register. |
|
|
|
Next it calculates how many winning numbers there are on this |
|
scratchcard. It does this by writing a 0 into the line below and then |
|
running macro "a once for each winning number. |
|
|
|
So now the line below contains a sum (0 +1 +1 +1 ...) that adds up to |
|
the number of winning numbers. The macro calculates the sum by plugging |
|
it into the expression register and then adds the text "@b`a" after the |
|
sum and "ma" before it, so the line looks something like: |
|
|
|
ma12@b`a |
|
|
|
It's a macro that runs macro "b once for each winning number, and |
|
then moves the cursor back where it started! |
|
|
|
Before macro "q yanks and runs it, though, it needs to handle the |
|
case where there weren't any winning numbers, because the command |
|
"0@b" won't run a macro zero times: it will run it once. Using a |
|
:global command to delete the line if it starts "ma0" solves this |
|
problem. |
|
|
|
With that out of the way, macro "q yanks the macro, runs it, and |
|
then finishes off by deleting the current scratchcard line and recursing |
|
by running itself. |
|
|
|
Et voila! |
Haha yeah, well spotted! I meant to add a note about that. It’s actually using the
q
macro from Day 1 Part 2, which happened to still be in my"q
register. I'll update it to clarify.Thanks!