diff --git a/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs b/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs index 5a651b9..a8ad71a 100644 --- a/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs +++ b/KfChatDotNetBot/Commands/Kasino/SlotsCommand.cs @@ -44,9 +44,11 @@ public class SlotsCommand : ICommand Window = TimeSpan.FromSeconds(15) }; + private decimal HOUSE_EDGE = (decimal)0.98; public async Task RunCommand(ChatBot botInstance, MessageModel messagen, UserDbModel user, GroupCollection arguments, CancellationToken ctx) { + var settings = await SettingsProvider.GetMultipleValuesAsync([ BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay, BuiltIn.Keys.KasinoSlotsEnabled ]); @@ -82,6 +84,10 @@ public class SlotsCommand : ICommand var wager = Convert.ToDecimal(amount.Value); + if (wager < (decimal)0.01){ + await botInstance.SendChatMessageAsync($"{user.FormatUsername()} you must bet a minimum of $0.01 KKK", true, autoDeleteAfter: TimeSpan.FromSeconds(30)); + return; + } var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx); if (gambler == null) throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}"); @@ -93,12 +99,24 @@ public class SlotsCommand : ICommand return; } + char rigged = '0'; + int rigCheck = Money.GetRandomNumber(gambler, 0, 1, 1); + if (HOUSE_EDGE > 1) + { + if (HOUSE_EDGE - rigCheck > 1) rigged = 'W'; + } + else + { + if (rigCheck - HOUSE_EDGE > 0) rigged = 'L'; + } + + decimal winnings; double delayHSec = 0; using (var board = new KiwiSlotBoard(wager)) { board.LoadAssets(); - board.ExecuteGameLoop(spins); + board.ExecuteGameLoop(spins, rigged); using (var finalImageStream = board.ExportAndCleanup()) { if (finalImageStream == null) @@ -169,6 +187,7 @@ public class SlotsCommand : ICommand private int _activeFeatureTier = 0, _currentFeatureSpin = 0; private bool _showGoldCircle = false; + private readonly RandomShim _rand = RandomShim.Create(StandardRng.Create()); private static readonly List ExpanderWild = ['N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2']; @@ -347,11 +366,12 @@ public class SlotsCommand : ICommand AnimatedImage.Frames.RemoveFrame(0); } - public void ExecuteGameLoop(int spins, int featureSpins = 0) + public void ExecuteGameLoop(int spins, int featureSpins = 0, char rigged = '0') { for (int sp = 0; sp < spins; sp++) { - GeneratePreBoard(featureSpins); + + GeneratePreBoard(featureSpins, rigged); var fCount = 0; for (var i = 0; i < 5; i++) for (var j = 0; j < 5; j++) if (_preboard[i, j] == FEATURE) fCount++; @@ -365,7 +385,7 @@ public class SlotsCommand : ICommand ProcessReelsAndWins(); var total = _activeFeatureTier switch { 3 => 3, 4 => 5, 5 => 10, _ => 0 }; if (total > 0 || featureSpins != 0 || spins > 1) AddPause(50); - if (featureSpins == 0) for (var s = 1; s <= total; s++) ExecuteGameLoop(1,s); + if (featureSpins == 0) for (var s = 1; s <= total; s++) ExecuteGameLoop(1,s, rigged); } } @@ -426,10 +446,35 @@ public class SlotsCommand : ICommand { var fc = 0; HashSet ex = []; for (var i = 0; i < 5; i++) { - for (var j = 0; j < 5; j++) { + for (var j = 0; j < 5; j++) + { + int loopCounter = 0; var r = _rand.NextDouble() * 100.6; - if (f != 0 && j > 2) r *= 1.05; - if (r < 22) _preboard[i, j] = 'A'; + if (f != 0 && j > 2) r *= 1.1; + if (rigged == 'L') r = _rand.NextDouble() * 97.01; + + if (rigged == 'W') // guarantee max win + { + if (i == 0) + { + _preboard[i, j] = EXPANDER; + continue; + } + else if (i < 4) + { + _preboard[i, j] = WILD; + continue; + } + else + { + _preboard[i, j] = FEATURE; + continue; + } + } + + + + /*if (r < 22) _preboard[i, j] = 'A'; else if (r < 44) _preboard[i, j] = 'B'; else if (r < 52) _preboard[i, j] = 'C'; else if (r < 66) _preboard[i, j] = 'D'; @@ -441,11 +486,213 @@ public class SlotsCommand : ICommand else if (r < 97) _preboard[i, j] = 'J'; else if (r < 98.5) _preboard[i, j] = WILD; else if (r < (j <= 2 ? 99 : 99.5)) { if (!ex.Contains(j)) { _preboard[i, j] = EXPANDER; ex.Add(j); } else _preboard[i, j] = WILD; } - else { if (fc < 5) { _preboard[i, j] = FEATURE; fc++; } else _preboard[i, j] = WILD; } + else { if (fc < 5) { _preboard[i, j] = FEATURE; fc++; } else _preboard[i, j] = WILD; }*/ + + _preboard[i, j] = PickSlotSymbol(r, i, j); + switch (_preboard[i, j]) + { + case EXPANDER: ex.Add(j); + break; + } + + /*if (rigged == 'L') //guarantee random losing board + { + //if i==0 and j==0 pick a random one, aka do nothing + if (i == 0 && j == 1) + { + //first row, just make sure the tiles do not have straight line match for the first three from the left. essentially make sure from first row 0 1 2 3 4, tiles 0, 1, and 2 all need to be different + while (_preboard[i, j - 1] == _preboard[i, j]) + { + r = _rand.NextDouble() * 97.01; + _preboard[i, j] = PickSlotSymbol(r, i, j); + loopCounter++; + if (loopCounter > 10000) throw new Exception("Failed to generate a losing board"); + } + + continue; + } + else if (i == 0 && j == 2) + { + while (_preboard[i, j - 1] == _preboard[i, j] || _preboard[i, j - 2] == _preboard[i, j]) + { + r = _rand.NextDouble() * 97.01; + _preboard[i, j] = PickSlotSymbol(r, i, j); + loopCounter++; + if (loopCounter > 10000) throw new Exception("Failed to generate a losing board2"); + } + + continue; + } + else if (i > 0 && j == 0) + { + //if its the first one in a row check to make sure its not the same as a diagonal on the row above + if (_preboard[i - 1, j + 1] == _preboard[i, j]) + { + while (_preboard[i - 1, j + 1] == _preboard[i, j]) + { + r = _rand.NextDouble() * 97.01; + _preboard[i, j] = PickSlotSymbol(r, i, j); + loopCounter++; + if (loopCounter > 10000) throw new Exception("Failed to generate a losing board3"); + } + } + + continue; + } + else if (i > 0 && j > 0 && j < 3) + { + //check both diagonals above and one space behind + if (j == 2) + { + if (_preboard[i - 1, j + 1] == _preboard[i, j] || + _preboard[i - 1, j - 1] == _preboard[i, j] || + _preboard[i, j - 1] == _preboard[i, j] || + _preboard[i, j - 2] == _preboard[i, j]) + { + while (_preboard[i - 1, j + 1] == _preboard[i, j] || + _preboard[i - 1, j - 1] == _preboard[i, j] || + _preboard[i, j - 1] == _preboard[i, j] || + _preboard[i, j - 2] == _preboard[i, j]) + { + r = _rand.NextDouble() * 97.01; + _preboard[i, j] = PickSlotSymbol(r, i, j); + loopCounter++; + if (loopCounter > 10000) throw new Exception("Failed to generate a losing board4"); + } + } + } + else + { + if (_preboard[i - 1, j + 1] == _preboard[i, j] || _preboard[i - 1, j - 1] == _preboard[i, j] || _preboard[i, j - 1] == _preboard[i, j]) + { + while (_preboard[i - 1, j + 1] == _preboard[i, j] || _preboard[i - 1, j - 1] == _preboard[i, j]) + { + r = _rand.NextDouble() * 97.01; + _preboard[i, j] = PickSlotSymbol(r, i, j); + loopCounter++; + if (loopCounter > 10000) throw new Exception("Failed to generate a losing board5"); + } + } + } + } + }*/ + } + } + if (rigged == 'L') RigSlotBoard(); + char PickSlotSymbol(double r, int i, int j) + { + if (r < 22) return 'A'; + else if (r < 44) return 'B'; + else if (r < 52) return 'C'; + else if (r < 66) return 'D'; + else if (r < 78) return 'E'; + else if (r < 84) return 'F'; + else if (r < 89) return 'G'; + else if (r < 92) return 'H'; + else if (r < 95) return 'I'; + else if (r < 97) return 'J'; + else if (r < 98.5) return WILD; + else if (r < (j <= 2 ? 99 : 99.5)) { if (!ex.Contains(j)) { return EXPANDER; } else return WILD; } + else { if (fc < 5) { fc++; + return FEATURE; + } else return WILD; } + } + void RigSlotBoard() + { + int totalRuns = 0; + double r; + List<(int r, int c)> positionsToCheck; + for (int row = 0; row < 5; row++) + { + for (int col = 0; col < 5; col++) + { + int loopCounter = 0; + if (row == 0 && col == 0) ; //do nothing + if (row == 0 && col == 1) + { + //check 1 slot behind + while (_preboard[row, col - 1] == _preboard[row, col]) + { + r = _rand.NextDouble() * 97.01; + _preboard[row, col] = PickSlotSymbol(r, row, col); + loopCounter++; + totalRuns++; + if (loopCounter > 10000) throw new Exception($"Failed to rig slot board after 10000 attempts. Got stuck on row {row} col {col}."); + } + } + if (row == 0 && col == 2) + { + //check 2 slots behind + while (_preboard[row, col - 1] == _preboard[row, col] || _preboard[row, col - 2] == _preboard[row, col]) + { + r = _rand.NextDouble() * 97.01; + _preboard[row, col] = PickSlotSymbol(r, row, col); + loopCounter++; + totalRuns++; + if (loopCounter > 10000) throw new Exception($"Failed to rig slot board after 10000 attempts. Got stuck on row {row} col {col}."); + } + } + if (row > 0 && col == 0) + { + //check the diagonal above and to the right + while (_preboard[row - 1, col + 1] == _preboard[row, col]) + { + r = _rand.NextDouble() * 97.01; + _preboard[row, col] = PickSlotSymbol(r, row, col); + loopCounter++; + totalRuns++; + if (loopCounter > 10000) throw new Exception($"Failed to rig slot board after 10000 attempts. Got stuck on row {row} col {col}."); + } + } + if (row > 1 && col == 0) + { + //check the diagnoals above and to the right for 2 spaces + while (_preboard[row - 1, col + 1] == _preboard[row, col] || + _preboard[row - 2, col + 2] == _preboard[row, col]) + { + r = _rand.NextDouble() * 97.01; + _preboard[row, col] = PickSlotSymbol(r, row, col); + loopCounter++; + totalRuns++; + if (loopCounter > 10000) throw new Exception($"Failed to rig slot board after 10000 attempts. Got stuck on row {row} col {col}."); + } + } + if (row > 0 && col == 1) + { + //check both diagonals above for 1 space, and one space behind + while (_preboard[row - 1, col - 1] == _preboard[row, col] || + _preboard[row + 1, col + 1] == _preboard[row, col] || + _preboard[row, col - 1] == _preboard[row, col]) + { + r = _rand.NextDouble() * 97.01; + _preboard[row, col] = PickSlotSymbol(r, row, col); + loopCounter++; + totalRuns++; + if (loopCounter > 10000) throw new Exception($"Failed to rig slot board after 10000 attempts. Got stuck on row {row} col {col}."); + } + } + if (row > 0 && col == 2) + { + //check both diagonals above for 1 space and 2 spaces behind + while (_preboard[row - 1, col - 1] == _preboard[row, col] || + _preboard[row + 1, col + 1] == _preboard[row, col] || + _preboard[row, col - 1] == _preboard[row, col] || + _preboard[row, col - 2] == _preboard[row, col]) + { + r = _rand.NextDouble() * 97.01; + _preboard[row, col] = PickSlotSymbol(r, row, col); + loopCounter++; + totalRuns++; + if (loopCounter > 10000) throw new Exception($"Failed to rig slot board after 10000 attempts. Got stuck on row {row} col {col}."); + } + } + } } } } + + public void Dispose() { _headerImg?.Dispose();