day12 🎉

This commit is contained in:
rhiannon morris 2022-12-22 00:04:14 +01:00
parent 442b930c41
commit 448a9c41c0
3 changed files with 197 additions and 230 deletions

View File

@ -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").
*/

2
aoc.m
View File

@ -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).

195
day12.m Normal file
View File

@ -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").
*/