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