I am newbie to python and learning it from books, forums and developers. Recently, I tried to implement various hand-ranking categories in the poker program. The goal is to calculate probabilities and see if it agrees with theoretical poker hand probabilities?
Source: https://en.wikipedia.org/wiki/Poker_probability?oldformat=true
Please find below the code and logic I've used so far to build it. The code contains Card and Deck classes which together implement a deck of standard playing cards used, as well as a sample PyTest test function test_xxx().
So far, I've written hasOnePair, hasTwoPairs, hasThreeOfAKind, hasFullHouse and hasFourOfaKind() functions and it is working fine but I am struggling with Straight, flush, StraightFlush.
Could someone please suggest or give guideline about how to approach straight, flush, royalflush, straightflush cases? Also, any further suggestions to update this code will be great.
import random
SUITS = ["Clubs", "Diamonds", "Hearts", "Spades"]
RANKS = ["", "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"]
# here's two Python classes whose objects represent playing cards and decks thereof
class Card():
"""
Represents a single playing card,
whose rank internally is int _rank: 1..13 => "Ace".."King"
and whose suit internally is int _suit 0..3 => "Clubs".."Spades"
"""
def __init__(self, rank=1, suit=3): # this is the constructor!
'''
Initialize card with given int suit and int rank
:param rank:
:param suit:
:return:
'''
self._rank = rank
self._suit = suit
def __str__(self): # this is the "stringifier"
"""
Return the string name of this card:
"Ace of Spades": translates int fields to strings
:return:
"""
# "Ace of Spades" is string for self._rank==1, self._suit==3
toreturn = RANKS[self._rank] + " of " + SUITS[self._suit]
return toreturn
class Deck():
"""
Represents a deck of 52 standard playing cards,
as a list of Card refs
"""
def __init__(self): # constructor
"""
Initialize deck: field _cards is list containing
52 Card refs, initially
:return: nothing
"""
self._cards = []
for rank in range(1, 14):
for suit in range(4):
c = Card(rank, suit) # create next Card with given value
self._cards.append(c) # add it to this Deck
def __str__(self):
"""
"Stringified" deck: string of Card named,
with \n for easier reading
:return:
"""
toreturn = ''
# for index in range(len(self._cards)):
# self._cards[index]
for c in self._cards:
temp = str(c) # temp is the stringified card
toreturn = toreturn + temp + "\n" # note \n at end
return toreturn
def shuffle(self):
random.shuffle(self._cards) # note random function to do this
def dealCard(self):
toreturn = self._cards.pop(0) # get and remove top card from deck
return toreturn
def buildDict(hand):
dict = {}
for card in hand:
dict[card._rank] = dict.get(card._rank, 0) + 1
return dict
def hasOnePair(dict):
twocount = 0
threecount = 0
for v in dict.values():
if v == 2:
twocount += 1
elif v == 3:
threecount += 1
if twocount==1 and threecount != 1:
return True
else:
return False
def hasTwoPairs(dict):
twocount1 = 0
threecount1 = 0
for v in dict.values():
if v ==2:
twocount1 += 1
elif v == 3:
threecount1 +=1
if twocount1 == 2 and threecount1 != 1:
return True
else:
return False
def hasThreeOfAKind(dict):
twocount = 0
threecount = 0
for v in dict.values():
if v == 2:
twocount += 1
elif v == 3:
threecount += 1
if twocount != 1 and threecount == 1:
return True
else:
return False
def hasFullHouse(dict):
twocount = 0
threecount = 0
for v in dict.values():
if v == 2:
twocount += 1
elif v == 3:
threecount += 1
if twocount == 1 and threecount == 1:
return True
else:
return False
def hasFourOfAKind(dict):
fourcount = 0
onecount = 0
for v in dict.values():
if v ==4:
fourcount += 1
elif v == 1:
onecount +=1
if fourcount == 1 and onecount == 1:
return True
else:
return False
def hasStraight(hand):
return False
def hasFlush(dict):
return False
def hasStraightFlush(dict):
return False
def hasRoyalFlush(dict):
return False
def main():
TRIALS = 1000 # int(input ("Input number of hands to test: "))
hand = [] # list of Card in hand
# accumulators for different counts
onepairCount = 0
twopairCount = 0
threeCount = 0
fourCount = 0
fullHouseCount = 0
StraightCount = 0
for num in range(TRIALS):
# create new Deck and shuffle
d = Deck()
d.shuffle()
# initialize hand as empty list
hand = []
# deal top 5 cards of deck, adding to hand
for count in range(5):
hand.append(d.dealCard())
# build the dictionary of card ranks in hand
dict = buildDict(hand)
# use dictionary to make hand checking easier
if hasOnePair(dict):
onepairCount += 1
elif hasTwoPairs(dict):
twopairCount += 1
elif hasThreeOfAKind(dict):
threeCount += 1
elif hasFourOfAKind(dict):
fourCount += 1
elif hasFullHouse(dict):
fullHouseCount += 1
elif hasStraight(dict):
StraightCount +=1
# add more if needed...
# print out results...
print("Number of one pair hands is: ", onepairCount)
print("% of hands: ", 100.0 * onepairCount / TRIALS)
print("Number of two pair hands is: ", twopairCount)
print("% of hands: ", 100.0 * twopairCount / TRIALS)
print("Number of trips hand is: ", threeCount)
print("% of hands: ", 100.0 * threeCount / TRIALS)
print("Number of quads hand is: ", fourCount)
print("% of hands: ", 100.0 * fourCount / TRIALS)
print("Number of trips hand is: ", fullHouseCount)
print("% of hands: ", 100.0 * fullHouseCount / TRIALS)
print("Number of trips hand is: ", StraightCount)
print("% of hands: ", 100.0 * StraightCount / TRIALS)
def card_example():
card1 = Card() # Card(1,3) => Ace of Clubs
card2 = Card(12, 2) # Card (12,2) => Queen of Hearts
card1._newfield = 47 # we can add new fields to any Python object!
# three ways of printing a Card
#
print(card1.__str__()) # calling the methods against card
print(str(card2)) # type-casting
print(card2) # short-cut: passing obj ref to print does str() automagically
print(card1._newfield) # see the new field value?
print(card1._rank) # see the rank (1..13)
print(card1._suit) # see the suit (0..3)
def deck_example():
"""
Test Deck: create, print then shuffle, print again
Then deal first two cards and print, along with bottom card
"""
deck = Deck()
print(str(deck)) # see entire deck before shuffling
print("Now we shuffle:\n")
deck.shuffle()
print(str(deck)) # see entire deck after shuffling
card1 = deck.dealCard()
card2 = deck.dealCard()
print("The first card dealt is", str(card1), "and the second is", str(card2))
print("Bottom of deck is", deck._cards[-1]) # can't hide the implementation!
if __name__ == "__main__": # only run this if this .py is NOT imported
# pass
# card_example() # uncomment to test creating & calling Card methods
# deck_example() # uncomment to test Deck: create, print, shuffle, print
main() # uncomment to run general poker odds calculations
#
# -------------------------------------------------------------------------
#
#pytest follows...
def test_one_pair():
testhand = [Card(2, 3), Card(1, 2),
Card(3, 1), Card(13, 2),
Card(2, 0)]
dict = buildDict(testhand)
assert hasOnePair(dict) #hasTwoPairs
def test_two_pair():
testhand = [Card(2, 3), Card(1, 2),
Card(3, 1), Card(3, 2),
Card(2, 0)]
dict = buildDict(testhand)
assert hasTwoPairs(dict)
def test_three_pair():
testhand = [Card(1, 3), Card(1, 2),
Card(1, 1), Card(13, 2),
Card(2, 0)]
dict = buildDict(testhand)
assert hasThreeOfAKind(dict)
def has_Four_Of_A_Kind():
testhand = [Card(1, 3), Card(1, 2),
Card(1, 1), Card(1, 0),
Card(2, 0)]
dict = buildDict(testhand)
assert hasFourOfAKind(dict)
def test_full_house():
testhand = [Card(1, 3), Card(1, 2),
Card(1, 1), Card(13, 2),
Card(13, 2)]
dict = buildDict(testhand)
assert hasFullHouse(dict)
def test_Straight():
testhand = [Card(11, 1), Card(10, 3),
Card(9, 2), Card(8, 1),
Card(7, 3)]
dict = buildDict(testhand)
assert hasStraight(dict)