mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-02 04:22:04 -04:00
added dice game. added random double generator.
This commit is contained in:
114
KfChatDotNetBot/Commands/Kasino/DiceCommand.cs
Normal file
114
KfChatDotNetBot/Commands/Kasino/DiceCommand.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using KfChatDotNetBot.Extensions;
|
||||
using KfChatDotNetBot.Models;
|
||||
using KfChatDotNetBot.Models.DbModels;
|
||||
using KfChatDotNetBot.Services;
|
||||
using KfChatDotNetBot.Settings;
|
||||
using KfChatDotNetWsClient.Models.Events;
|
||||
|
||||
namespace KfChatDotNetBot.Commands.Kasino;
|
||||
|
||||
[KasinoCommand]
|
||||
[WagerCommand]
|
||||
public class DiceCommand : ICommand
|
||||
{
|
||||
public List<Regex> Patterns => [
|
||||
new Regex(@"dice (?<amount>\d+)$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^dice (?<amount>\d+\.\d+)$", RegexOptions.IgnoreCase)
|
||||
];
|
||||
|
||||
public string? HelpText => "!dice, roll the dice (not really, you roll between 0 - 100)";
|
||||
public UserRight RequiredRight => UserRight.Loser;
|
||||
public TimeSpan Timeout => TimeSpan.FromSeconds(5);
|
||||
public RateLimitOptionsModel? RateLimitOptions => null;
|
||||
|
||||
private static double _houseEdge = 0.05; // house edge hack? are we doing perfect 50/50 games?
|
||||
|
||||
public async Task RunCommand(ChatBot botInstance, MessageModel messagen, UserDbModel user, GroupCollection arguments,
|
||||
CancellationToken ctx)
|
||||
{
|
||||
var cleanupDelay = TimeSpan.FromMilliseconds((await SettingsProvider.GetValueAsync(BuiltIn.Keys.KasinoKenoCleanupDelay)).ToType<int>());
|
||||
if (!arguments.TryGetValue("amount", out var amount))
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, not enough arguments. !dice <wager>",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
var wager = Convert.ToDecimal(amount.Value);
|
||||
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
||||
if (gambler == null)
|
||||
throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}");
|
||||
if (gambler.Balance < wager)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
var rolled = Money.GetRandomDouble(gambler);
|
||||
decimal newBalance;
|
||||
var colors =
|
||||
await SettingsProvider.GetMultipleValuesAsync([
|
||||
BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor
|
||||
]);
|
||||
if (rolled > 0.5 + _houseEdge)
|
||||
{
|
||||
// you win dice
|
||||
var effect = wager;
|
||||
await Money.NewWagerAsync(gambler.Id, wager, effect, WagerGame.Dice, ct: ctx);
|
||||
newBalance = gambler.Balance + effect;
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, you rolled a {rolled * 100:N2} and [B][COLOR={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]WON![/COLOR][/B] " +
|
||||
$"You won {await effect.FormatKasinoCurrencyAsync()} and your balance is now {await newBalance.FormatKasinoCurrencyAsync()}",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
// you lose dice
|
||||
await Money.NewWagerAsync(gambler.Id, wager, -wager, WagerGame.Dice, ct: ctx);
|
||||
newBalance = gambler.Balance - wager;
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, you rolled a {rolled:0:00} and [B][COLOR={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]LOST![/COLOR][/B] " +
|
||||
$"Your balance is now {await newBalance.FormatKasinoCurrencyAsync()}",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ConstructDiceGameOutput(double rolled)
|
||||
{
|
||||
// returns two rows as one string
|
||||
// row one has dice emoji shifted usisng spaces by appropriate ammount accourding to rolled
|
||||
// second row constructs the dice "meter" according to game specs
|
||||
var diceEmoji = "🎲";
|
||||
var invisibleAsciiSpace = "⠀"; //U+2800
|
||||
// 21 asciispaces fills the display, 20 dashes and one | fills the meter
|
||||
// rolled * 21 * invisible ascii space creates the illusion of dice being alligned with rolled number
|
||||
var toShift = (int)Math.Round(21 * rolled);
|
||||
string diceDisplayShifted = String.Concat(Enumerable.Repeat(invisibleAsciiSpace, toShift));
|
||||
diceDisplayShifted += diceEmoji;
|
||||
|
||||
const int DICE_METER_LENGTH = 20; // uhh this should influence how much the dice emoji is shifted as declared before just leave at 20 for now
|
||||
const string DICE_METER_LEFT = "─";
|
||||
const string DICE_METER_LEFT_COLOR = "#f1323e"; // red for lose
|
||||
const string DICE_METER_MIDDLE = "┃";
|
||||
const string DICE_METER_MIDDLE_COLOR = "#886cff"; // I forgot what this color is
|
||||
const string DICE_METER_RIGHT = "─";
|
||||
const string DICE_METER_RIGHT_COLOR = "#3dd179"; // green for win
|
||||
|
||||
string diceMeter = "";
|
||||
|
||||
// TODO: construct rigged dice display here
|
||||
|
||||
// no rig scenario
|
||||
diceMeter += $"[B][COLOR={DICE_METER_LEFT_COLOR}]";
|
||||
diceMeter += String.Concat(Enumerable.Repeat(DICE_METER_LEFT, DICE_METER_LENGTH / 2)); // ---------
|
||||
diceMeter += "[/COLOR]";
|
||||
diceMeter += $"[COLOR={DICE_METER_MIDDLE_COLOR}]{DICE_METER_MIDDLE}[/COLOR]"; // |
|
||||
diceMeter += $"[COLOR={DICE_METER_RIGHT_COLOR}]";
|
||||
diceMeter += String.Concat(Enumerable.Repeat(DICE_METER_RIGHT, DICE_METER_LENGTH / 2)); // --------
|
||||
diceMeter += "[/COLOR][/B]";
|
||||
|
||||
return $"{diceDisplayShifted}\n{diceMeter}";
|
||||
}
|
||||
}
|
||||
@@ -439,6 +439,28 @@ public static class Money
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get random number double [0, 1]
|
||||
/// </summary>
|
||||
/// <param name="gambler">Gambler entity to reference their random seed</param>
|
||||
/// <param name="iterations">Number of random number generator iterations to run before returning a result</param>
|
||||
/// <returns>A random number based on the given parameters</returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public static double GetRandomDouble(GamblerDbModel gambler, int iterations = 10)
|
||||
{
|
||||
var rng = StandardRng.Create();
|
||||
var random = RandomShim.Create(rng);
|
||||
var result = 0.0;
|
||||
var i = 0;
|
||||
if (iterations <= 0) throw new ArgumentException("Iterations cannot be 0 or lower");
|
||||
while (i < iterations)
|
||||
{
|
||||
i++;
|
||||
result = random.NextDouble();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the user's current VIP level
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user