Contract con_poker_card_games_v1
Creator | ae9cfa894495930b8d2f1707ab936325b5c848ace677bb8ba41dfe7dcdb3e3e6 |
Creation Hash | 2ec2dbf02b189931b8f212c9a4b5d357fb64f6493867771064169b71bd0c3f11 |
Created On | 883 days ago - 12/14/2021, 9:34:08 PM UTC+0 |
Contract Code
1
# con_poker_card_games_v1
2
import con_rsa_encryption as rsa
3
import con_phi_lst001 as phi
4
I = importlib
5
6
phi_balances = ForeignHash(foreign_contract='con_phi_lst001', foreign_name='balances')
7
8
games = Hash(default_value=None)
9
hands = Hash(default_value=None)
10
game_names = Hash(default_value=None)
11
players_games = Hash(default_value=[])
12
players_invites = Hash(default_value=[])
13
messages_hash = Hash(default_value=[])
14
player_metadata_contract = Variable()
15
poker_hand_evaluator_contract = Variable()
16
owner = Variable()
17
18
MAX_RANDOM_NUMBER = 99999999
19
ONE_CARD_POKER = 0
20
BLIND_POKER = 1
21
FIVE_CARD_STUD = 2
22
SEVEN_CARD_STUD = 3
23
ALL_GAME_TYPES = [
24
ONE_CARD_POKER,
25
BLIND_POKER,
26
FIVE_CARD_STUD,
27
SEVEN_CARD_STUD
28
]
29
NO_LIMIT = 0
30
POT_LIMIT = 1
31
ALL_BETTING_TYPES = [
32
NO_LIMIT,
33
POT_LIMIT
34
]
35
MAX_PLAYERS_BY_GAME_TYPE = {
36
ONE_CARD_POKER: 50,
37
BLIND_POKER: 50,
38
FIVE_CARD_STUD: 10,
39
SEVEN_CARD_STUD: 7
40
}
41
42
random.seed()
43
44
@construct
45
def seed():
46
owner.set(ctx.caller)
47
player_metadata_contract.set('con_gamma_phi_profile_v4')
48
poker_hand_evaluator_contract.set('con_poker_hand_evaluator_v1')
49
50
@export
51
def update_player_metadata_contract(contract: str):
52
assert ctx.caller == owner.get(), 'Only the owner can call update_player_metadata_contract()'
53
player_metadata_contract.set(contract)
54
55
@export
56
def update_poker_hand_evaluator_contract(contract: str):
57
assert ctx.caller == owner.get(), 'Only the owner can call update_player_metadata_contract()'
58
poker_hand_evaluator_contract.set(contract)
59
60
def get_players_and_assert_exists(game_id: str) -> dict:
61
players = games[game_id, 'players']
62
assert players is not None, f'Game {game_id} does not exist.'
63
return players
64
65
def create_game_id(creator: str) -> str:
66
return hashlib.sha3(":".join([creator, str(now)]))
67
68
def create_hand_id(game_id: str) -> str:
69
return hashlib.sha3(":".join([game_id, str(now)]))
70
71
def evaluate_hand(hand: list) -> int:
72
phe = I.import_module(poker_hand_evaluator_contract.get())
73
return phe.evaluate(hand)
74
75
@export
76
def game_message(game_id: str, message: str):
77
player = ctx.caller
78
players = get_players_and_assert_exists(game_id)
79
assert player in players, 'You do not belong to this game.'
80
messages = messages_hash[game_id, player] or []
81
messages.append(message)
82
messages_hash[game_id, player] = messages
83
84
@export
85
def hand_message(hand_id: str, message: str):
86
player = ctx.caller
87
active_players = hands[hand_id, 'active_players']
88
assert player in active_players, 'You do not belong to this hand.'
89
messages = messages_hash[hand_id, player] or []
90
messages.append(message)
91
messages_hash[hand_id, player] = messages
92
93
@export
94
def add_chips_to_game(game_id: str, amount: float):
95
player = ctx.caller
96
assert amount > 0, 'Amount must be a positive number'
97
98
players = get_players_and_assert_exists(game_id)
99
assert player in players, 'You do not belong to this game.'
100
101
games[game_id, player] = (games[game_id, player] or 0.0) + amount
102
assert phi_balances[player, ctx.this] >= amount, 'You have not approved enough for this amount of chips'
103
phi.transfer_from(amount, ctx.this, player)
104
105
106
@export
107
def withdraw_chips_from_game(game_id: str, amount: float):
108
player = ctx.caller
109
assert amount > 0, 'Amount must be a positive number'
110
111
players = get_players_and_assert_exists(game_id)
112
assert player in players, 'You do not belong to this game.'
113
114
current_chip_count = games[game_id, player]
115
116
assert current_chip_count >= amount, 'You cannot withdraw more than you have.'
117
118
games[game_id, player] = current_chip_count - amount
119
phi.transfer(
120
amount=amount,
121
to=player
122
)
123
124
125
@export
126
def respond_to_invite(game_id: str, accept: bool):
127
player = ctx.caller
128
player_invites = players_invites[player] or []
129
players = get_players_and_assert_exists(game_id)
130
assert player not in players, 'You are already a part of this game.'
131
#assert len(players) < MAX_PLAYERS, f'Only {MAX_PLAYERS} are allowed to play at the same time.'
132
declined = players_invites[player, 'declined'] or []
133
assert game_id in player_invites or game_id in declined or games[game_id, 'public'], 'You have not been invited to this game.'
134
if game_id in player_invites:
135
player_invites.remove(game_id)
136
players_invites[player] = player_invites
137
players_invites[player, game_id] = accept
138
if accept:
139
if game_id in declined:
140
declined.remove(game_id)
141
players_invites[player, 'declined'] = declined
142
players.append(player)
143
games[game_id, 'players'] = players
144
players_games[player] = (players_games[player] or []) + [game_id]
145
else:
146
if game_id not in declined:
147
declined.append(game_id)
148
players_invites[player, 'declined'] = declined
149
150
@export
151
def decline_all_invites():
152
# Nuclear option
153
player = ctx.caller
154
invites = players_invites[player] or []
155
for invite in invites:
156
players_invites[player, invite] = False
157
players_invites[player] = []
158
159
def send_invite_requests(game_id: str, others: list):
160
for other in others:
161
player_invites = players_invites[other] or []
162
player_invites.append(game_id)
163
players_invites[other] = player_invites
164
165
def validate_game_name(name: str):
166
assert name is not None and len(name) > 0, 'Game name cannot be null or empty'
167
assert isinstance(name, str), 'Game name must be a string.'
168
assert len(name) <= 24, 'Game name cannot be longer than 24 characters.'
169
assert all([c.isalnum() or c in ('_', '-') for c in name]), 'Game name has invalid characters. Each character must be alphanumeric, a hyphen, or an underscore.'
170
assert name[0] not in ('-', '_') and name[-1] not in ('-', '_'), 'Game name cannot start or end with a hyphen or underscore.'
171
172
@export
173
def start_game(name: str,
174
other_players: list,
175
ante: float,
176
allowed_game_types: list = ALL_GAME_TYPES,
177
allowed_betting_types: list = ALL_BETTING_TYPES,
178
public: bool = False) -> str:
179
180
creator = ctx.caller
181
182
assert len(allowed_game_types) > 0, 'Must allow at least one game type.'
183
for game_type in allowed_game_types:
184
assert game_type in ALL_GAME_TYPES, f'Invalid game type: {game_type}.'
185
assert len(allowed_betting_types) > 0, 'Must allow at least one betting type.'
186
for bet_type in allowed_betting_types:
187
assert bet_type in ALL_BETTING_TYPES, f'Invalid betting type: {bet_type}.'
188
assert ante >= 0, 'Ante must be non-negative.'
189
assert creator not in other_players, f'Caller can\'t be in other_players input.'
190
#assert other_players is not None and (len(other_players) > 0), 'You cannot play by yourself!'
191
#assert len(other_players) < MAX_PLAYERS, f'Only {MAX_PLAYERS} are allowed to play at the same time.'
192
193
game_id = create_game_id(creator=creator)
194
195
assert games[game_id, 'creator'] is None, f'Game {game_id} has already been created.'
196
197
validate_game_name(name)
198
assert game_names[name] is None, f'Game {name} has already been created.'
199
game_names[name] = game_id
200
201
games[game_id, 'players'] = [creator]
202
games[game_id, 'ante'] = ante
203
games[game_id, 'creator'] = creator
204
games[game_id, 'invitees'] = other_players
205
games[game_id, 'public'] = public
206
games[game_id, 'allowed_game_types'] = allowed_game_types
207
games[game_id, 'allowed_betting_types'] = allowed_betting_types
208
209
players_games[creator] = (players_games[creator] or []) + [game_id]
210
send_invite_requests(game_id, other_players)
211
212
return game_id
213
214
@export
215
def add_player_to_game(game_id: str, player_to_add: str):
216
player = ctx.caller
217
assert player != player_to_add, 'You cannot add yourself to a game.'
218
creator = games[game_id, 'creator']
219
assert player == creator, 'Only the game creator can add players.'
220
players = get_players_and_assert_exists(game_id)
221
assert player_to_add not in players, 'Player is already in the game.'
222
invitees = games[game_id, 'invitees']
223
assert player_to_add not in invitees, 'Player has already been invited.'
224
invitees.append(player_to_add)
225
#assert len(players) < MAX_PLAYERS, f'Only {MAX_PLAYERS} are allowed to play at the same time.'
226
games[game_id, 'invitees'] = invitees
227
send_invite_requests(game_id, [player_to_add])
228
229
@export
230
def leave_game(game_id: str):
231
player = ctx.caller
232
players = get_players_and_assert_exists(game_id)
233
assert player in players, 'You are not in this game.'
234
235
chips = games[game_id, player]
236
assert chips is None or chips == 0, 'You still have chips in this game. Please withdraw them before leaving.'
237
238
player_games = players_games[player]
239
player_games.remove(game_id)
240
players_games[player] = player_games
241
players.remove(player)
242
games[game_id, 'players'] = players
243
244
hand_id = games[game_id, 'current_hand']
245
246
if hand_id is not None:
247
# Check some stuff
248
active_players = hands[hand_id, 'active_players'] or []
249
if player in active_players:
250
folded = hands[hand_id, 'folded']
251
all_in = hands[hand_id, 'all_in']
252
next_better = hands[hand_id, 'next_better']
253
if next_better == player:
254
# Check for next better before removing player from hand state
255
next_better = get_next_better(active_players, folded, all_in, player)
256
hands[hand_id, 'next_better'] = next_better
257
active_players.remove(player)
258
hands[hand_id, 'active_players'] = active_players
259
if player in folded:
260
folded.remove(player)
261
hands[hand_id, 'folded'] = folded
262
if player in all_in:
263
all_in.remove(player)
264
hands[hand_id, 'all_in'] = all_in
265
266
@export
267
def start_hand(game_id: str, game_type: int, bet_type: int) -> str:
268
dealer = ctx.caller
269
270
allowed_game_types = games[game_id, 'allowed_game_types']
271
assert game_type in allowed_game_types, 'Invalid game type.'
272
273
allowed_betting_types = games[game_id, 'allowed_betting_types']
274
assert bet_type in allowed_betting_types, 'Invalid bet type.'
275
276
players = get_players_and_assert_exists(game_id)
277
assert dealer in players, 'You are not a part of this game.'
278
assert len(players) > 1, 'You cannot start a hand by yourself.'
279
280
previous_hand_id = games[game_id, 'current_hand']
281
if previous_hand_id is not None:
282
assert hands[previous_hand_id, 'payed_out'], 'The previous hand has not been payed out yet.'
283
284
hand_id = create_game_id(game_id)
285
# Update game state
286
games[game_id, 'current_hand'] = hand_id
287
# Update hand state
288
hands[hand_id, 'game_id'] = game_id
289
hands[hand_id, 'game_type'] = game_type
290
hands[hand_id, 'bet_type'] = bet_type
291
hands[hand_id, 'dealer'] = dealer
292
hands[hand_id, 'folded'] = []
293
hands[hand_id, 'completed'] = False
294
hands[hand_id, 'payed_out'] = False
295
hands[hand_id, 'reached_dealer'] = False
296
hands[hand_id, 'active_players'] = []
297
hands[hand_id, 'current_bet'] = 0
298
hands[hand_id, 'all_in'] = []
299
return hand_id
300
301
def active_player_sort(players: list) -> int:
302
def sort(player):
303
return players.index(player)
304
return sort
305
306
@export
307
def ante_up(hand_id: str):
308
player = ctx.caller
309
game_id = hands[hand_id, 'game_id']
310
assert game_id is not None, 'This game does not exist.'
311
players = get_players_and_assert_exists(game_id)
312
assert player in players, 'You are not a part of this game.'
313
ante = games[game_id, 'ante']
314
chips = games[game_id, player]
315
assert chips is not None and chips >= ante, 'You do not have enough chips.'
316
active_players = hands[hand_id, 'active_players'] or []
317
assert player not in active_players, 'You have already paid the ante.'
318
# Check max players
319
game_type = hands[hand_id, 'game_type']
320
max_players = MAX_PLAYERS_BY_GAME_TYPE[game_type]
321
assert len(active_players) < max_players, f'A maximum of {max_players} is allowed for this game type.'
322
# Pay ante
323
hands[hand_id, player, 'bet'] = ante
324
hands[hand_id, player, 'max_bet'] = chips
325
games[game_id, player] -= ante
326
# Update hand state
327
active_players.append(player)
328
active_players.sort(key=active_player_sort(players))
329
hands[hand_id, 'active_players'] = active_players
330
hands[hand_id, 'current_bet'] = ante
331
if chips == ante:
332
# All in
333
all_in = hands[hand_id, 'all_in']
334
all_in.append(player)
335
hands[hand_id, 'all_in'] = all_in
336
337
@export
338
def deal_cards(hand_id: str):
339
dealer = ctx.caller
340
341
active_players = hands[hand_id, 'active_players']
342
343
assert dealer == hands[hand_id, 'dealer'], 'You are not the dealer.'
344
assert len(active_players) > 1, f'Not enough active players: {len(active_players)} <= 1'
345
assert dealer in active_players, 'You are not actively part of this hand.'
346
347
game_type = hands[hand_id, 'game_type']
348
349
player_metadata = ForeignHash(foreign_contract=player_metadata_contract.get(), foreign_name='metadata')
350
351
cards = [
352
'2c', '2d', '2h', '2s',
353
'3c', '3d', '3h', '3s',
354
'4c', '4d', '4h', '4s',
355
'5c', '5d', '5h', '5s',
356
'6c', '6d', '6h', '6s',
357
'7c', '7d', '7h', '7s',
358
'8c', '8d', '8h', '8s',
359
'9c', '9d', '9h', '9s',
360
'Tc', 'Td', 'Th', 'Ts',
361
'Jc', 'Jd', 'Jh', 'Js',
362
'Qc', 'Qd', 'Qh', 'Qs',
363
'Kc', 'Kd', 'Kh', 'Ks',
364
'Ac', 'Ad', 'Ah', 'As',
365
]
366
random.shuffle(cards)
367
368
for i in range(len(active_players)):
369
player = active_players[i]
370
player_key = player_metadata[player, 'public_rsa_key']
371
assert player_key is not None, f'Player {player} has not setup their encryption keys.'
372
keys = player_key.split('|')
373
assert len(keys) == 2, 'Invalid keys'
374
375
if game_type == ONE_CARD_POKER:
376
player_hand = cards[i: i+1]
377
elif game_type == BLIND_POKER:
378
# Player's hand is actually everyone elses hand
379
player_hand = cards[0:i] + cards[i+1:len(active_players)]
380
assert len(player_hand) == len(active_players)-1, f'Something went wrong. {len(player_hand)} != {len(active_players)-1}'
381
elif game_type == FIVE_CARD_STUD:
382
player_hand = cards[5*i:5*i+5]
383
assert len(player_hand) == 5, 'Something went wrong.'
384
elif game_type == SEVEN_CARD_STUD:
385
player_hand = cards[7*i:7*i+7]
386
assert len(player_hand) == 7, 'Something went wrong.'
387
else:
388
assert False, 'Invalid game type.'
389
390
player_hand_str = ",".join(player_hand)
391
salt = str(random.randint(0, MAX_RANDOM_NUMBER))
392
393
player_hand_str_with_salt = f'{player_hand_str}:{salt}'
394
395
# Encrypt players hand with their personal keys
396
player_encrypted_hand = rsa.encrypt(
397
message_str=player_hand_str_with_salt,
398
n=int(keys[0]),
399
e=int(keys[1])
400
)
401
402
# For verification purposes
403
house_encrypted_hand = hashlib.sha3(player_hand_str_with_salt)
404
405
hands[hand_id, player, 'player_encrypted_hand'] = player_encrypted_hand
406
hands[hand_id, player, 'house_encrypted_hand'] = house_encrypted_hand
407
408
# Update hand state
409
all_in = hands[hand_id, 'all_in']
410
hands[hand_id, 'next_better'] = get_next_better(active_players, [], all_in, dealer)
411
ante = games[hands[hand_id, 'game_id'], 'ante']
412
hands[hand_id, 'pot'] = ante * len(active_players)
413
414
def get_next_better(players: list, folded: list, all_in: list, current_better: str) -> str:
415
if len(folded) >= len(players) - 1:
416
return None # No one needs to bet, only one player left in the hand
417
if len(players) == len(all_in):
418
return None # No one needs to bet, everyone is all in
419
non_folded_players = [p for p in players if p not in folded and p not in all_in]
420
current_index = non_folded_players.index(current_better)
421
assert current_index >= 0, 'Current better has folded, which does not make sense.'
422
return non_folded_players[(current_index + 1) % len(non_folded_players)]
423
424
@export
425
def bet_check_or_fold(hand_id: str, bet: float):
426
player = ctx.caller
427
428
assert hands[hand_id, player, 'player_encrypted_hand'] is not None, 'Hand does not exist'
429
assert not hands[hand_id, 'completed'], 'This hand has already completed.'
430
assert hands[hand_id, 'next_better'] == player, 'It is not your turn to bet.'
431
432
active_players = hands[hand_id, 'active_players']
433
folded = hands[hand_id, 'folded']
434
435
call_bet = hands[hand_id, 'current_bet'] or 0.0
436
player_previous_bet = hands[hand_id, player, 'bet'] or 0.0
437
dealer = hands[hand_id, 'dealer']
438
439
if dealer == player:
440
# Been around the circle once
441
hands[hand_id, 'reached_dealer'] = True
442
reached_dealer = True
443
else:
444
reached_dealer = hands[hand_id, 'reached_dealer']
445
446
all_in = hands[hand_id, 'all_in']
447
next_better = get_next_better(active_players, folded, all_in, player)
448
449
if next_better is None:
450
# No need to bet, this is the end of the hand
451
hands[hand_id, 'completed'] = True
452
453
else:
454
if bet < 0:
455
# Folding
456
folded.append(player)
457
hands[hand_id, 'folded'] = folded
458
if player in all_in:
459
all_in.remove(player)
460
hands[hand_id, 'all_in'] = all_in
461
if len(folded) == len(active_players) - 1:
462
hands[hand_id, 'completed'] = True
463
elif bet == 0:
464
# Checking
465
max_bet = hands[hand_id, player, 'max_bet']
466
if max_bet == player_previous_bet and player not in all_in:
467
all_in.append(player)
468
hands[hand_id, 'all_in'] = all_in
469
assert max_bet == player_previous_bet or player_previous_bet >= call_bet, 'Cannot check in this scenario. Current bet is above your bet and you are not all in.'
470
next_players_bet = hands[hand_id, next_better, 'bet']
471
if next_players_bet is not None and next_players_bet == call_bet and reached_dealer:
472
# Betting is over (TODO allow reraise)
473
hands[hand_id, 'completed'] = True
474
else:
475
# Betting
476
game_id = hands[hand_id, 'game_id']
477
assert games[game_id, player] >= bet, 'You do not have enough chips to make this bet'
478
bet_type = hands[hand_id, 'bet_type']
479
if bet_type == POT_LIMIT:
480
pot = hands[hand_id, 'pot']
481
assert bet <= pot, f'Cannot overbet the pot in pot-limit mode.'
482
current_bet = player_previous_bet + bet
483
max_bet = hands[hand_id, player, 'max_bet']
484
if max_bet == current_bet and player not in all_in:
485
all_in.append(player)
486
hands[hand_id, 'all_in'] = all_in
487
assert max_bet == current_bet or current_bet >= call_bet, 'Current bet is above your bet and you did not go all in.'
488
hands[hand_id, player, 'bet'] = current_bet
489
hands[hand_id, 'current_bet'] = current_bet
490
hands[hand_id, 'pot'] += bet
491
games[game_id, player] -= bet
492
493
hands[hand_id, 'next_better'] = next_better
494
495
@export
496
def verify_hand(hand_id: str, player_hand_str: str) -> str:
497
player = ctx.caller
498
assert hands[hand_id, 'completed'], 'This hand has not completed yet.'
499
folded = hands[hand_id, 'folded']
500
assert player not in folded, 'No need to verify your hand because you folded.'
501
active_players = hands[hand_id, 'active_players']
502
assert player in active_players, 'You are not an active player in this hand.'
503
504
# Check if player has bet enough
505
bet_should_equal = hands[hand_id, 'current_bet']
506
assert bet_should_equal is not None, 'There is no current bet.'
507
508
player_bet = hands[hand_id, player, 'bet']
509
assert player_bet is not None, 'You have not bet yet.'
510
511
assert bet_should_equal == player_bet, 'Bets have not stabilized.'
512
513
# For verification purposes
514
house_encrypted_hand = hashlib.sha3(player_hand_str)
515
516
previous_house_encrypted_hand = hands[hand_id, player, 'house_encrypted_hand']
517
518
verified = previous_house_encrypted_hand is not None and \
519
previous_house_encrypted_hand == house_encrypted_hand
520
521
if not verified:
522
# BAD ACTOR NEEDS TO BE PUNISHED
523
folded.append(player)
524
hands[hand_id, 'folded'] = folded
525
526
return 'Verification failed. Your hand has been forfeited.'
527
528
else:
529
cards = player_hand_str.split(':')[0].split(',')
530
531
game_type = hands[hand_id, 'game_type']
532
533
if game_type == BLIND_POKER:
534
j = 0
535
for p in active_players:
536
if p != player:
537
if p not in folded:
538
card = cards[j]
539
rank = evaluate_hand([card])
540
if hands[hand_id, p, 'rank'] is None:
541
hands[hand_id, p, 'rank'] = rank
542
hands[hand_id, p, 'hand'] = card
543
j += 1
544
else:
545
rank = evaluate_hand(cards)
546
hands[hand_id, player, 'rank'] = rank
547
hands[hand_id, player, 'hand'] = cards
548
549
return 'Verification succeeded.'
550
551
def find_winners(ranks: dict, players: list) -> list:
552
sorted_rank_values = sorted(ranks.keys(), reverse=True)
553
player_set = set(players)
554
for rank in sorted_rank_values:
555
players_with_rank = ranks[rank]
556
intersection = player_set.intersection(set(players_with_rank))
557
if len(intersection) > 0:
558
# Found players
559
winners = list(intersection)
560
break
561
return winners
562
563
def calculate_ranks(hand_id: str, players: list) -> dict:
564
ranks = {}
565
for p in players:
566
rank = hands[hand_id, p, 'rank']
567
assert rank is not None, f'Player {p} has not verified their hand yet.'
568
if rank not in ranks:
569
ranks[rank] = []
570
ranks[rank].append(p)
571
return ranks
572
573
@export
574
def payout_hand(hand_id: str):
575
pot = hands[hand_id, 'pot']
576
assert pot > 0, 'There is no pot to claim!'
577
assert not hands[hand_id, 'payed_out'], 'This hand has already been payed out.'
578
579
folded = hands[hand_id, 'folded']
580
all_in = hands[hand_id, 'all_in']
581
active_players = hands[hand_id, 'active_players']
582
583
remaining = [p for p in active_players if p not in folded]
584
assert len(remaining) > 0, 'There are no remaining players.'
585
586
payouts = {}
587
588
if len(remaining) == 1:
589
# Just pay out, everyone else folded
590
payouts[remaining[0]] = pot
591
else:
592
ranks = calculate_ranks(hand_id, remaining)
593
if len(all_in) > 0:
594
# Need to calculate split pots
595
all_in_map = {}
596
for player in all_in:
597
# Check all in amount
598
amount = hands[hand_id, player, 'max_bet']
599
all_in_map[player] = amount
600
pots = sorted(set(all_in_map.values()))
601
total_payed_out = 0
602
for bet in pots:
603
players_in_pot = []
604
for player in remaining:
605
if player not in all_in_map or all_in_map[player] >= bet:
606
players_in_pot.append(player)
607
pot_winners = find_winners(ranks, players_in_pot)
608
pot_payout = bet * len(players_in_pot)
609
total_payed_out += pot_payout
610
payout = pot_payout / len(pot_winners)
611
for winner in pot_winners:
612
if winner not in payouts:
613
payouts[winner] = 0
614
payouts[winner] += payout
615
remaining_to_payout = pot - total_payed_out
616
not_all_in = set(remaining).difference(set(all_in))
617
assert remaining_to_payout == 0 or len(not_all_in) > 0, 'Invalid state when calculating side pots.'
618
if remaining_to_payout > 0:
619
if len(not_all_in) == 1:
620
winners = not_all_in
621
else:
622
winners = find_winners(ranks, not_all_in)
623
payout = remaining_to_payout / len(winners)
624
for winner in winners:
625
if winner not in payouts:
626
payouts[winner] = 0
627
payouts[winner] += payout
628
else:
629
winners = find_winners(ranks, remaining)
630
payout = pot / len(winners)
631
for winner in winners:
632
payouts[winner] = payout
633
634
game_id = hands[hand_id, 'game_id']
635
for player, payout in payouts.items():
636
games[game_id, player] += payout
637
638
hands[hand_id, 'winners'] = list(payouts.keys())
639
hands[hand_id, 'payed_out'] = True
640
641
@export
642
def emergency_withdraw(amount: float):
643
assert ctx.caller == owner.get(), 'Only the owner can call emergency_withdraw()'
644
phi.transfer(
645
amount=amount,
646
to=ctx.caller
647
)
648
649
@export
650
def emergency_game_update(keys: list, value: Any):
651
assert ctx.caller == owner.get(), 'Only the owner can call emergency_game_update()'
652
if len(keys) == 1:
653
games[keys[0]] = value
654
elif len(keys) == 2:
655
games[keys[0], keys[1]] = value
656
elif len(keys) == 3:
657
games[keys[0], keys[1], keys[2]] = value
658
659
@export
660
def emergency_hand_update(keys: list, value: Any):
661
assert ctx.caller == owner.get(), 'Only the owner can call emergency_hand_update()'
662
if len(keys) == 1:
663
hands[keys[0]] = value
664
elif len(keys) == 2:
665
hands[keys[0], keys[1]] = value
666
elif len(keys) == 3:
667
hands[keys[0], keys[1], keys[2]] = value
668
669
@export
670
def change_ownership(new_owner: str):
671
assert ctx.caller == owner.get(), 'Only the owner can change ownership!'
672
673
owner.set(new_owner)
Byte Code
e30000000000000000000000000a000000400000007394030000640064016c005a01640064016c025a0365045a056506640264036404640564068d045a07650864016404640764088d035a09650864016404640964088d035a0a650864016404640a64088d035a0b650867006404640b64088d035a0c650867006404640c64088d035a0d650867006404640d64088d035a0e650f6404640e640f8d025a10650f64046410640f8d025a11650f64046411640f8d025a1264125a1364005a1464135a1564145a1664155a17651465156516651767045a1864005a1964135a1a6519651a67025a1b6514641665156416651664176517641869045a1c651d6a1e830001006419641a84005a1f6520640483016521641b9c01641c641d840483015a226520640483016521641b9c01641e641f840483015a236521652464209c026421642284045a256521652164239c026424642584045a266521652164209c026426642784045a276528652964289c026429642a84045a2a65206404830165216521642b9c02642c642d840483015a2b65206404830165216521642e9c02642f6430840483015a2c6520640483016521652d64319c0264326433840483015a2e6520640483016521652d64319c0264346435840483015a2f6520640483016521653064369c0264376438840483015a316520640483016439643a840083015a3265216528643b9c02643c643d84045a336521643e9c01643f644084045a346520640483016518651b6441660365216528652d652865286530652164429c0764436444840583015a356520640483016521652164459c0264466447840483015a36652064048301652164489c016449644a840483015a376520640483016521652965296521644b9c04644c644d840483015a3865286529644e9c02644f645084045a39652064048301652164519c0164526453840483015a3a652064048301652164519c0164546455840483015a3b6528652865286521652164569c056457645884045a3c6520640483016521652d64599c02645a645b840483015a3d652064048301652165216521645c9c03645d645e840483015a3e652465286528645f9c036460646184045a3f65216528652464629c036463646484045a40652064048301652164519c0164656466840483015a41652064048301652d64679c0164686469840483015a4265206404830165286543646a9c02646b646c840483015a4465206404830165286543646a9c02646d646e840483015a456520640483016521646f9c0164706471840483015a46640153002972e9000000004eda0e636f6e5f7068695f6c7374303031da0862616c616e636573da17636f6e5f706f6b65725f636172645f67616d65735f7631da0c7068695f62616c616e6365732904da10666f726569676e5f636f6e7472616374da0c666f726569676e5f6e616d65da08636f6e7472616374da046e616d65da0567616d65732903da0d64656661756c745f76616c756572080000007209000000da0568616e6473da0a67616d655f6e616d6573da0d706c61796572735f67616d6573da0f706c61796572735f696e7669746573da0d6d657373616765735f68617368da18706c617965725f6d657461646174615f636f6e7472616374290272080000007209000000da1d706f6b65725f68616e645f6576616c7561746f725f636f6e7472616374da056f776e657269ffe0f505e901000000e902000000e903000000e932000000e90a000000e907000000630000000000000000000000000200000043000000732400000074006a0174026a038301010074046a0164018301010074056a016402830101006400530029034eda18636f6e5f67616d6d615f7068695f70726f66696c655f7634da1b636f6e5f706f6b65725f68616e645f6576616c7561746f725f76312906da075f5f6f776e6572da03736574da03637478da0663616c6c6572da1a5f5f706c617965725f6d657461646174615f636f6e7472616374da1f5f5f706f6b65725f68616e645f6576616c7561746f725f636f6e7472616374a90072220000007222000000da00da045f5f5f5f26000000730600000000010c010a01722400000029017208000000630100000000000000010000000200000043000000732400000074006a0174026a0383006b027316740464018301820174056a067c00830101006400530029024e7a394f6e6c7920746865206f776e65722063616e2063616c6c207570646174655f706c617965725f6d657461646174615f636f6e747261637428292907721e000000721f000000721c000000da03676574da0e417373657274696f6e4572726f727220000000721d00000029017208000000722200000072220000007223000000da1f7570646174655f706c617965725f6d657461646174615f636f6e74726163742c00000073060000000002100106017227000000630100000000000000010000000200000043000000732400000074006a0174026a0383006b027316740464018301820174056a067c00830101006400530029024e7a394f6e6c7920746865206f776e65722063616e2063616c6c207570646174655f706c617965725f6d657461646174615f636f6e747261637428292907721e000000721f000000721c000000722500000072260000007221000000721d00000029017208000000722200000072220000007223000000da247570646174655f706f6b65725f68616e645f6576616c7561746f725f636f6e747261637433000000730600000000021001060172280000002902da0767616d655f6964da0672657475726e630100000000000000020000000400000043000000732800000074007c006401660219007d017c0164006b097324740164027c009b0064039d03830182017c01530029044eda07706c61796572737a0547616d65207a1020646f6573206e6f742065786973742e2902da075f5f67616d6573722600000029027229000000722b000000722200000072220000007223000000da1f5f5f6765745f706c61796572735f616e645f6173736572745f6578697374733a000000730600000000010c011801722d0000002902da0763726561746f72722a000000630100000000000000010000000500000043000000731800000074006a0164016a027c00740374048301670283018301530029024efa013a2905da07686173686c6962da0473686133da046a6f696eda03737472da036e6f772901722e000000722200000072220000007223000000da105f5f6372656174655f67616d655f696440000000730200000000017235000000630100000000000000010000000500000043000000731800000074006a0164016a027c00740374048301670283018301530029024e722f00000029057230000000723100000072320000007233000000723400000029017229000000722200000072220000007223000000da105f5f6372656174655f68616e645f6964440000007302000000000172360000002902da0468616e64722a000000630100000000000000020000000200000043000000731800000074006a0174026a03830083017d017c016a047c008301530029014e2905da0149da0d696d706f72745f6d6f64756c6572210000007225000000da086576616c7561746529027237000000da03706865722200000072220000007223000000da0f5f5f6576616c756174655f68616e6448000000730400000000010e01723c00000029027229000000da076d657373616765630200000000000000050000000400000043000000734800000074006a017d0274027c0083017d037c027c036b06731e740364018301820174047c007c0266021900702c67007d047c046a057c01830101007c0474047c007c0266023c006400530029024e7a1f596f7520646f206e6f742062656c6f6e6720746f20746869732067616d652e2906721e000000721f000000722d0000007226000000da0f5f5f6d657373616765735f68617368da06617070656e6429057229000000723d000000da06706c61796572722b000000da086d65737361676573722200000072220000007223000000da0c67616d655f6d6573736167654d000000730c000000000206010801100110010a0172420000002902da0768616e645f6964723d000000630200000000000000050000000400000043000000734c00000074006a017d0274027c006401660219007d037c027c036b067322740364028301820174047c007c0266021900703067007d047c046a057c01830101007c0474047c007c0266023c006400530029034eda0e6163746976655f706c61796572737a1f596f7520646f206e6f742062656c6f6e6720746f20746869732068616e642e2906721e000000721f000000da075f5f68616e64737226000000723e000000723f00000029057243000000723d000000724000000072440000007241000000722200000072220000007223000000da0c68616e645f6d65737361676557000000730c000000000206010c01100110010a01724600000029027229000000da06616d6f756e74630200000000000000040000000400000043000000737c00000074006a017d027c0164016b047316740264028301820174037c0083017d037c027c036b06732e740264038301820174047c007c026602190070407405640483017c01170074047c007c0266023c0074067c0274006a07660219007c016b057368740264058301820174086a097c0174006a077c02830301006400530029064e72010000007a20416d6f756e74206d757374206265206120706f736974697665206e756d6265727a1f596f7520646f206e6f742062656c6f6e6720746f20746869732067616d652e7a03302e307a35596f752068617665206e6f7420617070726f76656420656e6f75676820666f72207468697320616d6f756e74206f66206368697073290a721e000000721f0000007226000000722d000000722c000000da07646563696d616cda0e5f5f7068695f62616c616e636573da0474686973da03706869da0d7472616e736665725f66726f6d2904722900000072470000007240000000722b000000722200000072220000007223000000da116164645f63686970735f746f5f67616d656100000073120000000002060110010801100112010e010c010e01724d000000630200000000000000050000000400000043000000736c00000074006a017d027c0164016b047316740264028301820174037c0083017d037c027c036b06732e740264038301820174047c007c02660219007d047c047c016b05734a74026404830182017c047c01180074047c007c0266023c0074056a067c017c0264058d0201006400530029064e72010000007a20416d6f756e74206d757374206265206120706f736974697665206e756d6265727a1f596f7520646f206e6f742062656c6f6e6720746f20746869732067616d652e7a27596f752063616e6e6f74207769746864726177206d6f7265207468616e20796f7520686176652e29027247000000da02746f2907721e000000721f0000007226000000722d000000722c000000724b000000da087472616e736665722905722900000072470000007240000000722b000000da1263757272656e745f636869705f636f756e74722200000072220000007223000000da1877697468647261775f63686970735f66726f6d5f67616d656e0000007310000000000206011001080110010c0110011001725100000029027229000000da0661636365707463020000000000000006000000040000004300000073f600000074006a017d0274027c021900701067007d0374037c0083017d047c027c046b07732a740464018301820174027c02640266021900703867007d057c007c036b06735e7c007c056b06735e74057c00640366021900735e74046404830182017c007c036b0672787c036a067c00830101007c0374027c023c007c0174027c027c0066023c007c0172d47c007c056b0672a67c056a067c00830101007c0574027c02640266023c007c046a077c02830101007c0474057c00640566023c0074087c02190070c667007c006701170074087c023c006e1e7c007c056b0772f27c056a077c00830101007c0574027c02640266023c006400530029064e7a24596f752061726520616c726561647920612070617274206f6620746869732067616d652eda086465636c696e6564da067075626c69637a27596f752068617665206e6f74206265656e20696e766974656420746f20746869732067616d652e722b0000002909721e000000721f000000da115f5f706c61796572735f696e7669746573722d0000007226000000722c000000da0672656d6f7665723f000000da0f5f5f706c61796572735f67616d65732906722900000072520000007240000000da0e706c617965725f696e7669746573722b0000007253000000722200000072220000007223000000da11726573706f6e645f746f5f696e766974657a000000732a000000000206010c010801100110011201120108010a0108010c01040108010a010c010a010c01180108010a017259000000630000000000000000030000000500000043000000733800000074006a017d0074027c001900701067007d0178187c0144005d107d02640174027c007c0266023c0071185700670074027c003c006400530029024e462903721e000000721f000000725500000029037240000000da07696e7669746573da06696e76697465722200000072220000007223000000da136465636c696e655f616c6c5f696e766974657393000000730a000000000206010c010a011001725c00000029027229000000da066f74686572736302000000000000000400000004000000430000007330000000782a7c0144005d227d0274007c021900701467007d037c036a017c00830101007c0374007c023c00710657006400530029014e29027255000000723f00000029047229000000725d000000da056f746865727258000000722200000072220000007223000000da165f5f73656e645f696e766974655f72657175657374739c000000730800000000010a010c010a01725f0000002901720900000063010000000000000001000000030000004300000073800000007c0064006b09721474007c00830164016b04731c740164028301820174027c0074038302732e740164038301820174007c00830164046b017342740164058301820174046406640784007c00440083018301735c74016408830182017c0064011900640d6b0772747c00640e1900640f6b07737c7401640c830182016400530029104e72010000007a2147616d65206e616d652063616e6e6f74206265206e756c6c206f7220656d7074797a1b47616d65206e616d65206d757374206265206120737472696e672ee9180000007a2e47616d65206e616d652063616e6e6f74206265206c6f6e676572207468616e20323420636861726163746572732e630100000000000000020000000500000053000000731c00000067007c005d147d017c016a00830070167c0164026b069102710453002903da015ffa012d2902726100000072620000002901da076973616c6e756d2902da022e30da0163722200000072220000007223000000fa0a3c6c697374636f6d703ea8000000730200000006007a285f5f76616c69646174655f67616d655f6e616d652e3c6c6f63616c733e2e3c6c697374636f6d703e7a6247616d65206e616d652068617320696e76616c696420636861726163746572732e204561636820636861726163746572206d75737420626520616c7068616e756d657269632c20612068797068656e2c206f7220616e20756e64657273636f72652e7262000000726100000072140000007a3a47616d65206e616d652063616e6e6f74207374617274206f7220656e64207769746820612068797068656e206f7220756e64657273636f72652e290272620000007261000000e9ffffffff2902726200000072610000002905da036c656e7226000000da0a6973696e7374616e63657233000000da03616c6c29017209000000722200000072220000007223000000da145f5f76616c69646174655f67616d655f6e616d65a3000000731000000000010e010e0112011401140106011a01726b0000004629077209000000da0d6f746865725f706c6179657273da04616e7465da12616c6c6f7765645f67616d655f7479706573da15616c6c6f7765645f62657474696e675f74797065737254000000722a0000006306000000000000000a0000000500000043000000736c01000074006a017d0674027c03830164016b04731a740364028301820178247c0344005d1c7d077c0774046b067320740364037c079b0064049d03830182017120570074027c04830164016b047354740364058301820178247c0444005d1c7d087c0874056b06735a740364067c089b0064049d0383018201715a57007c0264016b05738a74036407830182017c067c016b07739a740364088301820174067c0664098d017d0974077c09640a6602190064006b0873c47403640b7c099b00640c9d038301820174087c008301010074097c00190064006b0873e87403640b7c009b00640c9d03830182017c0974097c003c007c06670174077c09640d66023c007c0274077c09640e66023c007c0674077c09640a66023c007c0174077c09640f66023c007c0574077c09641066023c007c0374077c09641166023c007c0474077c09641266023c00740a7c0619009001705267007c0967011700740a7c063c00740b7c097c01830201007c09530029134e72010000007a224d75737420616c6c6f77206174206c65617374206f6e652067616d6520747970652e7a13496e76616c69642067616d6520747970653a20da012e7a254d75737420616c6c6f77206174206c65617374206f6e652062657474696e6720747970652e7a16496e76616c69642062657474696e6720747970653a207a1a416e7465206d757374206265206e6f6e2d6e656761746976652e7a2743616c6c65722063616e277420626520696e206f746865725f706c617965727320696e7075742e2901722e000000722e0000007a0547616d65207a1a2068617320616c7265616479206265656e20637265617465642e722b000000726d000000da08696e7669746565737254000000726e000000726f000000290c721e000000721f00000072680000007226000000da0e414c4c5f47414d455f5459504553da11414c4c5f42455454494e475f54595045537235000000722c000000726b000000da0c5f5f67616d655f6e616d65737257000000725f000000290a7209000000726c000000726d000000726e000000726f0000007254000000722e000000da0967616d655f74797065da086265745f747970657229000000722200000072220000007223000000da0a73746172745f67616d65ae00000073340000000004060114010a011c0106010e010a011c01100110010a010a01160108011c0108010e010c010c010c010c010c010c0118010a01727700000029027229000000da0d706c617965725f746f5f616464630200000000000000060000000400000043000000738c00000074006a017d027c027c016b037316740264018301820174037c006402660219007d037c027c036b027332740264038301820174047c0083017d047c017c046b07734a740264048301820174037c006405660219007d057c017c056b07736674026406830182017c056a057c01830101007c0574037c00640566023c0074067c007c016701830201006400530029074e7a22596f752063616e6e6f742061646420796f757273656c6620746f20612067616d652e722e0000007a264f6e6c79207468652067616d652063726561746f722063616e2061646420706c61796572732e7a1e506c6179657220697320616c726561647920696e207468652067616d652e72710000007a20506c617965722068617320616c7265616479206265656e20696e76697465642e2907721e000000721f0000007226000000722c000000722d000000723f000000725f0000002906722900000072780000007240000000722e000000722b0000007271000000722200000072220000007223000000da126164645f706c617965725f746f5f67616d65ce00000073160000000002060110010c011001080110010c0110010a010c017279000000290172290000006301000000000000000a0000000500000043000000734201000074006a017d0174027c0083017d027c017c026b06731e740364018301820174047c007c01660219007d037c0364006b0873427c0364026b027342740364038301820174057c0119007d047c046a067c00830101007c0474057c013c007c026a067c01830101007c0274047c00640466023c0074047c006405660219007d057c0564006b099001723e74077c05640666021900709667007d067c017c066b069001723e74077c056407660219007d0774077c056408660219007d0874077c056409660219007d097c097c016b0272e874087c067c077c087c0183047d097c0974077c05640966023c007c066a067c01830101007c0674077c05640666023c007c017c076b069001721e7c076a067c01830101007c0774077c05640766023c007c017c086b069001723e7c086a067c01830101007c0874077c05640866023c0064005300290a4e7a19596f7520617265206e6f7420696e20746869732067616d652e72010000007a47596f75207374696c6c206861766520636869707320696e20746869732067616d652e20506c65617365207769746864726177207468656d206265666f7265206c656176696e672e722b000000da0c63757272656e745f68616e647244000000da06666f6c646564da06616c6c5f696eda0b6e6578745f6265747465722909721e000000721f000000722d0000007226000000722c000000725700000072560000007245000000da115f5f6765745f6e6578745f626574746572290a72290000007240000000722b000000da056368697073da0c706c617965725f67616d657372430000007244000000727b000000727c000000727d000000722200000072220000007223000000da0a6c656176655f67616d65dd000000733a00000000020601080110010c01180108010a0108010a010c010c010a0110010a010c010c010c010801060108010c010a010c010a010a010c010a010a0172810000002904722900000072750000007276000000722a000000630300000000000000090000000400000043000000732e01000074006a017d0374027c006401660219007d047c017c046b067322740364028301820174027c006403660219007d057c027c056b06733e740364048301820174047c0083017d067c037c066b067356740364058301820174057c06830164066b04736a740364078301820174027c006408660219007d077c0764006b09729274067c0764096602190073927403640a8301820174077c0083017d087c0874027c00640866023c007c0074067c08640b66023c007c0174067c08640c66023c007c0274067c08640d66023c007c0374067c08640e66023c00670074067c08640f66023c00641074067c08641166023c00641074067c08640966023c00641074067c08641266023c00670074067c08641366023c00641474067c08641566023c00670074067c08641666023c007c08530029174e726e0000007a12496e76616c69642067616d6520747970652e726f0000007a11496e76616c69642062657420747970652e7a20596f7520617265206e6f7420612070617274206f6620746869732067616d652e72140000007a24596f752063616e6e6f7420737461727420612068616e6420627920796f757273656c662e727a000000da0970617965645f6f75747a2d5468652070726576696f75732068616e6420686173206e6f74206265656e207061796564206f7574207965742e722900000072750000007276000000da066465616c6572727b00000046da09636f6d706c65746564da0e726561636865645f6465616c657272440000007201000000da0b63757272656e745f626574727c0000002908721e000000721f000000722c0000007226000000722d00000072680000007245000000723500000029097229000000727500000072760000007283000000726e000000726f000000722b000000da1070726576696f75735f68616e645f69647243000000722200000072220000007223000000da0a73746172745f68616e64fe0000007334000000000206010c0110010c0110010801100114010c0108010e01060108010c010c010c010c010c010c010c010c010c010c010c010c0172880000002902722b000000722a0000006301000000000000000200000003000000030000007310000000870066016401640284087d017c01530029034e630100000000000000010000000200000013000000730a00000088006a007c008301530029014e2901da05696e646578290172400000002901722b00000072220000007223000000da065f5f736f72741e010000730200000000017a245f5f6163746976655f706c617965725f736f72742e3c6c6f63616c733e2e5f5f736f727472220000002902722b000000728a00000072220000002901722b0000007223000000da145f5f6163746976655f706c617965725f736f72741c010000730400000000020c02728b000000290172430000006301000000000000000a0000000500000043000000734c01000074006a017d0174027c006401660219007d027c0264006b097322740364028301820174047c0283017d037c017c036b06733a740364038301820174057c026404660219007d0474057c027c01660219007d057c0564006b0972627c057c046b05736a740364058301820174027c00640666021900707867007d067c017c066b07738a740364078301820174027c006408660219007d0774067c0719007d0874077c0683017c086b0073ba740364097c089b00640a9d03830182017c0474027c007c01640b66033c007c0574027c007c01640c66033c0074057c027c016602050019007c04380003003c007c066a087c01830101007c066a09740a7c038301640d8d0101007c0674027c00640666023c007c0474027c00640e66023c007c057c046b029001724874027c00640f660219007d097c096a087c01830101007c0974027c00640f66023c006400530029104e72290000007a19546869732067616d6520646f6573206e6f742065786973742e7a20596f7520617265206e6f7420612070617274206f6620746869732067616d652e726d0000007a1d596f7520646f206e6f74206861766520656e6f7567682063686970732e72440000007a1f596f75206861766520616c726561647920706169642074686520616e74652e72750000007a0d41206d6178696d756d206f66207a1f20697320616c6c6f77656420666f7220746869732067616d6520747970652eda03626574da076d61785f6265742901da036b65797286000000727c000000290b721e000000721f00000072450000007226000000722d000000722c000000da184d41585f504c41594552535f42595f47414d455f545950457268000000723f000000da04736f7274728b000000290a724300000072400000007229000000722b000000726d000000727f00000072440000007275000000da0b6d61785f706c6179657273727c000000722200000072220000007223000000da07616e74655f7570230100007332000000000206010c011001080110010c010c011801100110010c010801060116010e010e0114010a0110010c010c010a010c010a017292000000630100000000000000120000003400000043000000731603000074006a017d0174027c006401660219007d027c0174027c006402660219006b02732a740364038301820174047c02830164046b04734a7403640574047c0283019b0064069d03830182017c017c026b06735a740364078301820174027c006408660219007d03740574066a0783006409640a640b640c8d047d04640d640e640f6410641164126413641464156416641764186419641a641b641c641d641e641f6420642164226423642464256426642764286429642a642b642c642d642e642f6430643164326433643464356436643764386439643a643b643c643d643e643f644067347d0574086a097c0583010100900178d4740a74047c0283018301440090015dc27d067c027c0619007d077c047c076441660219007d087c0864006b0990017334740364427c079b0064439d03830182017c086a0b644483017d0974047c09830164456b029001735474036446830182017c03740c6b02900172707c057c067c0664041700850219007d0a6ee47c03740d6b02900172d27c0564477c06850219007c057c066404170074047c0283018502190017007d0a74047c0a830174047c028301640418006b02900273547403644874047c0a83019b00644974047c028301640418009b009d04830182016e827c03740e6b029002720c7c05644a7c061400644a7c061400644a1700850219007d0a74047c0a8301644a6b02900273547403644b830182016e487c03740f6b02900272467c05644c7c061400644c7c061400644c1700850219007d0a74047c0a8301644c6b02900273547403644b830182016e0e644d900273547403644e83018201644f6a107c0a83017d0b741174086a1264477413830283017d0c7c0b9b0064507c0c9b009d037d0d74146a157c0d74167c0964471900830174167c0964041900830164518d037d0e74176a187c0d83017d0f7c0e74027c007c07645266033c007c0f74027c007c07645366033c0090017100570074027c006454660219007d1074197c0267007c107c01830474027c00645566023c00741a74027c006456660219006457660219007d117c1174047c028301140074027c00645866023c006400530029594e724400000072830000007a17596f7520617265206e6f7420746865206465616c65722e72140000007a1b4e6f7420656e6f7567682061637469766520706c61796572733a207a05203c3d20317a27596f7520617265206e6f74206163746976656c792070617274206f6620746869732068616e642e7275000000da086d657461646174617204000000da0f706c617965725f6d6574616461746129047206000000720700000072080000007209000000da023263da023264da023268da023273da023363da023364da023368da023373da023463da023464da023468da023473da023563da023564da023568da023573da023663da023664da023668da023673da023763da023764da023768da023773da023863da023864da023868da023873da023963da023964da023968da023973da025463da025464da025468da025473da024a63da024a64da024a68da024a73da025163da025164da025168da025173da024b63da024b64da024b68da024b73da024163da024164da024168da024173da0e7075626c69635f7273615f6b65797a07506c61796572207a2520686173206e6f7420736574757020746865697220656e6372797074696f6e206b6579732efa017c72150000007a0c496e76616c6964206b65797372010000007a16536f6d657468696e672077656e742077726f6e672e207a0420213d20e9050000007a15536f6d657468696e672077656e742077726f6e672e7219000000467a12496e76616c69642067616d6520747970652efa012c722f0000002903da0b6d6573736167655f737472da016eda0165da15706c617965725f656e637279707465645f68616e64da14686f7573655f656e637279707465645f68616e64727c000000727d0000007229000000726d000000da03706f74291b721e000000721f000000724500000072260000007268000000da0b466f726569676e4861736872200000007225000000da0672616e646f6dda0773687566666c65da0572616e6765da0573706c6974da0e4f4e455f434152445f504f4b4552da0b424c494e445f504f4b4552da0e464956455f434152445f53545544da0f534556454e5f434152445f5354554472320000007233000000da0772616e64696e74da114d41585f52414e444f4d5f4e554d424552da03727361da07656e6372797074da03696e7472300000007231000000727e000000722c00000029127243000000728300000072440000007275000000da115f5f706c617965725f6d65746164617461da056361726473da01697240000000da0a706c617965725f6b6579da046b657973da0b706c617965725f68616e64da0f706c617965725f68616e645f737472da0473616c74da19706c617965725f68616e645f7374725f776974685f73616c7472d000000072d1000000727c000000726d000000722200000072220000007223000000da0a6465616c5f6361726473400100007360000000000206010c01180106011a0110010c01020108010a01140116011601160116010a01160108010c011a010a0116010a0112010a0120010c012c010a01180118010a01180118020e010a0110010e0104011c010a020e0114010c0106011001140172ea0000002905722b000000727b000000727c000000da0e63757272656e745f626574746572722a000000630400000000000000060000000400000003000000736e00000074008801830174007c008301640118006b0572186400530074007c0083017400880083016b02722c640053008700870166026402640384087c00440083017d047c046a017c0383017d057c0564046b05735a74026405830182017c047c056401170074007c04830116001900530029064e7214000000630100000000000000020000000400000013000000732000000067007c005d187d017c0188016b0772047c0188006b0772047c019102710453007222000000722200000029027264000000da01702902727c000000727b0000007222000000722300000072660000007c010000730400000006000c017a255f5f6765745f6e6578745f6265747465722e3c6c6f63616c733e2e3c6c697374636f6d703e72010000007a3543757272656e74206265747465722068617320666f6c6465642c20776869636820646f6573206e6f74206d616b652073656e73652e29037268000000728900000072260000002906722b000000727b000000727c00000072eb000000da126e6f6e5f666f6c6465645f706c6179657273da0d63757272656e745f696e64657872220000002902727c000000727b0000007223000000727e0000007601000073100000000002140104011001040114020a011001727e00000029027243000000728c00000063020000000000000011000000050000004300000073f602000074006a017d0274027c007c0264016603190064006b097320740364028301820174027c006403660219000c007336740364048301820174027c006405660219007c026b02734e740364068301820174027c006407660219007d0374027c006408660219007d0474027c0064096602190070787404640a83017d0574027c007c02640b66031900708e7404640a83017d0674027c00640c660219007d077c077c026b0272b6640d74027c00640e66023c00640d7d086e0c74027c00640e660219007d0874027c00640f660219007d0974057c037c047c097c0283047d0a7c0a64006b0872f4640d74027c00640366023c0090016ef27c0164106b009001725a7c046a067c02830101007c0474027c00640866023c007c027c096b06900172347c096a077c02830101007c0974027c00640f66023c0074087c04830174087c038301641118006b02900272e6640d74027c00640366023c0090016e8c7c0164106b02900172ee74027c007c026412660319007d0b7c0b7c066b029001729c7c027c096b079001729c7c096a067c02830101007c0974027c00640f66023c007c0b7c066b02900173b87c067c056b05900173b8740364138301820174027c007c0a640b660319007d0c7c0c64006b09900272e67c0c7c056b02900272e67c08900272e6640d74027c00640366023c006ef874027c006414660219007d0d74097c0d7c02660219007c016b0590027314740364158301820174027c006416660219007d0e7c0e740a6b029002724874027c006417660219007d0f7c017c0f6b019002734874036418830182017c067c0117007d1074027c007c026412660319007d0b7c0b7c106b02900272887c027c096b07900272887c096a067c02830101007c0974027c00640f66023c007c0b7c106b02900273a47c107c056b05900273a474036419830182017c1074027c007c02640b66033c007c1074027c00640966023c0074027c0064176602050019007c01370003003c0074097c0d7c026602050019007c01380003003c007c0a74027c00640566023c0064005300291a4e72d00000007a1348616e6420646f6573206e6f7420657869737472840000007a20546869732068616e642068617320616c726561647920636f6d706c657465642e727d0000007a1b4974206973206e6f7420796f7572207475726e20746f206265742e7244000000727b00000072860000007a03302e30728c0000007283000000547285000000727c00000072010000007214000000728d0000007a5443616e6e6f7420636865636b20696e2074686973207363656e6172696f2e2043757272656e74206265742069732061626f766520796f75722062657420616e6420796f7520617265206e6f7420616c6c20696e2e72290000007a2d596f7520646f206e6f74206861766520656e6f75676820636869707320746f206d616b65207468697320626574727600000072d20000007a2943616e6e6f74206f7665726265742074686520706f7420696e20706f742d6c696d6974206d6f64652e7a3843757272656e74206265742069732061626f766520796f75722062657420616e6420796f7520646964206e6f7420676f20616c6c20696e2e290b721e000000721f000000724500000072260000007248000000727e000000723f00000072560000007268000000722c000000da09504f545f4c494d495429117243000000728c00000072400000007244000000727b000000da0863616c6c5f626574da13706c617965725f70726576696f75735f62657472830000007285000000727c000000727d000000728d000000da106e6578745f706c61796572735f6265747229000000727600000072d20000007286000000722200000072220000007223000000da116265745f636865636b5f6f725f666f6c64830100007370000000000206010c010e01100106010a010e010c010c01140116010c0108010c0106020c010c010e01080110010a010a010c010a010a010c01160110010a010e0114010a010c011c010e01140106010e020c010a0110010c010a010c01120108010e0114010a010c011c010e010c011401140172f30000002903724300000072e7000000722a00000063020000000000000010000000060000004300000073ac01000074006a017d0274027c00640166021900731a740364028301820174027c006403660219007d037c027c036b077336740364048301820174027c006405660219007d047c027c046b067352740364068301820174027c006407660219007d057c0564006b09736e740364088301820174027c007c026409660319007d067c0664006b09738c7403640a830182017c057c066b02739c7403640b8301820174046a057c0183017d0774027c007c02640c660319007d087c0864006b096fc27c087c076b027d097c0973e27c036a067c02830101007c0374027c00640366023c00640d53007c016a07640e8301640f19006a07641083017d0a74027c006411660219007d0b7c0b74086b0290017280640f7d0c78927c0444005d647d0d7c0d7c026b03900172167c0d7c036b07900172707c0a7c0c19007d0e74097c0e670183017d0f74027c007c0d64126603190064006b08900172707c0f74027c007c0d641266033c007c0e74027c007c0d641366033c007c0c641437007d0c9001711657006e2474097c0a83017d0f7c0f74027c007c02641266033c007c0a74027c007c02641366033c00641553006400530029164e72840000007a20546869732068616e6420686173206e6f7420636f6d706c65746564207965742e727b0000007a2f4e6f206e65656420746f2076657269667920796f75722068616e64206265636175736520796f7520666f6c6465642e72440000007a2a596f7520617265206e6f7420616e2061637469766520706c6179657220696e20746869732068616e642e72860000007a185468657265206973206e6f2063757272656e74206265742e728c0000007a15596f752068617665206e6f7420626574207965742e7a19426574732068617665206e6f742073746162696c697a65642e72d10000007a32566572696669636174696f6e206661696c65642e20596f75722068616e6420686173206265656e20666f726665697465642e722f000000720100000072cc0000007275000000da0472616e6b723700000072140000007a17566572696669636174696f6e207375636365656465642e290a721e000000721f0000007245000000722600000072300000007231000000723f00000072d700000072d9000000723c0000002910724300000072e70000007240000000727b0000007244000000da106265745f73686f756c645f657175616cda0a706c617965725f62657472d1000000da1d70726576696f75735f686f7573655f656e637279707465645f68616e64da08766572696669656472e20000007275000000da016a72ec000000da046361726472f4000000722200000072220000007223000000da0b7665726966795f68616e64c1010000734a0000000002060114010c0110010c0110010c0110010e01100110010a01060108010801080104010a010c01040214010c010a0104010a010a010a0108010a0114010e010e01100208010e010e0172fb0000002903da0572616e6b73722b000000722a000000630200000000000000080000000400000043000000735600000074007c006a018300640164028d027d0274027c0183017d0378387c0244005d307d047c007c0419007d057c036a0374027c05830183017d0674047c06830164036b04721e74057c0683017d075000711e57007c07530029044e542901da077265766572736572010000002906da06736f7274656472e5000000721d000000da0c696e74657273656374696f6e7268000000da046c697374290872fc000000722b000000da12736f727465645f72616e6b5f76616c756573da0a706c617965725f73657472f4000000da11706c61796572735f776974685f72616e6b72ff000000da0777696e6e657273722200000072220000007223000000da0e5f5f66696e645f77696e6e657273ec01000073120000000001100108010a0108010e010c0108010601720501000029037243000000722b000000722a000000630200000000000000050000000500000043000000735a00000069007d0278507c0144005d487d0374007c007c036401660319007d047c0464006b097334740164027c039b0064039d03830182017c047c026b07724467007c027c043c007c027c0419006a027c0383010100710a57007c02530029044e72f40000007a07506c61796572207a2120686173206e6f742076657269666965642074686569722068616e64207965742e290372450000007226000000723f00000029057243000000722b00000072fc00000072ec00000072f4000000722200000072220000007223000000da115f5f63616c63756c6174655f72616e6b73f80100007310000000000104010a010e011801080108011201720601000063010000000000000016000000070000000300000073bc02000074007c006401660219007d017c0164026b04731c740164038301820174007c006404660219000c007332740164058301820174007c00640666021900890074007c006407660219007d0274007c006408660219007d03870066016409640a84087c03440083017d0474027c04830164026b04737c7401640b8301820169007d0574027c048301640c6b02729c7c017c057c04640219003c0090016ec474037c007c0483027d0674027c02830164026b049002723269007d0778227c0244005d1a7d0874007c007c08640d660319007d097c097c077c083c0071be5700740474057c076a068300830183017d0a64027d0b789e7c0a44005d967d0c67007d0d788c7c0444005d847d087c087c076b07900173207c077c0819007c0c6b05900172047c0d6a077c088301010074087c067c0d83027d0e7c0c74027c0d830114007d0f7c0b7c0f37007d0b7c0f74027c0e83011b007d1078307c0e44005d287d117c117c056b079001727064027c057c113c007c057c11050019007c10370003003c009001715a570090017104570071f657007c017c0b18007d1274057c0483016a0974057c02830183017d137c1264026b02900173ca74027c13830164026b04900173ca7401640e830182017c1264026b049002726074027c138301640c6b02900172e87c137d146e0a74087c067c1383027d147c1274027c1483011b007d1078607c1444005d287d117c117c056b079002721a64027c057c113c007c057c11050019007c10370003003c009002710457006e2e74087c067c0483027d147c0174027c1483011b007d1078167c1444005d0e7d117c107c057c113c009002714e570074007c00640f660219007d15782a7c056a0a830044005d1e5c027d087d10740b7c157c086602050019007c10370003003c00900271765700740c7c056a0d8300830174007c00641066023c00641174007c00640466023c006400530029124e72d200000072010000007a195468657265206973206e6f20706f7420746f20636c61696d2172820000007a25546869732068616e642068617320616c7265616479206265656e207061796564206f75742e727b000000727c0000007244000000630100000000000000020000000400000013000000731800000067007c005d107d017c0188006b0772047c01910271045300722200000072220000002902726400000072ec0000002901727b0000007222000000722300000072660000000c020000730200000006007a1f7061796f75745f68616e642e3c6c6f63616c733e2e3c6c697374636f6d703e7a1f546865726520617265206e6f2072656d61696e696e6720706c61796572732e7214000000728d0000007a29496e76616c6964207374617465207768656e2063616c63756c6174696e67207369646520706f74732e7229000000720401000054290e724500000072260000007268000000720601000072fe000000721d000000da0676616c756573723f0000007205010000da0a646966666572656e6365da056974656d73722c000000720001000072e50000002916724300000072d2000000727c0000007244000000da0972656d61696e696e67da077061796f75747372fc000000da0a616c6c5f696e5f6d617072400000007247000000da04706f7473da0f746f74616c5f70617965645f6f7574728c000000da0e706c61796572735f696e5f706f74da0b706f745f77696e6e657273da0a706f745f7061796f7574da067061796f7574da0677696e6e6572da1372656d61696e696e675f746f5f7061796f7574da0a6e6f745f616c6c5f696e7204010000722900000072220000002901727b0000007223000000da0b7061796f75745f68616e6403020000736e00000000020c011001100106010c010c010c011201140104010c0110020a010e0104010a010e010c01100104010a0104010a0118010a010a010c0108010c010a010a010801200108011201100110010a010e0106020a010c010a010a01080118020a010c010a010e010c0112011a011401721601000029017247000000630100000000000000010000000400000043000000732a00000074006a0174026a0383006b027316740464018301820174056a067c0074006a0164028d0201006400530029034e7a2c4f6e6c7920746865206f776e65722063616e2063616c6c20656d657267656e63795f7769746864726177282929027247000000724e0000002907721e000000721f000000721c00000072250000007226000000724b000000724f00000029017247000000722200000072220000007223000000da12656d657267656e63795f77697468647261774102000073060000000002100106017217010000290272e5000000da0576616c7565630200000000000000020000000600000043000000737c00000074006a0174026a0383006b027316740464018301820174057c00830164026b0272307c0174067c00640319003c006e4874057c00830164046b0272527c0174067c00640319007c006402190066023c006e2674057c00830164056b0272787c0174067c00640319007c00640219007c006404190066033c006400530029064e7a2f4f6e6c7920746865206f776e65722063616e2063616c6c20656d657267656e63795f67616d655f757064617465282972140000007201000000721500000072160000002907721e000000721f000000721c000000722500000072260000007268000000722c000000290272e50000007218010000722200000072220000007223000000da15656d657267656e63795f67616d655f7570646174654802000073100000000002100106010c010e010c0116010c017219010000630200000000000000020000000600000043000000737c00000074006a0174026a0383006b027316740464018301820174057c00830164026b0272307c0174067c00640319003c006e4874057c00830164046b0272527c0174067c00640319007c006402190066023c006e2674057c00830164056b0272787c0174067c00640319007c00640219007c006404190066033c006400530029064e7a2f4f6e6c7920746865206f776e65722063616e2063616c6c20656d657267656e63795f68616e645f757064617465282972140000007201000000721500000072160000002907721e000000721f000000721c0000007225000000722600000072680000007245000000290272e50000007218010000722200000072220000007223000000da15656d657267656e63795f68616e645f7570646174655402000073100000000002100106010c010e010c0116010c01721a0100002901da096e65775f6f776e6572630100000000000000010000000200000043000000732400000074006a0174026a0383006b027316740464018301820174026a057c00830101006400530029024e7a244f6e6c7920746865206f776e65722063616e206368616e6765206f776e657273686970212906721e000000721f000000721c00000072250000007226000000721d0000002901721b010000722200000072220000007223000000da106368616e67655f6f776e65727368697060020000730400000000021601721c0100002947da12636f6e5f7273615f656e6372797074696f6e72de0000007202000000724b000000da09696d706f72746c6962723800000072d30000007249000000da0448617368722c0000007245000000727400000072570000007255000000723e000000da085661726961626c6572200000007221000000721c00000072dd00000072d800000072d900000072da00000072db0000007272000000da084e4f5f4c494d495472ef0000007273000000728f00000072d4000000da04736565647224000000da085f5f6578706f7274723300000072270000007228000000da0464696374722d00000072350000007236000000720001000072e0000000723c00000072420000007246000000da05666c6f6174724d0000007251000000da04626f6f6c7259000000725c000000725f000000726b0000007277000000727900000072810000007288000000728b000000729200000072ea000000727e00000072f300000072fb0000007205010000720601000072160100007217010000da03416e797219010000721a010000721c0100007222000000722200000072220000007223000000da083c6d6f64756c653e0100000073b00000000801080104010401040108010601080106010801060108010601080104010a01060108010401080102010a010c01040104010401040104010c0104010401080108010c01080308060601100606011006100610041004100506011209060112090601120c0601120b06011218100910070e0b06020201221d0601120e060110200601161d10070601101c060110350601100c0601123d0601142a120c120b0601103d060110060601120b0601120b0601