Contract con_hand_evaluator_v1


Contract Code


  
1 # con_hand_evaluator_v1
2 random.seed()
3 NUM_CARDS_IN_DECK = 52
4 NUM_VALUES_IN_DECK = 13
5 NUM_SUITS_IN_DECK = 4
6 NUM_CARDS_IN_HAND = 5
7 ACE_VALUE = 2 ** 13
8 STRAIGHT_LOW_ACE_INDICATOR = int("10000000011110", 2)
9 TEN_CARD_POSITION = 8
10 RANK_BASE_VALUE = 10 ** 9
11 RANK_ORDER = [
12 'high_card',
13 'pair',
14 'two_pairs',
15 'trips',
16 'straight',
17 'flush',
18 'full_house',
19 'quads',
20 'straight_flush',
21 'royal_flush',
22 ]
23 DECK = [
24 '2c', '3c', '4c', '5c', '6c', '7c', '8c', '9c', 'Tc', 'Jc', 'Qc', 'Kc', 'Ac',
25 '2d', '3d', '4d', '5d', '6d', '7d', '8d', '9d', 'Td', 'Jd', 'Qd', 'Kd', 'Ad',
26 '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah',
27 '2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As',
28 ]
29 RANKS = {
30 '2c': 13, '2d': 13, '2h': 13, '2s': 13,
31 '3c': 12, '3d': 12, '3h': 12, '3s': 12,
32 '4c': 11, '4d': 11, '4h': 11, '4s': 11,
33 '5c': 10, '5d': 10, '5h': 10, '5s': 10,
34 '6c': 9, '6d': 9, '6h': 9, '6s': 9,
35 '7c': 8, '7d': 8, '7h': 8, '7s': 8,
36 '8c': 7, '8d': 7, '8h': 7, '8s': 7,
37 '9c': 6, '9d': 6, '9h': 6, '9s': 6,
38 'Tc': 5, 'Td': 5, 'Th': 5, 'Ts': 5,
39 'Jc': 4, 'Jd': 4, 'Jh': 4, 'Js': 4,
40 'Qc': 3, 'Qd': 3, 'Qh': 3, 'Qs': 3,
41 'Kc': 2, 'Kd': 2, 'Kh': 2, 'Ks': 2,
42 'Ac': 1, 'Ad': 1, 'Ah': 1, 'As': 1,
43 }
44
45 # Python program to illustrate sum of two numbers.
46 def reduce(function, iterable, initializer=None):
47 value = initializer
48 i = 0
49 for element in iterable:
50 value = function(value, element, i)
51 i += 1
52 return value
53
54
55 def rank_value_fn(total, val, index):
56 return total + \
57 ((val == 1 and (2**(index+1))) or 0) + \
58 ((val > 1 and (2**(index+1) * ACE_VALUE * val)) or 0)
59
60
61 def evaluate_5_card_ints(hand: list) -> int:
62 '''
63 A: 8192
64 K: 4096
65 Q: 2048
66 J: 1024
67 T: 512
68 9: 256
69 8: 128
70 7: 64
71 6: 32
72 5: 16
73 4: 8
74 3: 4
75 2: 2
76 A: 1
77
78 10: Royal Flush
79 9: Straight Flush
80 8: Quads (4 of a kind)
81 7: Full House
82 6: Flush
83 5: Straight
84 4: Trips (3 of a kind)
85 3: Two Pairs
86 2: Pair
87 1: High Card
88
89 '''
90 assert len(hand) == 5, 'Invalid number of cards.'
91 suits = [0] * NUM_SUITS_IN_DECK
92 values = [0] * NUM_VALUES_IN_DECK
93
94 for card in hand:
95 suits[card // NUM_VALUES_IN_DECK] += 1
96 values[card % NUM_VALUES_IN_DECK] += 1
97
98 rank_value = reduce(
99 rank_value_fn,
100 values,
101 initializer=0
102 )
103
104 if 1 in values:
105 first_card_index = values.index(1)
106 else:
107 first_card_index = -1
108
109 is_straight = False
110 if first_card_index >= 0:
111 c = values[first_card_index:first_card_index+5]
112 if rank_value == STRAIGHT_LOW_ACE_INDICATOR or \
113 (len(c) == 5 and all([d == 1 for d in c])):
114 is_straight = True
115
116 n_pairs = values.count(2)
117 is_trips = 3 in values
118 ranks = {
119 'royal_flush': False,
120 'straight_flush': False,
121 'quads': 4 in values,
122 'full_house': is_trips and n_pairs == 1,
123 'flush': NUM_CARDS_IN_HAND in suits,
124 'straight': is_straight,
125 'trips': is_trips,
126 'two_pairs': n_pairs == 2,
127 'pair': n_pairs == 1,
128 'high_card': True,
129 }
130 ranks['straight_flush'] = ranks['flush'] and ranks['straight']
131 ranks['royal_flush'] = ranks['straight_flush'] and first_card_index == TEN_CARD_POSITION
132
133 rank_index = 0
134 #rank_description = ""
135 for r in range(len(RANK_ORDER)-1, -1, -1):
136 rank = RANK_ORDER[r]
137 if ranks[rank]:
138 rank_index = r
139 #rank_description = rank
140 break
141
142 rank_value += rank_index * RANK_BASE_VALUE - \
143 ((rank_value == STRAIGHT_LOW_ACE_INDICATOR and ACE_VALUE - 1) or 0)
144
145 return rank_value
146
147
148 def evaluate_7_card_ints(hand: list) -> int:
149 best_rank = None
150 assert len(hand) == 7, 'Invalid number of cards.'
151 # 7 choose 5
152 for i in range(7):
153 for j in range(i+1, 7):
154 new_hand = hand[0:i] + hand[i+1:j] + hand[j+1:]
155 rank = evaluate_5_card_ints(new_hand)
156 if best_rank is None or best_rank < rank:
157 best_rank = rank
158 return best_rank
159
160
161 def evaluate_omaha(hole_cards: list, board: list) -> int:
162 best_rank = None
163 assert len(hole_cards) == 4, 'Invalid number of hole cards'
164 assert len(board) == 5, 'Invalid number of board cards'
165 n = 0
166 for b in range(0, 4):
167 for r in range(b+1, 5):
168 for x in range(0, 3):
169 for y in range(x+1, 4):
170 new_hand = list(board)
171 new_hand[b] = hole_cards[x]
172 new_hand[r] = hole_cards[y]
173 rank = evaluate_5_card_ints(new_hand)
174 if best_rank is None or best_rank < rank:
175 best_rank = rank
176 n += 1
177 return best_rank
178
179
180 @export
181 def evaluate(hand: list) -> int:
182 if len(hand) == 1:
183 # Simple lookup
184 return 14 - RANKS[hand[0]]
185 else:
186 hand = [DECK.index(card) for card in hand]
187 if len(hand) == 5:
188 return evaluate_5_card_ints(hand)
189 elif len(hand) == 7:
190 return evaluate_7_card_ints(hand)
191 elif len(hand) == 9:
192 # Assume board is the last 5 cards
193 return evaluate_omaha(hand[:4], hand[4:])
194 else:
195 assert False, 'Invalid number of cards specified: {}'.format(len(hand))
196
197 @export
198 def find_winners(ranks: dict, players: list) -> list:
199 sorted_rank_values = sorted(ranks.keys(), reverse=True)
200 player_set = set(players)
201 for rank in sorted_rank_values:
202 players_with_rank = ranks[rank]
203 intersection = player_set.intersection(set(players_with_rank))
204 if len(intersection) > 0:
205 # Found players
206 winners = list(intersection)
207 break
208 return winners
209
210 @export
211 def get_next_better(players: list, folded: list, all_in: list, current_better: str) -> str:
212 if len(folded) >= len(players) - 1:
213 return None # No one needs to bet, only one player left in the hand
214 if len(players) == len(all_in):
215 return None # No one needs to bet, everyone is all in
216 non_folded_players = [p for p in players if p not in folded and p not in all_in]
217 if len(non_folded_players) == 1:
218 # No need to bet in this case
219 return None
220 if current_better not in non_folded_players:
221 return non_folded_players[0]
222 current_index = non_folded_players.index(current_better)
223 #assert current_index >= 0, 'Current better has folded, which does not make sense.'
224 return non_folded_players[(current_index + 1) % len(non_folded_players)]
225
226 @export
227 def get_deck(shuffled: bool = True) -> list:
228 cards = DECK.copy()
229 if shuffled:
230 random.shuffle(cards)
231 return cards
232

Byte Code

e3000000000000000000000000350000004000000073d201000065006a018300010064005a0264015a0364025a0464035a0564685a0665076405640483025a0864065a0964695a0a6409640a640b640c640d640e640f641064116412670a5a0b6413641464156416641764186419641a641b641c641d641e641f6420642164226423642464256426642764286429642a642b642c642d642e642f6430643164326433643464356436643764386439643a643b643c643d643e643f644064416442644364446445644667345a0c6401640164016401644764476447644764486448644864486407640764076407640864086408640864066406640664066449644964496449644a644a644a644a64036403640364036402640264026402644b644b644b644b6404640464046404644c644c644c644c644d9c345a0d646a644f645084015a0e6451645284005a0f6510650764539c026454645584045a116510650764539c026456645784045a1265106510650764589c036459645a84045a136514645b83016510650764539c02645c645d840483015a156514645b8301651665106510645e9c03645f6460840483015a176514645b83016510651065106518651864619c0564626463840483015a196514645b8301646b651a651064659c0264666467840583015a1b644e5300296ce934000000e90d000000e904000000e905000000e902000000da0e3130303030303030303131313130e908000000e90a000000e909000000da09686967685f63617264da0470616972da0974776f5f7061697273da057472697073da087374726169676874da05666c757368da0a66756c6c5f686f757365da057175616473da0e73747261696768745f666c757368da0b726f79616c5f666c757368da023263da023363da023463da023563da023663da023763da023863da023963da025463da024a63da025163da024b63da024163da023264da023364da023464da023564da023664da023764da023864da023964da025464da024a64da025164da024b64da024164da023268da023368da023468da023568da023668da023768da023868da023968da025468da024a68da025168da024b68da024168da023273da023373da023473da023573da023673da023773da023873da023973da025473da024a73da025173da024b73da024173e90c000000e90b000000e907000000e906000000e903000000e901000000293472140000007221000000722e000000723b00000072150000007222000000722f000000723c000000721600000072230000007230000000723d000000721700000072240000007231000000723e000000721800000072250000007232000000723f0000007219000000722600000072330000007240000000721a000000722700000072340000007241000000721b000000722800000072350000007242000000721c000000722900000072360000007243000000721d000000722a00000072370000007244000000721e000000722b00000072380000007245000000721f000000722c000000723900000072460000007220000000722d000000723a00000072470000004e630300000000000000060000000500000043000000732e0000007c027d0364017d0478207c0144005d187d057c007c037c057c0483037d037c04640237007d04710e57007c03530029034ee900000000724d000000a9002906da0866756e6374696f6eda086974657261626c65da0b696e697469616c697a6572da0576616c7565da0169da07656c656d656e74724f000000724f000000da00da085f5f7265647563651a000000730c0000000001040104010a010c010c017257000000630300000000000000030000000400000043000000733c0000007c007c0164016b02721664027c026401170013007018640317007c0164016b04723664027c02640117001300740014007c011400703864031700530029044e724d0000007205000000724e0000002901da094143455f56414c55452903da05746f74616cda0376616cda05696e646578724f000000724f0000007256000000da0f5f5f72616e6b5f76616c75655f666e23000000730400000000012201725c0000002902da0468616e64da0672657475726e6301000000000000000e0000000b00000043000000739e01000074007c00830164016b027314740164028301820164036701740214007d0164036701740314007d0278347c0044005d2c7d037c017c0374031a00050019006404370003003c007c027c0374031600050019006404370003003c00712e5700740474057c02640364058d037d0464047c026b0672807c026a06640483017d056e0464127d0564067d067c0564036b0572ca7c027c057c0564011700850219007d077c0474076b0273c674007c07830164016b0272ca74086407640884007c0744008301830172ca64097d067c026a09640a83017d08640b7c026b067d0964066406640c7c026b067c096ff07c0864046b02740a7c016b067c067c097c08640a6b027c0864046b026409640d9c0a7d0a7c0a640e190090016f1e7c0a640f19007c0a64103c007c0a6410190090016f347c05740b6b027c0a64113c0064037d0b7836740c7400740d83016404180064136414830344005d1e7d0c740d7c0c19007d0d7c0a7c0d1900900172547c0c7d0b50009001715457007c047c0b740e14007c0474076b0290017292740f64041800900170946403180037007d047c0453002915616c0100000a20202020413a20383139320a202020204b3a20343039360a20202020513a20323034380a202020204a3a20313032340a20202020543a20203531320a20202020393a20203235360a20202020383a20203132380a20202020373a20202036340a20202020363a20202033320a20202020353a20202031360a20202020343a20202020380a20202020333a20202020340a20202020323a20202020320a20202020413a20202020310a0a2020202031303a20526f79616c20466c7573680a20202020393a20537472616967687420466c7573680a20202020383a205175616473202834206f662061206b696e64290a20202020373a2046756c6c20486f7573650a20202020363a20466c7573680a20202020353a2053747261696768740a20202020343a205472697073202833206f662061206b696e64290a20202020333a2054776f2050616972730a20202020323a20506169720a20202020313a204869676820436172640a0a2020202072040000007a18496e76616c6964206e756d626572206f662063617264732e724e000000724d0000002901725200000046630100000000000000020000000400000053000000731400000067007c005d0c7d017c0164006b029102710453002901724d000000724f0000002902da022e30da0164724f000000724f0000007256000000fa0a3c6c697374636f6d703e54000000730200000006007a2a5f5f6576616c756174655f355f636172645f696e74732e3c6c6f63616c733e2e3c6c697374636f6d703e547205000000724c0000007203000000290a7213000000721200000072110000007210000000720f000000720e000000720d000000720c000000720b000000720a000000720f000000720e00000072120000007213000000e9ffffffff726200000072620000002910da036c656eda0e417373657274696f6e4572726f72da114e554d5f53554954535f494e5f4445434bda124e554d5f56414c5545535f494e5f4445434b7257000000725c000000725b000000da1a53545241494748545f4c4f575f4143455f494e44494341544f52da03616c6cda05636f756e74da114e554d5f43415244535f494e5f48414e44da1154454e5f434152445f504f534954494f4eda0572616e6765da0a52414e4b5f4f52444552da0f52414e4b5f424153455f56414c55457258000000290e725d000000da057375697473da0676616c756573da0463617264da0a72616e6b5f76616c7565da1066697273745f636172645f696e646578da0b69735f7374726169676874da0163da076e5f7061697273da0869735f7472697073da0572616e6b73da0a72616e6b5f696e646578da0172da0472616e6b724f000000724f0000007256000000da165f5f6576616c756174655f355f636172645f696e7473280000007346000000001d14010a010a010a01140118010e0108010c0204010401080110011601100104010a01080106010e0108010e01080116010a010c0104011a0108010a01040108010a011a01727c000000630100000000000000060000000600000043000000738e00000064007d0174007c00830164016b0273187401640283018201787074026401830144005d647d02785e74027c02640317006401830244005d4c7d037c0064047c02850219007c007c02640317007c038502190017007c007c036403170064008502190017007d0474037c0483017d057c0164006b08737e7c017c056b0072367c057d0171365700712257007c01530029054e724a0000007a18496e76616c6964206e756d626572206f662063617264732e724d000000724e000000290472630000007264000000726c000000727c0000002906725d000000da09626573745f72616e6b7254000000da016ada086e65775f68616e64727b000000724f000000724f0000007256000000da165f5f6576616c756174655f375f636172645f696e74736b00000073120000000001040114010e0114012c01080110010c0172800000002903da0a686f6c655f6361726473da05626f617264725e0000006302000000000000000a000000070000004300000073d000000064007d0274007c00830164016b027318740164028301820174007c01830164036b02732c740164048301820164057d03789a740264056401830244005d8c7d04788674027c04640617006403830244005d747d05786e740264056407830244005d607d06785a74027c06640617006401830244005d487d0774037c0183017d087c007c0619007c087c043c007c007c0719007c087c053c0074047c0883017d097c0264006b0873b07c027c096b0072b47c097d027c03640637007d03717457007160570071505700713c57007c02530029084e72030000007a1c496e76616c6964206e756d626572206f6620686f6c6520636172647372040000007a1d496e76616c6964206e756d626572206f6620626f617264206361726473724e000000724d000000724c000000290572630000007264000000726c000000da046c697374727c000000290a72810000007282000000727d000000da016eda0162727a000000da0178da0179727f000000727b000000724f000000724f0000007256000000da105f5f6576616c756174655f6f6d61686177000000732000000000010401140114010401100114011001140108010c010c0108011001040118017288000000da15636f6e5f68616e645f6576616c7561746f725f7631630100000000000000010000000500000043000000739200000074007c00830164016b02721c640274017c00640319001900180053006404640584007c00440083017d0074007c00830164066b02723e74027c008301530074007c00830164076b02725274037c008301530074007c00830164086b02727874047c0064006409850219007c00640964008502190083025300640a738e7405640b6a0674007c00830183018301820164005300290c4e724d000000e90e000000724e000000630100000000000000020000000400000053000000731600000067007c005d0e7d0174006a017c018301910271045300724f0000002902da044445434b725b0000002902725f0000007271000000724f000000724f000000725600000072610000008f000000730200000006007a1c6576616c756174652e3c6c6f63616c733e2e3c6c697374636f6d703e7204000000724a00000072090000007203000000467a25496e76616c6964206e756d626572206f66206361726473207370656369666965643a207b7d29077263000000da0552414e4b53727c000000728000000072880000007264000000da06666f726d61742901725d000000724f000000724f0000007256000000da086576616c756174658a000000731600000000020c0110020e010c0108010c0108010c011a020c01728e00000029037278000000da07706c6179657273725e000000630200000000000000080000000400000043000000735600000074007c006a018300640164028d027d0274027c0183017d0378387c0244005d307d047c007c0419007d057c036a0374027c05830183017d0674047c06830164036b04721e74057c0683017d075000711e57007c07530029044e542901da0772657665727365724e0000002906da06736f72746564da046b657973da03736574da0c696e74657273656374696f6e7263000000728300000029087278000000728f000000da12736f727465645f72616e6b5f76616c756573da0a706c617965725f736574727b000000da11706c61796572735f776974685f72616e6b7294000000da0777696e6e657273724f000000724f0000007256000000da0c66696e645f77696e6e6572739b00000073120000000002100108010a0108010e010c010801060172990000002905728f000000da06666f6c646564da06616c6c5f696eda0e63757272656e745f626574746572725e000000630400000000000000060000000400000003000000737e00000074008801830174007c008301640118006b0572186400530074007c0083017400880083016b02722c640053008700870166026402640384087c00440083017d0474007c04830164016b027250640053007c037c046b0772607c046404190053007c046a017c0383017d057c047c056401170074007c04830116001900530029054e724d000000630100000000000000020000000400000013000000732000000067007c005d187d017c0188016b0772047c0188006b0772047c01910271045300724f000000724f0000002902725f000000da01702902729b000000729a000000724f00000072560000007261000000af000000730400000006000c017a236765745f6e6578745f6265747465722e3c6c6f63616c733e2e3c6c697374636f6d703e724e00000029027263000000725b0000002906728f000000729a000000729b000000729c000000da126e6f6e5f666f6c6465645f706c6179657273da0d63757272656e745f696e646578724f0000002902729b000000729a0000007256000000da0f6765745f6e6578745f626574746572a800000073160000000003140104011001040114020c010401080108010a0172a0000000542902da0873687566666c6564725e000000630100000000000000020000000200000043000000731a00000074006a0183007d017c00721674026a037c01830101007c01530029014e2904728b000000da04636f7079da0672616e646f6dda0773687566666c65290272a1000000da056361726473724f000000724f0000007256000000da086765745f6465636bb900000073080000000002080104010a0172a600000069002000006900ca9a3b29014e290154291c72a3000000da0473656564da114e554d5f43415244535f494e5f4445434b72660000007265000000726a0000007258000000da03696e747267000000726b000000726e000000726d000000728b000000728c0000007257000000725c0000007283000000727c00000072800000007288000000da085f5f6578706f7274728e000000da04646963747299000000da0373747272a0000000da04626f6f6c72a6000000724f000000724f000000724f0000007256000000da083c6d6f64756c653e0100000073480000000801040104010401040104010a01040104010a010e0116011801180118010e010c010e01100110011001100114030a0908051043100c1213060112100601140c06010601120f0601