mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-06-19 10:35:17 -04:00
Cecil and shop update (#121)
* Implement FromToken method for Skew configuration Added a method to parse a serialized configuration token for Skew profiles. Tokens can be generated and viewed using the updated cecil helper tool. * Add Difficulty property to KasinoShop profile Add Difficulty property to KasinoShop profile * Add HOUSE_EDGE variable and custom difficulties Add HOUSE_EDGE variable and custom difficulties * Implement ShopSetDifficultyCommand for difficulty settings Added ShopSetDifficultyCommand to manage player difficulty settings in casino games, including validation for input parameters. * adds return to default difficulty setting adds return to default difficulty setting
This commit is contained in:
@@ -28,6 +28,8 @@ public class CecilCommand : ICommand
|
||||
};
|
||||
public bool WhisperCanInvoke => true;
|
||||
|
||||
public decimal HOUSE_EDGE = 0.98m;
|
||||
|
||||
public async Task RunCommand(ChatBot botInstance, BotCommandMessageModel message, UserDbModel user,
|
||||
GroupCollection arguments,
|
||||
CancellationToken ctx)
|
||||
@@ -71,17 +73,34 @@ public class CecilCommand : ICommand
|
||||
return;
|
||||
}
|
||||
|
||||
var difficulty = 1.0;
|
||||
|
||||
bool shopActive = botInstance.BotServices.KasinoShop != null;
|
||||
if (shopActive)
|
||||
{
|
||||
await GlobalShopFunctions.CheckProfile(botInstance, user, gambler);
|
||||
HOUSE_EDGE += botInstance.BotServices.KasinoShop!.Gambler_Profiles[user.KfId].HouseEdgeModifier;
|
||||
}
|
||||
|
||||
var difficulty = 1.0;
|
||||
bool customDiff = false;
|
||||
double result;
|
||||
if (arguments.TryGetValue("difficulty", out var diff))
|
||||
{
|
||||
difficulty = Convert.ToDouble(diff.Value);
|
||||
customDiff = true;
|
||||
}
|
||||
|
||||
if (!arguments.TryGetValue("maxwin", out var maxWin))
|
||||
if (!customDiff && shopActive &&
|
||||
botInstance.BotServices.KasinoShop!.Gambler_Profiles[user.KfId].Difficulty != "")
|
||||
{
|
||||
var skew = Skew.FromToken(botInstance.BotServices.KasinoShop!.Gambler_Profiles[user.KfId].Difficulty);
|
||||
skew.Rig((double)HOUSE_EDGE, 0);
|
||||
result = Cecil.Consult(skew);
|
||||
}
|
||||
else if (!arguments.TryGetValue("maxwin", out var maxWin))
|
||||
{
|
||||
var skew = new GammaSkew(difficulty, 0);
|
||||
skew.Rig((double)HOUSE_EDGE, 0);
|
||||
result = Cecil.Consult(skew, 0);
|
||||
}
|
||||
else
|
||||
@@ -93,8 +112,10 @@ public class CecilCommand : ICommand
|
||||
return;
|
||||
}
|
||||
var skew = new BetaSkew(difficulty, mWin, 0);
|
||||
skew.Rig((double)HOUSE_EDGE, 0);
|
||||
result = Cecil.Consult(skew);
|
||||
}
|
||||
|
||||
|
||||
var payout = wager * Convert.ToDecimal(result);
|
||||
var net = payout - wager;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using KfChatDotNetBot.Extensions;
|
||||
using KfChatDotNetBot.Models;
|
||||
@@ -107,6 +108,174 @@ public class ShopHelpCommand : ICommand
|
||||
}
|
||||
}
|
||||
|
||||
public class ShopSetDifficultyCommand : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
[
|
||||
new Regex(@"^shop difficulty (?<betstr>.*)$", RegexOptions.IgnoreCase),
|
||||
new Regex(@"^shop difficulty", RegexOptions.IgnoreCase)
|
||||
];
|
||||
|
||||
public string? HelpText => "Set your difficulty for cecil based games using the cecil tool";
|
||||
|
||||
public UserRight RequiredRight => UserRight.Loser;
|
||||
|
||||
public TimeSpan Timeout => TimeSpan.FromSeconds(30);
|
||||
|
||||
public RateLimitOptionsModel? RateLimitOptions => new RateLimitOptionsModel
|
||||
{
|
||||
MaxInvocations = 1,
|
||||
Window = TimeSpan.FromSeconds(120)
|
||||
};
|
||||
public bool WhisperCanInvoke => true;
|
||||
|
||||
public async Task RunCommand(ChatBot botInstance, BotCommandMessageModel message, UserDbModel user,
|
||||
GroupCollection arguments, CancellationToken ctx)
|
||||
{
|
||||
var cleanupDelay = TimeSpan.FromSeconds(10);
|
||||
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
||||
if (gambler == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}");
|
||||
}
|
||||
|
||||
bool shopActive = botInstance.BotServices.KasinoShop != null;
|
||||
if (!shopActive)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync("KasinoShop is not currently running.", true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
await GlobalShopFunctions.CheckProfile(botInstance, user, gambler);
|
||||
|
||||
|
||||
|
||||
|
||||
if (!arguments.TryGetValue("betstr", out var betstr))
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, use the cecil tool to set your difficulty. https://i.ddos.lgbt/raw/CecilHelper.html",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
//validate difficulty string
|
||||
|
||||
string diff = betstr.Value;
|
||||
if (diff.ToUpper() == "DEFAULT")
|
||||
{
|
||||
botInstance.BotServices.KasinoShop!.Gambler_Profiles[user.KfId].Difficulty = "";
|
||||
return;
|
||||
}
|
||||
var parts = diff.Trim().Split(':');
|
||||
string typeId = parts[0].ToUpper(CultureInfo.InvariantCulture);
|
||||
|
||||
if (typeId != "B" && typeId != "G")
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, distribution type {typeId} is not valid, type must be B for beta or G for gamma.", true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
if (typeId == "B")
|
||||
{
|
||||
if (parts.Length != 5)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, Beta profile format error. Expected 5 parameters, but found {parts.Length}.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out double weight) || weight <= 0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, volatility weight value '{parts[1]}' is not valid, value must be a number greater than 0.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[2], NumberStyles.Any, CultureInfo.InvariantCulture, out double maxWin) || maxWin <= 1.0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, Max Win value '{parts[2]}' is not valid, value must be a number greater than 1.0x.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[3], NumberStyles.Any, CultureInfo.InvariantCulture, out double lossRate) || lossRate < 0 || lossRate >= 1.0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, loss rate percentage '{parts[3]}' is not valid, value must be between 0.0 and 1.0.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[4], NumberStyles.Any, CultureInfo.InvariantCulture, out double targetEv) || targetEv <= 0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, target EV calculation setup value '{parts[4]}' is not valid, value must be a number greater than 0.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEv > 1.0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, house protection violation, target EV value {targetEv} cannot exceed 1.0 (100% RTP).",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// --- Gamma Validation: G:[weight]:[lossRate]:[targetEv] ---
|
||||
else if (typeId == "G")
|
||||
{
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, Gamma profile format error. Expected 4 parameters, but found {parts.Length}.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out double weight) || weight <= 0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, inverse risk weight value '{parts[1]}' is not valid, value must be a number greater than 0.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[2], NumberStyles.Any, CultureInfo.InvariantCulture, out double lossRate) || lossRate < 0 || lossRate >= 1.0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, loss rate percentage '{parts[2]}' is not valid, value must be between 0.0 and 1.0.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!double.TryParse(parts[3], NumberStyles.Any, CultureInfo.InvariantCulture, out double targetEv) || targetEv <= 0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, target EV calculation setup value '{parts[3]}' is not valid, value must be a number greater than 0.",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEv > 1.0)
|
||||
{
|
||||
await botInstance.SendChatMessageAsync(
|
||||
$"{user.FormatUsername()}, house protection violation, target EV value {targetEv} cannot exceed 1.0 (100% RTP).",
|
||||
true, autoDeleteAfter: cleanupDelay);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
botInstance.BotServices.KasinoShop.Gambler_Profiles[user.KfId].Difficulty = diff;
|
||||
await botInstance.BotServices.KasinoShop.SaveProfiles();
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class ShopListCommand : ICommand
|
||||
{
|
||||
public List<Regex> Patterns =>
|
||||
|
||||
Reference in New Issue
Block a user