aoc2022/day5.m

104 lines
3.1 KiB
Mathematica

:- module day5.
:- 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 maybe.
:- type parser(T) == (pred(T, chars, chars)).
:- mode parser == (pred(out, in, out) is semidet).
:- pred many(parser(T)::parser, list(T)::out, chars::in, chars::out) is semidet.
many(P, Out) --> if P(X) then many(P, Xs), {Out = [X | Xs]} else {Out = []}.
:- pred spaces(chars::in, chars::out) is det.
spaces --> if [' '] then spaces else [].
:- pred word0(string::in, chars::in, chars::out) is semidet.
word0(Str, In, Out) :- append(to_char_list(Str), Out, In).
:- pred word(string::in, chars::in, chars::out) is semidet.
word(Str) --> word0(Str), spaces.
:- pred number(int::out, chars::in, chars::out) is semidet.
:- use_module day4.
number(N) --> day4.number(N), spaces.
:- type slot == maybe(char).
:- pred slot(slot::out, chars::in, chars::out) is semidet.
slot(Res) -->
(if ['[', C, ']'] then {Res = yes(C)}
else [' ', ' ', ' '], {Res = no}),
([' '] ; =([])).
:- type instr == {int, int, int}.
:- pred instr(instr::out, chars::in, chars::out) is semidet.
instr({Count, From, To}) -->
word("move"), number(Count),
word("from"), number(From),
word("to"), number(To).
:- func transpose(list(list(T))) = list(list(T)) is semidet.
transpose([]) = [].
transpose([Xs]) = map(func(Z) = [Z], Xs).
transpose([Xs1, Xs2 | Xss]) =
map_corresponding(cons, Xs1, transpose([Xs2 | Xss])).
:- type tower == list(char).
:- type towers == list(tower).
:- pred lines(parser(T), list(T), lines, lines).
:- mode lines(parser, out, in, out) is semidet.
lines(P, Out) -->
if [Line], {P(X, to_char_list(Line), [])}
then lines(P, Xs), {Out = [X | Xs]}
else {Out = []}.
:- pred input(towers, list(instr), lines, lines).
:- mode input(out, out, in, out) is semidet.
input(Towers, Insts) -->
lines(many(slot), Slots), [_, _], lines(instr, Insts),
{map(filter_map(maybe_is_yes), transpose(Slots), Towers)}.
:- pred take_n(int::in, int::in, tower::out, towers::in, towers::out) is det.
take_n(Which, Count, Res, [T0 | Ts0], [T1 | Ts1]) :-
if Which = 1 then
det_split_list(Count, T0, Res, T1), Ts0 = Ts1
else
take_n(Which - 1, Count, Res, Ts0, Ts1), T0 = T1.
take_n(_, _, _, [], _) :- die("take_n: not enough lists").
:- pred put_n(part::in, int::in, tower::in, towers::in, towers::out) is det.
put_n(Part, Which, New, [T0 | Ts0], [T1 | Ts1]) :-
if Which = 1 then
(if Part = one then Onto = reverse(New) else Onto = New),
T1 = Onto ++ T0, Ts0 = Ts1
else
put_n(Part, Which - 1, New, Ts0, Ts1), T0 = T1.
put_n(_, _, _, [], _) :- die("put_n: not enough lists").
:- pred interp(part::in, instr::in, towers::in, towers::out) is det.
interp(Part, {Count, From, To}) -->
take_n(From, Count, Res), put_n(Part, To, Res).
:- pragma no_determinism_warning(run/3).
run(Part, Lines, string(Out)) :-
if input(Towers, Insts, Lines, []) then
foldl(interp(Part), Insts, Towers, Res),
from_char_list(map(det_head, Res), Out)
else
die("bad file").