diff --git a/day7.m b/day7.m index 9aff4a0..ab36eb8 100644 --- a/day7.m +++ b/day7.m @@ -10,42 +10,27 @@ :- 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 dir ---> dir(size :: int, dirs :: dirs). +:- func size(dir) = int. :- 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)}, +dir(dir(Size, Dirs)) --> + [Cd], {append("$ cd ", _, Cd)}, + ["$ ls"], + files(Files), dirs(Dirs), - {Total = foldl(func(Dir, Acc) = total(Dir) + Acc, Dirs, Immed)}, + {Size = Files + sum(map(size, Dirs))}, (["$ 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 files(int::out, lines::in, lines::out) is multi. +files(Size + Rest) --> + [Line], {to_int(head(words(Line)), Size)}, + files(Rest). +files(Size) --> + [Dir], {append("dir ", _, Dir)}, + files(Size). +files(0) --> []. :- pred dirs(dirs::out, lines::in, lines::out) is multi. dirs([D | Ds]) --> dir(D), dirs(Ds). @@ -53,28 +38,20 @@ 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), + 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. +size_lte(Size, Dir) :- size(Dir) =< Size. :- pred size_gte(int::in, dir::in) is semidet. -size_gte(Size, Dir) :- total(Dir) >= Size. +size_gte(Size, Dir) :- size(Dir) >= Size. -:- func sort_size(list(dir)) = list(dir). -sort_size(Dirs) = sort(func(D1, D2) = ordering(total(D1), total(D2)), Dirs). +:- pred go(part::in, dir::in, int::out) is det. +go(one, Top, sum(map(size, Found))) :- + find(size_lte(100_000), Top, Found). +go(two, Top, det_head(sort(map(size, Found)))) :- + find(size_gte(size(Top) - 40_000_000), Top, Found). -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"). +run(Part, Lines, univ(Out)) :- + if dir(Top, Lines, []) then go(Part, Top, Out) else die("bad input").