ShareMyScreen/AdventOfCode/2022/22/MonkeyMap
My first thought is to simulate each step, simply, but suppose Part 2 asks me to run it 1000 times? I'll code it efficiently.
I can come up with an efficient implementation. I will have 4 representations of the board, one for each direction of travel. At each cell in the board I will store the number of spaces I can move before hitting a wall or boundary. If the move crosses the boundary, the value there will indicate how far to move back to the other side to finish the move.
NB. convert a horizontal line to the correct format for moves toward the right. Board starts with NB. _1=wall, 0=out of bounds, 1=empty in bounds fmtline =: {{ ((y=0) * +/ y~:0) + ([ _1:^:(_1=[) +)/\. y }}"1 fmtline 0 0 1 _1 1 1 _1 0 0 5 5 0 _1 1 0 _1 5 5
The left part of fmtline counts the number of active cells on the line (5 here) and adds that to the value in the out-of-bounds cells. The only one of these cells that matters is the one after the last in-bounds cell. The right part does a reverse running sum, resetting to _1 when a wall is encountered.
fmtboards =: (fmtline , fmtline&.:|: , fmtline&.:(|."1) ,: fmtline&.:(|:@:|.)) lines =: 0 ,"(1) 0 ,~"(1) 0 , 0 ,~ <: '# .' i. ] onaoclines boards =. fmtboards lines $boards 4 14 18
I read the lines, surround the area with an out-of-bounds fringe, and translate from characters to the numeric form I have settled on. Then I create the boards to handle moves in all directions. Next I read my move instructions.
]lendir =: ((".@}: , 1 _1 0 {~ 'RLS' i. {:);.2~ e.&'RLS') 'S' ,~ wd 'clippaste' 10 1 5 _1 5 1 10 _1 4 1 5 _1 5 0
This is a hook, where e.&'RL' marks the end of the sections and (".@}: , _1 2 p. 'RL' i. {:) is applied to each section. 'RL' i. {: converts the last character to 0 or 1 and 1 _1 {~ translates them to 1 and _1. Since the last move doesn't contain a turn direction, I add one, indicating no turn.
With the boards set up, following instructions is easy. I start a script.
dirmove =: _2 ]\ 0 1 1 0 0 _1 _1 0 NB. moves in each direction (0=E, 1=S, 2=W, 3=N) runlendir =: {{ NB. y is list of instructions, boards and lines are set up dir =. 0 NB. start looking E pos =. 1 , 01 i.&1@:= 1 { lines NB. start on top line, at first open cell for_ld. y do. NB. for each len/dist pair 'l d' =. ld NB. separate len and dir mv =. dir { dirmove NB. y,x of a move of 1 unit while. l do. NB. till we have finished the move mvdist =. l <. (<dir,pos) { boards NB. get amount we can move from this square pos =. pos + mvdist * mv NB. move the limit if. (<pos) { lines do. break. end. NB. if we are still in the board, stop resetdist =. (<dir,pos) { boards NB. oob - board gives reset distance pos =. pos - resetdist * mv NB. back up to other side if. 0 > (<pos) { lines do. pos =. pos + (resetdist-1) * mv break. end. NB. if line starts with wall, stay at end l =. l - mvdist NB. pick up at start and finish move end. dir =. dir + d NB. move finished, turn end. }}
I win no prizes for guessing about Part 2. I will have to rewrite the code completely, step by step.
This isn't a programming problem, it's a topology problem! I fill a sheet of paper with sketches of cubes, with numbered faces and arrows for face orientation. After a while I have what I need: for each face and direction, the face entered and the direction, 48 numbers in all.
I also need to find the coordinate on the entered face. I don't want 48 more error-prone expressions, so I will calculate the coordinate based on the directions.
Since I must refer to the unfolded input board to find blockages, I will keep coordinates as coordinates in that board.
dirmove =: _2 ]\ 0 1 1 0 0 _1 _1 0 NB. moves in each direction (0=E, 1=S, 2=W, 3=N) M =: 4 fdtab =: _2 ]\ 5 2 1 1 2 1 3 1 NB. for each (face,dir), the new face,dir when a boundary is crossed fdtab =: fdtab ,: _2 ]\ 5 1 4 1 2 2 0 3 fdtab =: fdtab , _2 ]\ 1 0 4 0 3 2 0 0 fdtab =: fdtab , _2 ]\ 2 0 4 3 5 3 0 1 fdtab =: fdtab , _2 ]\ 5 0 3 3 2 3 1 3 fdtab =: fdtab , _2 ]\ 0 2 3 0 4 2 1 2 facebase =: M * _2 ]\ 0 2 1 2 1 1 1 0 2 2 2 3 NB. top-left cell of each face in lines move =: {{ NB. y is face,dir,ypos,xpos result is same format after move 'face dir posy posx' =. y pos =. (dir { dirmove) + oldpos =. posy,posx NB. advance to new position if. -. pos -:&(<.@(%&M)) oldpos do. NB. crossing face boundary? NB. face boundary crossed - update everything 'face dir' =. (<face,olddir =. dir) { fdtab NB. update face & direction in face NB. first, establish the coordinate in the direction of motion in the new face. NB. if that coordinate is increasing, start at 0; decreasing, at M-1 pos =. (_1=dir { dirmove) * (M-1) NB. establish the other coordinate, also within the new face staticdir =. M | +/ oldpos * 0=olddir{dirmove NB. the static coordinate in the old face if. olddir =&(1&(17 b.)) dir do. compstat =. dir~:olddir NB. same direction or 180 degrees diff: complement coord if dir change NB. otherwise the move is +-90 degrees. We start in a cell in normal board NB. orientation and move in the direction olddir. In one of the 90-degree-rotated NB. orientations, the static coordinate in the olddir increases with increasing NB. static coordinate in the newdir; for the orther rotation the directions oppose. NB. compstat is 1 when the directions oppose. else. compstat =. olddir { dir = 1 0 3 2 end. pos =. (face{facebase) + pos + (0 = dir{dirmove) * ((M-1)&-)^:compstat staticdir NB. add the static coord, compld as needed end. face,dir,pos NB. return new stats }} runlendir =: {{ NB. y is list of instructions, boards and lines are set up dir =. 0 NB. start looking E pos =. 0 , 01 i.&1@:= 0 { lines NB. start on top line, at first open cell face =. 0 NB. start in face 0 (good enough) for_ld. y do. NB. for each len/turn pair 'l turn' =. ld NB. separate len and turn for. i. l do. NB. till we have finished the move 'nface ndir nposy nposx' =. move face,dir,pos if. _1 = (<nposy,nposx) { lines do. break. end. NB. stop before wall if. 0 = (<nposy,nposx) { lines do. 'out of bounds' 13!:8 ] 1 end. NB. break if ob face =. nface [ dir =. ndir [ pos =. nposy,nposx NB. update pos,dir end. dir =. 4 | dir + turn NB. move finished, turn end. 0 250 4 #. (>:pos),dir }}
=&(1&(17 b.)) tests for equality in the lowest bit, because (17 b.) is bitwise AND. I could have used =&(2&|).
I should test the move code a bit. I spot-test a few cases and fix the bugs. Eventually I want to test them all.
] pp =. (i. 6) ,"0/ i. 4 0 0 0 1 0 2 0 3 1 0 ... ]ppp =. ([ , 2 2 + facebase {~ {.@[)"1 pp 0 0 2 10 0 1 2 10 0 2 2 10 0 3 2 10 1 0 6 10 ... (] -: move^:(4*M))"1 ppp 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
All good. Now to run on the example data. After I fix the result to match the problem's 1-origin indexing, I am pleased to see
runlendir mlendir 5031