Contract con_poker_card_games_v1


Contract Code


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

Byte Code

e30000000000000000000000000a000000400000007394030000640064016c005a01640064016c025a0365045a056506640264036404640564068d045a07650864016404640764088d035a09650864016404640964088d035a0a650864016404640a64088d035a0b650867006404640b64088d035a0c650867006404640c64088d035a0d650867006404640d64088d035a0e650f6404640e640f8d025a10650f64046410640f8d025a11650f64046411640f8d025a1264125a1364005a1464135a1564145a1664155a17651465156516651767045a1864005a1964135a1a6519651a67025a1b6514641665156416651664176517641869045a1c651d6a1e830001006419641a84005a1f6520640483016521641b9c01641c641d840483015a226520640483016521641b9c01641e641f840483015a236521652464209c026421642284045a256521652164239c026424642584045a266521652164209c026426642784045a276528652964289c026429642a84045a2a65206404830165216521642b9c02642c642d840483015a2b65206404830165216521642e9c02642f6430840483015a2c6520640483016521652d64319c0264326433840483015a2e6520640483016521652d64319c0264346435840483015a2f6520640483016521653064369c0264376438840483015a316520640483016439643a840083015a3265216528643b9c02643c643d84045a336521643e9c01643f644084045a346520640483016518651b6441660365216528652d652865286530652164429c0764436444840583015a356520640483016521652164459c0264466447840483015a36652064048301652164489c016449644a840483015a376520640483016521652965296521644b9c04644c644d840483015a3865286529644e9c02644f645084045a39652064048301652164519c0164526453840483015a3a652064048301652164519c0164546455840483015a3b6528652865286521652164569c056457645884045a3c6520640483016521652d64599c02645a645b840483015a3d652064048301652165216521645c9c03645d645e840483015a3e652465286528645f9c036460646184045a3f65216528652464629c036463646484045a40652064048301652164519c0164656466840483015a41652064048301652d64679c0164686469840483015a4265206404830165286543646a9c02646b646c840483015a4465206404830165286543646a9c02646d646e840483015a456520640483016521646f9c0164706471840483015a46640153002972e9000000004eda0e636f6e5f7068695f6c7374303031da0862616c616e636573da17636f6e5f706f6b65725f636172645f67616d65735f7631da0c7068695f62616c616e6365732904da10666f726569676e5f636f6e7472616374da0c666f726569676e5f6e616d65da08636f6e7472616374da046e616d65da0567616d65732903da0d64656661756c745f76616c756572080000007209000000da0568616e6473da0a67616d655f6e616d6573da0d706c61796572735f67616d6573da0f706c61796572735f696e7669746573da0d6d657373616765735f68617368da18706c617965725f6d657461646174615f636f6e7472616374290272080000007209000000da1d706f6b65725f68616e645f6576616c7561746f725f636f6e7472616374da056f776e657269ffe0f505e901000000e902000000e903000000e932000000e90a000000e907000000630000000000000000000000000200000043000000732400000074006a0174026a038301010074046a0164018301010074056a016402830101006400530029034eda18636f6e5f67616d6d615f7068695f70726f66696c655f7634da1b636f6e5f706f6b65725f68616e645f6576616c7561746f725f76312906da075f5f6f776e6572da03736574da03637478da0663616c6c6572da1a5f5f706c617965725f6d657461646174615f636f6e7472616374da1f5f5f706f6b65725f68616e645f6576616c7561746f725f636f6e7472616374a90072220000007222000000da00da045f5f5f5f26000000730600000000010c010a01722400000029017208000000630100000000000000010000000200000043000000732400000074006a0174026a0383006b027316740464018301820174056a067c00830101006400530029024e7a394f6e6c7920746865206f776e65722063616e2063616c6c207570646174655f706c617965725f6d657461646174615f636f6e747261637428292907721e000000721f000000721c000000da03676574da0e417373657274696f6e4572726f727220000000721d00000029017208000000722200000072220000007223000000da1f7570646174655f706c617965725f6d657461646174615f636f6e74726163742c00000073060000000002100106017227000000630100000000000000010000000200000043000000732400000074006a0174026a0383006b027316740464018301820174056a067c00830101006400530029024e7a394f6e6c7920746865206f776e65722063616e2063616c6c207570646174655f706c617965725f6d657461646174615f636f6e747261637428292907721e000000721f000000721c000000722500000072260000007221000000721d00000029017208000000722200000072220000007223000000da247570646174655f706f6b65725f68616e645f6576616c7561746f725f636f6e747261637433000000730600000000021001060172280000002902da0767616d655f6964da0672657475726e630100000000000000020000000400000043000000732800000074007c006401660219007d017c0164006b097324740164027c009b0064039d03830182017c01530029044eda07706c61796572737a0547616d65207a1020646f6573206e6f742065786973742e2902da075f5f67616d6573722600000029027229000000722b000000722200000072220000007223000000da1f5f5f6765745f706c61796572735f616e645f6173736572745f6578697374733a000000730600000000010c011801722d0000002902da0763726561746f72722a000000630100000000000000010000000500000043000000731800000074006a0164016a027c00740374048301670283018301530029024efa013a2905da07686173686c6962da0473686133da046a6f696eda03737472da036e6f772901722e000000722200000072220000007223000000da105f5f6372656174655f67616d655f696440000000730200000000017235000000630100000000000000010000000500000043000000731800000074006a0164016a027c00740374048301670283018301530029024e722f00000029057230000000723100000072320000007233000000723400000029017229000000722200000072220000007223000000da105f5f6372656174655f68616e645f6964440000007302000000000172360000002902da0468616e64722a000000630100000000000000020000000200000043000000731800000074006a0174026a03830083017d017c016a047c008301530029014e2905da0149da0d696d706f72745f6d6f64756c6572210000007225000000da086576616c7561746529027237000000da03706865722200000072220000007223000000da0f5f5f6576616c756174655f68616e6448000000730400000000010e01723c00000029027229000000da076d657373616765630200000000000000050000000400000043000000734800000074006a017d0274027c0083017d037c027c036b06731e740364018301820174047c007c0266021900702c67007d047c046a057c01830101007c0474047c007c0266023c006400530029024e7a1f596f7520646f206e6f742062656c6f6e6720746f20746869732067616d652e2906721e000000721f000000722d0000007226000000da0f5f5f6d657373616765735f68617368da06617070656e6429057229000000723d000000da06706c61796572722b000000da086d65737361676573722200000072220000007223000000da0c67616d655f6d6573736167654d000000730c000000000206010801100110010a0172420000002902da0768616e645f6964723d000000630200000000000000050000000400000043000000734c00000074006a017d0274027c006401660219007d037c027c036b067322740364028301820174047c007c0266021900703067007d047c046a057c01830101007c0474047c007c0266023c006400530029034eda0e6163746976655f706c61796572737a1f596f7520646f206e6f742062656c6f6e6720746f20746869732068616e642e2906721e000000721f000000da075f5f68616e64737226000000723e000000723f00000029057243000000723d000000724000000072440000007241000000722200000072220000007223000000da0c68616e645f6d65737361676557000000730c000000000206010c01100110010a01724600000029027229000000da06616d6f756e74630200000000000000040000000400000043000000737c00000074006a017d027c0164016b047316740264028301820174037c0083017d037c027c036b06732e740264038301820174047c007c026602190070407405640483017c01170074047c007c0266023c0074067c0274006a07660219007c016b057368740264058301820174086a097c0174006a077c02830301006400530029064e72010000007a20416d6f756e74206d757374206265206120706f736974697665206e756d6265727a1f596f7520646f206e6f742062656c6f6e6720746f20746869732067616d652e7a03302e307a35596f752068617665206e6f7420617070726f76656420656e6f75676820666f72207468697320616d6f756e74206f66206368697073290a721e000000721f0000007226000000722d000000722c000000da07646563696d616cda0e5f5f7068695f62616c616e636573da0474686973da03706869da0d7472616e736665725f66726f6d2904722900000072470000007240000000722b000000722200000072220000007223000000da116164645f63686970735f746f5f67616d656100000073120000000002060110010801100112010e010c010e01724d000000630200000000000000050000000400000043000000736c00000074006a017d027c0164016b047316740264028301820174037c0083017d037c027c036b06732e740264038301820174047c007c02660219007d047c047c016b05734a74026404830182017c047c01180074047c007c0266023c0074056a067c017c0264058d0201006400530029064e72010000007a20416d6f756e74206d757374206265206120706f736974697665206e756d6265727a1f596f7520646f206e6f742062656c6f6e6720746f20746869732067616d652e7a27596f752063616e6e6f74207769746864726177206d6f7265207468616e20796f7520686176652e29027247000000da02746f2907721e000000721f0000007226000000722d000000722c000000724b000000da087472616e736665722905722900000072470000007240000000722b000000da1263757272656e745f636869705f636f756e74722200000072220000007223000000da1877697468647261775f63686970735f66726f6d5f67616d656e0000007310000000000206011001080110010c0110011001725100000029027229000000da0661636365707463020000000000000006000000040000004300000073f600000074006a017d0274027c021900701067007d0374037c0083017d047c027c046b07732a740464018301820174027c02640266021900703867007d057c007c036b06735e7c007c056b06735e74057c00640366021900735e74046404830182017c007c036b0672787c036a067c00830101007c0374027c023c007c0174027c027c0066023c007c0172d47c007c056b0672a67c056a067c00830101007c0574027c02640266023c007c046a077c02830101007c0474057c00640566023c0074087c02190070c667007c006701170074087c023c006e1e7c007c056b0772f27c056a077c00830101007c0574027c02640266023c006400530029064e7a24596f752061726520616c726561647920612070617274206f6620746869732067616d652eda086465636c696e6564da067075626c69637a27596f752068617665206e6f74206265656e20696e766974656420746f20746869732067616d652e722b0000002909721e000000721f000000da115f5f706c61796572735f696e7669746573722d0000007226000000722c000000da0672656d6f7665723f000000da0f5f5f706c61796572735f67616d65732906722900000072520000007240000000da0e706c617965725f696e7669746573722b0000007253000000722200000072220000007223000000da11726573706f6e645f746f5f696e766974657a000000732a000000000206010c010801100110011201120108010a0108010c01040108010a010c010a010c01180108010a017259000000630000000000000000030000000500000043000000733800000074006a017d0074027c001900701067007d0178187c0144005d107d02640174027c007c0266023c0071185700670074027c003c006400530029024e462903721e000000721f000000725500000029037240000000da07696e7669746573da06696e76697465722200000072220000007223000000da136465636c696e655f616c6c5f696e766974657393000000730a000000000206010c010a011001725c00000029027229000000da066f74686572736302000000000000000400000004000000430000007330000000782a7c0144005d227d0274007c021900701467007d037c036a017c00830101007c0374007c023c00710657006400530029014e29027255000000723f00000029047229000000725d000000da056f746865727258000000722200000072220000007223000000da165f5f73656e645f696e766974655f72657175657374739c000000730800000000010a010c010a01725f0000002901720900000063010000000000000001000000030000004300000073800000007c0064006b09721474007c00830164016b04731c740164028301820174027c0074038302732e740164038301820174007c00830164046b017342740164058301820174046406640784007c00440083018301735c74016408830182017c0064011900640d6b0772747c00640e1900640f6b07737c7401640c830182016400530029104e72010000007a2147616d65206e616d652063616e6e6f74206265206e756c6c206f7220656d7074797a1b47616d65206e616d65206d757374206265206120737472696e672ee9180000007a2e47616d65206e616d652063616e6e6f74206265206c6f6e676572207468616e20323420636861726163746572732e630100000000000000020000000500000053000000731c00000067007c005d147d017c016a00830070167c0164026b069102710453002903da015ffa012d2902726100000072620000002901da076973616c6e756d2902da022e30da0163722200000072220000007223000000fa0a3c6c697374636f6d703ea8000000730200000006007a285f5f76616c69646174655f67616d655f6e616d652e3c6c6f63616c733e2e3c6c697374636f6d703e7a6247616d65206e616d652068617320696e76616c696420636861726163746572732e204561636820636861726163746572206d75737420626520616c7068616e756d657269632c20612068797068656e2c206f7220616e20756e64657273636f72652e7262000000726100000072140000007a3a47616d65206e616d652063616e6e6f74207374617274206f7220656e64207769746820612068797068656e206f7220756e64657273636f72652e290272620000007261000000e9ffffffff2902726200000072610000002905da036c656e7226000000da0a6973696e7374616e63657233000000da03616c6c29017209000000722200000072220000007223000000da145f5f76616c69646174655f67616d655f6e616d65a3000000731000000000010e010e0112011401140106011a01726b0000004629077209000000da0d6f746865725f706c6179657273da04616e7465da12616c6c6f7765645f67616d655f7479706573da15616c6c6f7765645f62657474696e675f74797065737254000000722a0000006306000000000000000a0000000500000043000000736c01000074006a017d0674027c03830164016b04731a740364028301820178247c0344005d1c7d077c0774046b067320740364037c079b0064049d03830182017120570074027c04830164016b047354740364058301820178247c0444005d1c7d087c0874056b06735a740364067c089b0064049d0383018201715a57007c0264016b05738a74036407830182017c067c016b07739a740364088301820174067c0664098d017d0974077c09640a6602190064006b0873c47403640b7c099b00640c9d038301820174087c008301010074097c00190064006b0873e87403640b7c009b00640c9d03830182017c0974097c003c007c06670174077c09640d66023c007c0274077c09640e66023c007c0674077c09640a66023c007c0174077c09640f66023c007c0574077c09641066023c007c0374077c09641166023c007c0474077c09641266023c00740a7c0619009001705267007c0967011700740a7c063c00740b7c097c01830201007c09530029134e72010000007a224d75737420616c6c6f77206174206c65617374206f6e652067616d6520747970652e7a13496e76616c69642067616d6520747970653a20da012e7a254d75737420616c6c6f77206174206c65617374206f6e652062657474696e6720747970652e7a16496e76616c69642062657474696e6720747970653a207a1a416e7465206d757374206265206e6f6e2d6e656761746976652e7a2743616c6c65722063616e277420626520696e206f746865725f706c617965727320696e7075742e2901722e000000722e0000007a0547616d65207a1a2068617320616c7265616479206265656e20637265617465642e722b000000726d000000da08696e7669746565737254000000726e000000726f000000290c721e000000721f00000072680000007226000000da0e414c4c5f47414d455f5459504553da11414c4c5f42455454494e475f54595045537235000000722c000000726b000000da0c5f5f67616d655f6e616d65737257000000725f000000290a7209000000726c000000726d000000726e000000726f0000007254000000722e000000da0967616d655f74797065da086265745f747970657229000000722200000072220000007223000000da0a73746172745f67616d65ae00000073340000000004060114010a011c0106010e010a011c01100110010a010a01160108011c0108010e010c010c010c010c010c010c0118010a01727700000029027229000000da0d706c617965725f746f5f616464630200000000000000060000000400000043000000738c00000074006a017d027c027c016b037316740264018301820174037c006402660219007d037c027c036b027332740264038301820174047c0083017d047c017c046b07734a740264048301820174037c006405660219007d057c017c056b07736674026406830182017c056a057c01830101007c0574037c00640566023c0074067c007c016701830201006400530029074e7a22596f752063616e6e6f742061646420796f757273656c6620746f20612067616d652e722e0000007a264f6e6c79207468652067616d652063726561746f722063616e2061646420706c61796572732e7a1e506c6179657220697320616c726561647920696e207468652067616d652e72710000007a20506c617965722068617320616c7265616479206265656e20696e76697465642e2907721e000000721f0000007226000000722c000000722d000000723f000000725f0000002906722900000072780000007240000000722e000000722b0000007271000000722200000072220000007223000000da126164645f706c617965725f746f5f67616d65ce00000073160000000002060110010c011001080110010c0110010a010c017279000000290172290000006301000000000000000a0000000500000043000000734201000074006a017d0174027c0083017d027c017c026b06731e740364018301820174047c007c01660219007d037c0364006b0873427c0364026b027342740364038301820174057c0119007d047c046a067c00830101007c0474057c013c007c026a067c01830101007c0274047c00640466023c0074047c006405660219007d057c0564006b099001723e74077c05640666021900709667007d067c017c066b069001723e74077c056407660219007d0774077c056408660219007d0874077c056409660219007d097c097c016b0272e874087c067c077c087c0183047d097c0974077c05640966023c007c066a067c01830101007c0674077c05640666023c007c017c076b069001721e7c076a067c01830101007c0774077c05640766023c007c017c086b069001723e7c086a067c01830101007c0874077c05640866023c0064005300290a4e7a19596f7520617265206e6f7420696e20746869732067616d652e72010000007a47596f75207374696c6c206861766520636869707320696e20746869732067616d652e20506c65617365207769746864726177207468656d206265666f7265206c656176696e672e722b000000da0c63757272656e745f68616e647244000000da06666f6c646564da06616c6c5f696eda0b6e6578745f6265747465722909721e000000721f000000722d0000007226000000722c000000725700000072560000007245000000da115f5f6765745f6e6578745f626574746572290a72290000007240000000722b000000da056368697073da0c706c617965725f67616d657372430000007244000000727b000000727c000000727d000000722200000072220000007223000000da0a6c656176655f67616d65dd000000733a00000000020601080110010c01180108010a0108010a010c010c010a0110010a010c010c010c010801060108010c010a010c010a010a010c010a010a0172810000002904722900000072750000007276000000722a000000630300000000000000090000000400000043000000732e01000074006a017d0374027c006401660219007d047c017c046b067322740364028301820174027c006403660219007d057c027c056b06733e740364048301820174047c0083017d067c037c066b067356740364058301820174057c06830164066b04736a740364078301820174027c006408660219007d077c0764006b09729274067c0764096602190073927403640a8301820174077c0083017d087c0874027c00640866023c007c0074067c08640b66023c007c0174067c08640c66023c007c0274067c08640d66023c007c0374067c08640e66023c00670074067c08640f66023c00641074067c08641166023c00641074067c08640966023c00641074067c08641266023c00670074067c08641366023c00641474067c08641566023c00670074067c08641666023c007c08530029174e726e0000007a12496e76616c69642067616d6520747970652e726f0000007a11496e76616c69642062657420747970652e7a20596f7520617265206e6f7420612070617274206f6620746869732067616d652e72140000007a24596f752063616e6e6f7420737461727420612068616e6420627920796f757273656c662e727a000000da0970617965645f6f75747a2d5468652070726576696f75732068616e6420686173206e6f74206265656e207061796564206f7574207965742e722900000072750000007276000000da066465616c6572727b00000046da09636f6d706c65746564da0e726561636865645f6465616c657272440000007201000000da0b63757272656e745f626574727c0000002908721e000000721f000000722c0000007226000000722d00000072680000007245000000723500000029097229000000727500000072760000007283000000726e000000726f000000722b000000da1070726576696f75735f68616e645f69647243000000722200000072220000007223000000da0a73746172745f68616e64fe0000007334000000000206010c0110010c0110010801100114010c0108010e01060108010c010c010c010c010c010c010c010c010c010c010c010c0172880000002902722b000000722a0000006301000000000000000200000003000000030000007310000000870066016401640284087d017c01530029034e630100000000000000010000000200000013000000730a00000088006a007c008301530029014e2901da05696e646578290172400000002901722b00000072220000007223000000da065f5f736f72741e010000730200000000017a245f5f6163746976655f706c617965725f736f72742e3c6c6f63616c733e2e5f5f736f727472220000002902722b000000728a00000072220000002901722b0000007223000000da145f5f6163746976655f706c617965725f736f72741c010000730400000000020c02728b000000290172430000006301000000000000000a0000000500000043000000734c01000074006a017d0174027c006401660219007d027c0264006b097322740364028301820174047c0283017d037c017c036b06733a740364038301820174057c026404660219007d0474057c027c01660219007d057c0564006b0972627c057c046b05736a740364058301820174027c00640666021900707867007d067c017c066b07738a740364078301820174027c006408660219007d0774067c0719007d0874077c0683017c086b0073ba740364097c089b00640a9d03830182017c0474027c007c01640b66033c007c0574027c007c01640c66033c0074057c027c016602050019007c04380003003c007c066a087c01830101007c066a09740a7c038301640d8d0101007c0674027c00640666023c007c0474027c00640e66023c007c057c046b029001724874027c00640f660219007d097c096a087c01830101007c0974027c00640f66023c006400530029104e72290000007a19546869732067616d6520646f6573206e6f742065786973742e7a20596f7520617265206e6f7420612070617274206f6620746869732067616d652e726d0000007a1d596f7520646f206e6f74206861766520656e6f7567682063686970732e72440000007a1f596f75206861766520616c726561647920706169642074686520616e74652e72750000007a0d41206d6178696d756d206f66207a1f20697320616c6c6f77656420666f7220746869732067616d6520747970652eda03626574da076d61785f6265742901da036b65797286000000727c000000290b721e000000721f00000072450000007226000000722d000000722c000000da184d41585f504c41594552535f42595f47414d455f545950457268000000723f000000da04736f7274728b000000290a724300000072400000007229000000722b000000726d000000727f00000072440000007275000000da0b6d61785f706c6179657273727c000000722200000072220000007223000000da07616e74655f7570230100007332000000000206010c011001080110010c010c011801100110010c010801060116010e010e0114010a0110010c010c010a010c010a017292000000630100000000000000120000003400000043000000731603000074006a017d0174027c006401660219007d027c0174027c006402660219006b02732a740364038301820174047c02830164046b04734a7403640574047c0283019b0064069d03830182017c017c026b06735a740364078301820174027c006408660219007d03740574066a0783006409640a640b640c8d047d04640d640e640f6410641164126413641464156416641764186419641a641b641c641d641e641f6420642164226423642464256426642764286429642a642b642c642d642e642f6430643164326433643464356436643764386439643a643b643c643d643e643f644067347d0574086a097c0583010100900178d4740a74047c0283018301440090015dc27d067c027c0619007d077c047c076441660219007d087c0864006b0990017334740364427c079b0064439d03830182017c086a0b644483017d0974047c09830164456b029001735474036446830182017c03740c6b02900172707c057c067c0664041700850219007d0a6ee47c03740d6b02900172d27c0564477c06850219007c057c066404170074047c0283018502190017007d0a74047c0a830174047c028301640418006b02900273547403644874047c0a83019b00644974047c028301640418009b009d04830182016e827c03740e6b029002720c7c05644a7c061400644a7c061400644a1700850219007d0a74047c0a8301644a6b02900273547403644b830182016e487c03740f6b02900272467c05644c7c061400644c7c061400644c1700850219007d0a74047c0a8301644c6b02900273547403644b830182016e0e644d900273547403644e83018201644f6a107c0a83017d0b741174086a1264477413830283017d0c7c0b9b0064507c0c9b009d037d0d74146a157c0d74167c0964471900830174167c0964041900830164518d037d0e74176a187c0d83017d0f7c0e74027c007c07645266033c007c0f74027c007c07645366033c0090017100570074027c006454660219007d1074197c0267007c107c01830474027c00645566023c00741a74027c006456660219006457660219007d117c1174047c028301140074027c00645866023c006400530029594e724400000072830000007a17596f7520617265206e6f7420746865206465616c65722e72140000007a1b4e6f7420656e6f7567682061637469766520706c61796572733a207a05203c3d20317a27596f7520617265206e6f74206163746976656c792070617274206f6620746869732068616e642e7275000000da086d657461646174617204000000da0f706c617965725f6d6574616461746129047206000000720700000072080000007209000000da023263da023264da023268da023273da023363da023364da023368da023373da023463da023464da023468da023473da023563da023564da023568da023573da023663da023664da023668da023673da023763da023764da023768da023773da023863da023864da023868da023873da023963da023964da023968da023973da025463da025464da025468da025473da024a63da024a64da024a68da024a73da025163da025164da025168da025173da024b63da024b64da024b68da024b73da024163da024164da024168da024173da0e7075626c69635f7273615f6b65797a07506c61796572207a2520686173206e6f7420736574757020746865697220656e6372797074696f6e206b6579732efa017c72150000007a0c496e76616c6964206b65797372010000007a16536f6d657468696e672077656e742077726f6e672e207a0420213d20e9050000007a15536f6d657468696e672077656e742077726f6e672e7219000000467a12496e76616c69642067616d6520747970652efa012c722f0000002903da0b6d6573736167655f737472da016eda0165da15706c617965725f656e637279707465645f68616e64da14686f7573655f656e637279707465645f68616e64727c000000727d0000007229000000726d000000da03706f74291b721e000000721f000000724500000072260000007268000000da0b466f726569676e4861736872200000007225000000da0672616e646f6dda0773687566666c65da0572616e6765da0573706c6974da0e4f4e455f434152445f504f4b4552da0b424c494e445f504f4b4552da0e464956455f434152445f53545544da0f534556454e5f434152445f5354554472320000007233000000da0772616e64696e74da114d41585f52414e444f4d5f4e554d424552da03727361da07656e6372797074da03696e7472300000007231000000727e000000722c00000029127243000000728300000072440000007275000000da115f5f706c617965725f6d65746164617461da056361726473da01697240000000da0a706c617965725f6b6579da046b657973da0b706c617965725f68616e64da0f706c617965725f68616e645f737472da0473616c74da19706c617965725f68616e645f7374725f776974685f73616c7472d000000072d1000000727c000000726d000000722200000072220000007223000000da0a6465616c5f6361726473400100007360000000000206010c01180106011a0110010c01020108010a01140116011601160116010a01160108010c011a010a0116010a0112010a0120010c012c010a01180118010a01180118020e010a0110010e0104011c010a020e0114010c0106011001140172ea0000002905722b000000727b000000727c000000da0e63757272656e745f626574746572722a000000630400000000000000060000000400000003000000736e00000074008801830174007c008301640118006b0572186400530074007c0083017400880083016b02722c640053008700870166026402640384087c00440083017d047c046a017c0383017d057c0564046b05735a74026405830182017c047c056401170074007c04830116001900530029064e7214000000630100000000000000020000000400000013000000732000000067007c005d187d017c0188016b0772047c0188006b0772047c019102710453007222000000722200000029027264000000da01702902727c000000727b0000007222000000722300000072660000007c010000730400000006000c017a255f5f6765745f6e6578745f6265747465722e3c6c6f63616c733e2e3c6c697374636f6d703e72010000007a3543757272656e74206265747465722068617320666f6c6465642c20776869636820646f6573206e6f74206d616b652073656e73652e29037268000000728900000072260000002906722b000000727b000000727c00000072eb000000da126e6f6e5f666f6c6465645f706c6179657273da0d63757272656e745f696e64657872220000002902727c000000727b0000007223000000727e0000007601000073100000000002140104011001040114020a011001727e00000029027243000000728c00000063020000000000000011000000050000004300000073f602000074006a017d0274027c007c0264016603190064006b097320740364028301820174027c006403660219000c007336740364048301820174027c006405660219007c026b02734e740364068301820174027c006407660219007d0374027c006408660219007d0474027c0064096602190070787404640a83017d0574027c007c02640b66031900708e7404640a83017d0674027c00640c660219007d077c077c026b0272b6640d74027c00640e66023c00640d7d086e0c74027c00640e660219007d0874027c00640f660219007d0974057c037c047c097c0283047d0a7c0a64006b0872f4640d74027c00640366023c0090016ef27c0164106b009001725a7c046a067c02830101007c0474027c00640866023c007c027c096b06900172347c096a077c02830101007c0974027c00640f66023c0074087c04830174087c038301641118006b02900272e6640d74027c00640366023c0090016e8c7c0164106b02900172ee74027c007c026412660319007d0b7c0b7c066b029001729c7c027c096b079001729c7c096a067c02830101007c0974027c00640f66023c007c0b7c066b02900173b87c067c056b05900173b8740364138301820174027c007c0a640b660319007d0c7c0c64006b09900272e67c0c7c056b02900272e67c08900272e6640d74027c00640366023c006ef874027c006414660219007d0d74097c0d7c02660219007c016b0590027314740364158301820174027c006416660219007d0e7c0e740a6b029002724874027c006417660219007d0f7c017c0f6b019002734874036418830182017c067c0117007d1074027c007c026412660319007d0b7c0b7c106b02900272887c027c096b07900272887c096a067c02830101007c0974027c00640f66023c007c0b7c106b02900273a47c107c056b05900273a474036419830182017c1074027c007c02640b66033c007c1074027c00640966023c0074027c0064176602050019007c01370003003c0074097c0d7c026602050019007c01380003003c007c0a74027c00640566023c0064005300291a4e72d00000007a1348616e6420646f6573206e6f7420657869737472840000007a20546869732068616e642068617320616c726561647920636f6d706c657465642e727d0000007a1b4974206973206e6f7420796f7572207475726e20746f206265742e7244000000727b00000072860000007a03302e30728c0000007283000000547285000000727c00000072010000007214000000728d0000007a5443616e6e6f7420636865636b20696e2074686973207363656e6172696f2e2043757272656e74206265742069732061626f766520796f75722062657420616e6420796f7520617265206e6f7420616c6c20696e2e72290000007a2d596f7520646f206e6f74206861766520656e6f75676820636869707320746f206d616b65207468697320626574727600000072d20000007a2943616e6e6f74206f7665726265742074686520706f7420696e20706f742d6c696d6974206d6f64652e7a3843757272656e74206265742069732061626f766520796f75722062657420616e6420796f7520646964206e6f7420676f20616c6c20696e2e290b721e000000721f000000724500000072260000007248000000727e000000723f00000072560000007268000000722c000000da09504f545f4c494d495429117243000000728c00000072400000007244000000727b000000da0863616c6c5f626574da13706c617965725f70726576696f75735f62657472830000007285000000727c000000727d000000728d000000da106e6578745f706c61796572735f6265747229000000727600000072d20000007286000000722200000072220000007223000000da116265745f636865636b5f6f725f666f6c64830100007370000000000206010c010e01100106010a010e010c010c01140116010c0108010c0106020c010c010e01080110010a010a010c010a010a010c01160110010a010e0114010a010c011c010e01140106010e020c010a0110010c010a010c01120108010e0114010a010c011c010e010c011401140172f30000002903724300000072e7000000722a00000063020000000000000010000000060000004300000073ac01000074006a017d0274027c00640166021900731a740364028301820174027c006403660219007d037c027c036b077336740364048301820174027c006405660219007d047c027c046b067352740364068301820174027c006407660219007d057c0564006b09736e740364088301820174027c007c026409660319007d067c0664006b09738c7403640a830182017c057c066b02739c7403640b8301820174046a057c0183017d0774027c007c02640c660319007d087c0864006b096fc27c087c076b027d097c0973e27c036a067c02830101007c0374027c00640366023c00640d53007c016a07640e8301640f19006a07641083017d0a74027c006411660219007d0b7c0b74086b0290017280640f7d0c78927c0444005d647d0d7c0d7c026b03900172167c0d7c036b07900172707c0a7c0c19007d0e74097c0e670183017d0f74027c007c0d64126603190064006b08900172707c0f74027c007c0d641266033c007c0e74027c007c0d641366033c007c0c641437007d0c9001711657006e2474097c0a83017d0f7c0f74027c007c02641266033c007c0a74027c007c02641366033c00641553006400530029164e72840000007a20546869732068616e6420686173206e6f7420636f6d706c65746564207965742e727b0000007a2f4e6f206e65656420746f2076657269667920796f75722068616e64206265636175736520796f7520666f6c6465642e72440000007a2a596f7520617265206e6f7420616e2061637469766520706c6179657220696e20746869732068616e642e72860000007a185468657265206973206e6f2063757272656e74206265742e728c0000007a15596f752068617665206e6f7420626574207965742e7a19426574732068617665206e6f742073746162696c697a65642e72d10000007a32566572696669636174696f6e206661696c65642e20596f75722068616e6420686173206265656e20666f726665697465642e722f000000720100000072cc0000007275000000da0472616e6b723700000072140000007a17566572696669636174696f6e207375636365656465642e290a721e000000721f0000007245000000722600000072300000007231000000723f00000072d700000072d9000000723c0000002910724300000072e70000007240000000727b0000007244000000da106265745f73686f756c645f657175616cda0a706c617965725f62657472d1000000da1d70726576696f75735f686f7573655f656e637279707465645f68616e64da08766572696669656472e20000007275000000da016a72ec000000da046361726472f4000000722200000072220000007223000000da0b7665726966795f68616e64c1010000734a0000000002060114010c0110010c0110010c0110010e01100110010a01060108010801080104010a010c01040214010c010a0104010a010a010a0108010a0114010e010e01100208010e010e0172fb0000002903da0572616e6b73722b000000722a000000630200000000000000080000000400000043000000735600000074007c006a018300640164028d027d0274027c0183017d0378387c0244005d307d047c007c0419007d057c036a0374027c05830183017d0674047c06830164036b04721e74057c0683017d075000711e57007c07530029044e542901da077265766572736572010000002906da06736f7274656472e5000000721d000000da0c696e74657273656374696f6e7268000000da046c697374290872fc000000722b000000da12736f727465645f72616e6b5f76616c756573da0a706c617965725f73657472f4000000da11706c61796572735f776974685f72616e6b72ff000000da0777696e6e657273722200000072220000007223000000da0e5f5f66696e645f77696e6e657273ec01000073120000000001100108010a0108010e010c0108010601720501000029037243000000722b000000722a000000630200000000000000050000000500000043000000735a00000069007d0278507c0144005d487d0374007c007c036401660319007d047c0464006b097334740164027c039b0064039d03830182017c047c026b07724467007c027c043c007c027c0419006a027c0383010100710a57007c02530029044e72f40000007a07506c61796572207a2120686173206e6f742076657269666965642074686569722068616e64207965742e290372450000007226000000723f00000029057243000000722b00000072fc00000072ec00000072f4000000722200000072220000007223000000da115f5f63616c63756c6174655f72616e6b73f80100007310000000000104010a010e011801080108011201720601000063010000000000000016000000070000000300000073bc02000074007c006401660219007d017c0164026b04731c740164038301820174007c006404660219000c007332740164058301820174007c00640666021900890074007c006407660219007d0274007c006408660219007d03870066016409640a84087c03440083017d0474027c04830164026b04737c7401640b8301820169007d0574027c048301640c6b02729c7c017c057c04640219003c0090016ec474037c007c0483027d0674027c02830164026b049002723269007d0778227c0244005d1a7d0874007c007c08640d660319007d097c097c077c083c0071be5700740474057c076a068300830183017d0a64027d0b789e7c0a44005d967d0c67007d0d788c7c0444005d847d087c087c076b07900173207c077c0819007c0c6b05900172047c0d6a077c088301010074087c067c0d83027d0e7c0c74027c0d830114007d0f7c0b7c0f37007d0b7c0f74027c0e83011b007d1078307c0e44005d287d117c117c056b079001727064027c057c113c007c057c11050019007c10370003003c009001715a570090017104570071f657007c017c0b18007d1274057c0483016a0974057c02830183017d137c1264026b02900173ca74027c13830164026b04900173ca7401640e830182017c1264026b049002726074027c138301640c6b02900172e87c137d146e0a74087c067c1383027d147c1274027c1483011b007d1078607c1444005d287d117c117c056b079002721a64027c057c113c007c057c11050019007c10370003003c009002710457006e2e74087c067c0483027d147c0174027c1483011b007d1078167c1444005d0e7d117c107c057c113c009002714e570074007c00640f660219007d15782a7c056a0a830044005d1e5c027d087d10740b7c157c086602050019007c10370003003c00900271765700740c7c056a0d8300830174007c00641066023c00641174007c00640466023c006400530029124e72d200000072010000007a195468657265206973206e6f20706f7420746f20636c61696d2172820000007a25546869732068616e642068617320616c7265616479206265656e207061796564206f75742e727b000000727c0000007244000000630100000000000000020000000400000013000000731800000067007c005d107d017c0188006b0772047c01910271045300722200000072220000002902726400000072ec0000002901727b0000007222000000722300000072660000000c020000730200000006007a1f7061796f75745f68616e642e3c6c6f63616c733e2e3c6c697374636f6d703e7a1f546865726520617265206e6f2072656d61696e696e6720706c61796572732e7214000000728d0000007a29496e76616c6964207374617465207768656e2063616c63756c6174696e67207369646520706f74732e7229000000720401000054290e724500000072260000007268000000720601000072fe000000721d000000da0676616c756573723f0000007205010000da0a646966666572656e6365da056974656d73722c000000720001000072e50000002916724300000072d2000000727c0000007244000000da0972656d61696e696e67da077061796f75747372fc000000da0a616c6c5f696e5f6d617072400000007247000000da04706f7473da0f746f74616c5f70617965645f6f7574728c000000da0e706c61796572735f696e5f706f74da0b706f745f77696e6e657273da0a706f745f7061796f7574da067061796f7574da0677696e6e6572da1372656d61696e696e675f746f5f7061796f7574da0a6e6f745f616c6c5f696e7204010000722900000072220000002901727b0000007223000000da0b7061796f75745f68616e6403020000736e00000000020c011001100106010c010c010c011201140104010c0110020a010e0104010a010e010c01100104010a0104010a0118010a010a010c0108010c010a010a010801200108011201100110010a010e0106020a010c010a010a01080118020a010c010a010e010c0112011a011401721601000029017247000000630100000000000000010000000400000043000000732a00000074006a0174026a0383006b027316740464018301820174056a067c0074006a0164028d0201006400530029034e7a2c4f6e6c7920746865206f776e65722063616e2063616c6c20656d657267656e63795f7769746864726177282929027247000000724e0000002907721e000000721f000000721c00000072250000007226000000724b000000724f00000029017247000000722200000072220000007223000000da12656d657267656e63795f77697468647261774102000073060000000002100106017217010000290272e5000000da0576616c7565630200000000000000020000000600000043000000737c00000074006a0174026a0383006b027316740464018301820174057c00830164026b0272307c0174067c00640319003c006e4874057c00830164046b0272527c0174067c00640319007c006402190066023c006e2674057c00830164056b0272787c0174067c00640319007c00640219007c006404190066033c006400530029064e7a2f4f6e6c7920746865206f776e65722063616e2063616c6c20656d657267656e63795f67616d655f757064617465282972140000007201000000721500000072160000002907721e000000721f000000721c000000722500000072260000007268000000722c000000290272e50000007218010000722200000072220000007223000000da15656d657267656e63795f67616d655f7570646174654802000073100000000002100106010c010e010c0116010c017219010000630200000000000000020000000600000043000000737c00000074006a0174026a0383006b027316740464018301820174057c00830164026b0272307c0174067c00640319003c006e4874057c00830164046b0272527c0174067c00640319007c006402190066023c006e2674057c00830164056b0272787c0174067c00640319007c00640219007c006404190066033c006400530029064e7a2f4f6e6c7920746865206f776e65722063616e2063616c6c20656d657267656e63795f68616e645f757064617465282972140000007201000000721500000072160000002907721e000000721f000000721c0000007225000000722600000072680000007245000000290272e50000007218010000722200000072220000007223000000da15656d657267656e63795f68616e645f7570646174655402000073100000000002100106010c010e010c0116010c01721a0100002901da096e65775f6f776e6572630100000000000000010000000200000043000000732400000074006a0174026a0383006b027316740464018301820174026a057c00830101006400530029024e7a244f6e6c7920746865206f776e65722063616e206368616e6765206f776e657273686970212906721e000000721f000000721c00000072250000007226000000721d0000002901721b010000722200000072220000007223000000da106368616e67655f6f776e65727368697060020000730400000000021601721c0100002947da12636f6e5f7273615f656e6372797074696f6e72de0000007202000000724b000000da09696d706f72746c6962723800000072d30000007249000000da0448617368722c0000007245000000727400000072570000007255000000723e000000da085661726961626c6572200000007221000000721c00000072dd00000072d800000072d900000072da00000072db0000007272000000da084e4f5f4c494d495472ef0000007273000000728f00000072d4000000da04736565647224000000da085f5f6578706f7274723300000072270000007228000000da0464696374722d00000072350000007236000000720001000072e0000000723c00000072420000007246000000da05666c6f6174724d0000007251000000da04626f6f6c7259000000725c000000725f000000726b0000007277000000727900000072810000007288000000728b000000729200000072ea000000727e00000072f300000072fb0000007205010000720601000072160100007217010000da03416e797219010000721a010000721c0100007222000000722200000072220000007223000000da083c6d6f64756c653e0100000073b00000000801080104010401040108010601080106010801060108010601080104010a01060108010401080102010a010c01040104010401040104010c0104010401080108010c01080308060601100606011006100610041004100506011209060112090601120c0601120b06011218100910070e0b06020201221d0601120e060110200601161d10070601101c060110350601100c0601123d0601142a120c120b0601103d060110060601120b0601120b0601