edit

Advent of Code 2025 solutions

Github

Day 1

+/~100!i:50+\.'@[;0;"--"5!]'0:"i/1" /p1
+/50(-/-100!@/1>:\-/1>/\,)':i       /p2

Parsing

i:50+\.'@[;0;"--"5!]'0:"i/1"
  1. 0:"i/1" reads lines from the input file
  2. @[;0;"--"5!]' for each line, edit the first (0th) character by:
    1. 5! taking modulo 5; the ASCII values of L and R are 76 and 82, so 1 and 2 mod 5
    2. index "--" with the mod 5 value; this gives "-" or " " because of a ngn/k quirk (the null value, returned when indexing outside an array, is a space for characters)
  3. .' evaluate each line (it’s a convenient way to parse numbers)
  4. 50+\ scan addition, i.e. rolling sum, seeded with 50

Part 1

+/~100!i
  1. 100! modulo 100 of the rolling sum
  2. ~ not, which is equivalent to “equals 0”
  3. +/ sum

Part 2

+/50(-/-100!@/1>:\-/1>/\,)':i
  1. 50()': for each value and its prior value (with 50 as the “prior” value for the first element)

    1. , make a pair out of the two values

      N.B. The ': “eachprior” adverb calls functions with the prior value (before in the array) as the second argument

      For an input with a single R50 line, i is 50 100, but the pair created here is 100 50

    2. -/1>/\ bubbler train #1: subtract 1 from both values if the first (current value) is larger – i.e. right turns

      The end result of the solution is the same regardless of whether > or < is used, there just needs to be a choice made

      1. 1(>/)\ apply >/ (greater-than fold), once. this results in a pair like ((100 50);1)
      2. -/ subtract fold. resulting in 100 50-1 or 99 49
    3. @/1>:\ bubbler train #2: sort in descending order (put the greater element first)

      1. 1(>:)\ grade, descending. this results in a pair of the values and permutation vector, like (99 49;0 1)
      2. @/ apply. results in 99 49@0 1 (it’s a no-op when the first value is greater already)
    4. -100! integer division (of both values independently) by 100

    5. -/ subtract

  2. +/ sum

Day 3

+/'(.*|)'',\:[,'0:"i/3";""]{(1_v_*x;x[1],(*x)v:*>y_*x)}/\:/:-|'!'2 12
  1. ,\:[,'0:"i/3";""] reads and creates a pair (line; "") (which hold unprocessed input and output) for each line of the input

  2. -|'!'2 12 creates a sequence -1 0 and -11 -10 ... -1 0 (for part1/part2)

  3. {(1_v_*x;x[1],(*x)v:*>y_*x)}/\:/: iterates through that sequence (y values) for each line of the input (x values are the in;out pairs)

    each iteration of this returns the x value pair for the next iteration:

    1. v:*>y_*x sets v to the index of the max value in the line, ignoring the last y.
    2. (*x)v gets the line at that position. x[1], concatenates the value to the output
    3. 1_v_*x drops the v+1 first characters of the line, leaving only what’s unprocessed
  4. (.*|)'' for both part 1 and 2, for each line, *| last element (get the output), . eval (to parse as a number)

  5. +/' for each part, sum

original ungolfed solution
i:.''0:"i/3"
f:{v:*>(-y)_*x;(1_v_*x;x[1],x[0;v])}
+/{10/*|(x;!0)f/1 0}'i  /p1
+/{10/*|(x;!0)f/|!12}'i /p2

Day 4

i:"@"=0:"i/4"
f:&/1(5>2(+3+/':,[;0]0,)/)\
+//f i         /p1
+//i-{x-f x}/i /p2

Parsing: 0:"i/4" read the file, "@"= equals to @ – resulting in a bit matrix (. -> 0, @ -> 1)

f is one huge bubbler train, let’s go in execution order:

  1. 2(...)/ n-do, apply the function inside twice (one for each 2D axis):
    1. ,[;0]0, prepend and append 0 (this is equal to {0,x,0})
    2. 3+/': sum (+/) of sliding windows of 3 (3 f':)
    3. + transpose (swap axes)
  2. 1(5>...)\ make it a pair (input; cells with <=4 neighbors)
  3. &/ and – turn off 0 cells

Part 1 is just “how many 1 cells have <=4 neighbors", so +// sum-converge is all

Part 2 is “if you remove cells with <=4 neighbors and repeat, how many do you remove", so... you {x-f x} remove with a / converge. i- remove from the input to invert (we want what we’ve removed, not what’s left after removing), and just +// sum-converge again to get the final answer.

Day 5

not golfed

(r;s):"\n"\'"\n\n"\1:"i/5"; s:.'s
r:@[+@/1<:\.''"-"\'r;1;1+]
r:r@\:&~<':*|r
r:r@'&'~1(,[;0]1_)\~>/@[r;1;0 :':]

+/~2!(,/+r)'s /p1
+/-/|r        /p2
  1. (r;s):"\n"\'"\n\n"\1:"i/5" read the input file as a single string (1: as opposed to the usual pre-split 0:), split ourselves

  2. s:.'s eval each line in the second block

  3. +@/1<:\.''"-"\'r split ranges on "-", .'' eval each each, @/1<:\ sort, + transpose.

    transposition transforms the list of ranges from ((start;end);(start;end);(start;end);...) to a pair of two lists (starts; ends)

  4. r:@[...;1;1+] add 1 to range ends

  5. r:r@\:&~<':*|r: remove ranges whose ends are below the end of the range before (i.e. which are fully contained in another range)

    1. *|r last element of r (range ends)
    2. <': lessthan-eachprior
    3. r@\:&~ r where not
  6. r:r@'&'~1(,[;0]1_)\~>/@[r;1;0 :':] merge ranges which overlap:

    1. @[r;1;0 :':] shift range ends to the right, inserting 0 at the start and dropping the last element
    2. ~>/ not greaterthan (less than or equals) comparison of starts and shifted ends
    3. 1(,[;0]1_)\ shift the comparison results list to the left, dropping the first element and inserting a 0 at the end
    4. r@'&'~ r where not

    for examples, ranges 1-4, 3-6, 10-12 (r: (1 3 10;4 6 12)) get processed as:

    1. (1 3 10;0 4 6) shift ends
    2. 0 1 0 compared (1 <= 0, 3 <= 4, 10 <= 6)
    3. (0 1 0;1 0 0) shift: we’re dropping the start of the second range and the end of the first range
    4. (0 2;1 2) indices where not, (1 10;6 12) from r

Part 1

  1. (,/+r) transpose r back and unravel, resulting in one single array like 1 6 10 12
  2. (,/+r)'s binary search every value in s within the list, get the index of the closest value in the ranges
  3. ~2! mod 2, invert – range starts are on even indices (0,2,…) and range ends are on odd indices (1,3,…)
  4. +/ sum

Part 2

  1. |r reverse r (from a pair (starts;ends) to (ends;starts))
  2. -/ subtract
  3. +/ sum

Day 6

goal instead of ngn/k today. very boring day, basically all parsing

o:(+/;*/)@"*"=rx/ +/\*|i:=-read"i/6"
\+/o@'+"v"$-1_i
\+/o@'"v"$1_'rx/^ *$/_"","c"$+"c"$-1_i

Day 7

(+//<':;+/*|:)@\:(2!*i){(x*~y)+(1_b,0)+0 :':b:x*y}\94=i:0:"i/7"

This solution uses the “many-worlds interpretation” (given part 2) to solve both parts: “times the beam is split” are positions where, on one line, there are worlds where there is a beam, and on the next line, there are no longer any worlds where there could be a beam.

  1. i:0:"i/7" read file, save into i
  2. 94= (94 is "^") turn the input into a bit matrix of splitters
  3. 2!*i use mod 2 to get a vector of zeros with a 1 for the inital beam ("S" is 83, "." is 46)
  4. {(x*~y)+(1_b,0)+0 :':b:x*y}\ scan through the input line matrix, adding up:
    1. 0 :':b:x*y the current beam count (x) where there are splitters (y the input line), shifted right (0 :': is just a nerd version of 0,-1_)
    2. 1_b,0 and shifted left
    3. x*~y and where there aren’t splitters, the current beam count
  5. (...)@\: apply eachleft:
    1. +//<': part 1: <': less than eachprior to obtain places where a beam splits, +// sum-converge to count
    2. +/*|: part 2: +/ sum of *|: the last line (the possible number of worlds only matters after all splits are done)

HomeAboutContact