My solution there was also quite verbose. Golfing an implementation might be a worthwhile exercise.
Thanks, -- Raul On Wed, Feb 2, 2022 at 6:15 AM 'Michael Day' via Programming <programm...@jsoftware.com> 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 ---------------------------------------------------------------------- For information about J forums see http://www.jsoftware.com/forums.htm