Checking for a full house

M

mwdsmith

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)
 
R

Robert Kern

mwdsmith said:
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)

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
(e-mail address removed)

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

Roy Smith

mwdsmith said:
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
 
R

Raymond Hettinger

[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
 
R

Raymond Hettinger

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
 
P

Paul Rubin

Raymond Hettinger said:
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')

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.
 
R

Raymond Hettinger

[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.
 
F

flupke

Paul said:
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.

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
 
R

Raymond Hettinger

[Benedict]
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:
hand.add(1)
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()
 
F

flupke

Raymond said:
[Benedict]


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:
hand.add(1)
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()

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
 
M

mwdsmith

Wow... This is amazing, I didn't know there were so many way's of
doing this! Thank you every one for all your suggestions. I
particularly like the sorting counting ones.

And no, Roy, this isn't a home work assignment, at the moment all of
those contain php and java RMI. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,240
Messages
2,571,211
Members
47,845
Latest member
vojosay

Latest Threads

Top