Last active
July 2, 2016 07:32
-
-
Save McSinyx/5ecd6d90e9866617e136b89713fcd23d to your computer and use it in GitHub Desktop.
Probabilities of Poker hands
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
This module contains code from | |
Think Python by Allen B. Downey | |
http://thinkpython.com/code/Card.py | |
Copyright 2016 Raphael McSinyx | |
Copyright 2012 Allen B. Downey | |
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html | |
""" | |
import random | |
from itertools import product | |
from functools import total_ordering | |
@total_ordering | |
class Card(): | |
""" | |
Represents a standard playing card. | |
Attributes: | |
suit: int from 0 to 3 | |
rank: int 1 to 13 | |
""" | |
suit_names = ('Clubs', 'Diamonds', 'Hearts', 'Spades') | |
rank_names = (None, 'Ace', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', | |
'Eight', 'Nine', 'Ten', 'Jack', 'Queen', 'King') | |
def __init__(self, suit=0, rank=1): | |
self.suit = suit | |
self.rank = rank | |
def __str__(self): | |
return "{} of {}".format(Card.rank_names[self.rank], | |
Card.suit_names[self.suit]) | |
def __eq__(self, other): | |
return (self.suit, self.rank) == (other.suit, other.rank) | |
def __lt__(self, other): | |
return (self.suit, self.rank) < (other.suit, other.rank) | |
class Deck(): | |
""" | |
Represents a deck of cards. | |
Attributes: | |
cards: list of Card objects. | |
""" | |
def __init__(self): | |
cards = product(range(4), range(1, 14)) | |
self.cards = [Card(suit, rank) for suit, rank in cards] | |
def __str__(self): | |
return '\n'.join(str(card) for card in self.cards) | |
def sort(self): # 18.2 | |
self.cards.sort() | |
def shuffle(self): | |
random.shuffle(self.cards) | |
def pop_card(self): | |
return self.cards.pop() | |
def add_cards(self, cards): | |
if isinstance(cards, list): | |
self.cards.extend(cards) | |
else: | |
self.cards.append(cards) | |
def move_cards(self, hand, num): | |
hand.add_cards([self.pop_card() for _ in range(num)]) | |
def deal_hands(self, hands, cards): # 18.3 | |
res = [] | |
for i in range(hands): | |
hand = Hand() | |
self.move_cards(hand, cards) | |
res.append(hand) | |
return res | |
def remove_card(self, card): | |
self.cards.remove(card) | |
class Hand(Deck): | |
""" | |
Represents a hand of playing cards. | |
""" | |
def __init__(self, cards=[], label=''): | |
self.cards = list(cards) | |
self.label = label | |
def find_defining_class(obj, method_name): | |
""" | |
Finds and returns the class object that will provide | |
the definition of method_name (as a string) if it is | |
invoked on obj. | |
obj: any python object | |
method_name: string method name | |
""" | |
for ty in type(obj).mro(): | |
if method_name in ty.__dict__: | |
return ty | |
return None | |
if __name__ == '__main__': | |
deck = Deck() | |
deck.shuffle() | |
hand = Hand() | |
print(find_defining_class(hand, 'shuffle')) | |
deck.move_cards(hand, 5) | |
hand.sort() | |
print(hand) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
This module contains code from | |
Think Python by Allen B. Downey | |
http://thinkpython.com/code/PokerHand.py | |
Copyright 2016 Raphael McSinyx | |
Copyright 2012 Allen B. Downey | |
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html | |
""" | |
from sys import argv | |
from itertools import islice, cycle | |
from functools import reduce | |
from operator import mul | |
from Card import * | |
class PokerHand(Hand): | |
def suit_hist(self): | |
""" | |
Build a histogram of the suits that appear in the hand. | |
Store the result in attribute suits. | |
""" | |
self.suits = {} | |
for card in self.cards: | |
self.suits[card.suit] = self.suits.get(card.suit, 0) + 1 | |
def rank_hist(self): | |
""" | |
Build a histogram of the ranks that appear in the hand. | |
Store the result in attribute ranks. | |
""" | |
self.ranks = {} | |
for card in self.cards: | |
self.ranks[card.rank] = self.ranks.get(card.rank, 0) + 1 | |
def has_2pairs(self): | |
""" | |
Return True if the hand has 2 pairs, False otherwise. | |
""" | |
self.rank_hist() | |
foo = True | |
for val in self.ranks.values(): | |
if val >= 2: | |
foo = not foo | |
if foo: | |
return True | |
return False | |
def has_set_of(self, n): | |
""" | |
Return True if the hand has at least n cards of a same rank, | |
False otherwise. | |
""" | |
self.rank_hist() | |
return not all(val < n for val in self.ranks.values()) | |
def has_straight(self): | |
""" | |
Return True if the hand has straight, False otherwise. | |
""" | |
self.rank_hist() | |
g = (islice(cycle(range(1, 14)), i, i + 5) for i in range(0, 10)) | |
for i in g: | |
if all(j in self.ranks for j in i): | |
return True | |
return False | |
def has_flush(self): | |
""" | |
Return True if the hand has a flush, False otherwise. | |
""" | |
self.suit_hist() | |
return not all(val < 5 for val in self.suits.values()) | |
def has_straight_flush(self, current=0): | |
""" | |
Return True if the hand has a straight flush, False otherwise. | |
""" | |
l = self.cards | |
length = len(l) | |
if length > 5: | |
for i in range(current, length): | |
p = PokerHand(islice(cycle(l), i, i + length - 1)) | |
if p.has_straight_flush(i): | |
return True | |
elif length == 5: | |
return self.has_flush() and self.has_straight() | |
return False | |
def classify(self): | |
""" | |
Set the label attribute of the hand. | |
""" | |
if self.has_straight_flush(): | |
self.label = "Straight flush" | |
elif self.has_set_of(4): | |
self.label = "Four of a kind" | |
elif self.has_2pairs() and self.has_set_of(3): | |
self.label = "Full house" | |
elif self.has_flush(): | |
self.label = "Flush" | |
elif self.has_straight(): | |
self.label = "Straight" | |
elif self.has_set_of(3): | |
self.label = "Three of a kind" | |
elif self.has_2pairs(): | |
self.label = "Two pairs" | |
elif self.has_set_of(2): | |
self.label = "Pair" | |
else: | |
self.label = "High card" | |
def main(cards_per_hand=7, samples=0, *args): | |
try: | |
cards_per_hand = int(cards_per_hand) % 52 | |
samples = abs(int(samples)) | |
except: | |
print("Usage: PokerHand.py [cards per hand] [sample size]") | |
else: | |
t = ("Straight flush", "Four of a kind", "Full house", "Flush", | |
"Straight", "Three of a kind", "Two pairs", "Pair", "High card") | |
d = dict.fromkeys(t, 0) | |
def deal_hand(): | |
deck = Deck() | |
deck.shuffle() | |
hand = PokerHand() | |
deck.move_cards(hand, cards_per_hand) | |
hand.classify() | |
d[hand.label] += 1 | |
if samples: | |
for _ in range(samples): | |
deal_hand() | |
else: | |
while not reduce(mul, d.values()): | |
deal_hand() | |
samples += 1 | |
print("Sample size:", samples) | |
print("Cards per hand:", cards_per_hand) | |
for s in t: | |
print("{}: {} ({:%})".format(s, d[s], d[s] / samples)) | |
if __name__ == '__main__': | |
main(*argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment