2

I am trying to write a predicate to analyse common poker hands; for example given a list of "cards" identify if the player has 4 of a kind; 3 of a kind; pair etc: My idea was to check for similar rank and remove if not:

this works for fourofakind(["A","J",10,"Q","A","A","A"])

but not all scenarios; any guidance on the logic here?

Thanks

false
  • 10,264
  • 13
  • 101
  • 209
Androider
  • 23
  • 4

3 Answers3

4

The problem is that you only check whether the first card in the hand appears four times in the set. You will need to do that for all cards.

I would introduce an auxiliary predicate that counts the number of cards you have seen, and let the main predicate iterate over the cards in the hand until you've found a set of four:

four([H|T]) :- four0(H,1,T), !. % find a set of four Hs
four([_|T]) :- four(T).         % else continue with remaining set

four0(_,4,_) :- !.                             % found four cards: stop
four0(X,I,[X|T]) :- !,I1 is I+1,four0(X,I1,T). % found another card: inc counter
four0(X,I,[_|T]) :- four0(X,I,T).              % else continue

If it wasn't for short lists you could improve it by, e.g., remembering what cards you already checked or removing them. It also would be much easier if the list was sorted to begin with.

BTW, you can simplify the nested list in your original first clause as [H,H,H,H], and in the second clause as [H1,H2|T]. It's easier on the eyes!

twinterer
  • 2,416
  • 1
  • 15
  • 13
  • Thanks, this helps a lot. My goal is to get stronger in my recursive thinking and your solution helps me just that! – Androider Dec 13 '11 at 14:47
3

Consider to put to good use the builtins: when you sort a list all elements get grouped, then check for a sequence become easy:

fourofakind(Hand) :-  % not intersted to what card is
 fourofakind(Hand, _).

fourofakind(Hand, C) :-
 msort(Hand, Sorted),
 append([_, [C,C,C,C], _], Sorted).

The predicate has 2 forms, the latter also provides the card code. Please use the msort call: using sort we lose duplicates...

CapelliC
  • 59,646
  • 5
  • 47
  • 90
2

As chac pointed out and to have again the debate we had in this post, you can use append to successfully parse your list once sorted quite easily. Without sorting, you could write :

fourofakind(Hand, X) :- append([_, [X], _, [X], _, [X], _, [X], _], Hand).

This basically tells prolog : I want my hand to have 4 times the sublist [X] with anything in-between.

Or, to use what @false describes as a very graphically appealing solution in his reply on the other thread (DCGs) :

four --> ..., [X], ..., [X], ..., [X], ..., [X], ... .

... --> [] | [_], ... .

?- Xs = "bacada", phrase(four, Xs).

You could too avoid using too many built-ins by doing the work with basic recursion :

three_of_a_kind(Item, [Item|Tail]) :- pair(Item, Tail).
three_of_a_kind(Item, [_Item|Tail]) :- three_of_a_kind(Item, Tail).

pair(Item, [Item|Tail]) :- one(Item, Tail).
pair(Item, [_NotItem|Tail]) :- pair(Item, Tail).

one(Item, [Item|_Tail]).
one(Item, [_NotItem|Tail]) :- one(Item, Tail).

Note that here one/2 is equivalent to the naive definition of member/2. I let you the task of adding four_of_a_kind/1 by looking at how three_of_a_kind/1 and pair/2 work ! Use of cut would be interesting too to remove unused choice points.

Community
  • 1
  • 1
m09
  • 7,490
  • 3
  • 31
  • 58
  • `append/2` is not available in all Prolog systems, and the OP didn't specify whether he uses SWI. – twinterer Dec 13 '11 at 11:54
  • true, if `append/2` isn't available, the msort solution is way more readable so I'd go with that I guess (if wanting to use append ofc !). – m09 Dec 13 '11 at 12:26
  • Thanks I like that this approach facilitates everything I need. I definitely need to get more familiar with the builtin's – Androider Dec 13 '11 at 14:45