diff --git a/19/part2.pl b/19/part2.pl new file mode 100644 index 0000000..6131250 --- /dev/null +++ b/19/part2.pl @@ -0,0 +1,75 @@ +:- use_module(library(pio)). +:- use_module(library(dcg/basics)). +:- initialization(main, main). + +main([FileName|_]) :- + input(FileName, Workflows), + findall(Limit, (wf(Workflows, in, Route, Limit), write(Route), write(" - "), write(Limit), nl), Limits), + or_limits(Limits, NewLimit), + write(NewLimit), nl, + choices(NewLimit, Answer), + write(Answer), nl. + +choices(Limit, N) :- + foldl([_-Min-Max, V0, V]>>(V is V0*(Max - Min + 1)), Limit, 1, N). + +or_limits([Limit], Limit). +or_limits([Limit1, Limit2 | Limits], Limit) :- + findall( + Attr-Min-Max, + ( member(Attr-Min1-Max1, Limit1), member(Attr-Min2-Max2, Limit2), + Min is min(Min1, Min2), Max is max(Max1, Max2)), + NewLimit), + or_limits([NewLimit|Limits], Limit). + + +wf(_, accept, [accept], [x-1-4000, m-1-4000, a-1-4000, s-1-4000]). +wf(Workflows, WorkflowName, [WorkflowName|Route], Limits) :- + \+ WorkflowName = accept, \+ WorkflowName = reject, + member(WorkflowName-Rules, Workflows), + with_rule(Workflows, Rules, Route, Limits). + +with_rule(Workflows, [EndRule], Route, Limits) :- + wf(Workflows, EndRule, Route, Limits). +with_rule(Workflows, [Attr-Cond-N-Dest|Rules], Route, NewLimits) :- + % Either take the first route with its limit + ( wf(Workflows, Dest, Route, Limits), + member(Attr-Min-Max, Limits), + combine(Min-Max, Cond-N, NewMin-NewMax) + % ...or skip the first route, given we satisfy its reverse limits + ; with_rule(Workflows, Rules, Route, Limits), + member(Attr-Min-Max, Limits), + negate(Cond-N, NotCond-NotN), + combine(Min-Max, NotCond-NotN, NewMin-NewMax) + ), + select(Attr-Min-Max, Limits, Attr-NewMin-NewMax, NewLimits). + +negate('<'-N, '>'-NewN) :- NewN is N - 1. +negate('>'-N, '<'-NewN) :- NewN is N + 1. +combine(Min-Max, '<'-N, Min-NewMax) :- NewMax is min(N-1, Max), Min < NewMax. +combine(Min-Max, '>'-N, NewMin-Max) :- NewMin is max(N+1, Min), NewMin < Max. + +% input parsing stuff below +input(FileName, Workflows) :- + phrase_from_file((workflows(Workflows), remainder(_)), FileName). + +workflows([]) --> "\n", !. +workflows([Name-Rules|Ws]) --> + string_without("{", NameStr), "{", rules(Rules), "}\n", workflows(Ws), + {atom_codes(Name, NameStr)}. + +rules([End]) --> dest(End). +rules([Rule|Rules]) --> rule(Rule), ",", rules(Rules). + +rule(Attr-Cond-N-Dest) --> attr(Attr), cond(Cond), number(N), ":", dest(Dest). + +attr(x) --> "x". +attr(m) --> "m". +attr(a) --> "a". +attr(s) --> "s". +cond('>') --> ">". +cond('<') --> "<". +dest(reject) --> "R", !. +dest(accept) --> "A", !. +dest(Dest) --> endrule(Dest). +endrule(Rule) --> string_without(",}", RuleStr), {atom_codes(Rule, RuleStr)}.