mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
Did some refactoring with blackjack and probably have completely broken it
This commit is contained in:
@@ -7,6 +7,7 @@ using KfChatDotNetBot.Services;
|
|||||||
using KfChatDotNetBot.Settings;
|
using KfChatDotNetBot.Settings;
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
using KfChatDotNetWsClient.Models.Events;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NLog;
|
||||||
using RandN.Compat;
|
using RandN.Compat;
|
||||||
using RandN;
|
using RandN;
|
||||||
|
|
||||||
@@ -37,6 +38,8 @@ public class BlackjackCommand : ICommand
|
|||||||
Flags = RateLimitFlags.NoAutoDeleteCooldownResponse
|
Flags = RateLimitFlags.NoAutoDeleteCooldownResponse
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private ApplicationDbContext _dbContext = new();
|
||||||
|
|
||||||
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments,
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments,
|
||||||
CancellationToken ctx)
|
CancellationToken ctx)
|
||||||
{
|
{
|
||||||
@@ -47,17 +50,23 @@ public class BlackjackCommand : ICommand
|
|||||||
if (arguments.TryGetValue("amount", out var amountGroup))
|
if (arguments.TryGetValue("amount", out var amountGroup))
|
||||||
{
|
{
|
||||||
await StartNewGame(botInstance, user, amountGroup.Value, cleanupDelay, ctx);
|
await StartNewGame(botInstance, user, amountGroup.Value, cleanupDelay, ctx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (arguments.TryGetValue("action", out var actionGroup))
|
|
||||||
|
if (arguments.TryGetValue("action", out var actionGroup))
|
||||||
{
|
{
|
||||||
await ContinueGame(botInstance, user, actionGroup.Value.ToLower(), cleanupDelay, ctx);
|
await ContinueGame(botInstance, user, actionGroup.Value.ToLower(), cleanupDelay, ctx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"User {user.KfUsername} somehow ran blackjack without an amount or action: {message.MessageRaw}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task StartNewGame(ChatBot botInstance, UserDbModel user, string amountStr,
|
private async Task StartNewGame(ChatBot botInstance, UserDbModel user, string amountStr,
|
||||||
TimeSpan cleanupDelay, CancellationToken ctx)
|
TimeSpan cleanupDelay, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
|
var logger = LogManager.GetCurrentClassLogger();
|
||||||
var wager = Convert.ToDecimal(amountStr);
|
var wager = Convert.ToDecimal(amountStr);
|
||||||
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
||||||
|
|
||||||
@@ -76,55 +85,43 @@ public class BlackjackCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for existing incomplete blackjack game
|
// Check for existing incomplete blackjack game
|
||||||
await using var db = new ApplicationDbContext();
|
var existingGame = await _dbContext.Wagers
|
||||||
var existingGame = await db.Wagers
|
.LastOrDefaultAsync(w => w.Gambler.Id == gambler.Id &&
|
||||||
.Where(w => w.Gambler.Id == gambler.Id &&
|
w.Game == WagerGame.Blackjack &&
|
||||||
w.Game == WagerGame.Blackjack &&
|
!w.IsComplete && w.GameMeta != null,
|
||||||
!w.IsComplete)
|
cancellationToken: ctx);
|
||||||
.OrderByDescending(w => w.Id)
|
|
||||||
.FirstOrDefaultAsync(ctx);
|
|
||||||
|
|
||||||
if (existingGame != null)
|
if (existingGame != null)
|
||||||
{
|
{
|
||||||
if (existingGame.GameMeta == null)
|
try
|
||||||
{
|
{
|
||||||
// Mark as complete with loss and continue
|
_ = JsonSerializer.Deserialize<BlackjackGameMetaModel>(existingGame.GameMeta!) ??
|
||||||
db.Attach(existingGame);
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Caught error when deserializing meta for wager ID {existingGame.Id}");
|
||||||
|
logger.Error(e);
|
||||||
|
await botInstance.SendChatMessageAsync(
|
||||||
|
$"{user.FormatUsername()}, somehow your previous blackjack game state got messed up. Please try again",
|
||||||
|
true, autoDeleteAfter: cleanupDelay);
|
||||||
existingGame.IsComplete = true;
|
existingGame.IsComplete = true;
|
||||||
existingGame.WagerEffect = -existingGame.WagerAmount;
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
existingGame.Multiplier = 0m;
|
throw;
|
||||||
await db.SaveChangesAsync(ctx);
|
|
||||||
}
|
}
|
||||||
else
|
// Check if game has timed out
|
||||||
|
var timeSinceStart = DateTimeOffset.UtcNow - existingGame.Time;
|
||||||
|
|
||||||
|
if (timeSinceStart > GameTimeout)
|
||||||
{
|
{
|
||||||
var existingGameState = JsonSerializer.Deserialize<BlackjackGameMetaModel>(existingGame.GameMeta);
|
await ForfeitGame(botInstance, user, gambler, existingGame, cleanupDelay, ctx);
|
||||||
|
return;
|
||||||
if (existingGameState == null)
|
|
||||||
{
|
|
||||||
db.Attach(existingGame);
|
|
||||||
existingGame.IsComplete = true;
|
|
||||||
existingGame.WagerEffect = -existingGame.WagerAmount;
|
|
||||||
existingGame.Multiplier = 0m;
|
|
||||||
await db.SaveChangesAsync(ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check if game has timed out
|
|
||||||
var timeSinceStart = DateTimeOffset.UtcNow - existingGameState.GameStarted;
|
|
||||||
|
|
||||||
if (timeSinceStart > GameTimeout)
|
|
||||||
{
|
|
||||||
await ForfeitGame(botInstance, user, gambler, existingGame, cleanupDelay, ctx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await botInstance.SendChatMessageAsync(
|
|
||||||
$"{user.FormatUsername()}, you already have an active blackjack game. Use !bj hit or !bj stand to continue.",
|
|
||||||
true, autoDeleteAfter: cleanupDelay);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await botInstance.SendChatMessageAsync(
|
||||||
|
$"{user.FormatUsername()}, you already have an active blackjack game. Use !bj hit or !bj stand to continue.",
|
||||||
|
true, autoDeleteAfter: cleanupDelay);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -140,16 +137,14 @@ public class BlackjackCommand : ICommand
|
|||||||
// Create game state
|
// Create game state
|
||||||
var newGameState = new BlackjackGameMetaModel
|
var newGameState = new BlackjackGameMetaModel
|
||||||
{
|
{
|
||||||
WagerId = 0, // Will be set after wager is created
|
|
||||||
PlayerHand = playerHand,
|
PlayerHand = playerHand,
|
||||||
DealerHand = dealerHand,
|
DealerHand = dealerHand,
|
||||||
Deck = deck,
|
Deck = deck,
|
||||||
GameStarted = DateTimeOffset.UtcNow,
|
|
||||||
HasDoubledDown = false
|
HasDoubledDown = false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create incomplete wager
|
// Create incomplete wager
|
||||||
var newBalance = await Money.NewWagerAsync(
|
await Money.NewWagerAsync(
|
||||||
gambler.Id,
|
gambler.Id,
|
||||||
wager,
|
wager,
|
||||||
-wager, // This will be the effect for incomplete wagers
|
-wager, // This will be the effect for incomplete wagers
|
||||||
@@ -161,15 +156,12 @@ public class BlackjackCommand : ICommand
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update wager ID in game state
|
// Update wager ID in game state
|
||||||
var createdWager = await db.Wagers
|
var createdWager = await _dbContext.Wagers
|
||||||
.Where(w => w.Gambler.Id == gambler.Id && w.Game == WagerGame.Blackjack && !w.IsComplete)
|
.LastOrDefaultAsync(
|
||||||
.OrderByDescending(w => w.Id)
|
w => w.Gambler.Id == gambler.Id && w.Game == WagerGame.Blackjack && !w.IsComplete && w.GameMeta != null,
|
||||||
.FirstAsync(ctx);
|
cancellationToken: ctx) ?? throw new InvalidOperationException();
|
||||||
|
|
||||||
newGameState.WagerId = createdWager.Id;
|
|
||||||
db.Attach(createdWager);
|
|
||||||
createdWager.GameMeta = JsonSerializer.Serialize(newGameState);
|
createdWager.GameMeta = JsonSerializer.Serialize(newGameState);
|
||||||
await db.SaveChangesAsync(ctx);
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
|
|
||||||
// Check for immediate blackjacks
|
// Check for immediate blackjacks
|
||||||
var playerValue = BlackjackHelper.CalculateHandValue(playerHand);
|
var playerValue = BlackjackHelper.CalculateHandValue(playerHand);
|
||||||
@@ -207,13 +199,10 @@ public class BlackjackCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find active game
|
// Find active game
|
||||||
await using var db = new ApplicationDbContext();
|
var activeWager = await _dbContext.Wagers
|
||||||
var activeWager = await db.Wagers
|
.LastOrDefaultAsync(w => w.Gambler.Id == gambler.Id &&
|
||||||
.Where(w => w.Gambler.Id == gambler.Id &&
|
w.Game == WagerGame.Blackjack &&
|
||||||
w.Game == WagerGame.Blackjack &&
|
!w.IsComplete && w.GameMeta != null, cancellationToken: ctx);
|
||||||
!w.IsComplete)
|
|
||||||
.OrderByDescending(w => w.Id)
|
|
||||||
.FirstOrDefaultAsync(ctx);
|
|
||||||
|
|
||||||
if (activeWager == null)
|
if (activeWager == null)
|
||||||
{
|
{
|
||||||
@@ -223,26 +212,19 @@ public class BlackjackCommand : ICommand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeWager.GameMeta == null)
|
var currentGameState = JsonSerializer.Deserialize<BlackjackGameMetaModel>(activeWager.GameMeta!);
|
||||||
{
|
|
||||||
await botInstance.SendChatMessageAsync(
|
|
||||||
$"{user.FormatUsername()}, your game data is corrupted. Please start a new game.",
|
|
||||||
true, autoDeleteAfter: cleanupDelay);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentGameState = JsonSerializer.Deserialize<BlackjackGameMetaModel>(activeWager.GameMeta);
|
|
||||||
if (currentGameState == null)
|
if (currentGameState == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
$"{user.FormatUsername()}, your game data is corrupted. Please start a new game.",
|
$"{user.FormatUsername()}, your game data is corrupted. Please start a new game.",
|
||||||
true, autoDeleteAfter: cleanupDelay);
|
true, autoDeleteAfter: cleanupDelay);
|
||||||
|
activeWager.IsComplete = true;
|
||||||
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check timeout
|
// Check timeout
|
||||||
var timeSinceStart = DateTimeOffset.UtcNow - currentGameState.GameStarted;
|
var timeSinceStart = DateTimeOffset.UtcNow - activeWager.Time;
|
||||||
if (timeSinceStart > GameTimeout)
|
if (timeSinceStart > GameTimeout)
|
||||||
{
|
{
|
||||||
await ForfeitGame(botInstance, user, gambler, activeWager, cleanupDelay, ctx);
|
await ForfeitGame(botInstance, user, gambler, activeWager, cleanupDelay, ctx);
|
||||||
@@ -296,8 +278,10 @@ public class BlackjackCommand : ICommand
|
|||||||
true, autoDeleteAfter: cleanupDelay);
|
true, autoDeleteAfter: cleanupDelay);
|
||||||
|
|
||||||
await ResolveGame(botInstance, user, gambler, wager, gameState, false, cleanupDelay, ctx);
|
await ResolveGame(botInstance, user, gambler, wager, gameState, false, cleanupDelay, ctx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (gameState.HasDoubledDown)
|
|
||||||
|
if (gameState.HasDoubledDown)
|
||||||
{
|
{
|
||||||
// Auto-stand after double down hit
|
// Auto-stand after double down hit
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
@@ -306,21 +290,18 @@ public class BlackjackCommand : ICommand
|
|||||||
true, autoDeleteAfter: cleanupDelay);
|
true, autoDeleteAfter: cleanupDelay);
|
||||||
|
|
||||||
await HandleStand(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
|
await HandleStand(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Continue game
|
||||||
// Continue game
|
wager.GameMeta = JsonSerializer.Serialize(gameState);
|
||||||
await using var db = new ApplicationDbContext();
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
db.Attach(wager);
|
|
||||||
wager.GameMeta = JsonSerializer.Serialize(gameState);
|
|
||||||
await db.SaveChangesAsync(ctx);
|
|
||||||
|
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
$"{user.FormatUsername()} hit and drew {card}[br]" +
|
$"{user.FormatUsername()} hit and drew {card}[br]" +
|
||||||
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(gameState.PlayerHand)} = {playerValue}[br]" +
|
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(gameState.PlayerHand)} = {playerValue}[br]" +
|
||||||
$"Use [B]!bj hit[/B] or [B]!bj stand[/B] to continue",
|
$"Use [B]!bj hit[/B] or [B]!bj stand[/B] to continue",
|
||||||
true, autoDeleteAfter: cleanupDelay);
|
true, autoDeleteAfter: cleanupDelay);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleStand(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler,
|
private async Task HandleStand(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler,
|
||||||
@@ -372,18 +353,13 @@ public class BlackjackCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Double the wager
|
// Double the wager
|
||||||
await using var db = new ApplicationDbContext();
|
|
||||||
db.Attach(wager);
|
|
||||||
db.Attach(gambler);
|
|
||||||
|
|
||||||
var additionalWager = wager.WagerAmount;
|
var additionalWager = wager.WagerAmount;
|
||||||
gambler.Balance -= additionalWager;
|
gambler.Balance -= additionalWager;
|
||||||
wager.WagerAmount *= 2;
|
wager.WagerAmount *= 2;
|
||||||
wager.WagerEffect -= additionalWager; // Subtract the additional wager
|
wager.WagerEffect -= additionalWager; // Subtract the additional wager
|
||||||
|
|
||||||
gameState.HasDoubledDown = true;
|
gameState.HasDoubledDown = true;
|
||||||
|
|
||||||
await db.SaveChangesAsync(ctx);
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
|
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
$"{user.FormatUsername()} doubled down! Wager is now {await wager.WagerAmount.FormatKasinoCurrencyAsync()}",
|
$"{user.FormatUsername()} doubled down! Wager is now {await wager.WagerAmount.FormatKasinoCurrencyAsync()}",
|
||||||
@@ -461,30 +437,13 @@ public class BlackjackCommand : ICommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update wager to complete
|
// Update wager to complete
|
||||||
await using var db = new ApplicationDbContext();
|
|
||||||
db.Attach(wager);
|
|
||||||
db.Attach(gambler);
|
|
||||||
|
|
||||||
wager.IsComplete = true;
|
wager.IsComplete = true;
|
||||||
wager.WagerEffect = finalEffect;
|
wager.WagerEffect = finalEffect;
|
||||||
wager.Multiplier = multiplier;
|
wager.Multiplier = multiplier;
|
||||||
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
var balanceAdjustment = finalEffect + wager.WagerAmount;
|
var balanceAdjustment = finalEffect + wager.WagerAmount;
|
||||||
gambler.Balance += balanceAdjustment;
|
await Money.ModifyBalanceAsync(gambler.Id, balanceAdjustment, TransactionSourceEventType.Gambling,
|
||||||
|
$"Blackjack outcome from wager {wager.Id}", null, ctx);
|
||||||
// Create transaction for the outcome
|
|
||||||
await db.Transactions.AddAsync(new TransactionDbModel
|
|
||||||
{
|
|
||||||
Gambler = gambler,
|
|
||||||
EventSource = TransactionSourceEventType.Gambling,
|
|
||||||
Time = DateTimeOffset.UtcNow,
|
|
||||||
Effect = balanceAdjustment,
|
|
||||||
Comment = $"Blackjack outcome from wager {wager.Id}",
|
|
||||||
NewBalance = gambler.Balance,
|
|
||||||
TimeUnixEpochSeconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
|
||||||
}, ctx);
|
|
||||||
|
|
||||||
await db.SaveChangesAsync(ctx);
|
|
||||||
|
|
||||||
// Display result
|
// Display result
|
||||||
var message = $"🃏 {user.FormatUsername()}'s blackjack game:[br]" +
|
var message = $"🃏 {user.FormatUsername()}'s blackjack game:[br]" +
|
||||||
@@ -509,14 +468,8 @@ public class BlackjackCommand : ICommand
|
|||||||
private async Task ForfeitGame(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler,
|
private async Task ForfeitGame(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler,
|
||||||
WagerDbModel wager, TimeSpan cleanupDelay, CancellationToken ctx)
|
WagerDbModel wager, TimeSpan cleanupDelay, CancellationToken ctx)
|
||||||
{
|
{
|
||||||
await using var db = new ApplicationDbContext();
|
|
||||||
db.Attach(wager);
|
|
||||||
|
|
||||||
wager.IsComplete = true;
|
wager.IsComplete = true;
|
||||||
wager.WagerEffect = -wager.WagerAmount;
|
await _dbContext.SaveChangesAsync(ctx);
|
||||||
wager.Multiplier = 0m;
|
|
||||||
|
|
||||||
await db.SaveChangesAsync(ctx);
|
|
||||||
|
|
||||||
await botInstance.SendChatMessageAsync(
|
await botInstance.SendChatMessageAsync(
|
||||||
$"{user.FormatUsername()}, your blackjack game timed out and you forfeited {await wager.WagerAmount.FormatKasinoCurrencyAsync()}",
|
$"{user.FormatUsername()}, your blackjack game timed out and you forfeited {await wager.WagerAmount.FormatKasinoCurrencyAsync()}",
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
|
|
||||||
public class BlackjackGameMetaModel
|
public class BlackjackGameMetaModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The wager ID associated with this game
|
|
||||||
/// </summary>
|
|
||||||
public required int WagerId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Player's hand
|
/// Player's hand
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -22,11 +17,6 @@ public class BlackjackGameMetaModel
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public required List<Card> Deck { get; set; }
|
public required List<Card> Deck { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When the game was started
|
|
||||||
/// </summary>
|
|
||||||
public required DateTimeOffset GameStarted { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether player has doubled down (can only hit once more)
|
/// Whether player has doubled down (can only hit once more)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user