poker.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
from collections import Counter


def invert_dict(d):
    return {v: k for (k, v) in d.items()}


def lazy_property(fn):
    attr_name = '_lazy_' + fn.__name__

    @property
    def _lazy_property(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)
    return _lazy_property


class Card:
    VALUES = dict(zip('123456789TJQKA', range(1, 15)))

    def __init__(self, card):
        self.rank, self.suit = card
        self.value = self.VALUES[self.rank]

    def __repr__(self):
        return ''.join((self.rank, self.suit))


class Hand:
    def __init__(self, cards):
        self.cards = list(map(Card, cards))

    def __repr__(self):
        return ' '.join(map(str, self.cards))

    @lazy_property
    def card_list(self):
        return str(self).split()

    @lazy_property
    def values(self):
        """
        Return the card values
        """
        return [card.value for card in self.cards]

    @lazy_property
    def suits(self):
        """
        Return the card suits
        """
        return [card.suit for card in self.cards]

    @lazy_property
    def value_counts(self):
        """
        Count the values of the cards
        """
        return Counter(self.values)

    def groups(self, size):
        """
        List groups of a required size
        """
        return [
            key
            for (key, value) in self.value_counts.items()
            if value == size
        ]

    @lazy_property
    def pairs(self):
        """
        Return all the pairs
        """
        return self.groups(2)

    @lazy_property
    def single_pair(self):
        """
        If there are just 2 cards of equal value (a pair)
        return that value

        Return 0 if there is no pair, or more than one pair
        """
        # If there's no pair, max_hand is 0
        return (self.pairs[0] if len(self.pairs) == 1 else 0)

    @lazy_property
    def double_pair(self):
        """
        If there are two pairs, return their values
        sorted in descending order

        (e.g. 4, 3, 3, 4, 5 -> (4, 3))

        Return (0, 0) if there are not two pairs.
        """
        return (
            tuple(sorted(self.pairs, reverse=True))
            if len(self.pairs) == 2
            else (0, 0)
        )

    @lazy_property
    def triple(self):
        """
        If there are 3 cards of equal value (a triple)
        return that value

        Return 0 if there is no triple
        """
        triples = self.groups(3)
        return triples[0] if triples else 0

    @lazy_property
    def square(self):
        """
        If there are 4 cards of equal value (a square)
        return that value

        Return 0 if there is no square
        """
        squares = self.groups(4)
        return squares[0] if squares else 0

    @lazy_property
    def straight(self):
        """
        If the hand consists of 5 consecutive valued cards,
        return the starting value.

        Return 0 if there is no straight.
        """
        values = sorted(self.values)
        first = values[0]
        is_straight = (values == list(range(first, first+5)))
        return first if is_straight else 0

    @lazy_property
    def flush(self):
        """
        If there's a flush, return its highest value.

        Return 0 if there is no flush.
        """
        return max(self.values) if len(set(self.suits)) == 1 else 0

    @lazy_property
    def straight_flush(self):
        """
        If there's a straight flush (i.e. there's a straight and a flush)
        return its highest value.

        Return 0 if there is not a straight flush.
        """
        return self.flush if self.straight and self.flush else 0

    @lazy_property
    def full(self):
        """
        If there's a full house (i.e. a triple and a pair)
        return their values (with the triple value first)

        Return (0, 0) if there is not a full house.
        """
        pair, triple = self.single_pair, self.triple
        return (triple, pair) if triple and pair else (0, 0)

    @property
    def score(self):
        """
        Create a score for this hand.

        This is a tuple of the various possible hand scores,
        with the highest priority first, to take advantage of Python's
        built in tuple comparison.
        """
        return (
            self.straight_flush,
            self.square,
            self.full,
            self.flush,
            self.straight,
            self.triple,
            self.double_pair,
            self.single_pair,
        )

    def __gt__(self, other):
        return self.score > other.score


def poker(hands):
    hands = [Hand(cards) for cards in hands]
    max_score = max(hands).score
    return [
        hand.card_list
        for hand in hands
        if hand.score == max_score
    ]

@cds-amal thinks this looks great

Comments


You're not logged in right now. Please login via GitHub to comment