From b1674355c0bb78dc9d149f67710a495870284772 Mon Sep 17 00:00:00 2001 From: Outrun Colors Date: Mon, 5 Sep 2022 13:27:00 -0500 Subject: [PATCH] Restructure blackjack to only use a single endpoint and better handle "game end" logic --- files/assets/js/casino.js | 50 +++++++------ files/helpers/blackjack.py | 140 ++++++++++++++++++------------------ files/routes/casino.py | 72 ++++++++----------- files/routes/users.py | 4 ++ files/templates/casino.html | 9 --- 5 files changed, 133 insertions(+), 142 deletions(-) diff --git a/files/assets/js/casino.js b/files/assets/js/casino.js index 46408d1c4..b619388a2 100644 --- a/files/assets/js/casino.js +++ b/files/assets/js/casino.js @@ -82,6 +82,8 @@ function handleSlotsResponse(xhr) { } // Blackjack + +// Checking existing status onload. // When the casino loads, look up the "blackjack status" of a player to either start a new game or continue an existing game. if ( document.readyState === "complete" || @@ -114,12 +116,16 @@ function handleBlackjackStatusResponse(xhr) { if (succeeded) { if (response.active) { updateBlackjack(response.game_state); + } else { + updateBlackjackActions(null); } } else { console.error("error"); } } +// DOM Manipulation + // When starting a new game or taking an action in an existing one, a new state will be returned, and the DOM must be updated. function updateBlackjack(state) { const { player, dealer, status } = state; @@ -232,7 +238,7 @@ function updateBlackjackActions(state) { clearBlackjackActions(); - if (state.status === "active") { + if (state && state.status === "active") { document.getElementById("casinoBlackjackWager").style.display = "none"; const actionLookup = { @@ -258,8 +264,7 @@ function updateBlackjackActions(state) { actionWrapper.innerHTML = actions.join("\n"); } else { // Game is over, deal a new game. - document.getElementById("casinoBlackjackWager").style.display = "flex"; - document.getElementById("casinoBlackjackBet").disabled = false; + showWagerWidget(); const deal = buildBlackjackAction( "casinoBlackjackDeal", @@ -272,30 +277,20 @@ function updateBlackjackActions(state) { } } -function dealBlackjack() { - const wager = document.getElementById("casinoBlackjackBet").value; - const currency = document.querySelector( - 'input[name="casinoBlackjackCurrency"]:checked' - ).value; - +function hideWagerWidget() { document.getElementById("casinoBlackjackBet").disabled = true; document.getElementById("casinoBlackjackDeal").disabled = true; document.getElementById("casinoBlackjackWager").style.display = "none"; document.getElementById("casinoBlackjackResult").style.visibility = "hidden"; - - const xhr = new XMLHttpRequest(); - xhr.open("post", "/casino/blackjack"); - xhr.onload = handleBlackjackResponse.bind(null, xhr); - - const form = new FormData(); - form.append("formkey", formkey()); - form.append("wager", wager); - form.append("currency", currency); - - xhr.send(form); } -function takeBlackjackAction(action) { +function showWagerWidget() { + document.getElementById("casinoBlackjackWager").style.display = "flex"; + document.getElementById("casinoBlackjackBet").disabled = false; +} + +// Actions, Requests & Responses +function takeBlackjackAction(action, args = {}) { const xhr = new XMLHttpRequest(); xhr.open("post", "/casino/blackjack/action"); xhr.onload = handleBlackjackResponse.bind(null, xhr); @@ -304,9 +299,20 @@ function takeBlackjackAction(action) { form.append("formkey", formkey()); form.append("action", action); + for (const [key, value] of Object.entries(args)) { + form.append(key, value); + } + xhr.send(form); } +const dealBlackjack = () => { + const wager = document.getElementById("casinoBlackjackBet").value; + const currency = document.querySelector('input[name="casinoBlackjackCurrency"]:checked').value; + + hideWagerWidget(); + takeBlackjackAction("deal", { currency, wager }); +} const hitBlackjack = takeBlackjackAction.bind(null, "hit"); const stayBlackjack = takeBlackjackAction.bind(null, "stay"); const doubleBlackjack = takeBlackjackAction.bind(null, "double_down"); @@ -324,7 +330,7 @@ function handleBlackjackResponse(xhr) { const succeeded = xhr.status >= 200 && xhr.status < 300 && response && !response.error; const blackjackResult = document.getElementById("casinoBlackjackResult"); - blackjackResult.classList.remove("text-success", "text-danger"); + blackjackResult.classList.remove("text-success", "text-danger", "text-warning"); if (succeeded) { if (response.game_state) { diff --git a/files/helpers/blackjack.py b/files/helpers/blackjack.py index ee6d17b86..726c7f2e3 100644 --- a/files/helpers/blackjack.py +++ b/files/helpers/blackjack.py @@ -54,32 +54,30 @@ def get_active_game(gambler): Casino_Game.user_id == gambler.id).one_or_none() if game: - return game, json.loads(game.game_state) + game_state = json.loads(game.game_state) + return game, game_state, get_safe_game_state(game_state) else: - return None, None + return None, None, None -def get_safe_game_state(gambler): - game, game_state = get_active_game(gambler) +def get_safe_game_state(game_state): + return { + "player": game_state['player'], + "dealer": [game_state['dealer'][0], "?"], + "actions": game_state['actions'], + "insurance": game_state['insurance'], + "doubled_down": game_state['doubled_down'], + "status": game_state['status'] + } - if game: - return { - "player": game_state['player'], - "dealer": [game_state['dealer'][0], "?"], - "actions": game_state['actions'], - "insurance": game_state['insurance'], - "doubled_down": game_state['doubled_down'], - "status": game_state['status'] - } - else: - return None def apply_blackjack_result(gambler): - game, game_state = get_active_game(gambler) + game, game_state, _ = get_active_game(gambler) - if game and game.active: + if game: result = game_state['status'] + if result == 'push' or result == 'insured_loss': reward = game.wager elif result == 'won': @@ -95,53 +93,58 @@ def apply_blackjack_result(gambler): gambler.winnings += reward game.winnings += reward - if result not in ('push','blackjack'): - game.active = False + game.active = False g.db.add(game) - -def deal_blackjack_game(gambler, wager_value, currency): - over_min = wager_value >= minimum_bet - under_max = wager_value <= maximum_bet - using_dramacoin = currency == "dramacoin" - using_marseybux = not using_dramacoin - has_proper_funds = (using_dramacoin and gambler.coins >= wager_value) or ( - using_marseybux and gambler.procoins >= wager_value) - currency_prop = "coins" if using_dramacoin else "procoins" - currency_value = getattr(gambler, currency_prop, 0) - - if (over_min and under_max and has_proper_funds): - build_game(gambler, currency_prop, wager_value) - - game, game_state = get_active_game(gambler) - player_value = get_hand_value(game_state['player']) - dealer_value = get_hand_value(game_state['dealer']) - - # Charge the gambler for the game, reduce their winnings, and start the game. - setattr(gambler, currency_prop, currency_value - wager_value) - gambler.winnings -= wager_value - game.winnings -= wager_value - - # if player_value == 21 and dealer_value == 21: - # game_state["status"] = 'push' - # save_game_state(game, game_state) - # apply_blackjack_result(gambler) - # elif player_value == 21: - # game_state["status"] = 'blackjack' - # save_game_state(game, game_state) - # apply_blackjack_result(gambler) - - g.db.flush() - - return True - else: - return False - # region Actions +def gambler_dealt(gambler, currency, wager): + existing_game, _, _ = get_active_game(gambler) + + if not existing_game: + over_min = wager >= minimum_bet + under_max = wager <= maximum_bet + using_dramacoin = currency == "dramacoin" + using_marseybux = not using_dramacoin + has_proper_funds = (using_dramacoin and gambler.coins >= wager) or ( + using_marseybux and gambler.procoins >= wager) + currency_prop = "coins" if using_dramacoin else "procoins" + currency_value = getattr(gambler, currency_prop, 0) + + if (over_min and under_max and has_proper_funds): + # Start the game. + build_game(gambler, currency_prop, wager) + game, game_state, safe_state = get_active_game(gambler) + player_value = get_hand_value(game_state['player']) + dealer_value = get_hand_value(game_state['dealer']) + + # Charge the gambler for the game, reduce their winnings. + setattr(gambler, currency_prop, currency_value - wager) + gambler.winnings -= wager + game.winnings -= wager + + # In two cases, the game is instantly over. + instantly_over = False + if player_value == 21 and dealer_value == 21: + instantly_over = True + game_state["status"] = 'push' + save_game_state(game, game_state) + apply_blackjack_result(gambler) + elif player_value == 21: + instantly_over = True + game_state["status"] = 'blackjack' + save_game_state(game, game_state) + apply_blackjack_result(gambler) + + g.db.flush() + + if instantly_over: + return True, game_state + else: + return True, safe_state def gambler_hit(gambler): - game, game_state = get_active_game(gambler) + game, game_state, safe_state = get_active_game(gambler) if game: player = game_state['player'] @@ -167,13 +170,14 @@ def gambler_hit(gambler): forced_stay_success, forced_stay_state = gambler_stayed(gambler) return forced_stay_success, forced_stay_state else: - return True, game_state + _, _, safe_state = get_active_game(gambler) + return True, safe_state else: - return False, game_state + return False, safe_state def gambler_stayed(gambler): - game, game_state = get_active_game(gambler) + game, game_state, safe_state = get_active_game(gambler) if game: player = game_state['player'] @@ -206,11 +210,11 @@ def gambler_stayed(gambler): return True, game_state else: - return False, game_state + return False, safe_state def gambler_doubled_down(gambler): - game, game_state = get_active_game(gambler) + game, game_state, safe_state = get_active_game(gambler) if game and not game_state['doubled_down']: currency_value = getattr(gambler, game.currency, 0) @@ -229,14 +233,13 @@ def gambler_doubled_down(gambler): g.db.flush() last_hit_success, last_hit_state = gambler_hit(gambler) - return last_hit_success, last_hit_state else: - return False, game_state + return False, safe_state def gambler_purchased_insurance(gambler): - game, game_state = get_active_game(gambler) + game, game_state, safe_state = get_active_game(gambler) if game and not game_state['insurance']: insurance_cost = game.wager / 2 @@ -253,9 +256,10 @@ def gambler_purchased_insurance(gambler): game_state['actions'] = determine_actions(game_state) save_game_state(game, game_state) - return True, game_state + _, _, safe_state = get_active_game(gambler) + return True, safe_state else: - return False, game_state + return False, safe_state # endregion diff --git a/files/routes/casino.py b/files/routes/casino.py index 3a1604c86..d4f192c9f 100644 --- a/files/routes/casino.py +++ b/files/routes/casino.py @@ -46,47 +46,12 @@ def pull_slots(v): @limiter.limit("3/second;30/minute;600/hour;12000/day") @auth_required def get_player_blackjack_status(v): - game, game_state = get_active_game(v) + game, _, safe_state = get_active_game(v) - if game and game.active: - safe_state = get_safe_game_state(v) - return {"active": True, "game_state": safe_state} + if game: + return { "active": True, "game_state": safe_state } else: - return {"active": False, "game_state": game_state} - - -@app.post("/casino/blackjack") -@limiter.limit("3/second;30/minute;600/hour;12000/day") -@auth_required -def deal_blackjack(v): - try: - wager = int(request.values.get("wager")) - except: - return {"error": "Invalid wager."} - - try: - currency = request.values.get("currency") - except: - return {"error": "Invalid currency (expected 'dramacoin' or 'marseybux')."} - - if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): - return {"error": f"Not enough {currency} to make that bet."} - - success = deal_blackjack_game(v, wager, currency) - - if success: - game, game_state = get_active_game(v) - - if game and game.active: - safe_state = get_safe_game_state(v) - if safe_state['status'] in ('push','blackjack'): - game.active = False - return {"game_state": safe_state, "gambler": { "coins": v.coins, "procoins": v.procoins }} - else: - return {"game_state": game_state, "gambler": { "coins": v.coins, "procoins": v.procoins }} - - else: - return {"error": "Wager must be more than 100 {currency}."} + return { "active": False } @app.post("/casino/blackjack/action") @@ -96,12 +61,29 @@ def player_took_blackjack_action(v): try: action = request.values.get("action") except: - return {"error": "Invalid action."} + return { "error": "Invalid action." } was_successful = False state = None - if action == 'hit': + if action == 'deal': + try: + currency = request.values.get("currency") + wager = int(request.values.get("wager")) + except: + return { "error": "Missing either currency or wager values." } + + existing_game, _, _ = get_active_game(v) + + if (currency == "dramacoin" and wager > v.coins) or (currency == "marseybux" and wager > v.procoins): + return {"error": f"Not enough {currency} to make that bet."} + elif existing_game: + return { "error": "Cannot start a new game while an existing game persists." } + else: + success, game_state = gambler_dealt(v, currency, wager) + was_successful = success + state = game_state + elif action == 'hit': success, game_state = gambler_hit(v) was_successful = success state = game_state @@ -119,6 +101,10 @@ def player_took_blackjack_action(v): state = game_state if was_successful: - return {"active": True, "game_state": state, "gambler": { "coins": v.coins, "procoins": v.procoins }} + return { + "active": True, + "game_state": state, + "gambler": { "coins": v.coins, "procoins": v.procoins } + } else: - return {"active": False, "game_state": None} + return { "active": False } diff --git a/files/routes/users.py b/files/routes/users.py index 79e90b02c..0df748527 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -32,6 +32,10 @@ def leaderboard_thread(): for user in users8: users9.append((user.id, votes3[user.id])) users9 = sorted(users9, key=lambda x: x[1], reverse=True) + + if (len(users9) < 2): + return + users9_1, users9_2 = zip(*users9[:25]) votes1 = db.query(Vote.user_id, func.count(Vote.user_id)).filter(Vote.vote_type==1).group_by(Vote.user_id).order_by(func.count(Vote.user_id).desc()).all() diff --git a/files/templates/casino.html b/files/templates/casino.html index c15a54f2a..2c459645a 100644 --- a/files/templates/casino.html +++ b/files/templates/casino.html @@ -234,15 +234,6 @@
-