diff --git a/aoc.m b/aoc.m index cb54831..2996ed8 100644 --- a/aoc.m +++ b/aoc.m @@ -50,6 +50,7 @@ run_day(Day, Part, Lines, Out) :- :- import_module day13. :- import_module day14. :- import_module day16. +:- import_module day18. :- pred solution(int::in, sol::out(sol)) is semidet. solution(1, day1.run). @@ -66,3 +67,4 @@ solution(11, day11.run). solution(13, day13.run). solution(14, day14.run). solution(16, day16.run). +solution(18, day18.run). diff --git a/day18.m b/day18.m new file mode 100644 index 0000000..ba61148 --- /dev/null +++ b/day18.m @@ -0,0 +1,130 @@ +:- module day18. +:- 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 digraph. +:- import_module ranges. +:- import_module solutions. +:- import_module set_tree234. + +:- type set(T) == set_tree234(T). + +:- type point3 ---> p(x :: int, y :: int, z :: int). + +:- type bounds ---> b(lo :: point3, hi :: point3). + +:- type dir ---> left; right; up; down; fore; back. + +:- type vertex ---> empty(point3) ; full(point3, dir). + +:- type points == set(point3). +:- type graph == digraph(vertex). +:- type key == digraph_key(vertex). + + +:- pred lines(lines::in, list(point3)::out) is semidet. +lines(Lines, Ps) :- map(line, Lines, Ps). + +:- pred line(string::in, point3::out) is semidet. +line(Str, P) :- line(P, to_char_list(Str), []). + +:- pred line(point3::out, chars::in, chars::out) is semidet. +line(p(X, Y, Z)) --> number(X), [','], number(Y), [','], number(Z). + +:- pred number(int::out, chars::in, chars::out) is semidet. +number(N) --> digits(S), {to_int(from_char_list(S), N)}. + +:- pred digits(chars::out, chars::in, chars::out) is semidet. +digits([C | Cs]) --> + [C], {is_digit(C)}, + (if digits(Cs0) then {Cs = Cs0} else {Cs = []}). + + +:- func pointwise(func(int) = int, point3) = point3. +pointwise(F, p(X,Y,Z)) = p(F(X), F(Y), F(Z)). + +:- pred pointwise(pred(int, int, int), point3, point3, point3). +:- mode pointwise(pred(in, in, out) is det, in, in, out) is det. +pointwise(P, p(X1,Y1,Z1), p(X2,Y2,Z2), p(X,Y,Z)) :- + P(X1, X2, X), P(Y1, Y2, Y), P(Z1, Z2, Z). + + +:- pred bounds(list(point3), bounds). +:- mode bounds(in, out) is semidet. +:- mode bounds(in(non_empty_list), out) is det. +bounds([P | Ps], b(Lo, Hi)) :- + foldl(pointwise(min), Ps, P, Lo0), foldl(pointwise(max), Ps, P, Hi0), + Lo = pointwise(func(I) = I - 1, Lo0), + Hi = pointwise(func(I) = I + 1, Hi0). + +:- pred in_bounds(bounds::in, point3::in) is semidet. +in_bounds(b(p(X1,Y1,Z1), p(X2,Y2,Z2)), p(X,Y,Z)) :- + X1 =< X, X =< X2, Y1 =< Y, Y =< Y2, Z1 =< Z, Z =< Z2. + +:- pred nondet_in_bounds(bounds::in, point3::out) is nondet. +nondet_in_bounds(b(p(X1,Y1,Z1), p(X2,Y2,Z2)), p(X,Y,Z)) :- + Xs = range(X1, X2), Ys = range(Y1, Y2), Zs = range(Z1, Z2), + nondet_member(X, Xs), nondet_member(Y, Ys), nondet_member(Z, Zs). + +:- pred adj0(point3::in, point3::out, dir::out) is multi. +adj0(p(X,Y,Z), p(X1,Y,Z), D) :- X1 = X - 1, D = left ; X1 = X + 1, D = right. +adj0(p(X,Y,Z), p(X,Y1,Z), D) :- Y1 = Y - 1, D = up ; Y1 = Y + 1, D = down. +adj0(p(X,Y,Z), p(X,Y,Z1), D) :- Z1 = Z - 1, D = fore ; Z1 = Z + 1, D = back. + +:- pred adj(bounds::in, point3::in, {point3,dir}::out) is nondet. +adj(Bounds, P, {Q, D}) :- adj0(P, Q, D), in_bounds(Bounds, Q). + + +:- pred build_graph(list(point3)::in, graph::out, key::out) is cc_nondet. +build_graph(PointList, Graph, StartKey) :- + bounds(PointList, Bounds), + list_to_set(PointList, Points), + build_graph0(Bounds, Points, Graph), + nondet_in_bounds(Bounds, Start), + not member(Start, Points), + search_key(Graph, empty(Start), StartKey). + +:- pred build_graph0(bounds::in, points::in, graph::out) is det. +build_graph0(Bounds, Full, Graph) :- + solutions(nondet_in_bounds(Bounds), AllPoints), + foldl(add_adj_vertices(Bounds, Full), AllPoints, init, Graph). + +:- pred add_adj_vertices(bounds::in, points::in, point3::in, + graph::in, graph::out) is det. +add_adj_vertices(Bounds, Full, P, !G) :- + if member(P, Full) then true else + solutions(adj(Bounds, P), Adj), + foldl(add_vertex(Full, P), Adj, !G). + +:- pred add_vertex(points::in, point3::in, {point3,dir}::in, + graph::in, graph::out) is det. +add_vertex(Full, From, {To, Dir}, !G) :- + ToV = ite((pred) is semidet :- member(To, Full), full(To, Dir), empty(To)), + add_vertices_and_edge(empty(From), ToV, !G). + + +:- func count_faces(graph, key) = int. +count_faces(Graph, Start) = Count :- + dfs(Graph, Start, SeenKeys), + foldl(pred(K::in, Acc0::in, Acc::out) is det :- + (if lookup_vertex(Graph, K) = full(_, _) then Acc = Acc0 + 1 + else Acc = Acc0), + SeenKeys, 0, Count). + + +run(one, _, _) :- die("bqn"). +run(two, Lines, Out) :- + if + lines(Lines, PointList), + build_graph(PointList, Graph, Start) + then + Out = int(count_faces(Graph, Start)) + else + die("bad input").