From a2eca138aa3e179655428460d1b006512a55695e Mon Sep 17 00:00:00 2001 From: Dory Date: Sat, 9 Dec 2023 22:01:20 -0800 Subject: [PATCH] idiomatic but slow d8p2 --- 08/part2.idiomaticbutslow.pl | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 08/part2.idiomaticbutslow.pl diff --git a/08/part2.idiomaticbutslow.pl b/08/part2.idiomaticbutslow.pl new file mode 100644 index 0000000..b5eadab --- /dev/null +++ b/08/part2.idiomaticbutslow.pl @@ -0,0 +1,86 @@ +% :- table step_at/2. +% :- table node_at/3. +:- table reachable_endnode/2. + +:- op(700, xfx, l). +:- op(700, xfx, r). +From l To :- From to To-_. +From r To :- From to _-To. + +answer(Answer) :- + starts(Starts), + routes(Starts, [Route1 | RestOfRoutes]), + foldl(unify2, RestOfRoutes, Route1, _-(_-Answer-_)). + +% unify2 combines 2 routes into one with its own stride-offsets-dests +unify2(Route1, Route2, NewRoute) :- + once(findnsols(2, S, converge(Route1, Route2, S), SolutionPair)), + SolutionPair = [NewA-LenA-NewZ, _-LenB-_], + NewStride is LenB - LenA, + NewRoute = NewA-(NewStride-LenA-[0-NewZ]). + +% Len = Stride1*X1 + Offset1 + Dest1 = Stride2*X2 + Offset2 + Dest2 +% For performance, Route1's Stride should =< Route2's Stride +converge(Route1, Route2, NewA-Len-NewZ) :- + writef('Combining %t - %t\n', [Route1, Route2]), + Route1 = A1-(Stride1-Offset1-Dests1), + Route2 = A2-(Stride2-Offset2-Dests2), + natnum(X2), + pick([Dests1, Dests2], [Dest1-Z1, Dest2-Z2]), + 0 is (Stride2*X2 + Offset2 + Dest2 - Offset1 - Dest1) mod Stride1, + Len is Stride2*X2 + Offset2 + Dest2, + atom_concat(A1, A2, NewA), atom_concat(Z1, Z2, NewZ). + +routes(Starts, Routes) :- + maplist([S, S-Route]>>(route(S, Route)), Starts, Routes). + +% route builds a route (Stride-Offset-Internals) for a particular starting node. +route(Start, Stride-FirstN-[0-FirstDest | Dests]) :- + direction_len(DirLen), + once(reachable_endnode(Start, FirstN-FirstDest)), + findall(ShiftedN-Dest, + ( reachable_endnode(Start, N-Dest), + ShiftedN is N - FirstN, + ShiftedN =\= 0, + (0 is ShiftedN mod DirLen -> !; true)), + TmpDests), + last(TmpDests, Stride-LastDest), + append(Dests, [Stride-LastDest], TmpDests). + +% Dest is reachable from Start after N steps +reachable_endnode(Start, N-Dest) :- + natnum(N), + node_at(N, Start, Dest), + is_end(Dest). + +starts(Starts) :- findall(X, X to _, Nodes), include(is_start, Nodes, Starts). + +is_start(Node) :- atom_chars(Node, [_, _, a]). +is_end(Node) :- atom_chars(Node, [_, _, z]). + +% node_at(N, Dir, From, Dest) :- Dest is reached after moving N step from From. +node_at(0, Start, Start). +node_at(N, From, Dest) :- + N > 0, + PrevN is N - 1, + step_at(PrevN, PrevStep), + G =.. [PrevStep, PrevNode, Dest], G, + node_at(PrevN, From, PrevNode). + +% Step is the N-th step (counting starts from 0). +step_at(N, Step) :- + direction_list(Dir), + length(Dir, DirLen), + divmod(N, DirLen, _, Remainder), + nth0(Remainder, Dir, Step). + +direction_list(Dir) :- direction(Str), atom_chars(Str, Dir). +direction_len(Len) :- direction_list(D), length(D, Len). + +% pick one item from each sublist of ListOfLists & put them into Items in order. +% [[1,2,3], [4], [5,6]] -> [1,4,5]; [1,4,6]; [2,4,5]; [2,4,6]; [3,4,5]; [3,4,6]. +pick(ListOfLists, Items) :- + maplist([SubList, X]>>(member(X, SubList)), ListOfLists, Items). + +natnum(0). +natnum(N) :- natnum(N0), N is N0 + 1.