[I sent this yesterday (Monday) evening to Chat,  but it hasn't appeared in my inbox or in the Chat Archive, so I tried again today - same non-result.  My trivial test msg appeared in Programming but not in Chat just now. So: Apologies for sending rather stale stuff to the Programming Forum and also if we end up with two or
three copies!]

Following Raul's suggestion in the Programming Forum,  copied just below, I've been trying to tidy up my code to make it somewhat more presentable,  but there's quite a lot of it;  what with utility functions, and comments on performance,  the script "aoc2123new.ijs" is nearly 600 lines long.  So I've put it up on
my Google Drive for a while,  with this link:
https://drive.google.com/file/d/1-zhVqU6IREiMuSHPGVQkRbiFQM9rVvxZ/view?usp=sharing
I _think_ it's self-contained,  but might easily have overlooked something.

The main function is "part2" and most of the work is done in "doamp", acronym for
DO All Moves with optional Predecessors.

When trying this link, when logged out, my browser says - "preview not available" but makes it available
for download.

Rather surprisingly,  my fairly naive approach is faster than Raul's for both parts of the day 23
problem;  less surprisingly,  it appears to take considerably more space.

ALthough the main function is called part2,  it can solve both parts!   Here are some extracts from
runtime diagnostics.

Sent in fixed width font!
NB. My part2 method for part1 data now runs in ~ 7.5 sec and max number of working states = 22553
NB.    timer'part2 13; data'
NB. ┌─┬─┐
NB. │1│1│
NB. └─┴─┘
NB. ┌─┬──┐
NB. │2│28│       NB. step number & number of states
NB. └─┴──┘
...........
NB. ┌─┬─────┐
NB. │7│22553│
NB. └─┴─────┘
...........
NB. ┌──┬────┐
NB. │12│8818│
NB. └──┴────┘
NB. ┌──────────────────────┬─────┐
NB. │ found target at cost │13520│
NB. └──────────────────────┴─────┘
NB. ┌──┬────┐
NB. │13│1992│
NB. └──┴────┘
NB. ┌─────────────┬─────┐
NB. │best found = │13520│
NB. └─────────────┴─────┘
NB. ┌─────────┬┐
NB. │7.6790009││
NB. └─────────┴┘

NB. Raul's method on part1 - run at similar time,  so runtime environment should be similar
NB.    timer'a23 q'[q=:>LF cut data
NB. ┌─────────┬┐
NB. │26.148003││
NB. └─────────┴┘
NB.    {.costs
NB. 13520

NB. space used much better for Raul .......
NB.     1 (7!:2@])  'part2 13; data'
.......
NB. ┌─────────────┬─────┐
NB. │best found = │13522│
NB. └─────────────┴─────┘
NB. 52867104

NB.       1 (7!:2@]) 'a23 mikesdata'      NB. space for Raul's fn in part 1
NB. 2528384

NB. My part2 method for part2 data now runs in ~ 12 sec and max number of working states = 34338
NB.    timer'part2b 28; data2'
NB. ┌─┬─┐
NB. │1│1│
NB. └─┴─┘
...
NB. ┌─┬─────┐
NB. │7│34338│
NB. └─┴─────┘
...
NB. ┌──┬────┐
NB. │27│1021│
NB. └──┴────┘
NB. ┌──────────────────────┬─────┐
NB. │ found target at cost │48708│
NB. └──────────────────────┴─────┘
NB. ┌──┬───┐
NB. │28│708│
NB. └──┴───┘
NB. ┌─────────────┬─────┐
NB. │best found = │48708│
NB. └─────────────┴─────┘
NB. ┌──────┬┐
NB. │12.186││
NB. └──────┴┘

NB. Raul's method, b23, for my part 2 data - run at similar time, so runtime environment should be similar
NB.    timer'b23 mikesdata'
NB. ┌───────────┬┐
NB. │3 50.538002││
NB. └───────────┴┘
NB.    {.costs
NB. 48708
NB.    #costs
NB. 106541

NB. space used much better for Raul .......
NB.     1 (7!:2@])  'part2 28; data2'
.......
NB. ┌──┬───┐
NB. │28│708│
NB. └──┴───┘
NB. ┌─────────────┬─────┐
NB. │best found = │48708│
NB. └─────────────┴─────┘
NB. 61575008
NB.    1 (7!:2@]) 'b23 mikesdata'
NB. 9098880

Sorry not to present more information in this note,  but the topic is rather stale,  so
apologies are perhaps also due for raking over its ashes!

Thanks,

Mike

On 02/02/2022 14:18, Raul Miller wrote:
My solution there was also quite verbose.

Golfing an implementation might be a worthwhile exercise.

Thanks,


On 02/02/2022 14:15, 'Michael Day' via Programming wrote:
More chat on this old thread.

Finished at last!  50 stars for something or other...
It took quite a lot of tweaking to build in the restrictions to the "amphipods' " movements that I'd mentioned in my msg of 23Jan, below. I'd overlooked the paragraph (originally without J's NB. as Raul pointed out. The NB.s arise from the way I process the results of text-capture from a browser
in order to embed them in a J script.)

The problem remained difficult for me probably because I didn't find a decent data structure. FWIW,  each "state" consisted of one item of cost with 7 hall cells + 8 room cells for part 1,  or with  7 + 16 room cells for part 2.  It might well be better to have 7 hall cells + 4 room objects, or even, possibly,  7 + 4 donors rooms + 4 sink rooms,  but I haven't explored these ideas.

The original approach considered single cell moves only,  ie those between adjacent pairs of room cells,  between any outermost room cell and its two neighbouring hall cells, and between pairs of hall cells;  distances traversed were either one or two units,  the latter where the
transient hall cell immediately outside a room was crossed.

After my epiphany,  I dispensed with hall-hall movements altogether.  However,  it was still tricky to limit hall-room and room-room movements according to the current occupancy of
the rooms.

The code is far too verbose and loopy to be worth posting!

I'll now have a look at Raul Miller's solution,  way below in this post,  and see what I missed.

And then back to Project Euler to see if any current problems are approachable!

Thanks for your patience,

Mike

On 23/01/2022 19:02, 'Michael Day' via Programming wrote:
Chat really - sorry!

An update on my progress on this so-far elusive problem for me, part2 at least.

I _nearly_ got the right answer - tweaking the code resulted in being able to reach a termination of the tree-search - too high! - another tweak - too low! further
tweaks - explosion of search space.

Then I noticed:
   NB. I MISSED THIS POINT!!!!!
   NB. Once an amphipod stops moving in the hallway, it will stay in that spot until it can move into a room. (That
NB. is, once any amphipod starts moving, any other amphipods currently in the hallway are locked in place and
NB. will not move again until they can move fully into a room.)

I'd completely overlooked this restriction - my amphipods could all move in the hallway ...

Back to the J-board.

I'm avoiding looking at Raul's solution,  still hoping to find one for myself!


READ THE PAPER BEFORE TACKLING THE QUESTIONS!



Cheers,


Mike



On 13/01/2022 01:19, Raul Miller wrote:
https://adventofcode.com/2021/day/23

For day 23, we needed to solve a lazy shrimp problem.

We had a room and hallway setup (view in monospace font):

sample=:];._2 {{)n
#############
#...........#
###B#C#B#D###
   #A#D#C#A#
   #########
}}

Ignore spaces and '#' (wall) characters. The important locations are
marked with . or letters ABCD.

Actually, also we partially ignore the '.' characters which align
vertically here with a letter -- those locations are significant but
must always be left open.

The horizontal section of dots was the 'hallway' and the sections
initially containing letters were 'rooms'.

The task involved moving each of the letters (the shrimp) into the
rooms so that the shrimp would be arranged in alphabetical order, left
to right with these constraints (stated slightly differently in the
puzzle):

(1) Each shrimp could only move twice
(2) each move must change the shrimp's horizontal position
(3) no shrimp could pass through another shrimp
(4) shrimp cannot break through the walls

also each move cost of distance * 10^'ABCD' i. shrimpId (we use
manhattan distance here, because of rule 4).

So the task was to to do all this while minimizing the total energy
cost expended by the shrimp.

For part B, the task description was the same except that the rooms
were extended with this for the middle of each room (with these
additional shrimp):

   #D#C#B#A#
   #D#B#A#C#

My initial approach here was to solve the puzzle by hand and write
some code to tally the costs. That got me part A, but part B was too
complicated for me to see the solution. It was not until a few days
ago that I managed to solve part B (but I did use my initial solution
to help debug my part B implementation (running the solver against the
part A data set)).

To actually solve this, I wound up using an A* algorithm. For part B
this still took about half an hour (and part A took significantly
longer, ironically).

A* is a highly serial approach, but has the advantage of eliminating
significant parts of some search spaces. Instead of doing a breadth
first or depth first search (which could be parallelized), A* uses a
queue of partial results which are sorted in lowest cost order.

I also maintained a "lowest cost" array for each visited state, and
when revisiting such a state I only proceed if my new cost is lower
than my old cost.

Also,

(a) I precalculated for each shrimp, a characterization of its two
moves (a coordinate pair, with a 0 marking the undetermined part of
each move), and

(b) I assigned the total cost of a shrimp's moves to the move into the
hallway (this meant that the second move was always cheaper than any
first move, which reflects the issue that blocking the hallway would
tend to drive up the costs of other moves).

Other than that, my code is nothing special -- actually, it's rather
verbose. But because a complete test of the code is time consuming and
I did not have any further use for it, I've left it in its crude "just
got it working" state. I apologize for the lack of readability and
lack of relevant abstraction here, but I'm feeling lazy.

(That said, the code echos every intermediate state as it's being
processed, and would probably run faster if I removed that.)

The first part -- a23 -- sets up a batch of globals to be used by the solver:

a23=: {{
    bounds=: $y
    roomsN=: _4]\I.,y e.'ABCD'
    hallN =: I.,(y='.')>"1 +./y e.'ABCD'
    doorsN=: ({.roomsN) - {:$y NB. room positions in hallN
    boardC=: ((#hallN)#'.'),(,y) ([-.-.)'ABCD'
    locationsN=: ($y)#:hallN,,roomsN
    assert. locationsN -:&# boardC
    mask=: (-.y e.'# ')*(1~:i.#y)+./'#'=2{y
    show=: (+./"1 mask)#($mask)$(,mask)#inv ([ assert@('literal'-:datatype))@]
    move =: +./\.'ABCD'~:"1 roomsN {,y
    roomsM=: ($roomsN)$(-#,roomsN){.i.#boardC
    agenda=: move <@(,.&0,0,.|.)@#"1&|: roomsM
    done=: ((#hallN)#'.'),,($roomsN)$'ABCD'
    seen=: ,:done [ costs=: ,999999999
    boardC 0 allpaths agenda
}}

The solver is... not pretty. Conceptually, various parts of this could
have been abstracted, resulting in tighter code.

allpaths=: {{
   mqueue=: 0#,m
   xqueue=: 0#,:x
   yqueue=: 0#,:y
   pqueue=: i.0
   whilst.#mqueue do.
     if. x e. seen do.
       ndx=.seen i. x
       if. m < ndx{costs do.
         costs=: m ndx} costs
         if.0=ndx do.
           B=: b=. mqueue < {.costs
           mqueue=: b#mqueue
           xqueue=: b#xqueue
           yqueue=: b#yqueue
           pqueue=: b#pqueue
         end.
       else.
         M=: m=. {.mqueue
         X=: x=. {.xqueue
         Y=: y=. {.yqueue
         if.0=ndx do.
           B=: b=. }.mqueue < {.costs
         else.
           B=: b=. 1
         end.
         mqueue=: b#}.mqueue
         xqueue=: b#}.xqueue
         yqueue=: b#}.yqueue
         pqueue=: b#}.pqueue
         echo x
         continue.
       end.
     else.
       seen=:seen,x
       costs=:costs,m
     end.
     echo (":(show x);'  ';":,.m,{.costs)-."1' '-.~,":2#<' '
     displaced=. (#hallN){.x
     free=. '.'=displaced
     pending=.i.0 0
     phase=.i.0
     plan=.i.0 0
     paths=. i.0
     for_room.'ABCD' do.
       t=. room_index {:: y
       if.0=#t do.continue. end.
       for_next. (1 (0}) </\ 0={."1 t) #t do.
         todo=. (<t -. next) room_index} y
         sign=. *hallN-room_index{doorsN
         if. 0={:next do. NB. move into hallN
           open=. (*/\.(_1=sign)#free),*/\(1=sign)#free
           piece=. ({. next){x
           assert. piece e. 'ABCD'
           moves=.(#~ openmask */ .<: open"_) next+"1]0,.I.open
           index=. 'ABCD'i.piece
           cost=. 10^index
           prices=. cost*+/"1|-/"2 moves{locationsN
           door=. index{doorsN
           seq=. +/piece=(#hallN){.x
           row=. 1+(+/0={."1 index{::y)-+/piece=(#hallN){.x
           dist=. +/"1|(({:"1 moves){locationsN)-"1 row,}.bounds#:door
           values=. dist*cost
           plan=.plan,(prices+values),.moves
         elseif.'.'=({:next){x do. NB. move from hallN
           'L R'=. sign </. displaced
           opts=.((room={:L-.'.')#L i:room),(#L)+(room={.R-.'.')#R i.room
           if.#opts do.
             'breakpoint here'
           end.
           moves=. (#~ openmask */ .<: (displaced e.'.',room)"_) next+"1]opts,.0
           plan=.plan,0,.moves
         else.
           continue.
         end.
         phase=. phase,(#moves)#*{. next
         pending=.pending,(#moves)#,:todo
       end.
     end.
     energy=. m+{."1 plan
     nexts=. (_2{."1 plan) |.@:{`[`]}"1 x
     B=: b=. energy < {.costs
     mqueue=: mqueue,~ b#energy
     xqueue=: xqueue,~ b#nexts
     yqueue=: yqueue,~ b#pending
     pqueue=: pqueue,~ b#{."1 plan
     if.-.0={.pqueue do.
       order=.  /: pqueue
       mqueue=: order{mqueue
       xqueue=: order{xqueue
       yqueue=: order{yqueue
       pqueue=: order{pqueue
     end.
     M=:m=. {.mqueue
     X=:x=. {.xqueue
     Y=:y=. {.yqueue
     mqueue=: }.mqueue
     xqueue=: }.xqueue
     yqueue=: }.yqueue
     pqueue=: }.pqueue
   end.
}}

The one bit of abstraction that I used here was:

thru=: [ ~.@, <. + i.@(+*)@-~

openmask=:{{
   (({:bounds)|hallN)&e.@thru/"1 y { {:"1 locationsN
}}

This is used in determining whether moves are possible.

Anyways, when this runs, it displays the board (what I think the
puzzle called the shell) positions while it's running, and for
plausible layouts displays the current and final cost to along with
each board position. Discarded states are instead displayed in a
compressed format.

When it finishes, this implementation displays the board in its final
state along with the minimal cost. But that's also available in
{.costs if you wanted to remove the echo from the code.

For part B, I used the same code, with the required extra content added:

b23=: {{
   a23 (3{.y),extension,3}.y
}}

extension=: ];._2 {{)n
###D#C#B#A###
###D#B#A#C###
}}

If I could find a production use for this kind of thing, I would
probably not implement it in J, except maybe this kind of prototype.
These kinds of highly serial algorithms favor compiled code, and we
currently do not have a J compiler.

FYI,









--
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus

----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to