:- 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").