mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 09:18:27 -04:00
Moved .rps to the new service, reimplemented logic, fixed an unknown bug with 0 amount (?!)
This commit is contained in:
@@ -2,10 +2,10 @@ namespace Nadeko.Econ.Gambling;
|
||||
|
||||
public sealed class BetrollGame
|
||||
{
|
||||
private readonly (decimal WhenAbove, decimal MultiplyBy)[] _thresholdPairs;
|
||||
private readonly Random _rng;
|
||||
private readonly (int WhenAbove, decimal MultiplyBy)[] _thresholdPairs;
|
||||
private readonly NadekoRandom _rng;
|
||||
|
||||
public BetrollGame(IReadOnlyList<(decimal WhenAbove, decimal MultiplyBy)> pairs)
|
||||
public BetrollGame(IReadOnlyList<(int WhenAbove, decimal MultiplyBy)> pairs)
|
||||
{
|
||||
_thresholdPairs = pairs.OrderByDescending(x => x.WhenAbove).ToArray();
|
||||
_rng = new();
|
||||
|
75
src/Nadeko.Econ/Gambling/Rps/RpsGame.cs
Normal file
75
src/Nadeko.Econ/Gambling/Rps/RpsGame.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace Nadeko.Econ.Gambling.Rps;
|
||||
|
||||
public sealed class RpsGame
|
||||
{
|
||||
private static readonly NadekoRandom _rng = new NadekoRandom();
|
||||
|
||||
const decimal WIN_MULTI = 1.95m;
|
||||
const decimal DRAW_MULTI = 1m;
|
||||
const decimal LOSE_MULTI = 0m;
|
||||
|
||||
public RpsGame()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public RpsResult Play(RpsPick pick, decimal amount)
|
||||
{
|
||||
var compPick = (RpsPick)_rng.Next(0, 3);
|
||||
if (compPick == pick)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Won = amount * DRAW_MULTI,
|
||||
Multiplier = DRAW_MULTI,
|
||||
ComputerPick = compPick,
|
||||
Result = RpsResultType.Draw,
|
||||
};
|
||||
}
|
||||
|
||||
if ((compPick == RpsPick.Paper && pick == RpsPick.Rock)
|
||||
|| (compPick == RpsPick.Rock && pick == RpsPick.Scissors)
|
||||
|| (compPick == RpsPick.Scissors && pick == RpsPick.Paper))
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Won = amount * LOSE_MULTI,
|
||||
Multiplier = LOSE_MULTI,
|
||||
Result = RpsResultType.Lose,
|
||||
ComputerPick = compPick,
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
Won = amount * WIN_MULTI,
|
||||
Multiplier = WIN_MULTI,
|
||||
Result = RpsResultType.Win,
|
||||
ComputerPick = compPick,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum RpsPick : byte
|
||||
{
|
||||
Rock = 0,
|
||||
Paper = 1,
|
||||
Scissors = 2,
|
||||
}
|
||||
|
||||
public enum RpsResultType : byte
|
||||
{
|
||||
Win,
|
||||
Draw,
|
||||
Lose
|
||||
}
|
||||
|
||||
|
||||
|
||||
public readonly struct RpsResult
|
||||
{
|
||||
public decimal Won { get; init; }
|
||||
public decimal Multiplier { get; init; }
|
||||
public RpsResultType Result { get; init; }
|
||||
public RpsPick ComputerPick { get; init; }
|
||||
}
|
@@ -13,6 +13,7 @@ using NadekoBot.Services.Database.Models;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Nadeko.Econ.Gambling.Rps;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
|
||||
@@ -759,65 +760,72 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
opts.Clean);
|
||||
}
|
||||
|
||||
public enum InputRpsPick : byte
|
||||
{
|
||||
R = 0,
|
||||
Rock = 0,
|
||||
Rocket = 0,
|
||||
P = 1,
|
||||
Paper = 1,
|
||||
Paperclip = 1,
|
||||
S = 2,
|
||||
Scissors = 2
|
||||
}
|
||||
|
||||
// todo check if trivia is being disposed
|
||||
[Cmd]
|
||||
public async partial Task Rps(RpsPick pick, ShmartNumber amount = default)
|
||||
public async partial Task Rps(InputRpsPick pick, ShmartNumber amount = default)
|
||||
{
|
||||
if (!await CheckBetOptional(amount) || amount == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string GetRpsPick(RpsPick p)
|
||||
static string GetRpsPick(InputRpsPick p)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case RpsPick.R:
|
||||
case InputRpsPick.R:
|
||||
return "🚀";
|
||||
case RpsPick.P:
|
||||
case InputRpsPick.P:
|
||||
return "📎";
|
||||
default:
|
||||
return "✂️";
|
||||
}
|
||||
}
|
||||
|
||||
if (!await CheckBetOptional(amount) || amount == 1)
|
||||
return;
|
||||
|
||||
var embed = _eb.Create();
|
||||
var res = await _gs.RpsAsync(ctx.User.Id, amount, (byte)pick);
|
||||
|
||||
var nadekoPick = (RpsPick)new NadekoRandom().Next(0, 3);
|
||||
|
||||
if (amount > 0)
|
||||
if (!res.TryPickT0(out var result, out _))
|
||||
{
|
||||
if (!await _cs.RemoveAsync(ctx.User.Id, amount, new("rps", "bet")))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var embed = _eb.Create();
|
||||
|
||||
string msg;
|
||||
if (pick == nadekoPick)
|
||||
if (result.Result == RpsResultType.Draw)
|
||||
{
|
||||
await _cs.AddAsync(ctx.User.Id, amount, new("rps", "draw"));
|
||||
embed.WithOkColor();
|
||||
msg = GetText(strs.rps_draw(GetRpsPick(pick)));
|
||||
}
|
||||
else if ((pick == RpsPick.Paper && nadekoPick == RpsPick.Rock)
|
||||
|| (pick == RpsPick.Rock && nadekoPick == RpsPick.Scissors)
|
||||
|| (pick == RpsPick.Scissors && nadekoPick == RpsPick.Paper))
|
||||
else if (result.Result == RpsResultType.Win)
|
||||
{
|
||||
amount = (long)(amount * Config.BetFlip.Multiplier);
|
||||
await _cs.AddAsync(ctx.User.Id, amount, new("rps", "win"));
|
||||
embed.WithOkColor();
|
||||
embed.AddField(GetText(strs.won), N(amount.Value));
|
||||
msg = GetText(strs.rps_win(ctx.User.Mention, GetRpsPick(pick), GetRpsPick(nadekoPick)));
|
||||
if((long)result.Won > 0)
|
||||
embed.AddField(GetText(strs.won), N(amount.Value));
|
||||
|
||||
msg = GetText(strs.rps_win(ctx.User.Mention,
|
||||
GetRpsPick(pick),
|
||||
GetRpsPick((InputRpsPick)result.ComputerPick)));
|
||||
}
|
||||
else
|
||||
{
|
||||
embed.WithErrorColor();
|
||||
msg = GetText(strs.rps_win(ctx.Client.CurrentUser.Mention, GetRpsPick(nadekoPick), GetRpsPick(pick)));
|
||||
msg = GetText(strs.rps_win(ctx.Client.CurrentUser.Mention,
|
||||
GetRpsPick((InputRpsPick)result.ComputerPick),
|
||||
GetRpsPick(pick)));
|
||||
}
|
||||
|
||||
embed.WithDescription(msg);
|
||||
embed
|
||||
.WithOkColor()
|
||||
.WithDescription(msg);
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
3
src/NadekoBot/Modules/Gambling/InputRpsPick.cs
Normal file
3
src/NadekoBot/Modules/Gambling/InputRpsPick.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
|
@@ -1,14 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
|
||||
public enum RpsPick
|
||||
{
|
||||
R = 0,
|
||||
Rock = 0,
|
||||
Rocket = 0,
|
||||
P = 1,
|
||||
Paper = 1,
|
||||
Paperclip = 1,
|
||||
S = 2,
|
||||
Scissors = 2
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ.Gambling;
|
||||
using Nadeko.Econ.Gambling.Rps;
|
||||
using OneOf;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
@@ -11,4 +12,5 @@ public interface IGamblingService
|
||||
Task<OneOf<BetflipResult, GamblingError>> BetFlipAsync(ulong userId, long amount, byte guess);
|
||||
Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount);
|
||||
Task<FlipResult[]> FlipAsync(int count);
|
||||
Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick);
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ.Gambling;
|
||||
using Nadeko.Econ.Gambling.Rps;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using OneOf;
|
||||
|
||||
@@ -20,11 +21,17 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
// todo ladder of fortune
|
||||
public async Task<OneOf<WofResult, GamblingError>> WofAsync(ulong userId, long amount)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("wof", "bet"));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("wof", "bet"));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
}
|
||||
}
|
||||
|
||||
var game = new WofGame(_bcs.Data.WheelOfFortune.Multipliers);
|
||||
@@ -41,16 +48,23 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
|
||||
public async Task<OneOf<BetrollResult, GamblingError>> BetRollAsync(ulong userId, long amount)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betroll", "bet"));
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
if (amount > 0)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betroll", "bet"));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
}
|
||||
}
|
||||
|
||||
var game = new BetrollGame(_bcs.Data.BetRoll.Pairs
|
||||
.Select(x => ((decimal)x.WhenAbove, (decimal)x.MultiplyBy))
|
||||
.Select(x => (x.WhenAbove, (decimal)x.MultiplyBy))
|
||||
.ToList());
|
||||
|
||||
var result = game.Roll(amount);
|
||||
|
||||
var won = (long)result.Won;
|
||||
@@ -64,11 +78,17 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
|
||||
public async Task<OneOf<BetflipResult, GamblingError>> BetFlipAsync(ulong userId, long amount, byte guess)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
if (amount > 0)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
}
|
||||
}
|
||||
|
||||
var game = new BetflipGame(_bcs.Data.BetFlip.Multiplier);
|
||||
@@ -85,6 +105,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
|
||||
public async Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount)
|
||||
{
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("slot", "bet"));
|
||||
@@ -106,7 +129,7 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public Task<FlipResult[]> FlipAsync(int count)
|
||||
{
|
||||
var game = new BetflipGame(0);
|
||||
@@ -123,8 +146,8 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
return Task.FromResult(results);
|
||||
}
|
||||
|
||||
// todo deck draw black/white?
|
||||
|
||||
// // todo deck draw black/white?
|
||||
//
|
||||
//
|
||||
// private readonly ConcurrentDictionary<ulong, Deck> _decks = new ConcurrentDictionary<ulong, Deck>();
|
||||
//
|
||||
@@ -177,79 +200,41 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
// return Task.FromResult(toReturn);
|
||||
// }
|
||||
//
|
||||
// public override async Task<RpsReply> Rps(RpsRequest request, ServerCallContext context)
|
||||
// {
|
||||
// if (request.Amount > 0)
|
||||
// {
|
||||
// var res = await _currency.TransferCurrencyAsync(new TransferCurrencyRequest
|
||||
// {
|
||||
// Amount = request.Amount,
|
||||
// FromId = request.UserId,
|
||||
// Type = "rps",
|
||||
// Subtype = "bet",
|
||||
// });
|
||||
//
|
||||
// if (!res.Success)
|
||||
// {
|
||||
// return new RpsReply
|
||||
// {
|
||||
// Result = RpsReply.Types.ResultType.NotEnough
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var botPick = _rng.Next(0, 3);
|
||||
// var userPick = (int) request.Pick;
|
||||
//
|
||||
// if (botPick == userPick)
|
||||
// {
|
||||
// if (request.Amount > 0)
|
||||
// {
|
||||
// await _currency.GrantToUserAsync(new GrantToUserRequest
|
||||
// {
|
||||
// Amount = request.Amount,
|
||||
// GranterId = 0,
|
||||
// Type = "rps",
|
||||
// Subtype = "draw",
|
||||
// UserId = request.UserId,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return new RpsReply
|
||||
// {
|
||||
// BotPick = (RpsPick) botPick,
|
||||
// WonAmount = request.Amount,
|
||||
// Result = RpsReply.Types.ResultType.Draw
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// if ((botPick == 1 && userPick == 2) || (botPick == 2 && userPick == 0) || (botPick == 0 && userPick == 1))
|
||||
// {
|
||||
// if (request.Amount > 0)
|
||||
// {
|
||||
// await _currency.GrantToUserAsync(new GrantToUserRequest
|
||||
// {
|
||||
// Amount = (long) (request.Amount * 1.95f),
|
||||
// GranterId = 0,
|
||||
// Type = "rps",
|
||||
// Subtype = "draw",
|
||||
// UserId = request.UserId,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return new RpsReply
|
||||
// {
|
||||
// BotPick = (RpsPick) botPick,
|
||||
// WonAmount = (long) (request.Amount * 1.95f),
|
||||
// Result = RpsReply.Types.ResultType.Won
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// return new RpsReply
|
||||
// {
|
||||
// BotPick = (RpsPick) botPick,
|
||||
// WonAmount = 0,
|
||||
// Result = RpsReply.Types.ResultType.Lost
|
||||
// };
|
||||
// }
|
||||
|
||||
public async Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick)
|
||||
{
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (pick > 2)
|
||||
throw new ArgumentOutOfRangeException(nameof(pick));
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("rps", "bet"));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
}
|
||||
}
|
||||
|
||||
var rps = new RpsGame();
|
||||
var result = rps.Play((RpsPick)pick, amount);
|
||||
|
||||
var won = (long)result.Won;
|
||||
if (won > 0)
|
||||
{
|
||||
var extra = result.Result switch
|
||||
{
|
||||
RpsResultType.Draw => "draw",
|
||||
RpsResultType.Win => "win",
|
||||
_ => "lose"
|
||||
};
|
||||
|
||||
await _cs.AddAsync(userId, won, new("rps", extra));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user