using Discord; using Discord.Commands; using ImageSharp; using NadekoBot.Extensions; using NadekoBot.Services; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using NadekoBot.Common; using NadekoBot.Common.Attributes; using SixLabors.Primitives; namespace NadekoBot.Modules.Gambling { public partial class Gambling { [Group] public class SlotCommands : NadekoSubmodule { private static int _totalBet; private static int _totalPaidOut; private static readonly HashSet _runningUsers = new HashSet(); private readonly IBotConfigProvider _bc; private const int _alphaCutOut = byte.MaxValue / 3; //here is a payout chart //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg //thanks to judge for helping me with this private readonly IImagesService _images; private readonly CurrencyService _cs; public SlotCommands(IImagesService images, IBotConfigProvider bc, CurrencyService cs) { _images = images; _bc = bc; _cs = cs; } public class SlotMachine { public const int MaxValue = 5; static readonly List> _winningCombos = new List>() { //three flowers (arr) => arr.All(a=>a==MaxValue) ? 30 : 0, //three of the same (arr) => !arr.Any(a => a != arr[0]) ? 10 : 0, //two flowers (arr) => arr.Count(a => a == MaxValue) == 2 ? 4 : 0, //one flower (arr) => arr.Any(a => a == MaxValue) ? 1 : 0, }; public static SlotResult Pull() { var numbers = new int[3]; for (var i = 0; i < numbers.Length; i++) { numbers[i] = new NadekoRandom().Next(0, MaxValue + 1); } var multi = 0; foreach (var t in _winningCombos) { multi = t(numbers); if (multi != 0) break; } return new SlotResult(numbers, multi); } public struct SlotResult { public int[] Numbers { get; } public int Multiplier { get; } public SlotResult(int[] nums, int multi) { Numbers = nums; Multiplier = multi; } } } [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] public async Task SlotStats() { //i remembered to not be a moron var paid = _totalPaidOut; var bet = _totalBet; if (bet <= 0) bet = 1; var embed = new EmbedBuilder() .WithOkColor() .WithTitle("Slot Stats") .AddField(efb => efb.WithName("Total Bet").WithValue(bet.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName("Paid Out").WithValue(paid.ToString()).WithIsInline(true)) .WithFooter(efb => efb.WithText($"Payout Rate: {paid * 1.0 / bet * 100:f4}%")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] public async Task SlotTest(int tests = 1000) { if (tests <= 0) return; //multi vs how many times it occured var dict = new Dictionary(); for (int i = 0; i < tests; i++) { var res = SlotMachine.Pull(); if (dict.ContainsKey(res.Multiplier)) dict[res.Multiplier] += 1; else dict.Add(res.Multiplier, 1); } var sb = new StringBuilder(); const int bet = 1; int 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]; } await Context.Channel.SendConfirmAsync("Slot Test Results", sb.ToString(), footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%"); } [NadekoCommand, Usage, Description, Aliases] public async Task Slot(int amount = 0) { if (!_runningUsers.Add(Context.User.Id)) return; try { if (amount < 1) { await ReplyErrorLocalized("min_bet_limit", 1 + _bc.BotConfig.CurrencySign).ConfigureAwait(false); return; } const int maxAmount = 9999; if (amount > maxAmount) { GetText("slot_maxbet", maxAmount + _bc.BotConfig.CurrencySign); await ReplyErrorLocalized("max_bet_limit", maxAmount + _bc.BotConfig.CurrencySign).ConfigureAwait(false); return; } if (!await _cs.RemoveAsync(Context.User, "Slot Machine", amount, false)) { await ReplyErrorLocalized("not_enough", _bc.BotConfig.CurrencySign).ConfigureAwait(false); return; } Interlocked.Add(ref _totalBet, amount); using (var bgFileStream = _images.SlotBackground.ToStream()) { var bgImage = ImageSharp.Image.Load(bgFileStream); var result = SlotMachine.Pull(); int[] numbers = result.Numbers; for (int i = 0; i < 3; i++) { using (var file = _images.SlotEmojis[numbers[i]].ToStream()) using (var randomImage = ImageSharp.Image.Load(file)) { bgImage.DrawImage(randomImage, 100, default, new Point(95 + 142 * i, 330)); } } var won = amount * result.Multiplier; var printWon = won; var n = 0; do { var digit = printWon % 10; using (var fs = _images.SlotNumbers[digit].ToStream()) using (var img = ImageSharp.Image.Load(fs)) { bgImage.DrawImage(img, 100, default, new Point(230 - n * 16, 462)); } n++; } while ((printWon /= 10) != 0); var printAmount = amount; n = 0; do { var digit = printAmount % 10; using (var fs = _images.SlotNumbers[digit].ToStream()) using (var img = ImageSharp.Image.Load(fs)) { bgImage.DrawImage(img, 100, default, new Point(395 - n * 16, 462)); } n++; } while ((printAmount /= 10) != 0); var msg = GetText("better_luck"); if (result.Multiplier != 0) { await _cs.AddAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false); Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier); if (result.Multiplier == 1) msg = GetText("slot_single", _bc.BotConfig.CurrencySign, 1); else if (result.Multiplier == 4) msg = GetText("slot_two", _bc.BotConfig.CurrencySign, 4); else if (result.Multiplier == 10) msg = GetText("slot_three", 10); else if (result.Multiplier == 30) msg = GetText("slot_jackpot", 30); } await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`{GetText("slot_bet")}:`{amount} `{GetText("slot_won")}:` {amount * result.Multiplier}{_bc.BotConfig.CurrencySign}").ConfigureAwait(false); } } finally { var _ = Task.Run(async () => { await Task.Delay(1500); _runningUsers.Remove(Context.User.Id); }); } } } } }