-
Notifications
You must be signed in to change notification settings - Fork 1
/
card.py
239 lines (189 loc) · 6.94 KB
/
card.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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
from typing import Sequence
from typing import List, Dict
class Card:
"""
Static class that handles cards. We represent cards as 32-bit integers, so
there is no object instantiation - they are just ints. Most of the bits are
used, and have a specific meaning. See below:
Card:
bitrank suit rank prime
+--------+--------+--------+--------+
|xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
+--------+--------+--------+--------+
1) p = prime number of rank (deuce=2,trey=3,four=5,...,ace=41)
2) r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12)
3) cdhs = suit of card (bit turned on based on suit of card)
4) b = bit turned on depending on rank of card
5) x = unused
This representation will allow us to do very important things like:
- Make a unique prime prodcut for each hand
- Detect flushes
- Detect straights
and is also quite performant.
"""
# the basics
STR_RANKS: str = '23456789TJQKA'
STR_SUITS: str = 'shdc'
INT_RANKS: range = range(13)
# tricks: List[str] =`` []
PRIMES: List[int] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41]
# conversion from string => int
CHAR_RANK_TO_INT_RANK: Dict[str, int] = dict(zip(list(STR_RANKS), INT_RANKS))
CHAR_SUIT_TO_INT_SUIT: Dict[str, int] = {
's': 1, # spades
'h': 2, # hearts
'd': 4, # diamonds
'c': 8, # clubs
'\u2660': 1, # spades (unicode)
'\u2764': 2, # hearts (unicode)
'\u2666': 4, # diamonds (unicode)
'\u2663': 8, # clubs (unicode)
}
INT_SUIT_TO_CHAR_SUIT: str = 'xshxdxxxc'
# for pretty printing
PRETTY_SUITS: Dict[int, str] = {
1: chr(9824), # spades
2: chr(9829), # hearts
4: chr(9830), # diamonds
8: chr(9827) # clubs
}
SUIT_COLORS: Dict[int, str] = {
2: "red",
4: "blue",
8: "green"
}
@staticmethod
def new(string: str) -> int:
"""
Converts Card string to binary integer representation of card, inspired by:
http://www.suffecool.net/poker/evaluator.html
"""
rank_char = string[0]
suit_char = string[1]
rank_int = Card.CHAR_RANK_TO_INT_RANK[rank_char]
suit_int = Card.CHAR_SUIT_TO_INT_SUIT[suit_char]
rank_prime = Card.PRIMES[rank_int]
bitrank = 1 << rank_int << 16
suit = suit_int << 12
rank = rank_int << 8
return bitrank | suit | rank | rank_prime
@staticmethod
def int_to_str(card_int: int) -> str:
rank_int = Card.get_rank_int(card_int)
suit_int = Card.get_suit_int(card_int)
return Card.STR_RANKS[rank_int] + Card.INT_SUIT_TO_CHAR_SUIT[suit_int]
@staticmethod
def get_rank_int(card_int: int) -> int:
return (card_int >> 8) & 0xF
@staticmethod
def get_suit_int(card_int: int) -> int:
return (card_int >> 12) & 0xF
@staticmethod
def get_bitrank_int(card_int: int) -> int:
return (card_int >> 16) & 0x1FFF
@staticmethod
def get_prime(card_int: int) -> int:
return card_int & 0x3F
@staticmethod
def hand_to_binary(card_strs: Sequence[str]) -> List[int]:
"""
Expects a list of cards as strings and returns a list
of integers of same length corresponding to those strings.
"""
bhand = []
for c in card_strs:
bhand.append(Card.new(c))
return bhand
@staticmethod
def prime_product_from_hand(card_ints: Sequence[int]) -> int:
"""
Expects a list of cards in integer form.
"""
product = 1
for c in card_ints:
product *= (c & 0xFF)
return product
@staticmethod
def prime_product_from_rankbits(rankbits: int) -> int:
"""
Returns the prime product using the bitrank (b)
bits of the hand. Each 1 in the sequence is converted
to the correct prime and multiplied in.
Params:
rankbits = a single 32-bit (only 13-bits set) integer representing
the ranks of 5 _different_ ranked cards
(5 of 13 bits are set)
Primarily used for evaulating flushes and straights,
two occasions where we know the ranks are *ALL* different.
Assumes that the input is in form (set bits):
rankbits
+--------+--------+
|xxxbbbbb|bbbbbbbb|
+--------+--------+
"""
product = 1
for i in Card.INT_RANKS:
# if the ith bit is set
if rankbits & (1 << i):
product *= Card.PRIMES[i]
return product
@staticmethod
def int_to_binary(card_int: int) -> str:
"""
For debugging purposes. Displays the binary number as a
human readable string in groups of four digits.
"""
bstr = bin(card_int)[2:][::-1] # chop off the 0b and THEN reverse string
output = list("".join(["0000" + "\t"] * 7) + "0000")
for i in range(len(bstr)):
output[i + int(i/4)] = bstr[i]
# output the string to console
output.reverse()
return "".join(output)
@staticmethod
def int_to_pretty_str(card_int: int) -> str:
"""
Prints a single card
"""
color = False
try:
from termcolor import colored
# for mac, linux: http://pypi.python.org/pypi/termcolor
# can use for windows: http://pypi.python.org/pypi/colorama
color = True
except ImportError:
pass
# suit and rank
suit_int = Card.get_suit_int(card_int)
rank_int = Card.get_rank_int(card_int)
# color
s = Card.PRETTY_SUITS[suit_int]
if color and suit_int in Card.SUIT_COLORS:
s = colored(s, Card.SUIT_COLORS[suit_int])
r = Card.STR_RANKS[rank_int]
return "[{}{}]".format(r,s)
@staticmethod
def print_pretty_card(card_int: int) -> None:
"""
Expects a single integer as input
"""
print(Card.int_to_pretty_str(card_int))
@staticmethod
def ints_to_pretty_str(card_ints: Sequence[int]) -> str:
"""
Expects a list of cards in integer form.
"""
output = " "
for i in range(len(card_ints)):
c = card_ints[i]
if i != len(card_ints) - 1:
output += str(Card.int_to_pretty_str(c)) + ","
else:
output += str(Card.int_to_pretty_str(c)) + " "
return output
@staticmethod
def print_pretty_cards(card_ints: Sequence[int]) -> None:
"""
Expects a list of cards in integer form.
"""
print(Card.ints_to_pretty_str(card_ints))