131 lines
4.3 KiB
Mathematica
131 lines
4.3 KiB
Mathematica
|
:- 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").
|