Was not in shape to implement the shortest path algorithm, therefore
searched for a graph package and found igraph.org which offers an R
interface. Together with the Rserver addon this reduced the task to create
the edge list for the graph with the risk levels as weights - note that
igraph needs the nodes to be greater than 0. Here's the J session:

   rd=: "."0;._2
   d=: rd 0 : 0
1163751742
1381373672
2136511328
3694931569
7463417111
1319128137
1359912421
3125421639
1293138521
2311944581
)
   load'stats/r/rserver'
   Rcmd'library(igraph)'

Attaching package: 'igraph'

The following objects are masked from 'package:stats':

    decompose, spectrum

The following object is masked from 'package:base':

    union

   es=: ,@}: ,. ,@}. NB. Edges utility to be applied on i.$d
   ltr=: 3 : 0       NB. Lowest total risk
'e' Rset >: (, |."1) (es, [: ,/ es"1) i.$y        NB. Edge list
'r' Rset (([:,}.), ([:,}."1), ([:,}:), [:,}:"1) y NB. Risk levels
Rcmd'g <- graph_from_edgelist(e)'
Rcmd'E(g)$weight <- r'
+/ }. (,y){~ <: Rget'unlist(shortest_paths(g,1,', ')$vpath)',~ (, ':'&,)":
#,y
)
   ltr d                                      NB. (*)
40
   mm=: {{ u/ (i.5) (9&|@+)&.(a:`<:)"0 _ y }} NB. Multiply matrix adverb
   ltr ,.mm ,mm d                             NB. (**)
315



On Wed, Jan 5, 2022 at 3:55 AM Raul Miller <[email protected]> wrote:

> https://adventofcode.com/2021/day/15
>
> For day 15, we had a cave with a "risk level map" and our task was to
> find the least risk path through the cave (from the top left corner to
> the lower right corner of the map).
>
> The provided example map looked like this:
>
> sample=: {{)n
> 1163751742
> 1381373672
> 2136511328
> 3694931569
> 7463417111
> 1319128137
> 1359912421
> 3125421639
> 1293138521
> 2311944581
> }}
>
> I didn't have any really great approach here, so I just used
> Dijkstra's shortest path algorithm (with connections horizontally and
> vertically and risk being the "path length" to enter a point on the
> map).
>
> unpack=:{{
>  _&".@>;._2 y
> }}
>
> dir=: 1 0,0 1,_1 0,:0 _1
>
> NB. extend each path y in each of the available directions (limited by x)
> NB. discard "usless" paths which immediately return to its previous
> location
> extend=:{{
>   if.1=#y do.
>     NB. Hack: avoid length error from uselessness test when starting
>     0 0,:1 _<.x return.
>   else.
>     next=. (x #: {:y)+"1/dir
>     paths=. ,/(|:y),"1 0"_1 x#.next
>     tolow=. ,/next +./ .< 0
>     tohigh=. ,/next +./ .>: x
>     useless =. =/"1 (_3 _1){"1 paths
>     |: paths #~ -.tolow+.tohigh+. useless
>   end.
> }}
>
> a15=:{{
>   i=. 0
>   'gridshape movecosts'=. ($;,) unpack y
>   mincosts=. 0,_#~1-~#movecosts
>   newpaths=. i.1 1
>   while. #newpaths do.
>     NB. find all 'newly' reachable locations
>     newpaths=. gridshape&extend newpaths
>     newcosts=. ((_2 { newpaths) { mincosts) + ({: newpaths) { movecosts
>     NB. discard everything which is not an improvement
>     k=. +/keep=. newcosts < ({: newpaths) { mincosts
>     if.0=k do. break.end. NB. done?
>     newpaths=. keep #"1 newpaths
>     newcosts=. keep # newcosts
>     loc=. {: newpaths
>     costbyloc=. loc <.//. newcosts
>     NB. pick best of each of the new competing paths
>     k=. +/keep=. newcosts = costbyloc {~ (i.~ ~.) loc
>     if. k~:#keep do.
>       newpaths=. keep#"1 newpaths
>       newcosts=. keep#newcosts
>       loc=. {: newpaths
>     end.
>     NB. pick arbitrarily when we have multiple new same-cost
>     NB. paths leading to the same place
>     k=. +/keep=. (i.@# = i.~) loc
>     if. k~:#keep do.
>       newpaths=. keep#"1 newpaths
>       newcosts=. keep#newcosts
>       loc=. {: newpaths
>     end.
>     NB. record results
>     mincosts=. newcosts loc} mincosts
>     NB. show progress
>     echo (i=.i+1),($newpaths),{:mincosts
>   end.
>   {: mincosts
> }}
>
> I threw the echo statements in there so that I could watch the
> algorithm's progress. This is basically an exhaustive search.
>
> I could have achieved some efficiency here by discarding older parts
> of the paths, and generally speaking this could have been tighter
> code.  I should have been able to merge the "lowest cost" by location
> step and the "pick arbitrarily when multiple paths lead to the same
> location" step. And, some lines might wrap here in this email message
> (hopefully not).
>
> But this worked, and I can understand the code.
>
> For part B, it turns out that our map was a map of one of 25 tiles of
> the actual cave. We add each value from +/~i.5 to this map (and wrap
> values greater than 9 back to 1) to get the "real map". Which mostly
> means it takes on the order of 25 times longer to find the shortest
> path (probably a little longer than that because of inefficiencies in
> my code).
>
>    a15 sample
> spits out 18 lines of progress reports and then returns the value 40
>
> In other words, my part B looked like this:
>
> tile=:{{
>   ,LF,.~' '-."1~":>:9|<:>,.each/,each/(+/~i.5)+each<unpack y
> }}
>
> b15=: {{
>    a15 tile y
> }}
>
>    b15 sample
> spits out 98 lines of progress reports and then returns the value 315
>
> FYI,
>
> --
> Raul
> ----------------------------------------------------------------------
> For information about J forums see http://www.jsoftware.com/forums.htm
>
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to