2022-09-13 01:07:39 +00:00
|
|
|
import json
|
|
|
|
from enum import Enum
|
2022-11-15 09:19:08 +00:00
|
|
|
from random import randint
|
|
|
|
import time
|
|
|
|
|
2022-09-13 01:07:39 +00:00
|
|
|
from flask import g
|
|
|
|
|
2022-12-14 19:30:05 +00:00
|
|
|
from files.classes.casino_game import CasinoGame
|
2022-11-15 09:19:08 +00:00
|
|
|
from files.helpers.alerts import *
|
|
|
|
from files.helpers.get import get_account
|
2023-02-18 19:57:34 +00:00
|
|
|
from files.helpers.casino import distribute_wager_badges
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
class RouletteAction(str, Enum):
|
2023-01-01 11:36:20 +00:00
|
|
|
STRAIGHT_UP_BET = "STRAIGHT_UP_BET",
|
2022-11-21 15:10:41 +00:00
|
|
|
LINE_BET = "LINE_BET"
|
|
|
|
COLUMN_BET = "COLUMN_BET"
|
|
|
|
DOZEN_BET = "DOZEN_BET"
|
|
|
|
EVEN_ODD_BET = "EVEN_ODD_BET"
|
|
|
|
RED_BLACK_BET = "RED_BLACK_BET"
|
|
|
|
HIGH_LOW_BET = "HIGH_LOW_BET"
|
|
|
|
|
|
|
|
@property
|
|
|
|
def validation_function(self):
|
2022-11-23 02:00:03 +00:00
|
|
|
if self == self.__class__.STRAIGHT_UP_BET: return lambda x: x is not None and x >= 0 and x <= 37
|
|
|
|
if self == self.__class__.LINE_BET: return lambda x: x in LINES
|
|
|
|
if self == self.__class__.COLUMN_BET: return lambda x: x in COLUMNS
|
|
|
|
if self == self.__class__.DOZEN_BET: return lambda x: x in DOZENS
|
|
|
|
if self == self.__class__.EVEN_ODD_BET: return lambda x: x in [y.value for y in RouletteEvenOdd]
|
|
|
|
if self == self.__class__.RED_BLACK_BET: return lambda x: x in [y.value for y in RouletteRedBlack]
|
|
|
|
if self == self.__class__.HIGH_LOW_BET: return lambda x: x in [y.value for y in RouletteHighLow]
|
2022-11-21 15:10:41 +00:00
|
|
|
raise ValueError("Unhandled validation function for RouletteAction")
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RouletteEvenOdd(str, Enum):
|
2022-10-28 23:39:31 +00:00
|
|
|
EVEN = "EVEN"
|
|
|
|
ODD = "ODD"
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RouletteRedBlack(str, Enum):
|
2022-10-28 23:39:31 +00:00
|
|
|
RED = "RED"
|
|
|
|
BLACK = "BLACK"
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RouletteHighLow(str, Enum):
|
2022-10-28 23:39:31 +00:00
|
|
|
HIGH = "HIGH"
|
|
|
|
LOW = "LOW"
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
REDS = (1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36)
|
|
|
|
BLACKS = (2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35)
|
|
|
|
LINES = {
|
2022-10-28 23:39:31 +00:00
|
|
|
1: (1, 2, 3, 4, 5, 6),
|
|
|
|
2: (7, 8, 9, 10, 11, 12),
|
|
|
|
3: (13, 14, 15, 16, 17, 18),
|
|
|
|
4: (19, 20, 21, 22, 23, 24),
|
|
|
|
5: (25, 26, 27, 28, 29, 30),
|
|
|
|
6: (31, 32, 33, 34, 35, 36)
|
2022-09-13 01:07:39 +00:00
|
|
|
}
|
|
|
|
COLUMNS = {
|
2022-10-28 23:39:31 +00:00
|
|
|
1: (1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34),
|
|
|
|
2: (2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35),
|
|
|
|
3: (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36)
|
2022-09-13 01:07:39 +00:00
|
|
|
}
|
|
|
|
DOZENS = {
|
2022-10-28 23:39:31 +00:00
|
|
|
1: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
|
|
|
|
2: (13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24),
|
|
|
|
3: (25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36)
|
2022-09-13 01:07:39 +00:00
|
|
|
}
|
|
|
|
PAYOUT_MULITPLIERS = {
|
2022-10-28 23:39:31 +00:00
|
|
|
RouletteAction.STRAIGHT_UP_BET: 35,
|
|
|
|
RouletteAction.LINE_BET: 5,
|
|
|
|
RouletteAction.COLUMN_BET: 2,
|
|
|
|
RouletteAction.DOZEN_BET: 2,
|
|
|
|
RouletteAction.EVEN_ODD_BET: 1,
|
|
|
|
RouletteAction.RED_BLACK_BET: 1,
|
|
|
|
RouletteAction.HIGH_LOW_BET: 1,
|
2022-09-13 01:07:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def get_active_roulette_games():
|
2023-03-16 06:27:58 +00:00
|
|
|
return g.db.query(CasinoGame).filter(
|
2022-12-14 19:30:05 +00:00
|
|
|
CasinoGame.active == True,
|
|
|
|
CasinoGame.kind == 'roulette'
|
2022-10-28 23:39:31 +00:00
|
|
|
).all()
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def charge_gambler(gambler, amount, currency):
|
2023-04-24 15:08:40 +00:00
|
|
|
charged = gambler.charge_account(currency, amount)[0]
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if not charged:
|
|
|
|
raise Exception("Gambler cannot afford charge.")
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def gambler_placed_roulette_bet(gambler, bet, which, amount, currency):
|
2022-10-28 23:39:31 +00:00
|
|
|
if not bet in (
|
|
|
|
RouletteAction.STRAIGHT_UP_BET,
|
|
|
|
RouletteAction.LINE_BET,
|
|
|
|
RouletteAction.COLUMN_BET,
|
|
|
|
RouletteAction.DOZEN_BET,
|
|
|
|
RouletteAction.EVEN_ODD_BET,
|
|
|
|
RouletteAction.RED_BLACK_BET,
|
|
|
|
RouletteAction.HIGH_LOW_BET
|
|
|
|
):
|
|
|
|
raise Exception(
|
|
|
|
f'Illegal bet {bet} passed to Roulette#gambler_placed_roulette_bet')
|
|
|
|
|
|
|
|
active_games = get_active_roulette_games()
|
|
|
|
|
|
|
|
if len(active_games) == 0:
|
|
|
|
parent_id = int(time.time())
|
|
|
|
else:
|
2022-10-30 00:32:40 +00:00
|
|
|
parent_id = active_games[0].game_state_json['parent_id']
|
2022-10-28 23:39:31 +00:00
|
|
|
|
|
|
|
charge_gambler(gambler, amount, currency)
|
|
|
|
|
2022-12-14 19:30:05 +00:00
|
|
|
game = CasinoGame()
|
2022-10-28 23:39:31 +00:00
|
|
|
game.user_id = gambler.id
|
|
|
|
game.currency = currency
|
|
|
|
game.wager = amount
|
|
|
|
game.winnings = 0
|
|
|
|
game.kind = 'roulette'
|
|
|
|
game.game_state = json.dumps(
|
|
|
|
{"parent_id": parent_id, "bet": bet, "which": which})
|
|
|
|
game.active = True
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(game)
|
|
|
|
g.db.flush()
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_roulette_bets_and_betters():
|
2022-10-28 23:39:31 +00:00
|
|
|
participants = []
|
|
|
|
bets = {
|
|
|
|
RouletteAction.STRAIGHT_UP_BET: [],
|
|
|
|
RouletteAction.LINE_BET: [],
|
|
|
|
RouletteAction.COLUMN_BET: [],
|
|
|
|
RouletteAction.DOZEN_BET: [],
|
|
|
|
RouletteAction.EVEN_ODD_BET: [],
|
|
|
|
RouletteAction.RED_BLACK_BET: [],
|
|
|
|
RouletteAction.HIGH_LOW_BET: [],
|
|
|
|
}
|
|
|
|
active_games = get_active_roulette_games()
|
|
|
|
|
|
|
|
for game in active_games:
|
|
|
|
if not game.user_id in participants:
|
|
|
|
participants.append(game.user_id)
|
|
|
|
|
|
|
|
user = get_account(game.user_id)
|
2022-10-30 00:32:40 +00:00
|
|
|
game_state = game.game_state_json
|
2022-10-28 23:39:31 +00:00
|
|
|
bet = game_state['bet']
|
|
|
|
bets[bet].append({
|
|
|
|
'game_id': game.id,
|
|
|
|
'gambler': game.user_id,
|
|
|
|
'gambler_username': user.username,
|
|
|
|
'gambler_profile_url': user.profile_url,
|
|
|
|
'bet': bet,
|
|
|
|
'which': game_state['which'],
|
|
|
|
'wager': {
|
|
|
|
'amount': game.wager,
|
|
|
|
'currency': game.currency
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return participants, bets, active_games
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def spin_roulette_wheel():
|
2022-10-28 23:39:31 +00:00
|
|
|
participants, bets, active_games = get_roulette_bets_and_betters()
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if len(participants) > 0:
|
|
|
|
number = randint(0, 37) # 37 is 00
|
2022-09-13 03:14:55 +00:00
|
|
|
|
2022-11-21 14:44:16 +00:00
|
|
|
winners, payouts, rewards_by_game_id = determine_roulette_winners(number, bets)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-11-12 09:09:36 +00:00
|
|
|
if number == 37: number = '00'
|
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Pay out to the winners and send a notification.
|
|
|
|
for user_id in winners:
|
|
|
|
gambler = get_account(user_id)
|
|
|
|
gambler_payout = payouts[user_id]
|
|
|
|
coin_winnings = gambler_payout['coins']
|
2022-11-21 23:08:29 +00:00
|
|
|
procoin_winnings = gambler_payout['marseybux']
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
gambler.pay_account('coins', coin_winnings)
|
2022-11-21 23:08:29 +00:00
|
|
|
gambler.pay_account('marseybux', procoin_winnings)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Notify the winners.
|
2023-02-26 10:20:32 +00:00
|
|
|
notification_text = f"Winning number: {number}\n\nCongratulations! One or more of your roulette bets paid off!\n\n"
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if coin_winnings > 0:
|
|
|
|
notification_text = notification_text + \
|
2023-02-26 10:20:32 +00:00
|
|
|
f"* You received {coin_winnings} coins.\n\n"
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if procoin_winnings > 0:
|
|
|
|
notification_text = notification_text + \
|
2023-02-26 10:20:32 +00:00
|
|
|
f"* You received {procoin_winnings} marseybux.\n\n"
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
send_repeatable_notification(user_id, notification_text)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Give condolences.
|
|
|
|
for participant in participants:
|
|
|
|
if not participant in winners:
|
|
|
|
send_repeatable_notification(
|
2023-02-26 10:20:32 +00:00
|
|
|
participant, f"Winning number: {number}\n\nSorry, none of your recent roulette bets paid off.")
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.flush()
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Adjust game winnings.
|
|
|
|
for game in active_games:
|
|
|
|
if rewards_by_game_id.get(game.id):
|
|
|
|
game.winnings = rewards_by_game_id[game.id]
|
|
|
|
else:
|
|
|
|
game.winnings = -game.wager
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2023-02-18 19:57:34 +00:00
|
|
|
distribute_wager_badges(game.user, game.wager, won=(game.winnings > 0))
|
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
game.active = False
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.add(game)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Commit early when dirty because of long-running tasks after roulette
|
2023-03-16 06:27:58 +00:00
|
|
|
g.db.commit()
|
2022-09-29 17:45:13 +00:00
|
|
|
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
def determine_roulette_winners(number, bets):
|
2022-10-28 23:39:31 +00:00
|
|
|
winners = []
|
|
|
|
payouts = {}
|
|
|
|
rewards_by_game_id = {}
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
def add_to_winnings(bet):
|
|
|
|
game_id = int(bet['game_id'])
|
|
|
|
gambler_id = bet['gambler']
|
|
|
|
wager_amount = bet['wager']['amount']
|
|
|
|
bet_kind = bet['bet']
|
|
|
|
reward = wager_amount * PAYOUT_MULITPLIERS[bet_kind]
|
|
|
|
payout = wager_amount + reward
|
|
|
|
currency = bet['wager']['currency']
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if not gambler_id in winners:
|
|
|
|
winners.append(gambler_id)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if not payouts.get(gambler_id):
|
|
|
|
payouts[gambler_id] = {
|
|
|
|
'coins': 0,
|
2022-11-21 23:08:29 +00:00
|
|
|
'marseybux': 0
|
2022-10-28 23:39:31 +00:00
|
|
|
}
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
if not rewards_by_game_id.get(game_id):
|
|
|
|
rewards_by_game_id[game_id] = reward
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
payouts[gambler_id][currency] += payout
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Straight-Up Bet
|
|
|
|
for bet in bets[RouletteAction.STRAIGHT_UP_BET]:
|
|
|
|
if int(bet['which']) == number:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-11-21 14:44:16 +00:00
|
|
|
if number == 0 or number == 37:
|
|
|
|
return winners, payouts, rewards_by_game_id
|
2022-11-21 21:51:47 +00:00
|
|
|
|
2022-11-21 14:44:16 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Line Bet
|
|
|
|
line = -1
|
|
|
|
for i in range(1, 7):
|
|
|
|
if number in LINES[i]:
|
|
|
|
line = i
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
for bet in bets[RouletteAction.LINE_BET]:
|
|
|
|
if int(bet['which']) == line:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Column Bet
|
|
|
|
column = -1
|
|
|
|
for i in range(1, 4):
|
|
|
|
if number in COLUMNS[i]:
|
|
|
|
column = i
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
for bet in bets[RouletteAction.COLUMN_BET]:
|
|
|
|
if int(bet['which']) == column:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Dozen Bet
|
|
|
|
dozen = -1
|
|
|
|
for i in range(1, 4):
|
|
|
|
if number in DOZENS[i]:
|
|
|
|
dozen = i
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
for bet in bets[RouletteAction.DOZEN_BET]:
|
|
|
|
if int(bet['which']) == dozen:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Even/Odd Bet
|
|
|
|
even_odd = RouletteEvenOdd.EVEN if number % 2 == 0 else RouletteEvenOdd.ODD
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
for bet in bets[RouletteAction.EVEN_ODD_BET]:
|
|
|
|
if bet['which'] == even_odd:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# Red/Black Bet
|
|
|
|
red_black = RouletteRedBlack.RED if number in REDS else RouletteRedBlack.BLACK
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
for bet in bets[RouletteAction.RED_BLACK_BET]:
|
|
|
|
if bet['which'] == red_black:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
# High/Low Bet
|
|
|
|
high_low = RouletteHighLow.HIGH if number > 18 else RouletteHighLow.LOW
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
for bet in bets[RouletteAction.HIGH_LOW_BET]:
|
|
|
|
if bet['which'] == high_low:
|
|
|
|
add_to_winnings(bet)
|
2022-09-13 01:07:39 +00:00
|
|
|
|
2022-10-28 23:39:31 +00:00
|
|
|
return winners, payouts, rewards_by_game_id
|
2022-09-13 01:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_roulette_bets():
|
2022-10-28 23:39:31 +00:00
|
|
|
return get_roulette_bets_and_betters()[1]
|