:- use_module(library(assoc)).
:- use_module(library(between)).
:- use_module(library(charsio)).
:- use_module(library(clpz)).
:- use_module(library(dcgs)).
:- use_module(library(debug)).
:- use_module(library(dif)).
:- use_module(library(iso_ext)).
:- use_module(library(lists)).
:- use_module(library(pairs)).
:- use_module(library(pio)).
:- use_module(library(reif)).
:- use_module(library(time)).
:- use_module('../helpers').
part_one(File, Solution) :-
once(phrase_from_file(input(Ws, Parts), File)),
list_to_assoc(Ws, Workflows),
parts_accepted(Workflows, Parts, Accepted),
maplist(part_score, Accepted, Scores),
sum_list(Scores, Solution).
% 377025 correct answer.
part_two(File, Solution) :-
once(phrase_from_file(input(Ws, _), File)),
list_to_assoc(Ws, Workflows),
findall(S, gen_solution(Workflows, S), Ss),
sum_list(Ss, Solution).
% 135506683246673 correct answer.
gen_solution(Workflows, Combinations) :-
[X,M,A,S] ins 1..4000,
part_accepted(Workflows, part(X,M,A,S), true),
maplist(fd_size, [X,M,A,S], Sizes),
foldl(foldl_mul, Sizes, 1, Combinations).
foldl_mul(X, M0, M) :- M is X * M0.
parts_accepted(Workflows, Parts, Accepted) :-
tfilter(part_accepted(Workflows), Parts, Accepted).
part_accepted(Workflows, Part, T) :-
get_assoc("in", Workflows, Rs),
part_workflow_result(Workflows, Part, Rs, T).
part_workflow_result(_, _, [accept|_], true).
part_workflow_result(_, _, [reject|_], false).
part_workflow_result(Workflows, Part, [send(W)|_], T) :-
get_assoc(W, Workflows, Rs1),
part_workflow_result(Workflows, Part, Rs1, T).
part_workflow_result(Workflows, Part, [condition(C, R, N, A)|Rs], T) :-
part_rating(R, Part, X),
if_(condition_t(C, X, N),
part_workflow_result(Workflows, Part, [A], T),
part_workflow_result(Workflows, Part, Rs, T)).
condition_t(<, X, N, T) :- #X #< #N #<==> #B, bit_t(B, T).
condition_t(>, X, N, T) :- #X #> #N #<==> #B, bit_t(B, T).
bit_t(0, false).
bit_t(1, true).
part_rating(x, part(X,_,_,_), X).
part_rating(m, part(_,M,_,_), M).
part_rating(a, part(_,_,A,_), A).
part_rating(s, part(_,_,_,S), S).
part_score(part(X,M,A,S), R) :-
R is X + M + A + S.
input(Ws, Ps) --> workflows(Ws), newline, newline, parts(Ps), whites.
workflows([W|Ws]) --> workflow(W), workflows_r(Ws).
workflows_r(Ws) --> newline, workflows(Ws).
workflows_r([]) --> [].
workflow(As-Rs) --> letters(As), "{", rules(Rs), "}".
rules([R|Rs]) --> rule(R), rules_r(Rs).
rules_r(Rs) --> ",", rules(Rs).
rules_r([]) --> [].
rule(A) --> action(A).
rule(condition(<, R, N, A)) --> category(R), "<", integer(N), ":", action(A).
rule(condition(>, R, N, A)) --> category(R), ">", integer(N), ":", action(A).
action(send(As)) --> chars(As, lower).
action(accept) --> "A".
action(reject) --> "R".
parts([P|Ps]) --> part(P), parts_r(Ps).
parts_r(Ps) --> newline, parts(Ps).
parts_r([]) --> [].
part(part(X,M,A,S)) --> "{x=", integer(X), ",m=", integer(M), ",a=", integer(A), ",s=", integer(S), "}".
category(x) --> "x".
category(m) --> "m".
category(a) --> "a".
category(s) --> "s".