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