ShareMyScreen/AdventOfCode/2022/05/SupplyStacksJP

From J Wiki
Jump to navigation Jump to search

The Problem << >>

Entire solution

i5=: (<;._2~ LF2&E.) LF,~in 5 NB. split stacks and instructions
NB. parse stacks
p0 =: [: <@deb"1@:(|:@|. #~ ' '~:{:) [: (];._2) 0 ,&LF@{:: ]
NB. parse instructions
p1 =: 0 _1 _1+"1 [:(".@(-.&'fromtve');._2) 1 ,&LF@{::  ]
NB. one operation (x stacks, y operation)
op=: {{
  'nn f t'=. _1 1 1*y
 ft=. f,t
 NB. rem nn    ; add from other row
 (((nn&}.&.>@[)`((,nn|.@{.])&.>)"0 |.)~ ft{x) ft } x
}}
NB. repeat op's: x: ops, y: stacks
rep=: ]`(}.@[ $: (op{.)~)@.(*@#@[)
NB. last items on stacks after applying instructions
part1 =: [: {:&> p1 rep p0
NB. Same for part 2, without |. in last line of op
opb=: {{
  'nn f t'=. _1 1 1*y
 ft=. f,t
 NB. rem nn    ; add from other row
 (((nn&}.&.>@[)`((,nn{.[)&.>)"0 |.)~ ft{x) ft } x
}}
repb=: ]`(}.@[ $: (opb{.)~)@.(*@#@[)
part2=: [: {:&> p1 repb p0
(part1;part2)i5

Data encoding

The problem's inputs are a graphical representation of stacked crates and a list of operations to perform on them: a number of crates to move from a source stack to a destination stack.

   15{.];._2 freads '05.txt'
    [H]         [D]     [P]
[W] [B]         [C] [Z] [D]
[T] [J]     [T] [J] [D] [J]
[H] [Z]     [H] [H] [W] [S]     [M]
[P] [F] [R] [P] [Z] [F] [W]     [F]
[J] [V] [T] [N] [F] [G] [Z] [S] [S]
[C] [R] [P] [S] [V] [M] [V] [D] [Z]
[F] [G] [H] [Z] [N] [P] [M] [N] [D]
 1   2   3   4   5   6   7   8   9

move 2 from 8 to 2
move 3 from 9 to 2
move 1 from 3 to 8
move 5 from 1 to 7
move 2 from 9 to 2

I split this input into two boxes; the crate stacks and the operations:

i5=: (<;._2~ LF2&E.) LF,~in 5

This uses the fact that an empty line, i.e. two successive linefeed (LF) characters separate the two parts of the input. The text file ends with an LF, so adding an additional LF allows using the position LF2 (defined in the stdlib as LF,LF) to split the parts using dyadic <;._2, The frets being provided by E. finding where LF2's start in the input.

This time, a larger part of the parsing of the two parts of i5 was done in the solutions for part 1 and 2.

Parsing stacks

For finding the letter's columns, I abused the column numbers as in p0, which parses the stacks and returns the stacks as boxed lists:

p0 =: [: <@deb"1@:(|:@|. #~ ' '~:{:) [: (];._2) 0 ,&LF@{:: ]

Explained from right to left:

  • 0 ,&LF@{:: ]
    takes the first box (to be applied to i5) containing the stacks and appends an LF;
  • ];._2
    turns an LF separated string into a matrix;
  • |:@|. #~ ' '~:{:
    is a fork copying (#~) rows from the clockwise rotated matrix (|:@|.) for which the last row of the original matrix was not space (' '~:{:), i.e. it was a number, which appear in the same columns as the crate letters;
  • <@deb"1@:
    deb removes extra blanks in each row (i.e. stack) after which the result is boxed (to avoid blanks to be added again by frame filling).

The result is a list of boxed stacks, starting with the stack number (assuming operations won't take from empty stacks).

Parsing instructions

Parsing of instructions is relatively straightforward: we're only interested in the numbers, so after selecting the relevant part of i5, and adding an LF, for each line, remove all occurring letters ('fromtve') and execute (".). Add 0 _1 _1 to convert 1-based column numbers in the input into 0-based numbers:

p1 =: 0 _1 _1+"1 [:(".@(-.&'fromtve');._2) 1 ,&LF@{::  ]

The result is a matrix of instructions per row, with columns giving numbers of crates to move, source and destination stack indices.

Part 1

Part 1 asks for moving one crate at a time, and returning the letters of the crates of the top boxes.

I implemented the operations as the explicit op verb, taking as right input an instruction, and as left input the state (list of boxed stacks):

op=: {{
 'nn f t'=. _1 1 1*y  NB. _1* for taking from the end
 ft=. f,t              NB. update from and to stacks at once
 NB. rem nn    ; add from other row
 (((nn&}.&.>@[)`((,nn|.@{.])&.>)"0 |.)~ ft{x) ft } x
}}

The interesting part is the amend on the last line: it updates the source (from) and destination (to) stacks in x at the same time: ft{x selects both stacks, and hook to the left ((v |.)~) causes the left verb to be presented "from,to" to the left and "to,from" to the right. The two verbs in the gerund on the left do the heavy lifting, with u`v"0 applying verb u between the first stack (from) and the second stack (to); and verb v between the second (to) and first (to) stack.

The first verb nn&}.&.>@[ drops |nn items from the back of the from stack.

The second verb (, nn |.@{. ])&.> is a hook that takes the last |nn items from the from stack (right), reverses them (as boxes are taken one by one), and adds to the to stack (left).

The amend replaces the from-to stacks by the processed versions in the stacks (x).

The last missing piece is a verb for applying all instructions. Among many possibilities, I chose a recursive approach using Self-Reference and Agenda that, while operations are available, executes them, and calls op on the new state and the instruction list without the first element:

rep =: ]    `(}.@[ $: (op{.)~)@.(*@#@[)
NB. condition|                   ^^^^^ instructions left?
NB.       1: |        ^^^^^^ execute first instruction on state
NB.          |     ^^ self-reference to rep
NB.          |^^^^ shortened instruction list
NB.   ^^^ 0: return state

Piecing together parsing and extracting the last elements of the stacks completes the solution:

part1 =: [: {:&> p1 rep p0

Part 2

Part two ask for a modification where, rather than moving boxes one by one, boxes are moved together. This amounts to simply removing the reverse in the second gerund (making the update to the destination stack) in op:

opb=: {{
 'nn f t'=. _1 1 1*y
 ft=. f,t
 NB. rem nn    ; add from other row
 (((nn&}.&.>@[)`((,nn{.[)&.>)"0 |.)~ ft{x) ft } x
}}

Although this adaption could have been more elegant, i.e. by converting op and rep to adverbs taking ] or|. as argument for parts 1 and 2, respectively, copying and pasting was faster:

repb=: ]`(}.@[ $: (opb{.)~)@.(*@#@[)
part2=: [: {:&> p1 repb p0