mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
Refactored krash
This commit is contained in:
@@ -67,7 +67,7 @@ public class KrashBetCommand : ICommand
|
|||||||
true, autoDeleteAfter: TimeSpan.FromSeconds(5));
|
true, autoDeleteAfter: TimeSpan.FromSeconds(5));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (botInstance.BotServices.KasinoKrash.theGame == null)
|
if (botInstance.BotServices.KasinoKrash.TheGame == null)
|
||||||
{
|
{
|
||||||
//start a new game
|
//start a new game
|
||||||
await botInstance.BotServices.KasinoKrash.StartGame(gambler, wager, multi);
|
await botInstance.BotServices.KasinoKrash.StartGame(gambler, wager, multi);
|
||||||
|
|||||||
@@ -416,7 +416,8 @@ public enum WagerGame
|
|||||||
[Description("Plinko")]
|
[Description("Plinko")]
|
||||||
Plinko,
|
Plinko,
|
||||||
[Description("Roulette but live")]
|
[Description("Roulette but live")]
|
||||||
Roulette
|
Roulette,
|
||||||
|
Krash
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GamblerState
|
public enum GamblerState
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using KfChatDotNetBot.Extensions;
|
using KfChatDotNetBot.Extensions;
|
||||||
using KfChatDotNetBot.Models;
|
|
||||||
using KfChatDotNetBot.Models.DbModels;
|
using KfChatDotNetBot.Models.DbModels;
|
||||||
using KfChatDotNetBot.Settings;
|
using KfChatDotNetBot.Settings;
|
||||||
using NLog;
|
using NLog;
|
||||||
using StackExchange.Redis;
|
using StackExchange.Redis;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using KfChatDotNetBot.Commands.Kasino;
|
|
||||||
using RandN;
|
using RandN;
|
||||||
using RandN.Compat;
|
using RandN.Compat;
|
||||||
|
|
||||||
@@ -19,8 +16,7 @@ public class KasinoKrash : IDisposable
|
|||||||
private IDatabase? _redisDb;
|
private IDatabase? _redisDb;
|
||||||
private ChatBot _kfChatBot;
|
private ChatBot _kfChatBot;
|
||||||
private CancellationToken _ct;
|
private CancellationToken _ct;
|
||||||
public decimal HOUSE_EDGE = 0.98m;
|
public KasinoKrashModel? TheGame;
|
||||||
public KasinoKrashModel? theGame;
|
|
||||||
|
|
||||||
|
|
||||||
public KasinoKrash(ChatBot kfChatBot, CancellationToken ct = default) //the service itself
|
public KasinoKrash(ChatBot kfChatBot, CancellationToken ct = default) //the service itself
|
||||||
@@ -37,8 +33,8 @@ public class KasinoKrash : IDisposable
|
|||||||
var redis = ConnectionMultiplexer.Connect(connectionString.Value);
|
var redis = ConnectionMultiplexer.Connect(connectionString.Value);
|
||||||
_redisDb = redis.GetDatabase();
|
_redisDb = redis.GetDatabase();
|
||||||
//attempt to pull a game from the db in case the bot crashed while a game was ongoing. if so it will restart the run
|
//attempt to pull a game from the db in case the bot crashed while a game was ongoing. if so it will restart the run
|
||||||
theGame = GetKrashState().Result;
|
TheGame = GetKrashState().Result;
|
||||||
if (theGame != null) _ = RunGame();
|
if (TheGame != null) _ = RunGame();
|
||||||
}
|
}
|
||||||
public bool IsInitialized()
|
public bool IsInitialized()
|
||||||
{
|
{
|
||||||
@@ -56,7 +52,7 @@ public class KasinoKrash : IDisposable
|
|||||||
{
|
{
|
||||||
if (_redisDb == null) throw new InvalidOperationException("Kasino krash service isn't initialized");
|
if (_redisDb == null) throw new InvalidOperationException("Kasino krash service isn't initialized");
|
||||||
await _redisDb.KeyDeleteAsync("Krash.State");
|
await _redisDb.KeyDeleteAsync("Krash.State");
|
||||||
theGame = null;
|
TheGame = null;
|
||||||
}
|
}
|
||||||
public async Task SaveKrashState(KasinoKrashModel krash)
|
public async Task SaveKrashState(KasinoKrashModel krash)
|
||||||
{
|
{
|
||||||
@@ -67,78 +63,72 @@ public class KasinoKrash : IDisposable
|
|||||||
|
|
||||||
public async Task AttemptKrash(GamblerDbModel gambler)
|
public async Task AttemptKrash(GamblerDbModel gambler)
|
||||||
{
|
{
|
||||||
if (theGame == null)
|
if (TheGame == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Failed to retrieve state or no krash is in progress");
|
throw new InvalidOperationException("Failed to retrieve state or no krash is in progress");
|
||||||
}
|
}
|
||||||
if (!theGame.bets.Any(x => x.gambler.User.KfId == gambler.User.KfId)) return;
|
if (TheGame.Bets.All(x => x.Gambler.User.KfId != gambler.User.KfId)) return;
|
||||||
if (!theGame.krashAccepted) return;
|
if (!TheGame.KrashAccepted) return;
|
||||||
|
|
||||||
//find which bet is yours
|
//find which bet is yours
|
||||||
int index = 0;
|
var index = TheGame.Bets.TakeWhile(bet => bet.Gambler.User.KfId != gambler.User.KfId).Count();
|
||||||
foreach (var bet in theGame.bets)
|
|
||||||
{
|
|
||||||
if (bet.gambler.User.KfId == gambler.User.KfId) break;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var krashBet = theGame.bets[index];
|
var krashBet = TheGame.Bets[index];
|
||||||
theGame.bets.RemoveAt(index);
|
TheGame.Bets.RemoveAt(index);
|
||||||
decimal payout = theGame.currentMulti * krashBet.wager - krashBet.wager;
|
var payout = TheGame.CurrentMulti * krashBet.Wager - krashBet.Wager;
|
||||||
var newBalance = await Money.NewWagerAsync(krashBet.gambler.Id, krashBet.wager, payout, WagerGame.Krash);
|
var newBalance = await Money.NewWagerAsync(krashBet.Gambler.Id, krashBet.Wager, payout, WagerGame.Krash, ct: _ct);
|
||||||
await _kfChatBot.SendChatMessageAsync(
|
await _kfChatBot.SendChatMessageAsync(
|
||||||
$"{krashBet.gambler.User.FormatUsername()}, you [color=limegreen][b]won[/b][/color] {await payout.FormatKasinoCurrencyAsync()}!",
|
$"{krashBet.Gambler.User.FormatUsername()}, you [color=limegreen][b]won[/b][/color] {await payout.FormatKasinoCurrencyAsync()}!",
|
||||||
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
||||||
if (_kfChatBot.BotServices.KasinoShop != null)
|
if (_kfChatBot.BotServices.KasinoShop != null)
|
||||||
{
|
{
|
||||||
await _kfChatBot.BotServices.KasinoShop.ProcessWagerTracking(krashBet.gambler, WagerGame.Krash, krashBet.wager,
|
await _kfChatBot.BotServices.KasinoShop.ProcessWagerTracking(krashBet.Gambler, WagerGame.Krash, krashBet.Wager,
|
||||||
payout, newBalance);
|
payout, newBalance);
|
||||||
}
|
}
|
||||||
await SaveKrashState(theGame);
|
await SaveKrashState(TheGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddParticipant(GamblerDbModel gambler, decimal wager, decimal multi = -1)
|
public async Task AddParticipant(GamblerDbModel gambler, decimal wager, decimal multi = -1)
|
||||||
{
|
{
|
||||||
if (theGame == null)
|
if (TheGame == null)
|
||||||
{
|
{
|
||||||
theGame = await GetKrashState();
|
TheGame = await GetKrashState();
|
||||||
if (theGame == null) throw new InvalidOperationException("Failed to retrieve state or no krash is in progress");
|
if (TheGame == null) throw new InvalidOperationException("Failed to retrieve state or no krash is in progress");
|
||||||
_ = RunGame();
|
_ = RunGame();
|
||||||
}
|
}
|
||||||
if (theGame.bets.Any(x => x.gambler.User.KfId == gambler.User.KfId)) return;
|
if (TheGame.Bets.Any(x => x.Gambler.User.KfId == gambler.User.KfId)) return;
|
||||||
if (!theGame.betsAccepted) return;
|
if (!TheGame.BetsAccepted) return;
|
||||||
KrashBet bet = new KrashBet{gambler = gambler, wager = wager, multi = multi};
|
var bet = new KrashBet{Gambler = gambler, Wager = wager, Multi = multi};
|
||||||
theGame.bets.Add(bet);
|
TheGame.Bets.Add(bet);
|
||||||
await SaveKrashState(theGame);
|
await SaveKrashState(TheGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartGame(GamblerDbModel creator, decimal wager, decimal multi = -1)
|
public async Task StartGame(GamblerDbModel creator, decimal wager, decimal multi = -1)
|
||||||
{
|
{
|
||||||
theGame = new KasinoKrashModel(creator);
|
TheGame = new KasinoKrashModel(creator);
|
||||||
theGame.bets.Add(new KrashBet{gambler = creator, wager = wager, multi = multi});
|
TheGame.Bets.Add(new KrashBet{Gambler = creator, Wager = wager, Multi = multi});
|
||||||
await SaveKrashState(theGame);
|
await SaveKrashState(TheGame);
|
||||||
_ = RunGame();
|
_ = RunGame();
|
||||||
}
|
}
|
||||||
public async Task RunGame() //running the actual game
|
public async Task RunGame() //running the actual game
|
||||||
{
|
{
|
||||||
if (theGame == null)
|
if (TheGame == null)
|
||||||
{
|
{
|
||||||
await RemoveKrashState();
|
await RemoveKrashState();
|
||||||
await _kfChatBot.SendChatMessageAsync("Krash error 1", true);
|
await _kfChatBot.SendChatMessageAsync("Krash error 1", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var msg = await _kfChatBot.SendChatMessageAsync(
|
var msg = await _kfChatBot.SendChatMessageAsync(
|
||||||
$"{theGame.creator.User.FormatUsername()} started a Krash! You have 30 seconds to place your bets.", true);
|
$"{TheGame.Creator.User.FormatUsername()} started a Krash! You have 30 seconds to place your bets.", true);
|
||||||
TimeSpan preGameTimer = TimeSpan.FromSeconds(30);
|
var preGameTimer = TimeSpan.FromSeconds(30);
|
||||||
TimeSpan interval = TimeSpan.FromSeconds(1);
|
var interval = TimeSpan.FromSeconds(1);
|
||||||
var timer = new PeriodicTimer(interval);
|
var timer = new PeriodicTimer(interval);
|
||||||
string bets;
|
|
||||||
while (await timer.WaitForNextTickAsync(_ct)) //timer before starting the game
|
while (await timer.WaitForNextTickAsync(_ct)) //timer before starting the game
|
||||||
{
|
{
|
||||||
bets = "";
|
var bets = "";
|
||||||
foreach (var bet in theGame.bets) bets += $"{bet.gambler.User.FormatUsername()} is betting {bet.wager}[br]";
|
foreach (var bet in TheGame.Bets) bets += $"{bet.Gambler.User.FormatUsername()} is betting {bet.Wager}[br]";
|
||||||
await _kfChatBot.KfClient.EditMessageAsync(msg.ChatMessageUuid,
|
await _kfChatBot.KfClient.EditMessageAsync(msg.ChatMessageUuid,
|
||||||
$"{theGame.creator.User.FormatUsername()} started a Krash! You have {preGameTimer} to place your bets.[br]{bets}");
|
$"{TheGame.Creator.User.FormatUsername()} started a Krash! You have {preGameTimer} to place your bets.[br]{bets}");
|
||||||
preGameTimer -= interval;
|
preGameTimer -= interval;
|
||||||
if (preGameTimer <= TimeSpan.Zero)
|
if (preGameTimer <= TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
@@ -147,56 +137,56 @@ public class KasinoKrash : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
//any bets placed after this point will be cancelled, must wait until the last game finishes to start a new one.
|
//any bets placed after this point will be cancelled, must wait until the last game finishes to start a new one.
|
||||||
theGame.betsAccepted = false;
|
TheGame.BetsAccepted = false;
|
||||||
await SaveKrashState(theGame);
|
await SaveKrashState(TheGame);
|
||||||
|
|
||||||
//start the display of the game
|
//start the display of the game
|
||||||
|
|
||||||
//change these to change the speed of the game
|
//change these to change the speed of the game
|
||||||
decimal growthRate = 1.02m;
|
var growthRate = 1.02m;
|
||||||
decimal growthAcceleration = 1.00185m;
|
var growthAcceleration = 1.00185m;
|
||||||
await _kfChatBot.KfClient.DeleteMessageAsync(msg.ChatMessageUuid!);
|
await _kfChatBot.KfClient.DeleteMessageAsync(msg.ChatMessageUuid!);
|
||||||
msg = await _kfChatBot.SendChatMessageAsync($"[center][b][size=200][color=limegreen]{theGame.currentMulti}x");
|
msg = await _kfChatBot.SendChatMessageAsync($"[center][b][size=200][color=limegreen]{TheGame.CurrentMulti}x");
|
||||||
decimal defaultGrowth = 0.01m;
|
var defaultGrowth = 0.01m;
|
||||||
interval = TimeSpan.FromSeconds(0.1);
|
interval = TimeSpan.FromSeconds(0.1);
|
||||||
timer = new PeriodicTimer(interval);
|
timer = new PeriodicTimer(interval);
|
||||||
while (await timer.WaitForNextTickAsync(_ct))
|
while (await timer.WaitForNextTickAsync(_ct))
|
||||||
{
|
{
|
||||||
await _kfChatBot.KfClient.EditMessageAsync(msg.ChatMessageUuid!, $"[center][b][size=200][color=limegreen]{theGame.currentMulti}x");
|
await _kfChatBot.KfClient.EditMessageAsync(msg.ChatMessageUuid!, $"[center][b][size=200][color=limegreen]{TheGame.CurrentMulti}x");
|
||||||
theGame.currentMulti += defaultGrowth;
|
TheGame.CurrentMulti += defaultGrowth;
|
||||||
defaultGrowth *= growthRate;
|
defaultGrowth *= growthRate;
|
||||||
growthRate *= growthAcceleration;
|
growthRate *= growthAcceleration;
|
||||||
if (theGame.currentMulti >= theGame.finalMulti) break;
|
if (TheGame.CurrentMulti >= TheGame.FinalMulti) break;
|
||||||
}
|
}
|
||||||
//at this point the game crashes and everybody who did not cash out or pre bet on a multi will have balance subtracted, winners will be paid out.
|
//at this point the game crashes and everybody who did not cash out or pre bet on a multi will have balance subtracted, winners will be paid out.
|
||||||
await _kfChatBot.KfClient.EditMessageAsync(msg.ChatMessageUuid!, $"[center][b][size=200][color=red]{theGame.finalMulti}x");
|
await _kfChatBot.KfClient.EditMessageAsync(msg.ChatMessageUuid!, $"[center][b][size=200][color=red]{TheGame.FinalMulti}x");
|
||||||
foreach (var bet in theGame.bets)
|
foreach (var bet in TheGame.Bets)
|
||||||
{
|
{
|
||||||
if (bet.multi <= theGame.finalMulti)
|
if (bet.Multi <= TheGame.FinalMulti)
|
||||||
{
|
{
|
||||||
//you win
|
//you win
|
||||||
decimal payout = theGame.currentMulti * bet.wager - bet.wager;
|
var payout = TheGame.CurrentMulti * bet.Wager - bet.Wager;
|
||||||
var newBalance = await Money.NewWagerAsync(bet.gambler.Id, bet.wager, payout, WagerGame.Krash);
|
var newBalance = await Money.NewWagerAsync(bet.Gambler.Id, bet.Wager, payout, WagerGame.Krash, ct: _ct);
|
||||||
await _kfChatBot.SendChatMessageAsync(
|
await _kfChatBot.SendChatMessageAsync(
|
||||||
$"{bet.gambler.User.FormatUsername()}, you [color=limegreen][b]won[/b][/color] {await payout.FormatKasinoCurrencyAsync()}!",
|
$"{bet.Gambler.User.FormatUsername()}, you [color=limegreen][b]won[/b][/color] {await payout.FormatKasinoCurrencyAsync()}!",
|
||||||
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
||||||
if (_kfChatBot.BotServices.KasinoShop != null)
|
if (_kfChatBot.BotServices.KasinoShop != null)
|
||||||
{
|
{
|
||||||
await _kfChatBot.BotServices.KasinoShop.ProcessWagerTracking(bet.gambler, WagerGame.Krash, bet.wager,
|
await _kfChatBot.BotServices.KasinoShop.ProcessWagerTracking(bet.Gambler, WagerGame.Krash, bet.Wager,
|
||||||
payout, newBalance);
|
payout, newBalance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//automatically lose, no pre entered multi or it was greater than the final multi and failed to cash out
|
//automatically lose, no pre entered multi or it was greater than the final multi and failed to cash out
|
||||||
var newBalance = await Money.NewWagerAsync(bet.gambler.Id, bet.wager, -bet.wager, WagerGame.Krash);
|
var newBalance = await Money.NewWagerAsync(bet.Gambler.Id, bet.Wager, -bet.Wager, WagerGame.Krash, ct: _ct);
|
||||||
await _kfChatBot.SendChatMessageAsync(
|
await _kfChatBot.SendChatMessageAsync(
|
||||||
$"{bet.gambler.User.FormatUsername()}, you [color=red][b]lost[/b][/color] {await bet.wager.FormatKasinoCurrencyAsync()}!",
|
$"{bet.Gambler.User.FormatUsername()}, you [color=red][b]lost[/b][/color] {await bet.Wager.FormatKasinoCurrencyAsync()}!",
|
||||||
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
true, autoDeleteAfter: TimeSpan.FromSeconds(10));
|
||||||
if (_kfChatBot.BotServices.KasinoShop != null)
|
if (_kfChatBot.BotServices.KasinoShop != null)
|
||||||
{
|
{
|
||||||
await _kfChatBot.BotServices.KasinoShop.ProcessWagerTracking(bet.gambler, WagerGame.Krash, bet.wager,
|
await _kfChatBot.BotServices.KasinoShop.ProcessWagerTracking(bet.Gambler, WagerGame.Krash, bet.Wager,
|
||||||
-bet.wager, newBalance);
|
-bet.Wager, newBalance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,26 +200,26 @@ public class KasinoKrash : IDisposable
|
|||||||
|
|
||||||
public class KasinoKrashModel
|
public class KasinoKrashModel
|
||||||
{
|
{
|
||||||
public GamblerDbModel creator;
|
public GamblerDbModel Creator;
|
||||||
public decimal finalMulti = 0;
|
public decimal FinalMulti = 0;
|
||||||
public decimal currentMulti = 1.01m;
|
public decimal CurrentMulti = 1.01m;
|
||||||
public List<KrashBet> bets = new();
|
public List<KrashBet> Bets = new();
|
||||||
public decimal HOUSE_EDGE = 0.98m;
|
public decimal HouseEdge = 0.98m;
|
||||||
public bool betsAccepted = true;
|
public bool BetsAccepted = true;
|
||||||
public bool krashAccepted = false;
|
public bool KrashAccepted = false;
|
||||||
public KasinoKrashModel(GamblerDbModel creator)
|
public KasinoKrashModel(GamblerDbModel creator)
|
||||||
{
|
{
|
||||||
this.creator = creator;
|
this.Creator = creator;
|
||||||
finalMulti = GetLinearWeightedRandom(1.01, 25000);
|
FinalMulti = GetLinearWeightedRandom(1.01, 25000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private decimal GetLinearWeightedRandom(double minValue, double maxValue)
|
private decimal GetLinearWeightedRandom(double minValue, double maxValue)
|
||||||
{
|
{
|
||||||
var random = RandomShim.Create(StandardRng.Create());
|
var random = RandomShim.Create(StandardRng.Create());
|
||||||
double r = random.NextDouble(); // Returns 0.0 to 1.0
|
var r = random.NextDouble(); // Returns 0.0 to 1.0
|
||||||
|
|
||||||
// The core 1/x logic
|
// The core 1/x logic
|
||||||
double result = 1.0 / (1.0 - r);
|
var result = 1.0 / (1.0 - r);
|
||||||
|
|
||||||
// Clamp the result to your specific range
|
// Clamp the result to your specific range
|
||||||
if (result < minValue) result = minValue;
|
if (result < minValue) result = minValue;
|
||||||
@@ -241,9 +231,9 @@ public class KasinoKrash : IDisposable
|
|||||||
|
|
||||||
public class KrashBet
|
public class KrashBet
|
||||||
{
|
{
|
||||||
public required GamblerDbModel gambler{ get; set;}
|
public required GamblerDbModel Gambler{ get; set;}
|
||||||
public required decimal wager { get; set; }
|
public required decimal Wager { get; set; }
|
||||||
public required decimal multi { get; set; }
|
public required decimal Multi { get; set; }
|
||||||
}
|
}
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user