Advent of Code 2021

December 2021 · 10 minute read

So I have decided to follow Advent-of-Code live this year [I am 10 days behind so it’s hardly live, but I should catch up soon :) ]

Advent of Code is a programming themed fun-little-event that takes place every year on http://adventofcode.com

You can find my solutions (in (Dyalog-)APL) below.

Day 1

q1 ←{ +/ (1↓⍵) > (¯1↓⍵) }
q1pt1 ←{
	q1 ⍎⍤1⊢↑ ⊃⎕NGET './q1.txt'1
}
q1pt2 ← {
	x ← ⍎⍤1⊢↑ ⊃⎕NGET './q1.txt'1
	q1 (2↓x) + (1↓¯1↓x) + (¯2↓x)
}
q1pt1_v2 ←{
	+/2</ ⍎⍤1⊢↑ ⊃⎕NGET './q1.txt'1
}
q1pt2_v2 ← {
	x ← ⍎⍤1⊢↑ ⊃⎕NGET './q1.txt'1
	+/2</3+/x
}

I learned about N op / a while after solving this for the 1st time. It sure makes it simpler

Day 2

C ← 'forward' 'down' 'up'
M ← (1 0) (0 1) (0 ¯1)
d ← ⊃⎕NGET './q2.txt'1

q2←{
	×/↑↑+/{M[C⍳⍵[1]]×(⍎⍤1⊢↑⍵[2])}¨' '(≠⊆⊢)¨d
}

q2pt2←{
	f←{
		h ← ⍵[1]
		a ← ⍵[2]
		d ← ⍵[3]
		⍵ + ((⍺[1]) (⍺[2]) (a×⍺[1]))
	}
	⍝ appending (0 0 0) as the initial value of my accumulator for my reduce
	x ← ⊖(↓↓0 0 0), {M[C⍳⍵[1]]×(⍎⍤1⊢↑⍵[2])}¨ ' '(≠⊆⊢)¨d_
	×/ (1 0 1)/ ↑f/ ↑¨x
}

The reduce (/) works right to left so I had to ⊖ it then swap ⍵ and ⍺

Day 3

C ← 'forward' 'down' 'up'
M ← (1 0) (0 1) (0 ¯1)
d ← ⊃⎕NGET './q2.txt'1

q2←{
	×/↑↑+/{M[C⍳⍵[1]]×(⍎⍤1⊢↑⍵[2])}¨' '(≠⊆⊢)¨d
}

q2pt2←{
	f←{
		h ← ⍵[1]
		a ← ⍵[2]
		d ← ⍵[3]
		⍵ + ((⍺[1]) (⍺[2]) (a×⍺[1]))
	}
	x ← ⊖(↓↓0 0 0), {M[C⍳⍵[1]]×(⍎⍤1⊢↑⍵[2])}¨ ' '(≠⊆⊢)¨d_
	×/ (1 0 1)/ ↑{}/ ↑¨x
}

Day 4

n ←{ ⍎⍤1⊢↑⍵ }

nonempty ←{ ↑,/0≠⍴¨⍵ }
playcards ←{ n↑↑(nonempty⊆⊢)' '∘(≠⊆⊢)¨1↓⍵ }

d ← ⊃⎕nget'q4.txt'1
P ← n','(≠⊆⊢)⊃d ⍝ Pouch
C ← playcards d ⍝ Cards
WT ← {⍵⍳1}¨ ↓⍉↑ ↑∨/¨{∨/5∊¨⍵}¨¨{(+/[1]⍵) (+/[2]⍵)}¨∨\C∘=¨P ⍝ Winning Turns
score ←{ P(⊢⌷⊣)WT(⊢⌷⊣)⍵ × +/ (↑,/,/⍵⌷C) ~ (1+⍵⌷WT)↑P }

q4pt1 ← score WT⍳⌊/WT
q4pt2 ← score WT⍳⌈/WT

I liked how neat ∨\C∘=¨P was to generate states of each bingo card across multiple turns. I am curious to see how I can use \ in other problems.

I got a better grasp of tacit programming this time around. I am not sure if Y(⊢⌷⊣)X is the best way to do Y[X] but at least I am able to create functions like it .

I do a lot of and , which also requires me to ¨ . I should figure out how to better handle boxes of values.

Day 5

valid_pt1 ← {(∆x ∆y)←|-/¨⍵ ⋄         (∆x=0)∨(∆y=0)}
valid_pt2 ← {(∆x ∆y)←|-/¨⍵ ⋄ (∆x=∆y)∨(∆x=0)∨(∆y=0)}
applyvent ← {{(⍵⌷M)+←1}¨⍵} ⍝ impure function. modifies M
range ← {d←⍵-⍺ ⋄ s←d÷|d ⋄ ⍺+s×⍳+1+|d} ⍝ 5 range 3 gives 5 4 3

d ← ⊃⎕nget'q5.txt'1
V ← {n¨','∘(≠⊆⊢)¨(1 0 1)/' '(≠⊆⊢)⍵}¨d
D ← 1+⌈/∊V
M ← D D⍴0 ⋄ valid ← valid_pt1 ⋄ applyvent¨0⌷{↑(↑,.(⊂,))/↑range/¨⍵}¨(↑valid¨⊆⊢){↓⍉↑⍵}¨V
q5pt1 ← +/∊M≥2
M ← D D⍴0 ⋄ valid ← valid_pt2 ⋄ applyvent¨0⌷{↑(↑,.(⊂,))/↑range/¨⍵}¨(↑valid¨⊆⊢){↓⍉↑⍵}¨V
q5pt2 ← +/∊M≥2

It was fun to solve this problem as I iterated over the map-view. I enjoy this style of interactive programming, doing as I’m seeing. It helps get rid of some hesitancy to approach even a seemingly difficult problem.

I ended relying on a mutable table to solve this and I wonder how I could have done it purely.

Day 6

f ← {s←⍵⋄((1↓s),0)+s[0]×(0 0 0 0 0 0 1 0 1)}
genS ← {x←⍵ ⋄ {+/x=⍵}¨⍳8+1}

d ← ⊃⎕nget'q6.txt'1
N ← n','(≠⊆⊢)d

q6pt1 ← 20⍕ +/↑(⊢f)/(⍳80),↓genS N
q6pt2 ← 20⍕ +/↑(⊢f)/(⍳256),↓genS N

Thinking of this problem as arrays of values helped me coming up with a solution.

The first solution I came up relied on keeping a record of each fish (as an integer) and resulted in memory overflow for part-2.

Day 7

d ← ⊃⎕nget'q7.txt'1
N ← n','(≠⊆⊢)⊃d
K ← 1+⌈/∊N
q7pt1 ← ⌊/+/ |(⍉↑K⍴¨N)-(0⌷¨⍳K(≢N))
g ← {0.5×⍵×⍵+1}
q7pt2 ← ⌊/+/g|(⍉↑K⍴¨N)-(0⌷¨⍳K(≢N))

Another problem that was represented by a table rather neatly. If I write about APL this could be the problem I solve.

Day 8

A ← 'abcdefg'
MK ← {(∊∘⍵)¨A}
MK_v2 ← A∊
Mb ← ↑MK ¨ ('abcfeg' 'cf' 'acdeg' 'acdfg' 'bcdf' 'abdfg' 'abdefg' 'acf' 'abcdefg' 'abcdfg')

l ← {{((~'|'∊⊢)¨⍵)⊆⍵}' '∘(≠⊆⊢)⍵}
loner ← {x←⍵ ⋄ {⍵/x}1={+/x=⍵}¨x}
len ← {∊(⍺=≢¨⍵)⊆⍵} ⍝ selects items with given length
decode ←{
     b←⍺∊⍵
     1(⊢⍳⊣)b∘(⊢≡⊣)¨↓Mb
 }

solve ← {
     x←⍵
     S_1←2 len x
     S_4←4 len x
     S_7←3 len x
     S_8←7 len x
     S_a←S_7~S_1
     S_g←⊃1 len{⍵~S_a,S_4}¨x
     S_e←⊃∊{⍵~S_g,S_a,S_4}¨x
     S_b←loner 1 len{⍵~S_7,S_e,S_g}¨x
     S_d←S_8~S_7,S_b,S_e,S_g
     S_c←loner 1 len{⍵~S_a,S_g,S_e,S_b,S_d}¨x
     S_f←⊃∊{⍵~S_a,S_c,S_g,S_e,S_b,S_d}¨x
     ∊(S_a S_b S_c S_d S_e S_f S_g)
 }

d ← ⊃⎕nget'q8.txt'1
q8pt2 ← +/{R←solve↑0⌷l ⍵ ⋄ ⊖{⍺+10×⍵}/⊖R∘decode¨↑1⌷l ⍵}¨d

OK, this one actually took me a while. Once I was able to decode S_g the rest came smoothly.

I want to be able to come up with tacit versions of len and loner, but not yet :)

Day 9

x←↑n¨¨⊃⎕NGET'q9.txt' 1
d←⍴x

q9_pt1 ← +/{o←4⌷⍵ ⋄ (o+1)×0=+/o>(0 1 0 1 0 1 0 1 0)/⍵}¨∊¨sx

connlabel←{{{⍵[1;0]=0:0 ⋄ ⌊/0~⍨,⍵}¨(⊂⊢)⌺3 1⊢{⍵[0;1]=0:0 ⋄ ⌊/0~⍨,⍵}¨(⊂⊢)⌺1 3⊢⍵}(⍣≡)1+∘⍸@⊢⍵}
m2←connlabel x<9
pt2←×/3↑(⊂∘⍒⌷⊢){+/∊m2=⍵}¨0~⍨∪∊m2

I insisted on coming up with an array-based solution to connected-component labeling. It took me a while but I discovered ⍣≡ in the process. I used to write recursive functions that I ran repeatedly until the system stabilized. Well, this does it all by itself.

Also would have been very handy in all the 2d filters I have been doing over matrixes.

Day 10

B        ← '>'   '}'  ']' ')' ' ' '(' '[' '{' '<'
Cost_b_1 ← 25137 1197 57  3   0   1   2   3   4
Cost_b_2 ← 25137 1197 57  3   0   0   0   0   0
Cost_b ← Cost_b_1

c←{Cost_b[B⍳⍵] }
cost←{
    x ← c⊃(⍵∊'>}])')/⍵
    x>0:0
    {⍺+⍵×5}/⊖0,⊖c¨⍵
}
f_i←{{(⍵,0)∨(0,⍵)}2=(1↓⍺[1]=⍵)+(¯1↓⍺[0]=⍵)}
f_rm←{∊(⊢⊆∘⍵)↑~∨/(⊢f_i∘⍵)¨'()' '{}' '[]' '<>'}
f_rms←{
    w_←f_rm ⍵
    (⍴w_)=(⍴⍵):w_
    f_rms w_
}
q10← {{⍵[0.5×(⍴⍵)-1]}{(⊂⍋⍵)⌷⍵}∊0(≠⊆⊢)cost¨f_rms¨d}
⍝ pt1
Cost_b ← Cost_b_1
q10 0
⍝ pt2
Cost_b ← Cost_b_2
q10 0

Day 11

step←{
     x0←↑⍵[1]
     d←⍴x0
     flash←{
         ⍝⎕←DISPLAY ⍺ ⍵
         x←⍵
         F←⍺
         F_new←F-⍨x>9
         0=+/∊F_new:F x
         0=+/{(⍵⌷x)+←1}¨N_diag¨(,F_new)/(,⍳d):F x
         (F∨F_new)∇(x)
     }
     l r←↑¨(d⍴0)flash 1+x0
     (+/⍵[0],∊l)({9<⍵:0 ⋄ ⍵}¨r)
 }
x←↑n¨¨⊃⎕NGET'q11.txt'1
pt1←0⌷⊃step/⊖(⊂0 x),⍳100
pt2←1+{⍵⍳1}99<|2-/⊃¨{⊃step/⊖(⊂0 x),⍳⍵}¨⍳1000

The scan operator goes through the same elements every time, without using the accumulated data. There should be a more performant version of it that doesn’t have O(N!) complexity.

Day 12

A_lower←819⌶⎕A

d←⊃⎕NGET'q12.txt' 1
L←'-'∘(≠⊆⊢)¨d
C←∪↑,/L
C_once←{⍵/C}{⍵∊A_lower}¨⊃¨C
D←⊃↑⍴C
M←D D⍴0⍴D*2
{(⍵⌷M)←1}¨{⍵⍳1}¨¨{(,⍵)∘≡¨C}¨¨,¨¨L ⍝ marks M with 1s

iCity←{1(⊢⍳⊣)(,⍵)∘≡¨C}

f←{
  c←⍺
  Open←⍵
  next←Open∩{⍵/C}(iCity↑c)⌷(M∨⍉M)
  c≡'end':1
  0=(⍴next):0
  Open_←Open~C_once∩(↓,c)
  ∊{⍵ f Open_}¨next
}

pt1←+/ 'start'f(C~↓,'start')

f2←{
     fin←{⍵}
     c←↑⍺[0]
     path←⍺[1]
     Closed←⍵
     C_open←{⍵~↓'start'}{⍵/C}(Closed<(2-+/Closed>1))
     next←C_open ∩ {⍵/C}(iCity↑c)⌷(M∨⍉M)
     c≡'end':(fin 1)
     0=(⍴next):0
     +/∊{n←↓,⍵ ⋄ (⍵(∊path,'-',⍵)) f2 (Closed+(C∊n)×n∊C_once)}¨next
 }

pt2←('start' 'start') f2 ((⍴C)⍴0)

the old travelling salesman problem with additional constraints.

Day 13

p←{'.#'[⍵]}¨                                                      ⍝ plot for binary matrix

{a←⍺[0] ⋄ b←⍺[1]>⍳a⌷⍴⍵ ⋄ (b⌿[a]⍵)((0,¯1↓~b)⌿[a]⍵)}                ⍝ cuts ⍵ at ⍺ to 2
                                                                  ⍝   where ⍺[0] is axis
                                                                  ⍝   where ⍺[1] is position at axis
f←{
     pt1 pt2←⍺ cut ⍵
     pt2_←⍺{⍺[0]=0:⊖⍵ ⋄ ⌽⍵}pt2                                    ⍝ flip horizontally or vertically
     sBigger←∊⌈/⍴¨pt1 pt2_
     ⊃∨/{⊖⌽sBigger↑⌽⊖⍵}¨⊢pt1 pt2_                                 ⍝ pad (in top and left) to match the bigger of the two
 }
dat←⊃⎕NGET'proj/apl/q13.txt'1
P←⊖¨n¨,','(≠⊆⊢)¨↑','∘((∊¨)⊆⊢)dat                                  ⍝ points as (i j) tuples
d←∊1+⌈/P                                                          ⍝ dimensions
F←(('yx'∘⍳∊)@0)¨(n@1)¨'='(≠⊆⊢)¨↑2∘⌷¨' '(≠⊆⊢)¨1↓,↑','∘((~∊¨)⊆⊢)dat ⍝ folds (as t
M←(1@P)d⍴0                                                        ⍝ the initial matrix

pt1←q13pt1←+/∊(⊃F) f M
pt2←p↑f/⌽(⊂M),F

I added documentation the APL way, as a second column. Looks neat doesn’t it :)

Day 14

fn←{
 turns←⍵
 dat←⊃⎕NGET'proj/apl/q14.txt' 1
 p0←2,/⊃dat                                                  ⍝ initial pairs
 Ma←{a b c←⍵ ⋄ (a b)(a c)(c b)}¨(∊(1 0 1)∘/)¨' '(≠⊆⊢)¨2↓dat  ⍝ all movez
 Mk←⊃¨Ma                                                     ⍝ keys of moves
 Punq←∪↑,/Ma,⊂p0                                             ⍝ all pairs, uniq
 M←↑(+/(¯1 1 1)∘×)¨(Punq∊⊂)¨¨Ma                              ⍝ ∆state of moves
 Ps←{↑(Mk∊⊂⍵)/M}¨Punq                                        ⍝ ∆state of pairs
 s0←⊃+/(Punq∊⊂)¨p0                                           ⍝ initial state
 sf←↑{⍵+∊+/Ps×⍵}/⌽(⊂s0),⍳turns                               ⍝ final state
 ⎕←⊢A←∪∊Punq                                                 ⍝ all letters available
 ⎕←⊢scr←⌈2÷⍨(+/sf∘×)¨(+/¨Punq∘=)¨A                           ⍝ scores
 (⌈/-⌊/)scr
}
pt1←fn 10
pt2←fn 40

I had to optimize the solution for pt2 again, keeping counts instead of data. The solution didn’t work at first, though. I thought there could be an error due to the operation, and manually subtracted 1 from the result.

Day 15

solv←{
  M←⍵
  d←⍴M
  O←(1@(⊂0 0))d⍴0
  V←d⍴0
  C←d⍴0
  g←⊂d-1
  H←+/¨|g-⍳d
  f←{
      ⍝ V  visited
      ⍝ O  open/available
      ⍝ H  heuristics (cost to 'g'oal)
      ⍝ C  costs (cost to start)

      o←⍸(O∧0=V)
      0=≢o:1                ⍝ no more open slots. finish
      x←o⌷⍨{⍵⍳⌊/⍵}C[o]+H[o] ⍝ take the least cost pos
      x≡g:1
      V[x]←1
      n←{(~0∘∊¨⍵≥0)/⍵}{(~0∘∊¨⍵<⊂d)/⍵}(0 ¯1)(¯1 0)(0 1)(1 0)+x      ⍝ neighbours
      c←C[x]
      C[n]←{⍵⌷O:(⍵⌷C)⌊c+⍵⌷M ⋄ c+⍵⌷M}¨n ⍝ update costs to neighbours
      O[n]←1                           ⍝ make available

      0
  }
  {f 0:C[g] ⋄ ∇ 0} 0
}
pt1←{
    M←↑n¨¨⊃⎕NGET'tmp.txt' 1
    solv M
}
pt2←{
    M←↑n¨¨⊃⎕NGET'tmp.txt' 1
    S←⊖⌽10 9↑⌽⊖↑{⍵⌽1+⍳9}¨∪,+/¨⍳5 5
    solv⍪/(⊃,/)¨↓{S∘(⌷⍨)¨⍵∘(,⍨)¨M}¨+/¨⍳5 5
}

Day 16, 17

I didn’t do as good a bookkeeping for questions beyond this point. The interface I am using (Ride) was crashing at an increased frequency, perhaps due to the amount of data. I actually gave up on solving some of the questions after a few of those crashes.

Day 18

mag←{
    ⍝⎕←↑⍵
    x←⍵
    n d←x
    2>≢n:⊃n
    i j←0 2+(⊢⍳⌈/)d
    l←1⌷⍴↑x
    a←↑i↑¨x
    c←↑j↓¨x
    m←+/3 2×⌷∘n¨(i(i+1))
    n←1-⍨i⌷d
    ∇↓(a,(2 1⍴m n),c)
}
itr←{
    s x←(9 4)<⌈/¨⍵
    x:∇ xpl ⍵
    s:∇ spl ⍵
    ⍝⎕←↑⍵
    ⍵
}
add←{0 1+↓⊃,/↑¨⍺ ⍵}
par←{
    x←⍵
    b←↑x∘=¨'[],'
    n←⍎¨(~∨⌿b)⊆x
    d←⊃¨(~∨⌿b)⊆(-⌿+\2↑b)
    n d
}

mag⊃⊢y←(itr add⍨)/⌽par¨⊃⎕NGET'proj/apl/q18.txt' 1

Day 19

'pmat' 'det'⎕CY'dfns'
RA←{⍵/⍨1=det¨⍵},(↓pmat 3)∘.{⍵@(⍺,¨⍳3)⊢3 3⍴0},1-2×⍳2 2 2
dat←getdata ⍬
D←≢dat
snsA←(∘.⊣)⍨dat

⍝ find matching sensors
matches←(∘.{cmn←⊃∩/∊¨⍺ ⍵ ⋄ 12=+⌿⍺∊cmn})⍨{(∘.far)⍨↓⍵}¨dat
snsM←↑¨(⍴snsA)⍴↑(,matches),.{⊂⍺/↓⍵}(,snsA)

fnd←(,⍨⍴dat)⍴⊃(,snsM),.(⊂relpos)(,⍉snsM)
⍝ found matches. fnd[i;j] ≡ r x y z,
⍝   `r` is ∆rot of "j" wrt "i"
⍝   `x y z` is ∆pos of "j" wrt "i"
fndr←⊃¨fnd    ⍝ found rotations
fndp←(1∘↓)¨fnd ⍝ found positions
I←⍸25>fndr    ⍝ indexes of matches in "fnd"

⍝{(⍵⌷xp)←⊂0 0 0 ⋄ (⍵⌷xr)←0}¨{(=/¨⍵)/⍵},⍳D D

open←(≢∪∊I)⍴0
imap←(⊃¨I){⊂(1∘⌷¨⍵)}⌸I
myi←(≢imap)⍴10+≢imap
0{myi[⍺]<⍵:⍵ ⋄ myi[⍺]←⍵ ⋄ ⍵>30:⍵ ⋄ ∇/(⊃imap[⍺]),⍵+1}0
Icand←⊃,/(((⊃¨I){⊂⍵}⌸I)@myi)27⍴9999
Idup←{i j←⍵ ⋄ open[i]←1 ⋄ open[j]}¨Icand
I_←(~Idup)/Icand
0≡≢∊~/∊¨I I_ ⍝ ensure nothing missing

P R←fill I_ ⍝ absolute positions and rotations
part1←≢∪⊃⍪/{↑(⊃P[⍵])∘+¨R[⍵]∘roti¨↓⊃dat[⍵]}¨⍳D
part2←{⌈/∊P(∘.{+/⍺-⍵})P}

Day 20

m x0←readdata ⍬

pad←{k←⍺ ⋄ r c←(2 0)+⍴⍵ ⋄ (r⍴k){⍺,⍵,⍺}(c⍴k){⍺⍪⍵⍪⍺}⍵}

⊢part1←+/∊({s←⊃⍵ ⋄ a←⊃1⌷⍵ ⋄ (m⌷⍨2⊥9⍴s),⊂({m⌷⍨2⊥∊{(s 0 1)[⍵]}¨⍵}⌺3 3)1+s pad a}⍣2)(0,(⊂x0))
⊢part2←+/∊({s←⊃⍵ ⋄ a←⊃1⌷⍵ ⋄ (m⌷⍨2⊥9⍴s),⊂({m⌷⍨2⊥∊{(s 0 1)[⍵]}¨⍵}⌺3 3)1+s pad a}⍣50)(0,(⊂x0))

Day 21

part1←{
   ⍝ both 'pad' and '⌺' uses 0 as fill-character
   ⍝  so I used 1 2 in place of 0 1 in the img matrix
    n←0
    scr←0 0
    pos←1-⍨4 1
    {i nr←⍵ ⋄ ⌈/scr≥1000:⍵ ⋄ nr_ ∆x←r nr ⋄ pos[i]←10|∆x+i⌷pos ⋄ scr[i]+←1+pos[i] ⋄ ∇(2|i+1)nr_}(0 0)
    (⌊/scr)×n×3
}
part2←{
    initpos←4 8
    N←21+10+1
    I←⍳N N
    IW←⍸¨⊂[1 2]21<(0 1)∘.⌷I ⍝ win indexes
    p←↓(initpos-1)(∘.=)⍳10 ⋄ s←(1@(⊂0 0))N N⍴0 ⋄ wins←0 0 ⋄ f 0
    (⊂p)(⊂s)
}

Day 22

todo

Day 23

todo

Day 24

K←⎕.v>
s0←'w0' 'x0' 'y0' 'z0'
doop←{ ⍝ do op at left to state at right
    k←⊃⍺
    v←1↓⍺
    k=INP:v inp ⍵
    k=ADD:v add ⍵
    k=MUL:v mul ⍵
    k=DIV:v div ⍵
    k=EQL:v eql ⍵
    k=MOD:v mod ⍵
    ⎕←'unknown op: ',k
    ⍵ ⍝noop
}
pt1←{doop/(⌽⊃,/8↑digiops),⊂s0}
pt2←{inpi←0 ⋄ ↑{⊃doop/(⌽⊃,/⍵⌷digiops),⊂'w0' 'x0' 'y0' 'z0'}¨⍳14}

⍝ the above didn't actually help solve the problem, but they explained the state of it.

P←(12 1 1)  (13 1 9)  (12 1 11)  (¯13 26 6)  (11 1 6)  (15 1 13)  (¯14 26 13)  (12 1 5)  (¯8 26 7)  (14 1 2)  (¯9 26 10)  (¯11 26 14)  (¯6 26 7)  (¯5 26 1)
itr←{
    i z←⍵
    a b c←i⊃P
    d←i⊃⍺
    k←d≠a+26|z
    0≠b∧k:1
    k=0:(i+1)z÷b
    (i+1)c+d+26×z÷b
}
soln←{{0=⊃(⊖⍵)(itr⍣14)0 0:⍵ ⋄ ∇ dc ⍵}14⍴9}

neither did this solve the problem, but it became more eligible that we are manipulating a number as if it’s a stack of integers that are <26.

I ended up solving it afterwards with pen and paper.

Day 25

K←⎕.v>
plt←{K∘(⌷⍨)¨⍵}
upd←{(mvS FF)(mvE FF)⍵}
mvS←{
    n _ s _ c←⍵[I]
    (c s)≡S O:O
    (n c)≡S O:S
    c
}
mvE←{
    _ e _ w c←⍵[I]
    (c e)≡E O:O
    (w c)≡E O:E
    c
}
pt1←{
    K←'⋄.v>'
    F O S E←⍳4
    I←(0 1)(1 2)(2 1)(1 0)(1 1) ⍝ neighbour indexes
    plt←{K∘(⌷⍨)¨⍵}
    X←dat←↑K∘⍳¨⊃⎕NGET'tmp.txt' 1
    ⍝⎕←plt¨{(upd⍣⍵)X}¨⍳⍵
    1↓({x n←⍵ ⋄ x_←(upd⊃⍵) ⋄ x_(n+1)}⍣{(⊃⍺)≡(⊃⍵)})(X 0)
}