aoc2022/day9.m

101 lines
2.6 KiB
Mathematica

:- module day9.
:- interface.
:- import_module basics.
:- pred run(part::in, lines::in, answer::out) is cc_multi.
:- implementation.
:- import_module int.
:- import_module string.
:- import_module list.
:- import_module set_bbbtree.
:- type set(T) == set_bbbtree(T).
:- type coord ---> c(x :: int, y :: int).
:- type rope == list(coord).
:- type direction ---> up; down; left; right.
:- pred to_direction(string, direction).
:- mode to_direction(in, out) is semidet.
:- mode to_direction(out, in) is det.
to_direction("U", up).
to_direction("D", down).
to_direction("L", left).
to_direction("R", right).
:- pred orth(direction::in, direction::in) is semidet.
orth(A, B) :-
((A = up ; A = down), (B = left ; B = right));
((A = left ; A = right), (B = up ; B = down)).
:- pred line(string::in, list(direction)::out) is semidet.
line(Str, replicate(N, D)) :-
words(Str) = [DD, NN],
to_direction(DD, D),
to_int(NN, N).
:- pred move_head(direction::out, coord::in, coord::out) is multi.
move_head(up, c(X, Y), c(X, Y+1)).
move_head(down, c(X, Y), c(X, Y-1)).
move_head(left, c(X, Y), c(X-1, Y)).
move_head(right, c(X, Y), c(X+1, Y)).
:- pred ok(coord::in, coord::in) is semidet.
ok(A, B) :- abs(A^x - B^x) =< 1, abs(A^y - B^y) =< 1.
:- pred ok(rope::in) is semidet.
ok([]). ok([_]).
ok([A, B | Rest]) :- ok(A, B), ok([B | Rest]).
:- pred same_rc(coord::in, coord::in) is semidet.
same_rc(A, B) :- A^x = B^x ; A^y = B^y.
:- pred move_tail1(coord::in, coord::in, coord::out) is nondet.
move_tail1(H, !T) :- ok(H, !.T).
move_tail1(H, !T) :-
if same_rc(H, !.T) then
move_head(_, !T),
ok(H, !.T)
else
move_head(D1, !T),
move_head(D2, !T),
orth(D1, D2),
ok(H, !.T).
:- pred move_tail(coord::in, rope::in, rope::out) is nondet.
move_tail(_, [], []).
move_tail(H, [!.T | !.Ts], [!:T | !:Ts]) :-
move_tail1(H, !T),
move_tail(!.T, !Ts).
:- pred move(direction::in, rope::in, rope::out) is nondet.
move(_, [], []).
move(D, [!.H | !.T], [!:H | !:T]) :-
move_head(D, !H),
move_tail(!.H, !T).
:- type seen == set(coord).
:- pred next(direction, rope, rope, seen, seen).
:- mode next(in, in, out, in, out) is nondet.
next(D, !Rope, !Seen) :-
move(D, !Rope),
last(!.Rope, Tail),
insert(Tail, !Seen).
:- pred run0(part::in, lines::in, int::out) is cc_nondet.
run0(Part, Lines, Out) :-
Start = replicate(part(Part, 2, 10), c(0, 0)),
map(line, Lines, Dirs),
foldl2(next, condense(Dirs), Start, _, init, End),
count(End, Out).
run(Part, Lines, int(Out)) :-
disable_warning [no_solution_disjunct] (
not run0(Part, Lines, Out) => die("bad input")
).