Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download

📚 The CoCalc Library - books, templates and other resources

Views: 96169
License: OTHER
1
"""This module contains a code example related to
2
3
Think Python, 2nd Edition
4
by Allen Downey
5
http://thinkpython2.com
6
7
Copyright 2015 Allen Downey
8
9
License: http://creativecommons.org/licenses/by/4.0/
10
"""
11
12
from __future__ import print_function, division
13
14
from Card import Hand, Deck
15
16
17
class Hist(dict):
18
"""A map from each item (x) to its frequency."""
19
20
def __init__(self, seq=[]):
21
"Creates a new histogram starting with the items in seq."
22
for x in seq:
23
self.count(x)
24
25
def count(self, x, f=1):
26
"Increments (or decrements) the counter associated with item x."
27
self[x] = self.get(x, 0) + f
28
if self[x] == 0:
29
del self[x]
30
31
32
class PokerHand(Hand):
33
"""Represents a poker hand."""
34
35
all_labels = ['straightflush', 'fourkind', 'fullhouse', 'flush',
36
'straight', 'threekind', 'twopair', 'pair', 'highcard']
37
38
def make_histograms(self):
39
"""Computes histograms for suits and hands.
40
41
Creates attributes:
42
43
suits: a histogram of the suits in the hand.
44
ranks: a histogram of the ranks.
45
sets: a sorted list of the rank sets in the hand.
46
"""
47
self.suits = Hist()
48
self.ranks = Hist()
49
50
for c in self.cards:
51
self.suits.count(c.suit)
52
self.ranks.count(c.rank)
53
54
self.sets = list(self.ranks.values())
55
self.sets.sort(reverse=True)
56
57
def has_highcard(self):
58
"""Returns True if this hand has a high card."""
59
return len(self.cards)
60
61
def check_sets(self, *t):
62
"""Checks whether self.sets contains sets that are
63
at least as big as the requirements in t.
64
65
t: list of int
66
"""
67
for need, have in zip(t, self.sets):
68
if need > have:
69
return False
70
return True
71
72
def has_pair(self):
73
"""Checks whether this hand has a pair."""
74
return self.check_sets(2)
75
76
def has_twopair(self):
77
"""Checks whether this hand has two pair."""
78
return self.check_sets(2, 2)
79
80
def has_threekind(self):
81
"""Checks whether this hand has three of a kind."""
82
return self.check_sets(3)
83
84
def has_fourkind(self):
85
"""Checks whether this hand has four of a kind."""
86
return self.check_sets(4)
87
88
def has_fullhouse(self):
89
"""Checks whether this hand has a full house."""
90
return self.check_sets(3, 2)
91
92
def has_flush(self):
93
"""Checks whether this hand has a flush."""
94
for val in self.suits.values():
95
if val >= 5:
96
return True
97
return False
98
99
def has_straight(self):
100
"""Checks whether this hand has a straight."""
101
# make a copy of the rank histogram before we mess with it
102
ranks = self.ranks.copy()
103
ranks[14] = ranks.get(1, 0)
104
105
# see if we have 5 in a row
106
return self.in_a_row(ranks, 5)
107
108
def in_a_row(self, ranks, n=5):
109
"""Checks whether the histogram has n ranks in a row.
110
111
hist: map from rank to frequency
112
n: number we need to get to
113
"""
114
count = 0
115
for i in range(1, 15):
116
if ranks.get(i, 0):
117
count += 1
118
if count == n:
119
return True
120
else:
121
count = 0
122
return False
123
124
def has_straightflush(self):
125
"""Checks whether this hand has a straight flush.
126
127
Clumsy algorithm.
128
"""
129
# make a set of the (rank, suit) pairs we have
130
s = set()
131
for c in self.cards:
132
s.add((c.rank, c.suit))
133
if c.rank == 1:
134
s.add((14, c.suit))
135
136
# iterate through the suits and ranks and see if we
137
# get to 5 in a row
138
for suit in range(4):
139
count = 0
140
for rank in range(1, 15):
141
if (rank, suit) in s:
142
count += 1
143
if count == 5:
144
return True
145
else:
146
count = 0
147
return False
148
149
def has_straightflush(self):
150
"""Checks whether this hand has a straight flush.
151
152
Better algorithm (in the sense of being more demonstrably
153
correct).
154
"""
155
# partition the hand by suit and check each
156
# sub-hand for a straight
157
d = {}
158
for c in self.cards:
159
d.setdefault(c.suit, PokerHand()).add_card(c)
160
161
# see if any of the partitioned hands has a straight
162
for hand in d.values():
163
if len(hand.cards) < 5:
164
continue
165
hand.make_histograms()
166
if hand.has_straight():
167
return True
168
return False
169
170
def classify(self):
171
"""Classifies this hand.
172
173
Creates attributes:
174
labels:
175
"""
176
self.make_histograms()
177
178
self.labels = []
179
for label in PokerHand.all_labels:
180
f = getattr(self, 'has_' + label)
181
if f():
182
self.labels.append(label)
183
184
185
class PokerDeck(Deck):
186
"""Represents a deck of cards that can deal poker hands."""
187
188
def deal_hands(self, num_cards=5, num_hands=10):
189
"""Deals hands from the deck and returns Hands.
190
191
num_cards: cards per hand
192
num_hands: number of hands
193
194
returns: list of Hands
195
"""
196
hands = []
197
for i in range(num_hands):
198
hand = PokerHand()
199
self.move_cards(hand, num_cards)
200
hand.classify()
201
hands.append(hand)
202
return hands
203
204
205
def main():
206
# the label histogram: map from label to number of occurances
207
lhist = Hist()
208
209
# loop n times, dealing 7 hands per iteration, 7 cards each
210
n = 10000
211
for i in range(n):
212
if i % 1000 == 0:
213
print(i)
214
215
deck = PokerDeck()
216
deck.shuffle()
217
218
hands = deck.deal_hands(7, 7)
219
for hand in hands:
220
for label in hand.labels:
221
lhist.count(label)
222
223
# print the results
224
total = 7.0 * n
225
print(total, 'hands dealt:')
226
227
for label in PokerHand.all_labels:
228
freq = lhist.get(label, 0)
229
if freq == 0:
230
continue
231
p = total / freq
232
print('%s happens one time in %.2f' % (label, p))
233
234
235
if __name__ == '__main__':
236
main()
237
238
239