3

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

false
  • 10,264
  • 13
  • 101
  • 209

3 Answers3

2

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.
repeat
  • 18,496
  • 4
  • 54
  • 166
1

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].
slago
  • 5,025
  • 2
  • 10
  • 23
  • Thank you so much! It worked! Just one question: NewAccumulator contains the rest of the distribution? – nathanel nakache Apr 11 '22 at 13:43
  • 1
    @nathanelnakache Initially `NewAccumulator` is the empty list (i.e., empty distribution). During the recursive process, it is updated. At the end of this process, it is the computed distribution. – slago Apr 11 '22 at 14:33
  • `clumped` :-| That's what I was searching for with group_by and count. – TessellatingHeckler Apr 11 '22 at 16:53
0

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.

TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87