$slot, $slottest and $slotstats added

This commit is contained in:
Kwoth 2017-01-18 05:49:12 +01:00
parent 6861965c5b
commit 4141ab4a9e
20 changed files with 409 additions and 0 deletions

View File

@ -0,0 +1,301 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Gambling
{
public partial class Gambling
{
[Group]
public class Slots : ModuleBase
{
private static int totalBet = 0;
private static int totalPaidOut = 0;
private const string backgroundPath = "data/slots/background.png";
private static readonly byte[] backgroundBuffer;
private static readonly byte[][] numbersBuffer = new byte[10][];
private static readonly byte[][] emojiBuffer;
const int alphaCutOut = byte.MaxValue / 3;
static Slots()
{
backgroundBuffer = File.ReadAllBytes(backgroundPath);
for (int i = 0; i < 10; i++)
{
numbersBuffer[i] = File.ReadAllBytes("data/slots/" + i + ".png");
}
int throwaway;
var emojiFiles = Directory.GetFiles("data/slots/emojis/", "*.png")
.Where(f => int.TryParse(Path.GetFileNameWithoutExtension(f), out throwaway))
.OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f)))
.ToArray();
emojiBuffer = new byte[emojiFiles.Length][];
for (int i = 0; i < emojiFiles.Length; i++)
{
emojiBuffer[i] = File.ReadAllBytes(emojiFiles[i]);
}
}
private static MemoryStream InternalGetStream(string path)
{
var ms = new MemoryStream();
using (var fs = File.Open(path, FileMode.Open))
{
fs.CopyTo(ms);
fs.Flush();
}
ms.Position = 0;
return ms;
}
//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
public class SlotMachine
{
public const int MaxValue = 5;
static readonly List<Func<int[], int>> winningCombos = new List<Func<int[], int>>()
{
//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 (int i = 0; i < numbers.Length; i++)
{
numbers[i] = new NadekoRandom().Next(0, MaxValue + 1);
}
int multi = 0;
for (int i = 0; i < winningCombos.Count; i++)
{
multi = winningCombos[i](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)
{
this.Numbers = nums;
this.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<int, int>();
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}%");
}
static HashSet<ulong> runningUsers = new HashSet<ulong>();
[NadekoCommand, Usage, Description, Aliases]
public async Task Slot(int amount = 0)
{
if (!runningUsers.Add(Context.User.Id))
return;
try
{
if (amount < 1)
{
await Context.Channel.SendErrorAsync($"You can't bet less than 1{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
return;
}
if (amount > 999)
{
await Context.Channel.SendErrorAsync($"You can't bet more than 999{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
return;
}
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false))
return;
Interlocked.Add(ref totalBet, amount);
using (var bgFileStream = new MemoryStream(backgroundBuffer))
{
var bgImage = new ImageSharp.Image(bgFileStream);
var result = SlotMachine.Pull();
int[] numbers = result.Numbers;
using (var bgPixels = bgImage.Lock())
{
for (int i = 0; i < 3; i++)
{
using (var file = new MemoryStream(emojiBuffer[numbers[i]]))
{
var randomImage = new ImageSharp.Image(file);
using (var toAdd = randomImage.Lock())
{
for (int j = 0; j < toAdd.Width; j++)
{
for (int k = 0; k < toAdd.Height; k++)
{
var x = 95 + 142 * i + j;
int y = 330 + k;
var toSet = toAdd[j, k];
if (toSet.A < alphaCutOut)
continue;
bgPixels[x, y] = toAdd[j, k];
}
}
}
}
}
var won = amount * result.Multiplier;
var printWon = won;
var n = 0;
do
{
var digit = printWon % 10;
using (var fs = new MemoryStream(numbersBuffer[digit]))
{
var img = new ImageSharp.Image(fs);
using (var pixels = img.Lock())
{
for (int i = 0; i < pixels.Width; i++)
{
for (int j = 0; j < pixels.Height; j++)
{
if (pixels[i, j].A < alphaCutOut)
continue;
var x = 230 - n * 16 + i;
bgPixels[x, 462 + j] = pixels[i, j];
}
}
}
}
n++;
} while ((printWon /= 10) != 0);
var printAmount = amount;
n = 0;
do
{
var digit = printAmount % 10;
using (var fs = new MemoryStream(numbersBuffer[digit]))
{
var img = new ImageSharp.Image(fs);
using (var pixels = img.Lock())
{
for (int i = 0; i < pixels.Width; i++)
{
for (int j = 0; j < pixels.Height; j++)
{
if (pixels[i, j].A < alphaCutOut)
continue;
var x = 395 - n * 16 + i;
bgPixels[x, 462 + j] = pixels[i, j];
}
}
}
}
n++;
} while ((printAmount /= 10) != 0);
}
var msg = "Better luck next time ^_^";
if (result.Multiplier != 0)
{
await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false);
Interlocked.Add(ref totalPaidOut, amount * result.Multiplier);
if (result.Multiplier == 1)
msg = $"A single {NadekoBot.BotConfig.CurrencySign}, x1 - Try again!";
else if (result.Multiplier == 4)
msg = $"Good job! Two {NadekoBot.BotConfig.CurrencySign} - bet x4";
else if (result.Multiplier == 10)
msg = "Wow! Lucky! Three of a kind! x10";
else if (result.Multiplier == 30)
msg = "WOAAHHHHHH!!! Congratulations!!! x30";
}
await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`Bet:`{amount} `Won:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
}
}
finally
{
var t = Task.Run(async () =>
{
await Task.Delay(3000);
runningUsers.Remove(Context.User.Id);
});
}
}
}
}
}

View File

@ -7079,6 +7079,87 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to slot.
/// </summary>
public static string slot_cmd {
get {
return ResourceManager.GetString("slot_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user..
/// </summary>
public static string slot_desc {
get {
return ResourceManager.GetString("slot_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}slot 5`.
/// </summary>
public static string slot_usage {
get {
return ResourceManager.GetString("slot_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to slotstats.
/// </summary>
public static string slotstats_cmd {
get {
return ResourceManager.GetString("slotstats_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows the total stats of the slot command for this bot&apos;s session..
/// </summary>
public static string slotstats_desc {
get {
return ResourceManager.GetString("slotstats_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}slotstats`.
/// </summary>
public static string slotstats_usage {
get {
return ResourceManager.GetString("slotstats_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to slottest.
/// </summary>
public static string slottest_cmd {
get {
return ResourceManager.GetString("slottest_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Tests to see how much slots payout for X number of plays..
/// </summary>
public static string slottest_desc {
get {
return ResourceManager.GetString("slottest_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}slottest 1000`.
/// </summary>
public static string slottest_usage {
get {
return ResourceManager.GetString("slottest_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to slowmode.
/// </summary>

View File

@ -2952,4 +2952,31 @@
<data name="startevent_usage" xml:space="preserve">
<value>`{0}startevent flowerreaction`</value>
</data>
<data name="slotstats_cmd" xml:space="preserve">
<value>slotstats</value>
</data>
<data name="slotstats_desc" xml:space="preserve">
<value>Shows the total stats of the slot command for this bot's session.</value>
</data>
<data name="slotstats_usage" xml:space="preserve">
<value>`{0}slotstats`</value>
</data>
<data name="slottest_cmd" xml:space="preserve">
<value>slottest</value>
</data>
<data name="slottest_desc" xml:space="preserve">
<value>Tests to see how much slots payout for X number of plays.</value>
</data>
<data name="slottest_usage" xml:space="preserve">
<value>`{0}slottest 1000`</value>
</data>
<data name="slot_cmd" xml:space="preserve">
<value>slot</value>
</data>
<data name="slot_desc" xml:space="preserve">
<value>Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user.</value>
</data>
<data name="slot_usage" xml:space="preserve">
<value>`{0}slot 5`</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB