-
Notifications
You must be signed in to change notification settings - Fork 0
/
hand.py
117 lines (93 loc) · 3.56 KB
/
hand.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!env python
from itertools import groupby, cycle
from card import Card
from categories import Categories
from suits import Suits
"""
4 types of combinations:
1. Value combination (ie. 3 Kings and 2 Queens -> full house)
2. Same suit (flush)
3. Incr value (straight)
4. 2 & 3 (straight flush)
"""
class Hand(object):
value_compositions = {
(1, 4): Categories.FourOfAKind,
(2, 3): Categories.FullHouse,
(1, 1, 3): Categories.ThreeOfAKind,
(1, 2, 2): Categories.TwoPair,
(1, 1, 1, 2): Categories.OnePair,
}
def __init__(self, cards: set):
self.cards = cards
self.category = None
def rank(self):
self.category = self.get_categories()
def add_card(self, card):
self.cards.add(card)
def groupby_value(self):
return tuple(tuple(v) for (k, v) in groupby(sorted(self.cards), lambda c: c.value))
def count_groups(self):
return tuple((len(g), g) for g in self.groupby_value())
def count_values(self):
groups = self.groupby_value()
return tuple(sorted(map(len, groups)))
@property
def is_flush(self):
return len({c.suit for c in self.cards}) == 1
@property
def is_straight(self):
return self.values == list(range(self.values[0], self.values[-1] + 1))
# We need to keep the cards that compose the hand associated with the "value composition"
def get_categories(self):
categories = set()
comp = Hand.value_compositions.get(self.count_values(), Categories.HighCard)
categories.add(comp)
if self.is_straight:
categories.add(Categories.Straight)
if self.is_flush:
categories.add(Categories.Flush)
if {Categories.Straight, Categories.Flush}.issubset(categories):
categories.add(Categories.StraightFlush)
return categories
@property
def punchers(self):
return self.cards - self.kickers
@property
def kickers(self):
cat = self.best_category
if cat in {Categories.StraightFlush, Categories.Flush, Categories.Straight}:
return set()
elif cat == Categories.HighCard:
return set(sorted(self.cards)[:-1])
else:
kicker_groups = {g for (i, g) in self.count_groups() if i == 1}
kickers = set()
for group in kicker_groups:
for card in group:
kickers.add(card)
return kickers
@property
def values(self):
return list(sorted(c.value for c in self.cards))
@property
def best_category(self):
return max(self.get_categories())
# TODO: Keep the cards associated with their value composition (count_values)
# in order to seperate the hand from the kicker.
def __eq__(self, other):
return self.best_category == other.best_category and self.values == other.values
def __gt__(self, other):
return self.best_category > other.best_category or \
(self.best_category == other.best_category and self.values > other.values)
def __lt__(self, other):
return self.best_category < other.best_category or \
(self.best_category == other.best_category and self.values < other.values)
def __str__(self):
return ', '.join(map(str, self.cards))
def __repr__(self):
return '<Hand(cards=%r, best_category=%r)>' % (self.cards, self.best_category)
@classmethod
def values_as_hand(cls, values):
cards = {Card(v, s) for (v, s) in zip(values, cycle(Suits))}
return Hand(cards)