show_hands(on).
card(tempura, 14).
card(sashimi, 14).
card(dumpling, 14).
card(roll-1, 6).
card(roll-2, 12).
card(roll-3, 8).
card(nigiri-egg, 5).
card(nigiri-salmon, 10).
card(nigiri-squid, 5).
card(pudding, 10).
card(wasabi, 6).
card(chopsticks, 4).
score(tempura, Hand, Score) :-
count(tempura, Hand, N),
Score is N // 2 * 5.
score(sashimi, Hand, Score) :-
count(sashimi, Hand, N),
Score is N // 3 * 10.
score(dumpling, Hand, Score) :-
count(dumpling, Hand, N),
( N >= 5 -> Score = 15
; nth0(N, [0,1,3,6,10], Score)
).
score(roll, Hands, Scores) :-
maplist(count(roll-1), Hands, R1),
maplist(count(roll-2), Hands, R2),
maplist(count(roll-3), Hands, R3),
maplist(multiply(2), R2, R2b),
maplist(multiply(3), R3, R3b),
sum_points([R1,R2b,R3b], R),
max2(R, Scores).
score(nigiri, Hand, Score) :-
count(nigiri-egg, Hand, E),
count(nigiri-salmon, Hand, S),
count(nigiri-squid, Hand, Q),
count(wasabi, Hand, W),
wasabi(Q, 3, W, 0, W1, Score1),
wasabi(S, 2, W1, Score1, W2, Score2),
wasabi(E, 1, W2, Score2, _, Score).
score(pudding, Hands, Scores) :-
maplist(count(pudding), Hands, P),
max_min(P, Scores).
score(Hands, Scores) :-
maplist(score(tempura), Hands, T),
maplist(score(sashimi), Hands, S),
maplist(score(dumpling), Hands, D),
score(roll, Hands, R),
maplist(score(nigiri), Hands, N),
sum_points([T,S,D,R,N], Scores).
wasabi(N, X, 0, S, 0, S1) :- S1 is S + N * X.
wasabi(N, X, W, S, W1, S1) :- W > N, W1 is W - N, S1 is S + N * X * 3.
wasabi(N, X, W, S, 0, S1) :- W =< N, S1 is S + W * X * 3 + (N - W) * X.
count(_, [], 0).
count(X, [X|Xs], N1) :- count(X, Xs, N), N1 is N + 1.
count(X, [Y|Ys], N) :- X \= Y, count(X, Ys, N).
max2(Xs, S) :-
sort(0, @>, Xs, [X,Y|_]),
count(X, Xs, Nx), count(Y, Xs, Ny),
( ( Nx > 1 ; Y =:= 0 ) -> K is 6 // Nx, subst(Xs, [X-K], S)
; K is 3 // Ny, subst(Xs, [X-6,Y-K], S)
).
max2(Xs, S) :-
sort(Xs, [X]),
length(Xs, N),
( X > 0 -> K is 6 // N ; K is 0 ),
make_list(K, N, S).
max_min(Xs, S) :-
sort(Xs, [X|Ys]), reverse(Ys, [Y|_]),
count(X, Xs, Nx), count(Y, Xs, Ny),
Kx is -6 // Nx, Ky is 6 // Ny,
subst(Xs, [X-Kx,Y-Ky], S).
max_min(Xs, S) :- sort(Xs, [_]), length(Xs, N), make_list(0, N, S).
random_scores(N, Scores) :-
all_cards(Cards),
shuffle(Cards, Cards0),
deal(Cards0, N, Hands1, Cards1),
score(Hands1, R1),
deal(Cards1, N, Hands2, Cards2),
score(Hands2, R2),
deal(Cards2, N, Hands3, _),
score(Hands3, R3),
append_hands([Hands1,Hands2,Hands3], Hands),
score(pudding, Hands, P),
( show_hands(on) ->
write('Round 1:'), nl,
maplist(show_hand, Hands1, R1),
write('Round 2:'), nl,
maplist(show_hand, Hands2, R2),
write('Round 3:'), nl,
maplist(show_hand, Hands3, R3),
write('Pudding scores: '),
write(P), nl
; true ),
sum_points([R1,R2,R3,P], Scores).
average_score(N, K, S) :-
sum_score(N, K, S0),
S is S0 / (N * K).
sum_score(_, 0, 0).
sum_score(N, K, S) :-
K > 0, K1 is K - 1,
sum_score(N, K1, S0),
random_scores(N, Scores),
sum_list(Scores, S1),
S is S0 + S1.
all_cards(Cards) :-
findall(L, (card(Type, N), make_list(Type, N, L)), Css),
append(Css, Cards).
deal(Cards, N, Hands, Remaining) :-
K is 12 - N, KN is K * N,
take(Cards, KN, Selected, Remaining),
distribute(Selected, K, Hands).
append_hands([X], X).
append_hands([X|Xs], Z) :- append_hands(Xs, Y), maplist(append, X, Y, Z).
add(X, Y, Z) :- Z is X + Y.
multiply(X, Y, Z) :- Z is X * Y.
sum_points([X], X).
sum_points([X|Xs], Z) :- sum_points(Xs, Y), maplist(add, X, Y, Z).
subst([], _, []).
subst([X|Xs], S, [N|Ys]) :- member(X-N, S), subst(Xs, S, Ys).
subst([X|Xs], S, [0|Ys]) :- \+ member(X-_, S), subst(Xs, S, Ys).
make_list(_, 0, []).
make_list(X, N, [X|L]) :- N > 0, N1 is N - 1, make_list(X, N1, L).
take(Xs, 0, [], Xs).
take([X|Xs], N, [X|Ys], Zs) :-
N > 0, N1 is N - 1,
take(Xs, N1, Ys, Zs).
distribute([], _, []).
distribute(Xs, N, [Ys|Yss]) :-
take(Xs, N, Ys, Zs),
distribute(Zs, N, Yss).
shuffle(Cards, Shuffled) :- random_permutation(Cards, Shuffled).
show_hand(Xs, Score) :-
sort(0, @=<, Xs, Sorted),
maplist(show, Sorted),
write(' -> '),
write(Score),
nl.
show(tempura) :- write('T ').
show(sashimi) :- write('S ').
show(dumpling) :- write('D ').
show(roll-N) :- write('R'), write(N), write(' ').
show(nigiri-egg) :- write('NE ').
show(nigiri-salmon) :- write('NS ').
show(nigiri-squid) :- write('NQ ').
show(pudding) :- write('P ').
show(wasabi) :- write('W ').
show(chopsticks) :- write('C ').