diff --git a/aoc.m b/aoc.m index 99f7a3e..2e96b18 100644 --- a/aoc.m +++ b/aoc.m @@ -44,6 +44,7 @@ run_day(Day, Part, Lines, Out) :- :- import_module day4. :- import_module day5. :- import_module day6. +:- import_module day7. :- pred solution(int::in, sol::out(sol)) is semidet. solution(1, day1.run). @@ -52,3 +53,4 @@ solution(3, day3.run). solution(4, day4.run). solution(5, day5.run). solution(6, day6.run). +solution(7, day7.run). diff --git a/day7.m b/day7.m new file mode 100644 index 0000000..9aff4a0 --- /dev/null +++ b/day7.m @@ -0,0 +1,80 @@ +:- module day7. +:- interface. +:- import_module basics. +:- import_module univ. + +:- pred run(part::in, lines::in, univ::out) is cc_multi. + +:- implementation. +:- import_module int. +:- import_module string. +:- import_module list. + + +:- type words == list(string). + +:- type file ---> file(name :: string, size :: int). +:- type files == list(file). + +:- type dir ---> + dir(name :: string, + immed :: int, total :: int, + files :: files, dirs :: dirs). +:- type dirs == list(dir). +:- func total(dir) = int. + +:- pred line(words::out, lines::in, lines::out) is semidet. +line(words(Line)) --> [Line]. + +:- pred dir(dir::out, lines::in, lines::out) is nondet. +dir(dir(Name, Immed, Total, Files, Dirs)) --> + cd_down(Name), + ["$ ls"], files(Files), + {Immed = foldl(func(File, Acc) = size(File) + Acc, Files, 0)}, + dirs(Dirs), + {Total = foldl(func(Dir, Acc) = total(Dir) + Acc, Dirs, Immed)}, + (["$ cd .."] ; []). + +:- pred cd_down(string::out, lines::in, lines::out) is semidet. +cd_down(D) --> line(["$", "cd", D]). + +:- pred files(files::out, lines::in, lines::out) is multi. +files([file(Name, Size) | Files]) --> + line([SSize, Name]), {to_int(SSize, Size)}, + files(Files). +files(Files) --> + line([Dir, _]), {Dir = "dir"}, + files(Files). +files([]) --> []. + +:- pred dirs(dirs::out, lines::in, lines::out) is multi. +dirs([D | Ds]) --> dir(D), dirs(Ds). +dirs([]) --> []. + +:- pred find(pred(dir)::(pred(in) is semidet), dir::in, list(dir)::out) is det. +find(P, Dir, Out) :- + map(find(P), dirs(Dir), Outs), + Children = condense(Outs), + (if P(Dir) then Out = [Dir | Children] else Out = Children). + +:- pred size_lte(int::in, dir::in) is semidet. +size_lte(Size, Dir) :- total(Dir) =< Size. + +:- pred size_gte(int::in, dir::in) is semidet. +size_gte(Size, Dir) :- total(Dir) >= Size. + +:- func sort_size(list(dir)) = list(dir). +sort_size(Dirs) = sort(func(D1, D2) = ordering(total(D1), total(D2)), Dirs). + +run(one, Lines, univ(Out)) :- + if dir(Top, Lines, []) then + find(size_lte(100_000), Top, Found), + Out = sum(map(total, Found)) + else + die("bad input"). +run(two, Lines, univ(Out)) :- + if dir(Top, Lines, []) then + find(size_gte(total(Top) - 40_000_000), Top, Found), + Out = det_head(sort(map(total, Found))) + else + die("bad input").