mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Many changes. Will update merge request description with details
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Common.Blackjack;
|
||||
|
||||
public class Blackjack
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Common.Blackjack;
|
||||
|
||||
public abstract class Player
|
||||
|
@@ -30,14 +30,23 @@ public partial class Gambling
|
||||
|
||||
var num1 = gen / 10;
|
||||
var num2 = gen % 10;
|
||||
|
||||
|
||||
using var img1 = await GetDiceAsync(num1);
|
||||
using var img2 = await GetDiceAsync(num2);
|
||||
using var img = new[] { img1, img2 }.Merge(out var format);
|
||||
await using var ms = await img.ToStreamAsync(format);
|
||||
|
||||
var fileName = $"dice.{format.FileExtensions.First()}";
|
||||
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.AddField(GetText(strs.roll2), gen)
|
||||
.WithImageUrl($"attachment://{fileName}");
|
||||
|
||||
await ctx.Channel.SendFileAsync(ms,
|
||||
$"dice.{format.FileExtensions.First()}",
|
||||
Format.Bold(ctx.User.ToString()) + " " + GetText(strs.dice_rolled(Format.Code(gen.ToString()))));
|
||||
fileName,
|
||||
embed: eb.Build());
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -105,14 +114,18 @@ public partial class Gambling
|
||||
foreach (var d in dice)
|
||||
d.Dispose();
|
||||
|
||||
var imageName = $"dice.{format.FileExtensions.First()}";
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
|
||||
.AddField(GetText(strs.total), values.Sum(), true)
|
||||
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(values.Count.ToString()))))
|
||||
.WithImageUrl($"attachment://{imageName}");
|
||||
|
||||
await ctx.Channel.SendFileAsync(ms,
|
||||
$"dice.{format.FileExtensions.First()}",
|
||||
Format.Bold(ctx.User.ToString())
|
||||
+ " "
|
||||
+ GetText(strs.dice_rolled_num(Format.Bold(values.Count.ToString())))
|
||||
+ " "
|
||||
+ GetText(strs.total_average(Format.Bold(values.Sum().ToString()),
|
||||
Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))));
|
||||
imageName,
|
||||
embed: eb.Build());
|
||||
}
|
||||
|
||||
private async Task InternallDndRoll(string arg, bool ordered)
|
||||
@@ -130,9 +143,8 @@ public partial class Gambling
|
||||
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithDescription(ctx.User.Mention
|
||||
+ " "
|
||||
+ GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
||||
.AddField(Format.Bold("Result"),
|
||||
string.Join(" ", rolls.Select(c => Format.Code($"[{c}]"))));
|
||||
|
||||
@@ -160,10 +172,9 @@ public partial class Gambling
|
||||
var sum = arr.Sum();
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithDescription(ctx.User.Mention
|
||||
+ " "
|
||||
+ GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
||||
.AddField(Format.Bold("Rolls"),
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
||||
.AddField(Format.Bold(GetText(strs.rolls)),
|
||||
string.Join(" ",
|
||||
(ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x
|
||||
=> Format.Code(x.ToString()))))
|
||||
|
@@ -1,5 +1,7 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Image = SixLabors.ImageSharp.Image;
|
||||
@@ -9,23 +11,23 @@ namespace NadekoBot.Modules.Gambling;
|
||||
public partial class Gambling
|
||||
{
|
||||
[Group]
|
||||
public partial class DrawCommands : NadekoModule
|
||||
public partial class DrawCommands : GamblingSubmodule<IGamblingService>
|
||||
{
|
||||
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
|
||||
private readonly IImageCache _images;
|
||||
|
||||
public DrawCommands(IImageCache images)
|
||||
public DrawCommands(IImageCache images, GamblingConfigService gcs) : base(gcs)
|
||||
=> _images = images;
|
||||
|
||||
private async Task<(Stream ImageStream, string ToSend)> InternalDraw(int num, ulong? guildId = null)
|
||||
private async Task InternalDraw(int count, ulong? guildId = null)
|
||||
{
|
||||
if (num is < 1 or > 10)
|
||||
throw new ArgumentOutOfRangeException(nameof(num));
|
||||
if (count is < 1 or > 10)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var cards = guildId is null ? new() : _allDecks.GetOrAdd(ctx.Guild, _ => new());
|
||||
var images = new List<Image<Rgba32>>();
|
||||
var cardObjects = new List<Deck.Card>();
|
||||
for (var i = 0; i < num; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (cards.CardPool.Count == 0 && i != 0)
|
||||
{
|
||||
@@ -43,22 +45,43 @@ public partial class Gambling
|
||||
|
||||
var currentCard = cards.Draw();
|
||||
cardObjects.Add(currentCard);
|
||||
var cardName = currentCard.ToString().ToLowerInvariant().Replace(' ', '_');
|
||||
images.Add(Image.Load(await File.ReadAllBytesAsync($"data/images/cards/{cardName}.jpg")));
|
||||
var image = await GetCardImageAsync(currentCard);
|
||||
images.Add(image);
|
||||
}
|
||||
|
||||
var imgName = "cards.jpg";
|
||||
using var img = images.Merge();
|
||||
foreach (var i in images)
|
||||
i.Dispose();
|
||||
|
||||
var toSend = $"{Format.Bold(ctx.User.ToString())}";
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithOkColor();
|
||||
|
||||
var toSend = string.Empty;
|
||||
if (cardObjects.Count == 5)
|
||||
toSend += $" drew `{Deck.GetHandValue(cardObjects)}`";
|
||||
eb.AddField(GetText(strs.hand_value), Deck.GetHandValue(cardObjects), true);
|
||||
|
||||
if (guildId is not null)
|
||||
toSend += "\n" + GetText(strs.cards_left(Format.Bold(cards.CardPool.Count.ToString())));
|
||||
toSend += GetText(strs.cards_left(Format.Bold(cards.CardPool.Count.ToString())));
|
||||
|
||||
return (img.ToStream(), toSend);
|
||||
eb.WithDescription(toSend)
|
||||
.WithAuthor(ctx.User)
|
||||
.WithImageUrl($"attachment://{imgName}");
|
||||
|
||||
if (count > 1)
|
||||
eb.AddField(GetText(strs.cards), count.ToString(), true);
|
||||
|
||||
await using var imageStream = await img.ToStreamAsync();
|
||||
await ctx.Channel.SendFileAsync(imageStream,
|
||||
imgName,
|
||||
embed: eb.Build());
|
||||
}
|
||||
|
||||
private async Task<Image<Rgba32>> GetCardImageAsync(Deck.Card currentCard)
|
||||
{
|
||||
var cardName = currentCard.ToString().ToLowerInvariant().Replace(' ', '_');
|
||||
var cardBytes = await File.ReadAllBytesAsync($"data/images/cards/{cardName}.jpg");
|
||||
return Image.Load(cardBytes);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -66,30 +89,24 @@ public partial class Gambling
|
||||
public async partial Task Draw(int num = 1)
|
||||
{
|
||||
if (num < 1)
|
||||
num = 1;
|
||||
return;
|
||||
|
||||
if (num > 10)
|
||||
num = 10;
|
||||
|
||||
var (imageStream, toSend) = await InternalDraw(num, ctx.Guild.Id);
|
||||
await using (imageStream)
|
||||
{
|
||||
await ctx.Channel.SendFileAsync(imageStream, num + " cards.jpg", toSend);
|
||||
}
|
||||
await InternalDraw(num, ctx.Guild.Id);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async partial Task DrawNew(int num = 1)
|
||||
{
|
||||
if (num < 1)
|
||||
num = 1;
|
||||
return;
|
||||
|
||||
if (num > 10)
|
||||
num = 10;
|
||||
|
||||
var (imageStream, toSend) = await InternalDraw(num);
|
||||
await using (imageStream)
|
||||
{
|
||||
await ctx.Channel.SendFileAsync(imageStream, num + " cards.jpg", toSend);
|
||||
}
|
||||
await InternalDraw(num);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -108,5 +125,98 @@ public partial class Gambling
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.deck_reshuffled);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public partial Task BetDraw(ShmartNumber amount, InputValueGuess val, InputColorGuess? col = null)
|
||||
=> BetDrawInternal(amount, val, col);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public partial Task BetDraw(ShmartNumber amount, InputColorGuess col, InputValueGuess? val = null)
|
||||
=> BetDrawInternal(amount, val, col);
|
||||
|
||||
public async Task BetDrawInternal(long amount, InputValueGuess? val, InputColorGuess? col)
|
||||
{
|
||||
var res = await _service.BetDrawAsync(ctx.User.Id,
|
||||
amount,
|
||||
(byte?)val,
|
||||
(byte?)col);
|
||||
|
||||
if (!res.TryPickT0(out var result, out _))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.AddField(GetText(strs.guess), GetGuessInfo(val, col), true)
|
||||
.AddField(GetText(strs.card), GetCardInfo(result.Card), true)
|
||||
.AddField(GetText(strs.won), N((long)result.Won), false)
|
||||
.WithImageUrl("attachment://card.png");
|
||||
|
||||
using var img = await GetCardImageAsync(result.Card);
|
||||
await using var imgStream = await img.ToStreamAsync();
|
||||
await ctx.Channel.SendFileAsync(imgStream, "card.png", embed: eb.Build());
|
||||
}
|
||||
|
||||
private string GetGuessInfo(InputValueGuess? valG, InputColorGuess? colG)
|
||||
{
|
||||
var val = valG switch
|
||||
{
|
||||
InputValueGuess.H => "Hi ⬆️",
|
||||
InputValueGuess.L => "Lo ⬇️",
|
||||
_ => "❓"
|
||||
};
|
||||
|
||||
var col = colG switch
|
||||
{
|
||||
InputColorGuess.Red => "R 🔴",
|
||||
InputColorGuess.Black => "B ⚫",
|
||||
_ => "❓"
|
||||
};
|
||||
|
||||
return $"{val} / {col}";
|
||||
}
|
||||
private string GetCardInfo(Deck.Card card)
|
||||
{
|
||||
var val = card.Number switch
|
||||
{
|
||||
< 7 => "Lo ⬇️",
|
||||
> 7 => "Hi ⬆️",
|
||||
_ => "7 💀"
|
||||
};
|
||||
|
||||
var col = card.Number == 7
|
||||
? "7 💀"
|
||||
: card.Suit switch
|
||||
{
|
||||
Deck.CardSuit.Diamonds or Deck.CardSuit.Hearts => "R 🔴",
|
||||
_ => "B ⚫"
|
||||
};
|
||||
|
||||
return $"{val} / {col}";
|
||||
}
|
||||
|
||||
public enum InputValueGuess
|
||||
{
|
||||
High = 0,
|
||||
H = 0,
|
||||
Hi = 0,
|
||||
Low = 1,
|
||||
L = 1,
|
||||
Lo = 1,
|
||||
}
|
||||
|
||||
public enum InputColorGuess
|
||||
{
|
||||
R = 0,
|
||||
Red = 0,
|
||||
B = 1,
|
||||
Bl = 1,
|
||||
Black = 1,
|
||||
}
|
||||
}
|
||||
}
|
@@ -76,17 +76,23 @@ public partial class Gambling
|
||||
foreach (var i in imgs)
|
||||
i.Dispose();
|
||||
|
||||
var imgName = $"coins.{format.FileExtensions.First()}";
|
||||
|
||||
var msg = count != 1
|
||||
? Format.Bold(ctx.User.ToString())
|
||||
+ " "
|
||||
+ GetText(strs.flip_results(count, headCount, tailCount))
|
||||
: Format.Bold(ctx.User.ToString())
|
||||
+ " "
|
||||
+ GetText(strs.flipped(headCount > 0
|
||||
? Format.Bold(GetText(strs.heads))
|
||||
: Format.Bold(GetText(strs.tails))));
|
||||
? Format.Bold(GetText(strs.flip_results(count, headCount, tailCount)))
|
||||
: GetText(strs.flipped(headCount > 0
|
||||
? Format.Bold(GetText(strs.heads))
|
||||
: Format.Bold(GetText(strs.tails))));
|
||||
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(msg)
|
||||
.WithImageUrl($"attachment://{imgName}");
|
||||
|
||||
await ctx.Channel.SendFileAsync(stream, $"{count} coins.{format.FileExtensions.First()}", msg);
|
||||
await ctx.Channel.SendFileAsync(stream,
|
||||
imgName,
|
||||
embed: eb.Build());
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
@@ -345,7 +345,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
("award", var name, ulong userId) => GetText(strs.curtr_award(name, userId)),
|
||||
("take", var name, ulong userId) => GetText(strs.curtr_take(name, userId)),
|
||||
("blackjack", _, _) => $"Blackjack - {subType}",
|
||||
("wheel", _, _) => $"Wheel Of Fortune - {subType}",
|
||||
("wheel", _, _) => $"Lucky Ladder - {subType}",
|
||||
("lula", _, _) => $"Lucky Ladder - {subType}",
|
||||
("rps", _, _) => $"Rock Paper Scissors - {subType}",
|
||||
(null, _, _) => null,
|
||||
(_, null, _) => null,
|
||||
@@ -671,7 +672,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithAuthor(ctx.User)
|
||||
.WithFooter(str)
|
||||
.WithDescription(Format.Bold(str))
|
||||
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
|
||||
.WithOkColor();
|
||||
|
||||
@@ -772,7 +773,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
Scissors = 2
|
||||
}
|
||||
|
||||
// todo check if trivia is being disposed
|
||||
[Cmd]
|
||||
public async partial Task Rps(InputRpsPick pick, ShmartNumber amount = default)
|
||||
{
|
||||
@@ -839,7 +839,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
if (!await CheckBetMandatory(amount))
|
||||
return;
|
||||
|
||||
var res = await _gs.WofAsync(ctx.User.Id, amount);
|
||||
var res = await _gs.LulaAsync(ctx.User.Id, amount);
|
||||
if (!res.TryPickT0(out var result, out _))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
@@ -871,4 +871,99 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
|
||||
await ctx.Channel.EmbedAsync(eb);
|
||||
}
|
||||
|
||||
|
||||
public enum GambleTestTarget
|
||||
{
|
||||
Slot,
|
||||
BetDraw,
|
||||
BetDrawHL,
|
||||
BetDrawRB,
|
||||
Betflip,
|
||||
BetflipT,
|
||||
Lula,
|
||||
Rps,
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async partial Task BetTest()
|
||||
{
|
||||
await SendConfirmAsync(GetText(strs.available_tests),
|
||||
Enum.GetValues<GambleTestTarget>()
|
||||
.Select(x => $"`{x}`")
|
||||
.Join(", "));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async partial Task BetTest(GambleTestTarget target, int tests = 1000)
|
||||
{
|
||||
if (tests <= 0)
|
||||
return;
|
||||
|
||||
await ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
var streak = 0;
|
||||
var maxW = 0;
|
||||
var maxL = 0;
|
||||
|
||||
var dict = new Dictionary<decimal, int>();
|
||||
for (var i = 0; i < tests; i++)
|
||||
{
|
||||
var multi = target switch
|
||||
{
|
||||
GambleTestTarget.BetDraw => (await _gs.BetDrawAsync(ctx.User.Id, 0, 1, 0)).AsT0.Multiplier,
|
||||
GambleTestTarget.BetDrawRB => (await _gs.BetDrawAsync(ctx.User.Id, 0, null, 1)).AsT0.Multiplier,
|
||||
GambleTestTarget.BetDrawHL => (await _gs.BetDrawAsync(ctx.User.Id, 0, 0, null)).AsT0.Multiplier,
|
||||
GambleTestTarget.Slot => (await _gs.SlotAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||
GambleTestTarget.Betflip => (await _gs.BetFlipAsync(ctx.User.Id, 0, 0)).AsT0.Multiplier,
|
||||
GambleTestTarget.BetflipT => (await _gs.BetFlipAsync(ctx.User.Id, 0, 1)).AsT0.Multiplier,
|
||||
GambleTestTarget.Lula => (await _gs.LulaAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||
GambleTestTarget.Rps => (await _gs.RpsAsync(ctx.User.Id, 0, (byte)(i % 3))).AsT0.Multiplier,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(target))
|
||||
};
|
||||
|
||||
if (dict.ContainsKey(multi))
|
||||
dict[multi] += 1;
|
||||
else
|
||||
dict.Add(multi, 1);
|
||||
|
||||
if (multi < 1)
|
||||
{
|
||||
if (streak <= 0)
|
||||
--streak;
|
||||
else
|
||||
streak = -1;
|
||||
|
||||
maxL = Math.Max(maxL, -streak);
|
||||
}
|
||||
else if (multi > 1)
|
||||
{
|
||||
if (streak >= 0)
|
||||
++streak;
|
||||
else
|
||||
streak = 1;
|
||||
|
||||
maxW = Math.Max(maxW, streak);
|
||||
}
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
decimal payout = 0;
|
||||
foreach (var key in dict.Keys.OrderByDescending(x => x))
|
||||
{
|
||||
sb.AppendLine($"x**{key}** occured `{dict[key]}` times. {dict[key] * 1.0f / tests * 100}%");
|
||||
payout += key * dict[key];
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"Longest win streak: `{maxW}`");
|
||||
sb.AppendLine($"Longest lose streak: `{maxL}`");
|
||||
|
||||
await SendConfirmAsync(GetText(strs.test_results_for(target)),
|
||||
sb.ToString(),
|
||||
footer: $"Total Bet: {tests} | Payout: {payout:F0} | {payout * 1.0M / tests * 100}%");
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable warnings
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
@@ -73,178 +73,58 @@ public partial class Gambling
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async partial Task SlotTest(int tests = 1000)
|
||||
public sealed class SlotInteraction : NInteraction
|
||||
{
|
||||
if (tests <= 0)
|
||||
return;
|
||||
//multi vs how many times it occured
|
||||
|
||||
int streak = 0;
|
||||
int maxW = 0;
|
||||
int maxL = 0;
|
||||
|
||||
var dict = new Dictionary<decimal, int>();
|
||||
for (var i = 0; i < tests; i++)
|
||||
public SlotInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action) : base(client, userId, action)
|
||||
{
|
||||
var res = await _service.SlotAsync(ctx.User.Id, 0);
|
||||
var multi = res.AsT0.Multiplier;
|
||||
if (dict.ContainsKey(multi))
|
||||
dict[multi] += 1;
|
||||
else
|
||||
dict.Add(multi, 1);
|
||||
|
||||
if (multi == 0)
|
||||
{
|
||||
if (streak <= 0)
|
||||
--streak;
|
||||
else
|
||||
streak = -1;
|
||||
|
||||
maxL = Math.Max(maxL, -streak);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (streak >= 0)
|
||||
++streak;
|
||||
else
|
||||
streak = 1;
|
||||
|
||||
maxW = Math.Max(maxW, streak);
|
||||
}
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
decimal payout = 0;
|
||||
foreach (var key in dict.Keys.OrderByDescending(x => x))
|
||||
{
|
||||
sb.AppendLine($"x{key} occured `{dict[key]}` times. {dict[key] * 1.0f / tests * 100}%");
|
||||
payout += key * dict[key];
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine($"Longest win streak: `{maxW}`");
|
||||
sb.AppendLine($"Longest lose streak: `{maxL}`");
|
||||
|
||||
await SendConfirmAsync("Slot Test Results",
|
||||
sb.ToString(),
|
||||
footer: $"Total Bet: {tests} | Payout: {payout:F0} | {payout * 1.0M / tests * 100}%");
|
||||
protected override NadekoInteractionData Data { get; } = new(Emoji.Parse("🔁"),
|
||||
"slot:again",
|
||||
"Pull Again");
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async partial Task Slot(ShmartNumber amount)
|
||||
{
|
||||
if (!await CheckBetMandatory(amount))
|
||||
return;
|
||||
|
||||
// var slotInteraction = CreateSlotInteractionIntenal(amount);
|
||||
|
||||
await ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
if (!_runningUsers.Add(ctx.User.Id))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!await CheckBetMandatory(amount))
|
||||
return;
|
||||
|
||||
await ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
var maybeResult = await _service.SlotAsync(ctx.User.Id, amount);
|
||||
|
||||
if (!maybeResult.TryPickT0(out var result, out var error))
|
||||
if (await InternalSlotAsync(amount) is not SlotResult result)
|
||||
{
|
||||
if (error == GamblingError.InsufficientFunds)
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_slotStatsLock)
|
||||
{
|
||||
totalBet += amount;
|
||||
totalPaidOut += result.Won;
|
||||
}
|
||||
var msg = GetSlotMessageInternal(result);
|
||||
|
||||
long ownedAmount;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
ownedAmount = uow.Set<DiscordUser>().FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
||||
?? 0;
|
||||
}
|
||||
using var image = await GenerateSlotImageAsync(amount, result);
|
||||
await using var imgStream = await image.ToStreamAsync();
|
||||
|
||||
var slotBg = await _images.GetSlotBgAsync();
|
||||
using (var bgImage = Image.Load<Rgba32>(slotBg, out _))
|
||||
{
|
||||
var numbers = new int[3];
|
||||
result.Rolls.CopyTo(numbers, 0);
|
||||
|
||||
Color fontColor = Config.Slots.CurrencyFontColor;
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(Format.Bold(msg))
|
||||
.WithImageUrl($"attachment://result.png")
|
||||
.WithOkColor();
|
||||
|
||||
// var inter = slotInteraction.GetInteraction();
|
||||
await ctx.Channel.SendFileAsync(imgStream,
|
||||
"result.png",
|
||||
embed: eb.Build()
|
||||
// components: inter.CreateComponent()
|
||||
);
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new()
|
||||
{
|
||||
TextOptions = new()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 140
|
||||
}
|
||||
},
|
||||
((long)result.Won).ToString(),
|
||||
_fonts.DottyFont.CreateFont(65),
|
||||
fontColor,
|
||||
new(227, 92)));
|
||||
|
||||
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new()
|
||||
{
|
||||
TextOptions = new()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 135
|
||||
}
|
||||
},
|
||||
amount.ToString(),
|
||||
bottomFont,
|
||||
fontColor,
|
||||
new(129, 472)));
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new()
|
||||
{
|
||||
TextOptions = new()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 135
|
||||
}
|
||||
},
|
||||
ownedAmount.ToString(),
|
||||
bottomFont,
|
||||
fontColor,
|
||||
new(325, 472)));
|
||||
//sw.PrintLap("drew red text");
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
using var img = Image.Load(await _images.GetSlotEmojiAsync(numbers[i]));
|
||||
bgImage.Mutate(x => x.DrawImage(img, new Point(148 + (105 * i), 217), 1f));
|
||||
}
|
||||
|
||||
var multi = result.Multiplier.ToString("0.##");
|
||||
var msg = result.WinType switch
|
||||
{
|
||||
SlotWinType.SingleJoker => GetText(strs.slot_single(CurrencySign, multi)),
|
||||
SlotWinType.DoubleJoker => GetText(strs.slot_two(CurrencySign, multi)),
|
||||
SlotWinType.TrippleNormal => GetText(strs.slot_three(multi)),
|
||||
SlotWinType.TrippleJoker => GetText(strs.slot_jackpot(multi)),
|
||||
_ => GetText(strs.better_luck),
|
||||
};
|
||||
|
||||
await using (var imgStream = await bgImage.ToStreamAsync())
|
||||
{
|
||||
await ctx.Channel.SendFileAsync(imgStream,
|
||||
"result.png",
|
||||
Format.Bold(ctx.User.ToString()) + " " + msg);
|
||||
}
|
||||
}
|
||||
// await inter.RunAsync(resMsg);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -252,5 +132,156 @@ public partial class Gambling
|
||||
_runningUsers.TryRemove(ctx.User.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// private SlotInteraction CreateSlotInteractionIntenal(long amount)
|
||||
// {
|
||||
// return new SlotInteraction((DiscordSocketClient)ctx.Client,
|
||||
// ctx.User.Id,
|
||||
// async (smc) =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (await InternalSlotAsync(amount) is not SlotResult result)
|
||||
// {
|
||||
// await smc.RespondErrorAsync(_eb, GetText(strs.not_enough(CurrencySign)), true);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var msg = GetSlotMessageInternal(result);
|
||||
//
|
||||
// using var image = await GenerateSlotImageAsync(amount, result);
|
||||
// await using var imgStream = await image.ToStreamAsync();
|
||||
//
|
||||
// var guid = Guid.NewGuid();
|
||||
// var imgName = $"result_{guid}.png";
|
||||
//
|
||||
// var slotInteraction = CreateSlotInteractionIntenal(amount).GetInteraction();
|
||||
//
|
||||
// await smc.Message.ModifyAsync(m =>
|
||||
// {
|
||||
// m.Content = msg;
|
||||
// m.Attachments = new[]
|
||||
// {
|
||||
// new FileAttachment(imgStream, imgName)
|
||||
// };
|
||||
// m.Components = slotInteraction.CreateComponent();
|
||||
// });
|
||||
//
|
||||
// _ = slotInteraction.RunAsync(smc.Message);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Log.Error(ex, "Error pulling slot again");
|
||||
// }
|
||||
// // finally
|
||||
// // {
|
||||
// // await Task.Delay(1000);
|
||||
// // _runningUsers.TryRemove(ctx.User.Id);
|
||||
// // }
|
||||
// });
|
||||
// }
|
||||
|
||||
private string GetSlotMessageInternal(SlotResult result)
|
||||
{
|
||||
var multi = result.Multiplier.ToString("0.##");
|
||||
var msg = result.WinType switch
|
||||
{
|
||||
SlotWinType.SingleJoker => GetText(strs.slot_single(CurrencySign, multi)),
|
||||
SlotWinType.DoubleJoker => GetText(strs.slot_two(CurrencySign, multi)),
|
||||
SlotWinType.TrippleNormal => GetText(strs.slot_three(multi)),
|
||||
SlotWinType.TrippleJoker => GetText(strs.slot_jackpot(multi)),
|
||||
_ => GetText(strs.better_luck),
|
||||
};
|
||||
return msg;
|
||||
}
|
||||
|
||||
private async Task<SlotResult?> InternalSlotAsync(long amount)
|
||||
{
|
||||
var maybeResult = await _service.SlotAsync(ctx.User.Id, amount);
|
||||
|
||||
if (!maybeResult.TryPickT0(out var result, out var error))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return null;
|
||||
}
|
||||
|
||||
lock (_slotStatsLock)
|
||||
{
|
||||
totalBet += amount;
|
||||
totalPaidOut += result.Won;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<Image<Rgba32>> GenerateSlotImageAsync(long amount, SlotResult result)
|
||||
{
|
||||
long ownedAmount;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
ownedAmount = uow.Set<DiscordUser>().FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
||||
?? 0;
|
||||
}
|
||||
|
||||
var slotBg = await _images.GetSlotBgAsync();
|
||||
var bgImage = Image.Load<Rgba32>(slotBg, out _);
|
||||
var numbers = new int[3];
|
||||
result.Rolls.CopyTo(numbers, 0);
|
||||
|
||||
Color fontColor = Config.Slots.CurrencyFontColor;
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new()
|
||||
{
|
||||
TextOptions = new()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 140
|
||||
}
|
||||
},
|
||||
((long)result.Won).ToString(),
|
||||
_fonts.DottyFont.CreateFont(65),
|
||||
fontColor,
|
||||
new(227, 92)));
|
||||
|
||||
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new()
|
||||
{
|
||||
TextOptions = new()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 135
|
||||
}
|
||||
},
|
||||
amount.ToString(),
|
||||
bottomFont,
|
||||
fontColor,
|
||||
new(129, 472)));
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new()
|
||||
{
|
||||
TextOptions = new()
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrapTextWidth = 135
|
||||
}
|
||||
},
|
||||
ownedAmount.ToString(),
|
||||
bottomFont,
|
||||
fontColor,
|
||||
new(325, 472)));
|
||||
//sw.PrintLap("drew red text");
|
||||
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
using var img = Image.Load(await _images.GetSlotEmojiAsync(numbers[i]));
|
||||
bgImage.Mutate(x => x.DrawImage(img, new Point(148 + (105 * i), 217), 1f));
|
||||
}
|
||||
|
||||
return bgImage;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,311 +0,0 @@
|
||||
#nullable disable
|
||||
using Nadeko.Common;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Common;
|
||||
|
||||
public class Deck
|
||||
{
|
||||
public enum CardSuit
|
||||
{
|
||||
Spades = 1,
|
||||
Hearts = 2,
|
||||
Diamonds = 3,
|
||||
Clubs = 4
|
||||
}
|
||||
|
||||
private static readonly Dictionary<int, string> _cardNames = new()
|
||||
{
|
||||
{ 1, "Ace" },
|
||||
{ 2, "Two" },
|
||||
{ 3, "Three" },
|
||||
{ 4, "Four" },
|
||||
{ 5, "Five" },
|
||||
{ 6, "Six" },
|
||||
{ 7, "Seven" },
|
||||
{ 8, "Eight" },
|
||||
{ 9, "Nine" },
|
||||
{ 10, "Ten" },
|
||||
{ 11, "Jack" },
|
||||
{ 12, "Queen" },
|
||||
{ 13, "King" }
|
||||
};
|
||||
|
||||
private static Dictionary<string, Func<List<Card>, bool>> handValues;
|
||||
|
||||
public List<Card> CardPool { get; set; }
|
||||
private readonly Random _r = new NadekoRandom();
|
||||
|
||||
static Deck()
|
||||
=> InitHandValues();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the BlackJackGame, this allows you to create multiple games running at one time.
|
||||
/// </summary>
|
||||
public Deck()
|
||||
=> RefillPool();
|
||||
|
||||
/// <summary>
|
||||
/// Restart the game of blackjack. It will only refill the pool for now. Probably wont be used, unless you want to have
|
||||
/// only 1 bjg running at one time,
|
||||
/// then you will restart the same game every time.
|
||||
/// </summary>
|
||||
public void Restart()
|
||||
=> RefillPool();
|
||||
|
||||
/// <summary>
|
||||
/// Removes all cards from the pool and refills the pool with all of the possible cards. NOTE: I think this is too
|
||||
/// expensive.
|
||||
/// We should probably make it so it copies another premade list with all the cards, or something.
|
||||
/// </summary>
|
||||
protected virtual void RefillPool()
|
||||
{
|
||||
CardPool = new(52);
|
||||
//foreach suit
|
||||
for (var j = 1; j < 14; j++)
|
||||
// and number
|
||||
for (var i = 1; i < 5; i++)
|
||||
//generate a card of that suit and number and add it to the pool
|
||||
|
||||
// the pool will go from ace of spades,hears,diamonds,clubs all the way to the king of spades. hearts, ...
|
||||
CardPool.Add(new((CardSuit)i, j));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take a card from the pool, you either take it from the top if the deck is shuffled, or from a random place if the
|
||||
/// deck is in the default order.
|
||||
/// </summary>
|
||||
/// <returns>A card from the pool</returns>
|
||||
public Card Draw()
|
||||
{
|
||||
if (CardPool.Count == 0)
|
||||
Restart();
|
||||
//you can either do this if your deck is not shuffled
|
||||
|
||||
var num = _r.Next(0, CardPool.Count);
|
||||
var c = CardPool[num];
|
||||
CardPool.RemoveAt(num);
|
||||
return c;
|
||||
|
||||
// if you want to shuffle when you fill, then take the first one
|
||||
/*
|
||||
Card c = cardPool[0];
|
||||
cardPool.RemoveAt(0);
|
||||
return c;
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles the deck. Use this if you want to take cards from the top of the deck, instead of randomly. See DrawACard
|
||||
/// method.
|
||||
/// </summary>
|
||||
private void Shuffle()
|
||||
{
|
||||
if (CardPool.Count <= 1)
|
||||
return;
|
||||
var orderedPool = CardPool.Shuffle();
|
||||
CardPool ??= orderedPool.ToList();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> string.Concat(CardPool.Select(c => c.ToString())) + Environment.NewLine;
|
||||
|
||||
private static void InitHandValues()
|
||||
{
|
||||
bool HasPair(List<Card> cards)
|
||||
{
|
||||
return cards.GroupBy(card => card.Number).Count(group => group.Count() == 2) == 1;
|
||||
}
|
||||
|
||||
bool IsPair(List<Card> cards)
|
||||
{
|
||||
return cards.GroupBy(card => card.Number).Count(group => group.Count() == 3) == 0 && HasPair(cards);
|
||||
}
|
||||
|
||||
bool IsTwoPair(List<Card> cards)
|
||||
{
|
||||
return cards.GroupBy(card => card.Number).Count(group => group.Count() == 2) == 2;
|
||||
}
|
||||
|
||||
bool IsStraight(List<Card> cards)
|
||||
{
|
||||
if (cards.GroupBy(card => card.Number).Count() != cards.Count())
|
||||
return false;
|
||||
var toReturn = cards.Max(card => card.Number) - cards.Min(card => card.Number) == 4;
|
||||
if (toReturn || cards.All(c => c.Number != 1))
|
||||
return toReturn;
|
||||
|
||||
var newCards = cards.Select(c => c.Number == 1 ? new(c.Suit, 14) : c).ToArray();
|
||||
return newCards.Max(card => card.Number) - newCards.Min(card => card.Number) == 4;
|
||||
}
|
||||
|
||||
bool HasThreeOfKind(List<Card> cards)
|
||||
{
|
||||
return cards.GroupBy(card => card.Number).Any(group => group.Count() == 3);
|
||||
}
|
||||
|
||||
bool IsThreeOfKind(List<Card> cards)
|
||||
{
|
||||
return HasThreeOfKind(cards) && !HasPair(cards);
|
||||
}
|
||||
|
||||
bool IsFlush(List<Card> cards)
|
||||
{
|
||||
return cards.GroupBy(card => card.Suit).Count() == 1;
|
||||
}
|
||||
|
||||
bool IsFourOfKind(List<Card> cards)
|
||||
{
|
||||
return cards.GroupBy(card => card.Number).Any(group => group.Count() == 4);
|
||||
}
|
||||
|
||||
bool IsFullHouse(List<Card> cards)
|
||||
{
|
||||
return HasPair(cards) && HasThreeOfKind(cards);
|
||||
}
|
||||
|
||||
bool HasStraightFlush(List<Card> cards)
|
||||
{
|
||||
return IsFlush(cards) && IsStraight(cards);
|
||||
}
|
||||
|
||||
bool IsRoyalFlush(List<Card> cards)
|
||||
{
|
||||
return cards.Min(card => card.Number) == 1
|
||||
&& cards.Max(card => card.Number) == 13
|
||||
&& HasStraightFlush(cards);
|
||||
}
|
||||
|
||||
bool IsStraightFlush(List<Card> cards)
|
||||
{
|
||||
return HasStraightFlush(cards) && !IsRoyalFlush(cards);
|
||||
}
|
||||
|
||||
handValues = new()
|
||||
{
|
||||
{ "Royal Flush", IsRoyalFlush },
|
||||
{ "Straight Flush", IsStraightFlush },
|
||||
{ "Four Of A Kind", IsFourOfKind },
|
||||
{ "Full House", IsFullHouse },
|
||||
{ "Flush", IsFlush },
|
||||
{ "Straight", IsStraight },
|
||||
{ "Three Of A Kind", IsThreeOfKind },
|
||||
{ "Two Pairs", IsTwoPair },
|
||||
{ "A Pair", IsPair }
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetHandValue(List<Card> cards)
|
||||
{
|
||||
if (handValues is null)
|
||||
InitHandValues();
|
||||
|
||||
foreach (var kvp in handValues.Where(x => x.Value(cards)))
|
||||
return kvp.Key;
|
||||
return "High card " + (cards.FirstOrDefault(c => c.Number == 1)?.GetValueText() ?? cards.Max().GetValueText());
|
||||
}
|
||||
|
||||
public class Card : IComparable
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<CardSuit, string> _suitToSuitChar = new Dictionary<CardSuit, string>
|
||||
{
|
||||
{ CardSuit.Diamonds, "♦" },
|
||||
{ CardSuit.Clubs, "♣" },
|
||||
{ CardSuit.Spades, "♠" },
|
||||
{ CardSuit.Hearts, "♥" }
|
||||
};
|
||||
|
||||
private static readonly IReadOnlyDictionary<string, CardSuit> _suitCharToSuit = new Dictionary<string, CardSuit>
|
||||
{
|
||||
{ "♦", CardSuit.Diamonds },
|
||||
{ "d", CardSuit.Diamonds },
|
||||
{ "♣", CardSuit.Clubs },
|
||||
{ "c", CardSuit.Clubs },
|
||||
{ "♠", CardSuit.Spades },
|
||||
{ "s", CardSuit.Spades },
|
||||
{ "♥", CardSuit.Hearts },
|
||||
{ "h", CardSuit.Hearts }
|
||||
};
|
||||
|
||||
private static readonly IReadOnlyDictionary<char, int> _numberCharToNumber = new Dictionary<char, int>
|
||||
{
|
||||
{ 'a', 1 },
|
||||
{ '2', 2 },
|
||||
{ '3', 3 },
|
||||
{ '4', 4 },
|
||||
{ '5', 5 },
|
||||
{ '6', 6 },
|
||||
{ '7', 7 },
|
||||
{ '8', 8 },
|
||||
{ '9', 9 },
|
||||
{ 't', 10 },
|
||||
{ 'j', 11 },
|
||||
{ 'q', 12 },
|
||||
{ 'k', 13 }
|
||||
};
|
||||
|
||||
public CardSuit Suit { get; }
|
||||
public int Number { get; }
|
||||
|
||||
public string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
var str = string.Empty;
|
||||
|
||||
if (Number is <= 10 and > 1)
|
||||
str += "_" + Number;
|
||||
else
|
||||
str += GetValueText().ToLowerInvariant();
|
||||
return str + "_of_" + Suit.ToString().ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly string[] _regIndicators =
|
||||
{
|
||||
"🇦", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":keycap_ten:",
|
||||
"🇯", "🇶", "🇰"
|
||||
};
|
||||
|
||||
public Card(CardSuit s, int cardNum)
|
||||
{
|
||||
Suit = s;
|
||||
Number = cardNum;
|
||||
}
|
||||
|
||||
public string GetValueText()
|
||||
=> _cardNames[Number];
|
||||
|
||||
public override string ToString()
|
||||
=> _cardNames[Number] + " Of " + Suit;
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is not Card card)
|
||||
return 0;
|
||||
return Number - card.Number;
|
||||
}
|
||||
|
||||
public static Card Parse(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
|
||||
if (input.Length != 2
|
||||
|| !_numberCharToNumber.TryGetValue(input[0], out var n)
|
||||
|| !_suitCharToSuit.TryGetValue(input[1].ToString(), out var s))
|
||||
throw new ArgumentException("Invalid input", nameof(input));
|
||||
|
||||
return new(s, n);
|
||||
}
|
||||
|
||||
public string GetEmojiString()
|
||||
{
|
||||
var str = string.Empty;
|
||||
|
||||
str += _regIndicators[Number - 1];
|
||||
str += _suitToSuitChar[Suit];
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
namespace NadekoBot.Modules.Gambling.Common;
|
||||
using Nadeko.Econ;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Common;
|
||||
|
||||
public class QuadDeck : Deck
|
||||
{
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ.Gambling;
|
||||
using Nadeko.Econ.Gambling.Betdraw;
|
||||
using Nadeko.Econ.Gambling.Rps;
|
||||
using OneOf;
|
||||
|
||||
@@ -7,10 +8,11 @@ namespace NadekoBot.Modules.Gambling;
|
||||
|
||||
public interface IGamblingService
|
||||
{
|
||||
Task<OneOf<LuLaResult, GamblingError>> WofAsync(ulong userId, long amount);
|
||||
Task<OneOf<LuLaResult, GamblingError>> LulaAsync(ulong userId, long amount);
|
||||
Task<OneOf<BetrollResult, GamblingError>> BetRollAsync(ulong userId, long amount);
|
||||
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);
|
||||
Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? guessValue, byte? guessColor);
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using Nadeko.Econ.Gambling;
|
||||
using Nadeko.Econ.Gambling.Betdraw;
|
||||
using Nadeko.Econ.Gambling.Rps;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using OneOf;
|
||||
@@ -17,9 +18,7 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
_cs = cs;
|
||||
}
|
||||
|
||||
// todo input checks
|
||||
// todo ladder of fortune
|
||||
public async Task<OneOf<LuLaResult, GamblingError>> WofAsync(ulong userId, long amount)
|
||||
public async Task<OneOf<LuLaResult, GamblingError>> LulaAsync(ulong userId, long amount)
|
||||
{
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
@@ -64,9 +63,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
var game = new BetrollGame(_bcs.Data.BetRoll.Pairs
|
||||
.Select(x => (x.WhenAbove, (decimal)x.MultiplyBy))
|
||||
.ToList());
|
||||
|
||||
|
||||
var result = game.Roll(amount);
|
||||
|
||||
|
||||
var won = (long)result.Won;
|
||||
if (won > 0)
|
||||
{
|
||||
@@ -81,6 +80,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (guess > 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(guess));
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
||||
@@ -102,6 +104,42 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? guessValue, byte? guessColor)
|
||||
{
|
||||
if (amount < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
if (guessColor is null && guessValue is null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
if (guessColor > 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(guessColor));
|
||||
|
||||
if (guessValue > 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(guessValue));
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
var isTakeSuccess = await _cs.RemoveAsync(userId, amount, new("betflip", "bet"));
|
||||
|
||||
if (!isTakeSuccess)
|
||||
{
|
||||
return GamblingError.InsufficientFunds;
|
||||
}
|
||||
}
|
||||
|
||||
var game = new BetdrawGame();
|
||||
var result = game.Draw((BetdrawValueGuess?)guessValue, (BetdrawColorGuess?)guessColor, amount);
|
||||
|
||||
var won = (long)result.Won;
|
||||
if (won > 0)
|
||||
{
|
||||
await _cs.AddAsync(userId, won, new("betflip", "win"));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount)
|
||||
{
|
||||
@@ -132,6 +170,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
||||
|
||||
public Task<FlipResult[]> FlipAsync(int count)
|
||||
{
|
||||
if (count < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var game = new BetflipGame(0);
|
||||
|
||||
var results = new FlipResult[count];
|
||||
|
Reference in New Issue
Block a user