'Get a distribution of a list in prolog
I would like to calcul the distribution of a card hand in prolog. It means get this result:
?- distribution([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6],D).
D=[[spade,3],[heart,3],[diamond,4],[club,3]]
But I don't know how to procede. I am able to get each color one by one, using this script:
distribution([], []).
distribution([H|T], [E|D]):-
atom_chars(H, X),
nth0(0, X, E),
(
E == 's' -> distribution(T, D);
E == 'h' -> distribution(T, D);
E == 'd' -> distribution(T, D);
E == 'c' -> distribution(T, D)
).
Can anyone help me? Thanks
Solution 1:[1]
I came up with [edit, much better with clumped from slago's answer]:
card_suite(Card, Suite) :- % from s5
atom_concat(Char, _, Card), % to s + 5 = s5
select(Char-Suite, [s-spade, h-heart, d-diamond, c-club], _). % to spade
cards_distribution(Cards, Distribution) :- % from [s5, c7, s2]
sort(0, @=<, Cards, SortedCards), % to [c7, s2, s5]
maplist(card_suite, SortedCards, Suites), % to [club, spade, spade]
clumped(Suites, Distribution). % to [club-1, spade-2]
e.g.
?- cards_distribution([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6,s8],D).
D = [club-3, diamond-4, heart-3, spade-4]
Related logically pure group counting code.
Solution 2:[2]
A possible solution is:
distribution(Cards, Distribution) :-
distribution(Cards, [], Distribution).
distribution([], Distribution, Distribution).
distribution([Card|Cards], Accumulator, Distribution) :-
suit(Card, Suit),
update(Accumulator, Suit, NewAccumulator),
distribution(Cards, NewAccumulator, Distribution).
suit(Card, Suit) :-
atom_chars(Card, [First|_]),
text(First, Suit).
text(c, club).
text(s, spade).
text(h, heart).
text(d, diamond).
update([], Suit, [[Suit,1]]).
update([[S,N]|Rest], Suit, Distribution) :-
( S = Suit
-> succ(N, N1),
Distribution = [[S,N1]|Rest]
; Distribution = [[S,N]|Rest1],
update(Rest, Suit, Rest1) ).
Example:
?- distribution([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6],D).
D = [[spade, 3], [heart, 3], [diamond, 4], [club, 3]].
Remark In Prolog, it is more idiomatic to represent a pair [x,y] as a term of the form x-y.
Using swi-prolog built-in predicates, another possible solution is:
distribution2(Cards, Distribution) :-
maplist(suit, Cards, Suits),
msort(Suits, SortedSuits),
clumped(SortedSuits, Distribution).
Example:
?- distribution2([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6],D).
D = [club-3, diamond-4, heart-3, spade-3].
Solution 3:[3]
Why not represent cards using compound terms?
Consider the queen of hearts (?): hq as an atom, h(q) as a compound.
This makes thing a lot easier: no need to sort and everything stays purely relational.
cards_([],[],[],[],[]). cards_([Card|Cards],Cs,Ds,Hs,Ss) :- card_(Card,Cards,Cs,Ds,Hs,Ss). card_(c(X),Cards,[X|Cs],Ds,Hs,Ss) :- cards_(Cards,Cs,Ds,Hs,Ss). card_(d(X),Cards,Cs,[X|Ds],Hs,Ss) :- cards_(Cards,Cs,Ds,Hs,Ss). card_(h(X),Cards,Cs,Ds,[X|Hs],Ss) :- cards_(Cards,Cs,Ds,Hs,Ss). card_(s(X),Cards,Cs,Ds,Hs,[X|Ss]) :- cards_(Cards,Cs,Ds,Hs,Ss).
Sample query for getting the respective counts:
?- Cards = [s(q),s(9),s(8),h(a),h(k),h(5),d(a),d(k),d(j),d(4),c(a),c(7),c(6)], cards_(Cards,Clubs,Diamonds,Hearts,Spades), length(Clubs,N_Clubs), length(Diamonds,N_Diamonds), length(Hearts,N_Hearts), length(Spades,N_Spades). Cards = [s(q),s(9),s(8),h(a),h(k),h(5),d(a),d(k),d(j),d(4),c(a),c(7),c(6)], Clubs = [a,7,6], Diamonds = [a,k,j,4], Hearts = [a,k,5], Spades = [q,9,8], N_Clubs = 3, N_Diamonds = 4, N_Hearts = 3, N_Spades = 3.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | repeat |
