I will put each stack into a box with the first atom in the box being the top container in the stack. Fortunately the input seems to have spaces to make all lines the same length, so I will read in the array, transpose it, and take the second line of each group of 4 lines.

```   ] stacks =: (4,:2) <@}:@(-.&' ')@{:;.3 |: ]  onaoclines
+--+---+-+
|NZ|DCM|P|
+--+---+-+
```

Subarrays-Cut u;.3 is handy when you need it. For each block of 2 lines spaced 4 lines apart, I am taking the second line, discarding spaces, and boxing.

```NB. slow-motion replay
] onaoclines   NB. the lines from the clipboard
[D]
[N] [C]
[Z] [M] [P]
1   2   3
|: ] onaoclines  NB. transpose rows and columns
[[
NZ1
]]

[[[
DCM2
]]]

[
P3
]
(4,:2) <;.3 |: ] onaoclines  NB. put each set of 2 rows, spaced 4 rows apart, into a box
+----+----+----+
| [[ |[[[ |  [ |
| NZ1|DCM2|  P3|
+----+----+----+
NB. subsequent operations run on each 2-line section, before the result is enclosed in a box
(4,:2) <@{:;.3 |: ] onaoclines  NB. take second line of each pair
+----+----+----+
| NZ1|DCM2|  P3|
+----+----+----+
(4,:2) <@(-.&' ')@{:;.3 |: ] onaoclines  NB. discard spaces
+---+----+--+
|NZ1|DCM2|P3|
+---+----+--+
(4,:2) <@}:@(-.&' ')@{:;.3 |: ] onaoclines  NB. discard last (numeric) character
+--+---+-+
|NZ|DCM|P|
+--+---+-+
```

Now all that's left is to write a language to interpret the lines and execute them one by one. If I define verbs for move, from, and to, I can execute the puzzle input as J sentences.

Moving n crates from a stack means taking off the first n atoms in that box, reversing them, and putting them onto the target stack. The spec doesn't discuss what happens if the target and source stacks are the same, so I'm going to hope they never are.

```   from =: to =: ,  NB. combine args into a list, so '1 from 2 to 3' => 1 2 3
move =: {{  NB. y is count,from,to
'count src targ' =. y  NB. arguments
src =. <: src [ targ =. <: targ  NB. convert index origin to 0
srctop =. count {. src {:: stacks  NB. the boxes to be moved, in initial order
stacks =: (<count }. src {:: stacks) src} stacks  NB. Remove from source stack
stacks =: (<(|. srctop) , targ {:: stacks) targ} stacks  NB. Put onto target stack in reverse order
i. 0 0  NB. quiet return
}}
```

[i. 0 0, an array of 0 lines, is what you return when you want no typeout (0 lines looks the same as nothing). An empty list, say '' or i. 0, types 1 empty line.]

Now I read the input, execute it, and see what is left on the top of each stack.

```   ". onaoclines
{.@> stacks
CMZ
```

The spec doesn't say what to do if a stack is empty, so I hope that doesn't happen.

...and it doesn't.

Part 2 simply removes the reversal of the removed boxes. That's a simple rewrite of move and a rerun:

```   move =: {{
'count src targ' =. y  NB. arguments
src =. <: src [ targ =. <: targ  NB. convert index origin to 0
srctop =. count {. src {:: stacks  NB. the boxes to be moved, in initial order
stacks =: (<count }. src {:: stacks) src} stacks  NB. Remove from source stack
stacks =: (<srctop , targ {:: stacks) targ} stacks  NB. Put onto target stack in unchanged order
i. 0 0
}}
```