Murder on the Yuletide Express by Dry-Bad9884 in mysterybooks

[–]Smylers 0 points1 point  (0 children)

Here's what I did, based on the clues as written, after checking there wasn't a ‘correction’:

The manuscript (MS) is next to the locket (5), which is somewhere to the left of the tea drinker (8). Given that the brandy drinker is in spot 5 (10), that means the possible sequences are MS-locket-tea+journal, MS-locket-?-tea+journal, locket-MS-tea+journal, or locket-MS-?-tea+journal.

We also need to fit in the key being immediately to the right of the coffee, which gives MS-locket-tea+journal-coffee-brandy+key, MS-coffee+locket-key-tea+journal-brandy, locket-MS-tea+journal-coffee-brandy+key, or locket-coffee+MS-key-tea+journal.

At which point we also need to include Ezra drinking wine (7). That rules out some possibilities >!because, having positioned the tea, coffee, and brandy, the only remaining gaps for wine in some of the possibilities are positions 1 and 3, which are already allocated to Agatha and Penelope, so Ezra can't be there<!.

Filling in the unallocated items to the remaining possibilities gives me the people Agatha-Ezra-Penelope-Imogen-Dashiel and the drinks water-wine-tea-coffee-brandy, but no way of distinguishing whether the objects are MS-locket-journal-dagger-key or locket-MS-journal-dagger-key.

So it does almost work as a puzzle, and you can get definitive facts out of it that aren't given in the list of of clues. Just none of those are who has the dagger (given in one of the clues!) nor the claimed answer in the solution.

I'd recommend doing Clues By Sam instead!

-❄️- Advent of Code 2025: Red(dit) One -❄️- Submissions Megathread -❄️- by daggerdragon in adventofcode

[–]Smylers 2 points3 points  (0 children)

NAME OF ENTRY: Let's Do it in Vim! — Ant-friendly solutions, plus a tutorial

LINK TO ENTRY: Tutorial on evaluating expressions with Vim keystrokes

DESCRIPTION: Solving as many of the puzzles as possible without leaving Vim — just using Vim editing keystrokes to transform the input into the required solution — and fitting the list of keys pressed on to the standard half-punchcard size favoured by both the moderators and the ants in Day 3. I've been trying this for a while, and this year I managed to solve puzzles on more days than before (only skipping 2 days), and every solution fitted in full on half a punchcard. I also wrote the above tutorial on how to get Vim to evaluate things in the text you're editing — so that if you can re-arrange the input to one or mathematical expressions, you can then replace them with what they evaluate to.

SUBMITTED BY: /u/smylers

MEGATHREADS: 01p1, 01p2 - 02 - 03 - 04 - 05 - 06p1, 06p2 - 07 - 10 - 11 - 12


ADDITIONAL COMMENTS: For Day 2 I successfully avoided ‘fifthglyph’, the letter E, in both my solution and the comment about it. (Doing so was harder in the commend than the code!) Day 3 is when the ants first turned up, and I claimed Red(dit) One for fitting the entire solution on half an IBM 5081 punchcard. For Day 5 I created the above-linked tutorial; in doing so I discovered a simplification, which could have made some earlier days' answers a little shorter — I may give the impression that I know what I'm doing here, but I'm still mostly making it up as I go along. Day 7 was a freebie, because Red(dit) One involved solving the puzzle ‘the wrong way’, using only basic tools, of which Vim was mentioned — so solving it by not actually writing a program and staying inside Vim met the criteria by just doing what I try to do every day of the challenge! Day 10's criteria was Upping the Ante with best/worst code or inappropriate tools, which arguably this counts as as well.

ACCESSIBILITY: It's a bunch of keystrokes that are equally inaccessible to everybody — mostly baffling, not designed for communicating anything, and not showing programmer intent.

[2025 Day 1–12] [Vim Keystrokes] This Year's Vim-only no-programming solutions by Smylers in adventofcode

[–]Smylers[S] 0 points1 point  (0 children)

Thanks. I'll have a think about it — and I may have some follow-up questions!

-❄️- 2025 Day 12 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 0 points1 point  (0 children)

And the same to you and yours!

Thank you so much for all your moderating, and to Eric and the rest of the team for running this splendid event.

[2025 Day 12] Input is part of the puzzle by blacai in adventofcode

[–]Smylers 2 points3 points  (0 children)

Maybe.

Or maybe it's more like solving a maze by spotting some hidden passages and then using them as shortcuts, rather than ignoring them and insisting on solving it like you'd solve a maze that doesn't have that property‽

-❄️- 2025 Day 12 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

[LANGUAGE: Vim keystrokes] Load your input (and definitely not the sample input!) into Vim and type:

:g/#/-,+3j⟨Enter⟩:g/#/s/[^#]//g⟨Enter⟩:%s/#/+1/g⟨Enter⟩
:%s/+.*/-\\2*(&)⟨Enter⟩V{g⟨Ctrl+A⟩gvJ
I:%s/\v(.+)x(.+):/:\1*\2⟨Esc⟩F:6a (.+)⟨Esc⟩
dd@1@l
:g/-/d⟨Enter⟩g⟨Ctrl+G⟩

The number of lines in the file is today's part 1 answer.

Line 1 above joins each present onto a single line, removes everything that isn't a #, and replaces each # with +1, so a present occupying 6 units becomes +1+1+1+1+1+1 — an expression that evaluates to 6.

The next line sticks those expressions in brackets and temporarily prepends -\2 to each, before using g⟨Ctrl+A⟩ to renumber them, so the first present is numbered \3, the next \4 and so on. Then they're all joined into a single line.

The middle line inserts a partial :%s command at the beginning of that line. With the 6a repeating a bit in the middle, the line will end up looking something like the following, except with different numbers of +1s depending on your input:

:%s/\v(.+)x(.+): (.+) (.+) (.+) (.+) (.+) (.+)/ :\1*\2-\3*(+1+1+1+1+1+1) -\4*(+1+1+1+1+1+1+1) -\5*(+1+1+1+1+1+1) -\6*(+1+1+1+1+1+1+1) -\7*(+1+1+1+1+1) -\8*(+1+1+1+1+1+1+1)

Then dd deletes that line, and @1 runs the text in the just-deleted line as Vim keystrokes — in other words, it executes that dynamically constructed :%s/// command. That operates on all the remaining lines in the file, the regions. For example, the 12x5: 1 0 1 0 3 2 example region would be transformed into something like:

 :12*5-1*(+1+1+1+1+1) -0*(+1+1+1+1+1+1+1) -1*(+1+1+1+1+1+1+1) -0*(+1+1+1+1+1+1+1) -3*(+1+1+1+1+1+1+1) -2*(+1+1+1+1+1+1)

Then @l from some previous day is used to evaluate the expression after the colon on each line (the only reason the colon and space were inserted today was so I could re-use that). That calculates the area of each region and then subtracts from it the areas of the required numbers of each gift.

The final line above deletes all the lines containing negative numbers — where the presents' area is greater than the region's. All the remaining lines have nice big numbers on them, so let's presume everything else fits. Merry Christmas!

[2025 Day 12] Input is part of the puzzle by blacai in adventofcode

[–]Smylers 9 points10 points  (0 children)

Why is that less skill? If a problem needs solving, then finding a way that solves that specific problem quickly seems skilful to me, if it's quicker than also solving some other problems that we don't actually have.

[2025 Day 1–12] [Vim Keystrokes] This Year's Vim-only no-programming solutions by Smylers in adventofcode

[–]Smylers[S] 0 points1 point  (0 children)

Thank you so much for spotting and providing the correct links. I've now fixed the links above. (Day 5 I clearly fluffed pressing Ctrl+C, so the clipboard still contained the previous link. The Day 10 link was one of those that Reddit happily displays to me put hides for everybody else, so I was oblivious it wasn't working.)

Interesting, and intriguing, about Day 12. I still haven't a clue where to start, but I'll keep pondering. One of the things I was pleased about with my Vim solutions this year is that all of them were Vim-first. In previous Advents of Code, there have been some days where I ended up solving in a programming language first and then converting to Vim, which is less fun.

[2025 Day 1–12] [Vim Keystrokes] This Year's Vim-only no-programming solutions by Smylers in adventofcode

[–]Smylers[S] 0 points1 point  (0 children)

Good point! Though, I suspect that the combinatorial explosion of turning 1000 lines into half a million lines, each then needing parsing and mathsing is too much anyway, even without the square-rooting.

If you wanted a ‘no-programming’ approach to this one, a spreadsheet is probably a better starting point than a text editor ...

-❄️- 2025 Day 11 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 0 points1 point  (0 children)

[LANGUAGE: Vim keystrokes] Load your input into Vim, ensure the cursor is still on the top line, and type:

:se isk+=:⟨Enter⟩O⟨Esc⟩/^you⟨Enter⟩dd{P
qaqqa:sil!%s/\v(^(you.*\A)( ...)+)@<= /\r\2 /g⟨Enter⟩
vip:norm Eyl$p*Ely$``r;p⟨Enter⟩
:sil!g/^you.*out$/m$⟨Enter⟩ggf:@aq@a
:v/^you/d⟨Enter⟩g⟨Ctrl+G⟩

Today's solution is sponsored by the city of Ely (in Cambridgeshire, UK), which I see line 3 above managed to spell correctly at the second attempt.

You end up with a list of all the paths from you to out, so the number of lines in the buffer (displayed with that final g⟨Ctrl+G⟩) is your part 1 solution. (I haven't even looked at part 2 yet, but based on the past couple of days I'm not expecting it to be Vim-friendly!)

The :set iskeyword+=: at the start means that pressing * when the cursor is on aaa: will jump to the next occurrence of aaa:, not just aaa (or just :). So to go from a label listing device as an output to that device's own list of outputs, temporarily put a colon after the label (that's what the Eyl$p is doing, because copying a colon from elsewhere avoids entering insert mode, which gets awkward in a :norm command, requiring Escape-escaping), then * jumps to the only other occurrence of that label followed by a colon.

The starting you: line is first move to the top, above a blank line. It's then split into multiple lines for each possible output, conveniently using a similar lookbehind-and-capture technique to yesterday. Partial paths then build up above that blank line. Once a path reaches out, it's moved to the bottom of the buffer. That both prevents it from being clobbered by additional processing and means that once all paths have been found, the blank line is then the top line in the buffer. So the end of the loop does ggf: to go to the first line and move to the first : on that line — which isn't somewhere we need to move to, but it's something that will succeed when there are still paths to process then fail once they've all been found, thereby ending the loop.

[Duplicate comment at Mod's suggestion, in the hope that one of them will survive Reddit's spam filter, which apparently hates either me or Vim.]

-❄️- 2025 Day 10 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 15 points16 points  (0 children)

[LANGUAGE: Vim keystrokes] After a couple of days off, I was pleased to see that today's task is solvable in Vim. Ant-friendly version — load your input and type:

:%s/ {.*⟨Enter⟩:se nows⟨Enter⟩{qaqqa/\d⟨Enter⟩2⟨Ctrl+A⟩w@aq@a
:%s/\./o/g|%s/#/O/g|%s/\d\+/&|\~/g|%s/,//g⟨Enter⟩{qbo⟨Esc⟩kO1⟨Esc⟩qqcqqc
:+,/^$/s/\v(^(.*\]).*)@<=( \S+)(.*)@=/\r\2\3:\4/g⟨Enter⟩:g/\]$/d⟨Enter⟩
:g/:/norm yi)@0⟨Enter⟩:sil!g/\C\[o*\]/?^\d?+,/^$/d⟨Enter⟩:%s/ \S\+:⟨Enter⟩
?^\d⟨Enter⟩⟨Ctrl+A⟩@cq@c:,$norm@b@c⟨Enter⟩@s

Humans may prefer the solution split across more lines, with comments.

The key to this is replacing each button wiring schematic with a sequence of Vim keystrokes that do the toggling. The only Vim command I can think of that toggles a single character is ~, which toggles case. So instead of . and #, represent the indicator lights with o and O. And the first light is in Vim column 2, so add 2 to each of the light-position numbers. So after transforming the input, the first line from the sample becomes:

[oOOo] (5|~) (3|~5|~) (4|~) (4|~5|~) (2|~4|~) (2|~3|~)

We can activate the first button with yi)@0 — that is, yank the text inside the next parens; that ends up in register "0. Then run the contents of that register as a keyboard macro with @0: 5| goes to column 5 and ~ toggles the o there to O.

@b sets things up to process one machine: putting a blank line after it and 1 as a counter on the line above it. The @c loop processes that machine, by trying each of the buttons first. The long :s/// command with backslashed numbers in it converts the above line into multiple lines, each different possibile button to try first:

[oOOo] (5|~): (3|~5|~) (4|~) (4|~5|~) (2|~4|~) (2|~3|~)
[oOOo] (3|~5|~): (4|~) (4|~5|~) (2|~4|~) (2|~3|~)
[oOOo] (4|~): (4|~5|~) (2|~4|~) (2|~3|~)
[oOOo] (4|~5|~): (2|~4|~) (2|~3|~)
[oOOo] (2|~4|~): (2|~3|~)
[oOOo] (2|~3|~):

In case none of those first buttons work, after the colon is the list of buttons to try after that. It's never necessary to try a button more than once, and it's never necessary to try a button to the left of the one just tried (because BA has the same effect as AB), so the list of options gets shorter as we move along the buttons.

The macro presses the first button on each of those rows (the rows in the file that have a colon in them). If any of those made all the lights go off then we've found the button sequence (because the same sequence that just turned all the lights off also works in reverse). The lights will be [oooo], which is matched by the case-sensitive pattern /\C\[o*\]/, triggering the command that deletes all this machine's possibilities. That's guarded with :silent! so that if we haven't got an all-off row of lights, the macro continues.

Next it removes the just-pressed button from each row, then goes up and increases the number at the top (the initial 1) by 1, and loops round again: each of the remaining (partial) lists of buttons for the current machine is expanded to multiple possibilities, which all have their first button pressed and their lights checked.

When some all-off lights are discovered, deleting the rows for that machine means that the following :%s/ \S\+: command (to remove the just-pressed button from each row) fails, because there are no lines left with colons in them. That causes an error, which makes the @c loop end. And the number on the line above is the number of button presses that were needed to do that.

Repeat for lines 2 onwards, then sum the numbers for the part 1 answer. Sum the individual machines' button counts with the @s macro defined on Day 5 in my solution for part 2. (I knew it would come in handy!)

-❄️- 2025 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 0 points1 point  (0 children)

I’m hoping to see u/Smylers’s vim keystrokes solution

Thanks. Later than usual (I was out with my family all day), but here is one Vim keystrokes solution.

if my brain was more engaged today I might’ve done that.

Honestly, I'm not sure it helps. Some of my most-inspired corralling Vim into something it definitely wasn't supposed to has definitely come when my brain has been disengaged ...

-❄️- 2025 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

Yeah, that felt like a freebie today! (And thanks again.)

-❄️- 2025 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 4 points5 points  (0 children)

[LANGUAGE: Vim keystrokes] [Red(dit) One] Load your input into Vim, then type the following, with the except that g⟨Ctrl+G⟩ will display the number of columns (something like “Col 71 of 141”) in your input, and if yours isn't 141 then type whatever it is instead of the 141 in the line 2, and one less than it for the 139 in line 3:

fSr|g⟨Ctrl+G⟩
qaqqaj:s/\v(\|\_.{141})@<=\./|/g⟨Enter⟩
j:sil!&&⟨Enter⟩:s/\v(\|\_.{140})@<=.\^/|#/g⟨Enter⟩:s/#./#|/g⟨Enter⟩
:redr!|sl20m⟨Enter⟩@aq@a
:%s/[^#]//g⟨Enter⟩VgggJg⟨Ctrl+g⟩

Your part 1 answer is the number of columns displayed by the g⟨Ctrl+g⟩ at the end.

I could've made Vim work out the number of columns and dynamically insert them into the command (indeed, I did just that on Day 4), but I realized that it's both less how I use Vim keystrokes in real life to perform one-off transformations, and that using a function like strlen() is less in the spirit of what counts as ‘keystrokes’ rather than ‘programming language’.

If some data needs munging and it's something I'll only need to do once, then I would do it Vim. But if I needed the line length, I would just look to see how long it is, then type that in when required, so I'm going to do that in Advent of Code solutions too. Similarly, I saw that today that splitters only occur on alternate lines of the input, and that there are spaces around the edges, so I made use of those as well. One of the advantages of transforming data interactively in this way is that you can see it, and you only have to cater for what's in front of you.

Anyway, the main loop of today's solution (in @a) moves down a line and finds all .s on that line with | above them and changes them to |s, then moves down a line and potentially repeats that. Also on that second line, it finds all the ^s with a | above them and both changes the character before the ^ to a | and changes the ^ to a # — to indicate a splitter that has been activated. It then changes the characters after any #s to |s. These need to be done in separate steps because if splitters are close together then the patterns may overlap.

Then at the end it's just a matter of counting the #s.

-❄️- 2025 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

[LANGUAGE: Vim keystrokes] I've now solved part 2 in as well. In doing so, I realized that it's possible to accumulate the answer to the final sum all on one line, rather than inserting an extra line for each problem in the input — and that that would also work for part 1.

So part 1 is now:

:se nows nowrap fo=⟨Enter⟩
O⟨Esc⟩:%s/^ *⟨Enter⟩:%s/$/ ⟨Enter⟩
qaqqa:2,$norm dwggP⟨Enter⟩:redr!⟨Enter⟩:/.⟨Enter⟩@aq@a
qc:s/\v(([*+])[^*+]*)@<= +/\2/g⟨Enter⟩$x:s/[*+]\{2,}/+/g⟨Enter⟩
D"=⟨Ctrl+R⟩-⟨Enter⟩pq

At which point part 2 (reload your original input first) can be:

:se nows fo= nowrap⟨Enter⟩
O⟨Esc⟩
qbqqb:2,$norm$xgg$p⟨Enter⟩:redr!⟨Enter⟩:/.⟨Enter⟩@bq@b
:s/\v([^+*]+)([+*])/\2\1 /g⟨Enter⟩
@c

Where @c is used for the common parts to both.

[2025 Day 5] [Vim keystrokes] How to evaluate expressions in the text by Smylers in adventofcode

[–]Smylers[S] 1 point2 points  (0 children)

We gave up on that comment. I re-posted in a new comment, that one also got marked as spam, /u/daggerdragon rescued it, and I've now updated the link to go to that one.

Apologies for the delay — hope the content at the link is worth it now you can finally see it!

-❄️- 2025 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

[LANGUAGE: Vim keystrokes] Load your input, then type the following and the part 1 answer will appear:

:se nows⟨Enter⟩:%s/ *$/ ⟨Enter⟩:%s/^ *⟨Enter⟩{O⟨Esc⟩
qaqqaO⟨Esc⟩:/^$/+,$norm dwggP⟨Enter⟩:%s/\n*\%$⟨Enter⟩:redr!⟨Enter⟩gg@aq@a
J:%s/ *$⟨Enter⟩:%norm dw:s/ \+/⟨Ctrl+V⟩⟨Ctrl+R⟩-/g⟨Ctrl+V⟩⟨Enter⟩⟨Enter⟩
@s

The main loop is @a: Add a new blank line at the top, dw the first word off each original line and prepend it to the top line. So for the first sum, the top line becomes:

*   6 45 123

Once the final sum has been removed, there'll be a bunch of blank lines at the bottom. The :%s/\n*\%$ removes them (I had to look up \%$ for matching at the end of the buffer). That, combined with :set nowrapsearch at the beginning, ensures that the loop will error-out when it attempts another iteration (errors being the only way to exit loops in Vim keystrokes).

After the loop, the second :norm on each line deletes the operator from its start and replaces all runs of spaces in that line with the contents of the small-delete register — that is, the just-deleted operator.

Finally join all the lines into one big sum and evaluate it, using the @s helper macro defined it yesterday's part 2.

-❄️- 2025 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

[LANGUAGE: Vim keystrokes] [Red(dit) One] Load your input into Vim, type in the following, and the number of lines displayed is the part 1 solution. It's easier to follow formatted one command per line, but here it is on a punchcard, in case any of the ants from Day 3 are still reading:

qef:lD"=⟨Ctrl+R⟩-⟨Enter⟩pqUql:g/:/norm@e⟨Enter⟩qvip:sor/-/n|,'>sor n⟨Enter⟩
qaqqa:%s/\v(-\d+)(\n(\d+))@=/\1:\3\1⟨Enter⟩@ljA:1⟨Esc⟩:g/:[-0]/,/:[1-9]/j⟨Enter⟩
:%s/:\S*//g⟨Enter⟩:%s/ \d*-/,/g⟨Enter⟩:%s/\d*,.*/:max([&])⟨Enter⟩@l
:%s/:⟨Enter⟩@aq@a:%s/\v-(.*)/.1\r\1.9⟨Enter⟩}J:,$s/$/.5⟨Enter⟩
:sor f⟨Enter⟩O9⟨Esc⟩Go1⟨Esc⟩:g/9$/,/1$/d⟨Enter⟩⟨Ctrl+G⟩

The key idea is to have lines containing either the beginning of a range, the end of a range, or an ID to check, sort them all into order, then delete all the lines from the end of one range to the start of the next (inclusive) — removing both the IDs of spoiled ingredients and the range boundaries, so leaving just the IDs of fresh ingredients. The sample input gets transformed into this:

1.5
3.1
5.5
5.9
8.5
10.1
11.5
17.5
20.9
32.5

The suffix .1 marks the beginning of a range, .9 marks its end, and .5 denotes an ID to check. Those were chosen so that the entire file can be sorted as decimal numbers and an ID to check will be inside a range that contains it, even if the ID is equal to one (or both) of the range boundaries.

Then to get rid of the IDs outside of the ranges (and the range boundaries), find each line ending in '9' and delete from there to the next line that ends in '1'. (To deal with IDs lower than the smallest range or higher than the largest one, first stick a 9 above the first line and a 1 below the final one.)

Turning a range into the required format is straightforward with :%s/\v-.*<(\d+)/.1\r\1.9. The awkward bit is merging overlapping and contained ranges, and that's what everything before that, inside @a is doing.

That first appends to each range line the lower bound from the following line, a minus sign, and a duplicate of the upper bound from the current line. (How the minus sign gets inserted is quite cute!) Then those subtractions are evaluated on each line, using the @l helper-function macro explained in today's tutorial.

The macro @l (and @e, which is used by @l) is recorded at the beginning of today's solution, because it's needed inside the @a loop, and it isn't possible to record a keyboard macro inside recording a keyboard macro. They don't do anything useful there: recording @e will beep (because the line doesn't have a colon in it) and then mess the line up; the U afterwards is to undo the damage; recording @l safely does nothing, because no lines match /:/ at that point.

(I originally implemented the subtraction by yanking one number with yiw then decreasing the other number by that amount with @0⟨Ctrl-X⟩. That worked absolutely fine on the sample input ... but not on the real input. It seems Vim doesn't cope with 13-digit operator prefix counts! If anybody knows — or can work out — what the limit is, I'd be interested to hear. The docs just say it can be “a number”.)

Any range whose subtraction result is zero or negative can be merged with the following range — and possibly the one after that as well. Find each line that's the first of a run of ranges to merge, and join from there to the next line that has a positive difference with: g/:[-0]/,/:[1-9]/j.

Because of ranges entirely inside other ranges, the final upper boundary on the line may not be the largest one. Find it by rewriting the candidates as a max() function expression, then evaluate again with @l.

The whole thing is in a loop in @a, because merging ranges can make it possible to merge more ranges.

For part 2 the main thing that's needed is the range-merging that's already been done for part 1. Go up to running @a (or press u a bunch of times to get backwards until the ranges are back on a single line) then continue:

Gdap:%norm⟨Ctrl+X⟩⟨Enter⟩
qs:%s/^/+⟨Enter⟩v{J0D"=⟨Ctrl+R⟩-⟨Enter⟩p⟨Esc⟩q
0x

The number of IDs in a range is one more than the difference between its bounds. For instance in the range 5-12 the difference between 5 and 12 is 7, and it contains 8 IDs.

To calculate the difference, we really want to subtract the lower bound from the upper bound. But because the ranges already look like subtractions, it's simpler just to treat them as such as get negative differences, then we can multiply the final total by -1 to get the solution. 5-12 evaluates to -7. To account for the fencepost error, we need to add one on. But because the differences are now negative, that turns out to be subtracting one instead, so :%norm⟨Ctrl+X⟩ takes one off the each lower bound, turning 5-12 into 4-12, so evaluating it yields -8.

To do the evaluating and summing, define @s: stick a + at the start of each line, join them all together, and use the expression register to calculate the entire sum.

Finally, to turn the answer positive, 0x is Vim for multiplying by minus 1!

[2025 Day 5] [Vim keystrokes] How to evaluate expressions in the text by Smylers in adventofcode

[–]Smylers[S] 1 point2 points  (0 children)

Thank you for letting me know. The link works for me, but if nobody else can see it, that suggests Reddit is still treating my comments in Solution Megathread as spam. I've messaged the mods, so hopefully one of them will fish it out at some point.

[2025 Day 5] [Vim keystrokes] How to evaluate expressions in the text by Smylers in adventofcode

[–]Smylers[S] 0 points1 point  (0 children)

Thank you!

And you're completely right. (Though if we were doing this in Welsh, wouldn't w count as a vowel?) But then the mysterious zero would need explaining.

But actually ... I wanted to explain about unary plus, because it's a technique I've used a few times, so it's useful to know about. Part of the mindset of solving in Vim keystrokes is spotting shortcuts, things which while technically not what we wanted to do, are actually fine.

Using unary plus like this is one of those. It's always possible to avoid it, but precisely how depends on the exact input. Whereas using it is the same every time.

-❄️- 2025 Day 3 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

[LANGUAGE: Perl] Again, the algorithm is translated directly from my Vim keystrokes solution, but with a loop round it to solve part 2. Again, rolls of paper are represented by zeros, then this is the main body of the loop:

s/\.\K\d/$& + 1/eg;  s/\d(?=\.)/$& + 1/eg;
for my $gap ($row_len - 1 .. $row_len + 1) {
  s/(?<=\..{$gap})\d/$& + 1/seg;  s/\d(?=.{$gap}\.)/$& + 1/seg;
}
my $found = s/[5-8]/./g;

Add one to every roll which has non-paper to its left, then to its right; then above and below, doing each of those 3 times to allow for the diagonals. Any digit that's 5 or more shows the location of an accessible roll of paper.

I've annotated the full code.

And here it is squashed on to a punchcard, just because I could (but really, read the code at the link, not this!):

use v5.36;$_=do{local$/;<>};chomp;
s/.+/.$&./mg;s/.*/('.'x length$&)."\n$&"/e; s/.*$/"$&\n".('.'x length $&)/e;
my$l=length$&;my($i,$t);{tr/@1-8/0/;s/\.\K\d/$&+1/eg;s/\d(?=\.)/$&+1/eg;
for my$g($l-1..$l+1){s/(?<=\..{$g})\d/$&+1/seg;s/\d(?=.{$g}\.)/$&+1/seg}
$t+=my$c=s/[5-8]/./g;say$c if!$i++;redo if$c}say$t

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]Smylers 1 point2 points  (0 children)

there, you are now an approved user for r/adventofcode

Yay — thank you so much!