# Checking for a full house

5 views

### mwdsmith

May 25, 2005, 9:43:44 PM5/25/05
to
Hi, I'm new to python, and I'm not sure if this is the place to post
this kind of question; so feel free to tell me if I should take this
elsewhere.

So, to start me off on python, I decided to put together a little
script to test the probabilities of rolling certain combinations of
dice. Below is my code for checking for a full house (when rolling
with 5 dice). A roll is a list, eg [1, 3, 5, 1, 4] (this example is
not a full house)

def removeAll(element, num2Rem, list):
l = list[:]
for num in range(0, num2Rem):
l.remove(element)
return l

def isfullHouse(roll):
for die in range(1,7):
if roll.count(die)==3:
l = removeAll(die, 3, roll)
if l[0]==l[1]:
return 1
return 0

My questions is this: is there a better way to do this? A way that's
more natural to python, or just more efficient perhaps?

ps. A roll of [1, 2, 1, 1, 2] is a full house (three of one kind and
two of another)

### Robert Kern

May 25, 2005, 9:58:32 PM5/25/05
to pytho...@python.org

def isFullHouse(roll):
counts = [roll.count(die) for die in range(1,7)]
counts.sort()
return counts == [0, 0, 0, 0, 2, 3]

--
Robert Kern
rk...@ucsd.edu

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter

### Tony Meyer

May 25, 2005, 10:00:40 PM5/25/05
to mwdsmith, pytho...@python.org
> def removeAll(element, num2Rem, list):
> l = list[:]
> for num in range(0, num2Rem):
> l.remove(element)
> return l
>
> def isfullHouse(roll):
> for die in range(1,7):
> if roll.count(die)==3:
> l = removeAll(die, 3, roll)
> if l[0]==l[1]:
> return 1
> return 0
>
> My questions is this: is there a better way to do this? A way that's
> more natural to python, or just more efficient perhaps?

This is an alternative that doesn't muck about making a second list:

def isfullHouse(roll):
has_pair = False
has_triple = False
for die in xrange(1,7):
if has_pair and has_triple:
return True
count = roll.count(die)
if count > 3:
return False # easy out; can't have four/five and full house
if count == 2:
has_pair = True
elif count == 3:
has_triple = True
return False

If you want it in one line (and have Python 2.4):

def isfullHouse(roll):
return len(set(roll)) != 2

If you create a set from the list, it will remove duplicates. If there are
3,4, or 5 different numbers, then you don't have a triplet. (I assume that
[1,1,1,1,1] isn't a full house, or this answer is wrong, since that will
have the length be 1).

(These are both untested).

=Tony.Meyer

### Robert Kern

May 25, 2005, 10:13:49 PM5/25/05
to pytho...@python.org
Tony Meyer wrote:

> def isfullHouse(roll):
> return len(set(roll)) != 2

[1, 1, 1, 1, 2] is not a full house.

### Roy Smith

May 25, 2005, 10:20:00 PM5/25/05
to
"mwdsmith" <realla...@yahoo.com> wrote:
> Hi, I'm new to python, and I'm not sure if this is the place to post
> this kind of question; so feel free to tell me if I should take this
> elsewhere.

Well, that depends. Are we doing your homework assignment for you? :-)

> So, to start me off on python, I decided to put together a little
> script to test the probabilities of rolling certain combinations of
> dice. Below is my code for checking for a full house (when rolling
> with 5 dice). A roll is a list, eg [1, 3, 5, 1, 4] (this example is
> not a full house)
>
> def removeAll(element, num2Rem, list):
> l = list[:]
> for num in range(0, num2Rem):
> l.remove(element)
> return l
>
> def isfullHouse(roll):
> for die in range(1,7):
> if roll.count(die)==3:
> l = removeAll(die, 3, roll)
> if l[0]==l[1]:
> return 1
> return 0
>
> My questions is this: is there a better way to do this? A way that's
> more natural to python, or just more efficient perhaps?

Well, I think I might do two trips through a dictionary. Something like
the following. The arguments to the two dict() constructors are list
comprehensions. I don't normally use them a lot, and I don't normally like
complicated expressions like this, but I tried a few ways, and this just
seems to be the most compact way by far to do the counting. So, even
though I'm not convinced this is the clearest way to write it, you did ask
for the most pythonic way, and I think this might be that :-)

#!/usr/bin/env python

def isFullHouse (roll):
# count how many of each value you have, i.e. how many 1's,
# how many 2's, etc
valueDict = dict ([(x, 0) for x in range(1,7)])
for die in roll:
valueDict[die] += 1

# count how many of each set size you have, i.e. how many
# voids, how many singletons, how many pairs, etc.
setDict = dict ([(x, 0) for x in range(0,6)])
for setSize in valueDict.values():
setDict[setSize] += 1

# A full house is one pair and one three-of-a-kind
return setDict[2] == 1 and setDict[3] == 1

print isFullHouse ([1, 1, 1, 2, 2]) == True
print isFullHouse ([1, 1, 1, 1, 1]) == False
print isFullHouse ([1, 2, 3, 4, 5]) == False
print isFullHouse ([3, 4, 3, 3, 4]) == True
print isFullHouse ([1, 2, 1, 1, 1]) == False

### Tony Meyer

May 25, 2005, 10:38:23 PM5/25/05
to Robert Kern, pytho...@python.org
[Tony Meyer]

>> def isfullHouse(roll):
>> return len(set(roll)) != 2

[Robert Kern]

> [1, 1, 1, 1, 2] is not a full house.

Opps. I did say it was untested (that should have been == not !=, too).

def isfullHouse(roll):
return len(set(roll)) == 2 and roll.count(min(roll)) != 1 and
roll.count(max(roll)) != 1

Although your solution looks a lot nicer than this (though it may use more
memory, and might be slower).

=Tony.Meyer

### Robert Kern

May 25, 2005, 11:11:11 PM5/25/05
to pytho...@python.org

It *is* slower as written, although a more optimized version using
itertools and sets (left as an exercise for the bored) gets close to
yours. I don't think memory is an issue with 5-element lists of small
integers.

### Raymond Hettinger

May 26, 2005, 1:39:10 AM5/26/05
to
[mwdsmith]

> Below is my code for checking for a full house (when rolling
> with 5 dice).

There are many ways to do this. Here's one:

.def isfullHouse(hand):
. counts = [hand.count(card) for card in set(hand)]
. return (3 in counts) and (2 in counts) and len(hand)==5

And here's a one-liner:

.def isfullHouse(hand):
. return sorted(hand.count(card) for card in set(hand)) == [2,3]

Raymond Hettinger

### Raymond Hettinger

May 26, 2005, 1:56:14 AM5/26/05
to
Your use case for gathering roll statistics probably needs a more
general solution:

hands = {
(1,1,1,1,1): 'nothing',
(1,1,1,2): 'one pair',
(1,2,2): 'two pair',
(1,1,3): 'three of a kind',
(2,3): 'full house',
(1,4): 'four of a kind',
(5): 'flush (five of a kind)'
}

def eval_hand(hand):
counts = tuple(sorted(hand.count(card) for card in set(hand)))
return hands.get(counts, 'misdeal')

Raymond Hettinger

### Paul Rubin

May 26, 2005, 2:21:25 AM5/26/05
to

1. "Flush" means 5 cards of the same suit (i.e. all hearts), not 5 of
a kind.

2. That code doesn't detect flushes or straights.

### Raymond Hettinger

May 26, 2005, 2:56:25 AM5/26/05
to
[Paul Rubin]

> 1. "Flush" means 5 cards of the same suit (i.e. all hearts), not 5 of
> a kind.

More importantly, the (5) should be (5,). Also the poker terminology
should be expressed in terms of dice rolls (the OP's use case).

> 2. That code doesn't detect flushes or straights.

It also doesn't cook hamburgers, drink beer, play chess, or anything
else unrelated to the OP's use case.

### flupke

May 26, 2005, 3:08:49 AM5/26/05
to

It would be interesting to see how complicated it would get to write
code to detect the correct hands in poker with wildcards included.
I tried that a year or 2 ago when i wanted to write a pokergame in java
but i failed miserably in doing so.

I have a gut feeling that with python this is easier. I might give it
another go. Is there any of such code available somewhere?
The reason i wanted to do so is that there isn't a game that allows a
player to specify it's own games. I wanted to make a simple network
enabled game with simple graphics and scriptable poker games via a wizard.
But as i said, i first wanted to construct my "card" code to deal,
evaluate a hand and compare 2 or more hands, and to find the highest
hand in a number of hands. And all this with wildcards, cards in the
hands varying from 5 to 7 and high low. I toyed with on several
occasions but never could seem to find an easy way to do it.

Benedict

### Raymond Hettinger

May 26, 2005, 5:32:31 AM5/26/05
to
[Benedict]

>> It would be interesting to see how complicated it would get to write
>> code to detect the correct hands in poker with wildcards included.

There is an interesting SF project with code written in C:
http://pokersource.sourceforge.net/

In contrast, Python makes short work of these kind of problems. Here
are some primitives to get you started (just add a representation for
suits, flush detection, and controlling logic for ranking hands):

def is_straight(hand, numwildcards=0):
"""Checks for a five card straight

Inputs: list of non-wildcards plus wildcard count
2,3,4, ... 10, 11 for Jack, 12 for Queen,
13 for King, 14 for Ace
Hand can be any length (i.e. it works for seven card games).

Outputs: highest card in a five card straight
or 0 if not a straight.
Original list is not mutated.
Ace can also be a low card (i.e. A2345).

>>> is_straight([14,2,3,4,5])
5
>>> is_straight([14,2,3,4,6])
0
>>> is_straight([10,11,12,13,14])
14
>>> is_straight([2,3,5], 2)
6
>>> is_straight([], 5)
14
>>> is_straight([2,4,6,8,10], 3)
12
>>> is_straight([2,4,4,5,5], 2)
6
"""

hand = set(hand)
if 14 in hand:
for low in (10,9,8,7,6,5,4,3,2,1):
needed = set(range(low, low+5))
if len(needed & hand) + numwildcards >= 5:
return low+4
return 0

def groups(hand, numwildcards=0):
"""Checks for pairs, threes-of-kind, fours-of-a-kind,
and fives-of-a-kind

Inputs: list of non-wildcards plus wildcard count
2,3,4, ... 10, 11 for Jack, 12 for Queen,
13 for King, 14 for Ace
Hand can be any length (i.e. it works for seven card games)
Output: tuple with counts for each value (high cards first)
for example (3, 14), (2, 11) full-house Aces over Jacks
for example (2, 9), (2, 7) two-pair Nines and Sevens
Maximum count is limited to five (there is no seven of a kind).
Original list is not mutated.

>>> groups([11,14,11,14,14])
[(3, 14), (2, 11)]
>>> groups([7, 9, 10, 9, 7])
[(2, 9), (2, 7)]
>>> groups([11,14,11,14], 1)
[(3, 14), (2, 11)]
>>> groups([9,9,9,9,8], 2)
[(5, 9), (2, 8)]
>>> groups([], 7)
[(5, 14), (2, 13)]
"""

result = []
counts = [(hand.count(v), v) for v in range(2,15)]
for c, v in sorted(counts, reverse=True):
newcount = min(5, c + numwildcards) # Add wildcards upto five
numwildcards -= newcount - c # Wildcards remaining
if newcount > 1:
result.append((newcount, v))
return result

import doctest
print doctest.testmod()

### flupke

May 26, 2005, 8:43:00 AM5/26/05
to

Wow Raymond that's amazing !
Makes we feel stupid, happy and in awe of python at the same time.
I only felt this weird before when i got married :)

I will have a good look at your code. Excellent :)

Regards,
Benedict Verheyen