Contract con_poker_card_games_v2


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