MarseyWorld/files/helpers/roulette.py

315 lines
8.1 KiB
Python

import json
from enum import Enum
from random import randint
import time
from flask import g
from files.classes.casino_game import CasinoGame
from files.helpers.alerts import *
from files.helpers.get import get_account
from files.helpers.casino import distribute_wager_badges
class RouletteAction(str, Enum):
STRAIGHT_UP_BET = "STRAIGHT_UP_BET",
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):
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]
raise ValueError("Unhandled validation function for RouletteAction")
class RouletteEvenOdd(str, Enum):
EVEN = "EVEN"
ODD = "ODD"
class RouletteRedBlack(str, Enum):
RED = "RED"
BLACK = "BLACK"
class RouletteHighLow(str, Enum):
HIGH = "HIGH"
LOW = "LOW"
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 = {
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)
}
COLUMNS = {
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)
}
DOZENS = {
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)
}
PAYOUT_MULITPLIERS = {
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,
}
def get_active_roulette_games():
return g.db.query(CasinoGame).filter(
CasinoGame.active == True,
CasinoGame.kind == 'roulette'
).all()
def charge_gambler(gambler, amount, currency):
charged = gambler.charge_account(currency, amount)
if not charged:
raise Exception("Gambler cannot afford charge.")
def gambler_placed_roulette_bet(gambler, bet, which, amount, currency):
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:
parent_id = active_games[0].game_state_json['parent_id']
charge_gambler(gambler, amount, currency)
game = CasinoGame()
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
g.db.add(game)
g.db.flush()
def get_roulette_bets_and_betters():
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)
game_state = game.game_state_json
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
def spin_roulette_wheel():
participants, bets, active_games = get_roulette_bets_and_betters()
if len(participants) > 0:
number = randint(0, 37) # 37 is 00
winners, payouts, rewards_by_game_id = determine_roulette_winners(number, bets)
if number == 37: number = '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']
procoin_winnings = gambler_payout['marseybux']
gambler.pay_account('coins', coin_winnings)
gambler.pay_account('marseybux', procoin_winnings)
# Notify the winners.
notification_text = f"Winning number: {number}\n\nCongratulations! One or more of your roulette bets paid off!\n\n"
if coin_winnings > 0:
notification_text = notification_text + \
f"* You received {coin_winnings} coins.\n\n"
if procoin_winnings > 0:
notification_text = notification_text + \
f"* You received {procoin_winnings} marseybux.\n\n"
send_repeatable_notification(user_id, notification_text)
# Give condolences.
for participant in participants:
if not participant in winners:
send_repeatable_notification(
participant, f"Winning number: {number}\n\nSorry, none of your recent roulette bets paid off.")
g.db.flush()
# 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
distribute_wager_badges(game.user, game.wager, won=(game.winnings > 0))
game.active = False
g.db.add(game)
# Commit early when dirty because of long-running tasks after roulette
g.db.commit()
def determine_roulette_winners(number, bets):
winners = []
payouts = {}
rewards_by_game_id = {}
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']
if not gambler_id in winners:
winners.append(gambler_id)
if not payouts.get(gambler_id):
payouts[gambler_id] = {
'coins': 0,
'marseybux': 0
}
if not rewards_by_game_id.get(game_id):
rewards_by_game_id[game_id] = reward
payouts[gambler_id][currency] += payout
# Straight-Up Bet
for bet in bets[RouletteAction.STRAIGHT_UP_BET]:
if int(bet['which']) == number:
add_to_winnings(bet)
if number == 0 or number == 37:
return winners, payouts, rewards_by_game_id
# Line Bet
line = -1
for i in range(1, 7):
if number in LINES[i]:
line = i
for bet in bets[RouletteAction.LINE_BET]:
if int(bet['which']) == line:
add_to_winnings(bet)
# Column Bet
column = -1
for i in range(1, 4):
if number in COLUMNS[i]:
column = i
for bet in bets[RouletteAction.COLUMN_BET]:
if int(bet['which']) == column:
add_to_winnings(bet)
# Dozen Bet
dozen = -1
for i in range(1, 4):
if number in DOZENS[i]:
dozen = i
for bet in bets[RouletteAction.DOZEN_BET]:
if int(bet['which']) == dozen:
add_to_winnings(bet)
# Even/Odd Bet
even_odd = RouletteEvenOdd.EVEN if number % 2 == 0 else RouletteEvenOdd.ODD
for bet in bets[RouletteAction.EVEN_ODD_BET]:
if bet['which'] == even_odd:
add_to_winnings(bet)
# Red/Black Bet
red_black = RouletteRedBlack.RED if number in REDS else RouletteRedBlack.BLACK
for bet in bets[RouletteAction.RED_BLACK_BET]:
if bet['which'] == red_black:
add_to_winnings(bet)
# High/Low Bet
high_low = RouletteHighLow.HIGH if number > 18 else RouletteHighLow.LOW
for bet in bets[RouletteAction.HIGH_LOW_BET]:
if bet['which'] == high_low:
add_to_winnings(bet)
return winners, payouts, rewards_by_game_id
def get_roulette_bets():
return get_roulette_bets_and_betters()[1]