diff --git a/aoc.m b/aoc.m index 9bd6492..c5df9e4 100644 --- a/aoc.m +++ b/aoc.m @@ -25,7 +25,7 @@ main2(!IO) :- ) else die("expected day, part, filename")). -:- type sol == (pred(part, list(string), univ)). +:- type sol == (pred(part, lines, univ)). :- inst sol == (pred(in, in, out) is cc_multi). :- pred solution(int::in, sol::out(sol)) is semidet. @@ -37,8 +37,10 @@ solution(2, day2.run). solution(3, day3.run). :- import_module day4. solution(4, day4.run). +:- import_module day5. +solution(5, day5.run). -:- pred run_day(int, part, list(string), univ). +:- pred run_day(int, part, lines, univ). :- mode run_day(in, in, in, out) is cc_multi. run_day(Day, Part, Lines, Out) :- if solution(Day, P) then diff --git a/basics.m b/basics.m index b28338b..345b485 100644 --- a/basics.m +++ b/basics.m @@ -11,11 +11,13 @@ :- import_module list. :- import_module string. +:- type lines == list(string). + :- pred die(string::in) is erroneous. :- pred die(string::in, list(poly_type)::in) is erroneous. :- pred need(io.res(T)::in, T::out) is det. -:- pred read_lines_need(string::in, list(string)::out, io::di, io::uo) is det. +:- pred read_lines_need(string::in, lines::out, io::di, io::uo) is det. :- pred wrap_main(pred(io, io), io, io). diff --git a/day1.m b/day1.m index 3676d28..851ea34 100644 --- a/day1.m +++ b/day1.m @@ -1,12 +1,12 @@ :- module day1. :- interface. :- import_module basics. -:- import_module list. :- import_module univ. -:- pred run(part::in, list(string)::in, univ::out) is cc_multi. +:- pred run(part::in, lines::in, univ::out) is cc_multi. :- implementation. +:- import_module list. :- import_module std_util. :- import_module maybe. :- import_module string. @@ -16,7 +16,7 @@ run(one, Lines, univ(det_head(go(Lines)))). run(two, Lines, univ(sum(take_upto(3, go(Lines))))). -:- func go(list(string)) = list(int). +:- func go(lines) = list(int). go(Lines) = sort(converse(ordering), map(sum, gather(map(to_int_may, Lines)))). diff --git a/day4.m b/day4.m index 3e29d45..97cc7a1 100644 --- a/day4.m +++ b/day4.m @@ -1,19 +1,20 @@ :- module day4. :- interface. :- import_module basics. -:- import_module list. :- import_module univ. -:- pred run(part::in, list(string)::in, univ::out) is cc_multi. +:- pred run(part::in, lines::in, univ::out) is cc_multi. + +:- pred number(int::out, chars::in, chars::out) is semidet. :- implementation. :- import_module int. :- import_module char. :- import_module string. +:- import_module list. :- type ranges ---> r(int, int, int, int). -:- 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. @@ -41,8 +42,7 @@ all_overlap(r(A, B, C, D)) :- C >= A, D =< B ; D >= B, C =< A. any_overlap(r(A, B, C, D)) :- A =< C, B >= C ; C =< A, D >= A. -:- pred go(pred(ranges)::(pred(in) is semidet), - list(string)::in, int::out) is det. +:- pred go(pred(ranges)::(pred(in) is semidet), lines::in, int::out) is det. go(P, Lines, Out) :- map(ranges, Lines, Ranges), filter(P, Ranges, Overlaps), diff --git a/day5.m b/day5.m new file mode 100644 index 0000000..a8e0342 --- /dev/null +++ b/day5.m @@ -0,0 +1,104 @@ +:- module day5. +:- interface. +:- import_module basics. +:- import_module univ. + +:- pred run(part::in, lines::in, univ::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, univ(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").