From 448a9c41c02d02cca68b1e0f04e0437c2c51c2ac Mon Sep 17 00:00:00 2001 From: rhiannon morris Date: Thu, 22 Dec 2022 00:04:14 +0100 Subject: [PATCH] =?UTF-8?q?day12=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abandoned/day12.m | 230 ---------------------------------------------- aoc.m | 2 + day12.m | 195 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 230 deletions(-) delete mode 100644 abandoned/day12.m create mode 100644 day12.m diff --git a/abandoned/day12.m b/abandoned/day12.m deleted file mode 100644 index dc3728b..0000000 --- a/abandoned/day12.m +++ /dev/null @@ -1,230 +0,0 @@ -:- module day12. -:- interface. -:- import_module basics. - -:- pred run(part::in, lines::in, answer::out) is cc_multi. - -:- implementation. -:- import_module int. -:- import_module char. -:- import_module string. -:- import_module list. -:- import_module array2d. - -:- type point == {int, int}. - -:- type grid ---> g(array :: array2d(char), start :: point, end :: point). - -:- pred grid(lines::in, grid::out) is semidet. -grid(Lines, g(Arr, {SX, SY}, {EX, EY})) :- - Arr = from_lists(map(to_char_list, Lines)), - find(Arr, 'S', SX, SY), - find(Arr, 'E', EX, EY). - -:- pred find_from(array2d(T)::in, T::in, int::in, int::in, int::out, int::out) - is semidet. -find_from(Arr, A, X1, Y1, X2, Y2) :- - bounds(Arr, W, H), - Y1 < H, - (if X1 < W then - B = Arr^elem(X1, Y1), - (if A = B then X2 = X1, Y2 = Y1 - else find_from(Arr, A, X1+1, Y1, X2, Y2)) - else - find_from(Arr, A, 0, Y1+1, X2, Y2)). - -:- pred find(array2d(T)::in, T::in, int::out, int::out) is semidet. -find(Arr, A, X, Y) :- find_from(Arr, A, 0, 0, X, Y). - -:- func get(grid, point) = char. -get(G, {X, Y}) = G^array^elem(X, Y). - -:- func height(char) = int. -height(C) = I :- - if C = 'S' then I = 0 - else if C = 'E' then I = 25 - else I = to_int(C) - to_int('a'). - -:- func height(grid, point) = int. -height(G, P) = height(get(G, P)). - -:- pred adj_point(point::in, point::out) is multi. -adj_point({X, Y}, {X+1, Y}). -adj_point({X, Y}, {X-1, Y}). -adj_point({X, Y}, {X, Y+1}). -adj_point({X, Y}, {X, Y-1}). - -:- pred adj(grid::in, point::in, point::out) is nondet. -adj(G, P, Q) :- - adj_point(P, Q), in_bounds(G, Q), - abs(height(G, P) - height(G, Q)) =< 1. - -:- pred in_bounds(grid::in, point::in) is semidet. -in_bounds(G, {X, Y}) :- - bounds(G^array, W, H), - 0 =< X, X < W, - 0 =< Y, Y < H. - -:- pred index(grid::in, point::out) is nondet. -index(G, {I, J}) :- - bounds(G^array, W, H), - nondet_int_in_range(0, W-1, I), - nondet_int_in_range(0, H-1, J). - - -:- type path == list(point). - -:- type distance ---> i(int); inf. - -:- import_module solutions. - -:- import_module map. -:- type pm == map(point, point). - -:- type nq ---> nq(pq, dm). -:- type dm == map(point, distance). -:- type pq == list({point, distance}). - -:- pred remove_min(point::out, distance::out, pq::in, pq::out) is semidet. -remove_min(X, D, [{X0, D0} | PQ0], PQ) :- - if - remove_min(X1, D1, PQ0, PQ1), - D1 @< D0 - then - X = X1, D = D1, - PQ = [{X0, D0} | PQ1] - else - X = X0, D = D0, - PQ = PQ0. - -:- pred seen(nq::in, point::in) is semidet. -seen(nq(_, S), X) :- contains(S, X). - -:- pred next(point::out, distance::out, nq::in, nq::out) is semidet. -next(X, D, nq(!.Q, !.S), nq(!:Q, !:S)) :- - remove_min(X, D, !Q), set(X, D, !S). - -:- pred set_distance_pq(point::in, distance::in, pq::in, pq::out) is det. -set_distance_pq(_, _, [], []). -set_distance_pq(X, D, [{Y, E} | Xs], Zs) :- - if X = Y then - Zs = [{X, D} | Xs] - else - set_distance_pq(X, D, Xs, Xs2), - Zs = [{Y, E} | Xs2]. - -:- pred set_distance(point::in, distance::in, nq::in, nq::out) is det. -set_distance(X, D, nq(!.Q, !.S), nq(!:Q, !:S)) :- - set_distance_pq(X, D, !Q), - (if contains(!.S, X) then det_update(X, D, !S) else true). - -:- func get_distance(point, pq, dm) = distance. -:- mode get_distance(in, in, in) = out is semidet. -get_distance(X, [], S) = search(S, X). -get_distance(X, [{Y, D} | Xs], S) = D1 :- - if X = Y then D1 = D else D1 = get_distance(X, Xs, S). - -:- func get_distance(point, nq) = distance. -:- mode get_distance(in, in) = out is semidet. -get_distance(X, nq(Q, S)) = get_distance(X, Q, S). - - -:- pred insert_init(point::in, point::in, pq::in, pq::out) is det. -insert_init(From, X, !Queue) :- - (if X = From then D = i(0) else D = inf), - cons({X, D}, !Queue). - -:- pred init_queue(grid::in, point::in, nq::out) is det. -init_queue(G, From, nq(Q, init)) :- - aggregate(index(G), insert_init(From), [], Q). - - -:- pred get_path_rev(pm::in, point::in, point::in, path::out) is det. -get_path_rev(Map, From, To, Path) :- - if From = To then - Path = [] - else if search(Map, To, Prev) then - get_path_rev(Map, From, Prev, Tail), - Path = [To | Tail] - else - To = {X, Y}, - die("no predecessor for {%d, %d}", [i(X), i(Y)]). - -:- pred get_path(pm::in, point::in, point::in, path::out) is det. -get_path(Map, From, To, reverse(Path)) :- get_path_rev(Map, From, To, Path). - -:- func incr(distance) = distance. -incr(inf) = inf. -incr(i(N)) = i(N + 1). - -:- pred neighbours(grid::in, point::in, nq::in, point::out) is nondet. -neighbours(G, Point, Queue, N) :- - adj(G, Point, N), - not seen(Queue, N). - -:- pred path(grid::in, point::in, point::in, path::out, - nq::in, nq::out, pm::in, pm::out) is semidet. -path(G, From, To, Path, !Queue, !Prev) :- - next(Point, Distance, !Queue), - Distance = inf => die("ow"), - Neighs = solutions(neighbours(G, Point, !.Queue)), - foldl2(pred(Neigh::in, % 🐴 - !.Queue::in, !:Queue::out, - !.Prev::in, !:Prev::out) is det :- ( - Alt = incr(Distance), - (if Alt @< get_distance(Neigh, !.Queue) then - set_distance(Neigh, Alt, !Queue), - set(Neigh, Point, !Prev) - else true)), - Neighs, !Queue, !Prev), - (if Point = To then - get_path(!.Prev, From, To, Path) - else - path(G, From, To, Path, !Queue, !Prev)). - -:- pred path(grid::in, point::in, point::in, path::out) is semidet. -path(G, From, To, Path) :- - init_queue(G, From, Queue), - path(G, From, To, Path, Queue, _, init, _). - -:- pred path(grid::in, path::out) is semidet. -path(G, Path) :- path(G, G^start, G^end, Path). - - -run(_, Lines, Out) :- - if - grid(Lines, G), - init_queue(G, G^start, Q), - path(G, G^start, G^end, Path, Q, nq(Q1, S1), init, Prev) - then - Out = 'new other'({"RESULT", length(Path):int, - "G", G, "Path", Path, - "Q1", Q1, - "S1", to_assoc_list(S1):list(_), - "Prev", to_assoc_list(Prev):list(_)}) - else - die("bad input"). - -/* omg could u imagine if this had worked tho - -:- pragma memo(path/5, [fast_loose]). -:- pred path(grid::in, point::in, point::in, path::in, int::out) - is nondet. -path(G, P, P, _, 0). -path(G, P, Q, Seen, Len + 1) :- - adj(G, P, P1), - not member(P1, Seen), - path(G, P1, Q, [P|Seen], Len). - -:- pred path(grid::in, point::in, point::in, int::out) is nondet. -path(G, P, Q, Len) :- path(G, P, Q, [], Len). - -:- import_module solutions. - -run(_, Lines, Out) :- - if grid(Lines, G) then - solutions(path(G, G^start, G^end), Paths), - Out = int(det_head(Paths)) - else - die("bad input"). -*/ diff --git a/aoc.m b/aoc.m index 2996ed8..2a7c0a3 100644 --- a/aoc.m +++ b/aoc.m @@ -47,6 +47,7 @@ run_day(Day, Part, Lines, Out) :- :- import_module day9. :- import_module day10. :- import_module day11. +:- import_module day12. :- import_module day13. :- import_module day14. :- import_module day16. @@ -64,6 +65,7 @@ solution(8, day8.run). solution(9, day9.run). solution(10, day10.run). solution(11, day11.run). +solution(12, day12.run). solution(13, day13.run). solution(14, day14.run). solution(16, day16.run). diff --git a/day12.m b/day12.m new file mode 100644 index 0000000..c5ac667 --- /dev/null +++ b/day12.m @@ -0,0 +1,195 @@ +:- module day12. +:- interface. +:- import_module basics. + +:- pred run(part::in, lines::in, answer::out) is cc_multi. + +:- implementation. +:- import_module int. +:- import_module char. +:- import_module string. +:- import_module list. +:- import_module array2d. +:- import_module map. +:- import_module psqueue. +:- import_module solutions. + +:- type point == {int, int}. + +:- type grid ---> g(array :: array2d(char), start :: point, end :: point). + +:- pred grid(lines::in, grid::out) is nondet. +grid(Lines, g(Arr, {SX, SY}, {EX, EY})) :- + Arr = from_lists(map(to_char_list, Lines)), + find(Arr, 'S', SX, SY), + find(Arr, 'E', EX, EY). + +:- pred find(array2d(T)::in, T::in, int::out, int::out) is nondet. +find(Arr, X, I, J) :- index_arr(Arr, I, J), Arr^elem(I, J) = X. + +:- func grid^elem(point) = char. +G^elem({X, Y}) = G^array^elem(X, Y). + +:- func height(char) = int. +height(C) = I :- + if C = 'S' then I = 0 + else if C = 'E' then I = 25 + else I = to_int(C) - to_int('a'). + +:- func height(grid, point) = int. +height(G, P) = height(G^elem(P)). + +:- pred adj_point(point::in, point::out) is multi. +adj_point({X, Y}, {X+1, Y}). +adj_point({X, Y}, {X-1, Y}). +adj_point({X, Y}, {X, Y+1}). +adj_point({X, Y}, {X, Y-1}). + +:- pred adj(grid::in, point::in, point::out) is nondet. +adj(G, P, Q) :- + adj_point(P, Q), in_bounds(G, Q), + height(G, Q) =< height(G, P) + 1. + +:- pred in_bounds(grid::in, point::in) is semidet. +in_bounds(G, {X, Y}) :- in_bounds(G^array, X, Y). + + +:- pred bounds(grid::in, int::out, int::out) is det. +bounds(G, W, H) :- bounds(G^array, W, H). + +:- pred index(grid::in, point::out) is nondet. +index(Grid, {I, J}) :- index_arr(Grid^array, I, J). + +:- pred index_arr(array2d(T)::in, int::out, int::out) is nondet. +index_arr(Arr, I, J) :- + bounds(Arr, W, H), + nondet_int_in_range(0, W-1, I), + nondet_int_in_range(0, H-1, J). + + +:- type path == list(point). +:- type distance ---> i(int); inf. +:- type pm == map(point, point). +:- type nq == psqueue(distance, point). + +:- pred (distance::in) < (distance::in) is semidet. +i(_) < inf. +i(M) < i(N) :- M < N. + +:- pred min(distance::in, distance::in, distance::out) is det. +min(A, B, ite(A < B, A, B)). + +:- func init_dist(point, point) = distance. +init_dist(Start, P) = ite(unify(P, Start), i(0), inf). + +:- pred init_queue(grid::in, point::in, nq::out) is det. +init_queue(G, Start, Q) :- + Points = solutions(index(G)), + foldl(pred(P::in, !.Q::in, !:Q::out) is det :- + det_insert(init_dist(Start, P), P, !Q), + Points, psqueue.init, Q). + +:- pred init_queue(grid::in, nq::out) is det. +init_queue(G, Q) :- init_queue(G, G^start, Q). + +:- pred incr(distance::in, distance::out) is det. +incr(inf, inf). +incr(i(N), i(N+1)). + +:- pred neighbour(grid::in, nq::in, point::in, {point,distance}::out) is nondet. +neighbour(G, Q, P, {N, D}) :- adj(G, P, N), search(Q, N, D). + +:- pred update_distance(point::in, distance::in, {point,distance}::in, + pm::in, pm::out, nq::in, nq::out) is det. +update_distance(Prev, DNew, {P, DOld}, !M, !Q) :- + if DNew < DOld then + (if adjust(func(_) = DNew, P, !Q) then true else die("point disappeared")), + set(P, Prev, !M) + else true. + +:- pred path0(grid::in, point::in, pm::in, pm::out, nq::in, nq::out) is det. +path0(G, End, !M, !Q) :- + det_remove_least(Distance, Point, !Q), + (if Point = End then true else + solutions(neighbour(G, !.Q, Point), Neighbours), + incr(Distance, NeighDistance), % 🐴↔🐴 + foldl2(update_distance(Point, NeighDistance), Neighbours, !M, !Q), + path0(G, End, !M, !Q)). + +:- func get_path_len(grid, pm, point, point) = distance. +get_path_len(G, M, Start, Point) = Out :- + if Point = Start then + Out = i(0) + else if search(M, Point, Prev) then + incr(get_path_len(G, M, Start, Prev), Out) + else + Out = inf. + + +:- pred path_len(grid::in, point::in, point::in, distance::out) is det. +path_len(Grid, Start, End, Len) :- + init_queue(Grid, Start, Queue), + path0(Grid, End, init, Prevs, Queue, _), + Len = get_path_len(Grid, Prevs, Start, End). + +:- pred path_len(grid::in, distance::out) is det. +path_len(Grid, Len) :- path_len(Grid, Grid^start, Grid^end, Len). + + +:- pred start(grid::in, point::out) is nondet. +start(Grid, P) :- index(Grid, P), height(Grid, P) = 0. + +:- pred shortest(grid::in, list(point)::in, point::in, distance::out) is det. +shortest(Grid, Starts, End, Distance) :- + map(pred(P::in, D::out) is det :- path_len(Grid, P, End, D), + Starts, Distances), + foldl(min, Distances, inf, Distance). + +:- func distance(distance) = answer. +distance(inf) = string("∞"). +distance(i(N)) = int(N). + + +run(one, Lines, Out) :- + if + grid(Lines, Grid), + path_len(Grid, Len) + then + Out = distance(Len) + else + die("bad input"). +run(two, Lines, Out) :- + % i did floyd warshall and it was WAY too slow + % like several seconds per row ://// + if grid(Lines, Grid) then + solutions(start(Grid), Starts), + shortest(Grid, Starts, Grid^end, Distance), + Out = distance(Distance) + else + die("bad input"). + + + +/* omg could u imagine if this had worked tho + +:- pragma memo(path/5, [fast_loose]). +:- pred path(grid::in, point::in, point::in, path::in, int::out) + is nondet. +path(G, P, P, _, 0). +path(G, P, Q, Seen, Len + 1) :- + adj(G, P, P1), + not member(P1, Seen), + path(G, P1, Q, [P|Seen], Len). + +:- pred path(grid::in, point::in, point::in, int::out) is nondet. +path(G, P, Q, Len) :- path(G, P, Q, [], Len). + +:- import_module solutions. + +run(one, Lines, Out) :- + if grid(Lines, G) then + solutions(path(G, G^start, G^end), Paths), + Out = int(det_head(Paths)) + else + die("bad input"). +*/