module Degenz.SlotMachine open System open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities open DSharpPlus.EventArgs open Degenz.Messaging open Degenz.Types open Npgsql.FSharp type SlotSymbol = { index : int emojiName : string reel1Count : int reel2Count : int reel3Count : int } let BigBrother = { index = 0 ; reel1Count = 1 ; reel2Count = 1 ; reel3Count = 1 ; emojiName = "bigbrother" } let Eye = { index = 1 ; reel1Count = 3 ; reel2Count = 2 ; reel3Count = 1 ; emojiName = "aneye" } let Obey = { index = 2 ; reel1Count = 2 ; reel2Count = 2 ; reel3Count = 2 ; emojiName = "obey" } let AnonMask = { index = 3 ; reel1Count = 1 ; reel2Count = 2 ; reel3Count = 4 ; emojiName = "anonmask" } let Ramen = { index = 5 ; reel1Count = 3 ; reel2Count = 3 ; reel3Count = 1 ; emojiName = "ramen" } let Sushi = { index = 4 ; reel1Count = 3 ; reel2Count = 2 ; reel3Count = 2 ; emojiName = "sushi" } let Pizza = { index = 6 ; reel1Count = 2 ; reel2Count = 4 ; reel3Count = 0 ; emojiName = "pizza" } let Alcohol = { index = 7 ; reel1Count = 1 ; reel2Count = 1 ; reel3Count = 1 ; emojiName = "alcohol" } let Circuit = { index = 0 ; reel1Count = 0 ; reel2Count = 0 ; reel3Count = 2 ; emojiName = "circuitboard" } let OldTv = { index = 9 ; reel1Count = 1 ; reel2Count = 2 ; reel3Count = 2 ; emojiName = "oldtv" } let Pills = { index = 10 ; reel1Count = 2 ; reel2Count = 1 ; reel3Count = 2 ; emojiName = "pills" } let Rat = { index = 11 ; reel1Count = 2 ; reel2Count = 1 ; reel3Count = 1 ; emojiName = "rat" } let symbols = [ BigBrother ; Eye ; Obey ; AnonMask ; Sushi ; Ramen ; Pizza ; Alcohol ; Circuit ; OldTv ; Pills ; Rat ] //let symbols = [ BigBrother ; Eye ; Obey ; AnonMask ; Sushi ; Ramen ; Pizza ; Alcohol ] let getReel fn = List.fold (fun acc elem -> (List.replicate (fn elem) elem) @ acc) [] symbols |> List.toArray type Prize = | Money of int | Jackpot type Slot = | Symbol of SlotSymbol | Any let prizeTable = [| Symbol BigBrother , Symbol BigBrother , Symbol BigBrother , Jackpot Symbol Eye , Symbol Eye , Symbol Eye , Money 500 Symbol Eye , Symbol Eye , Symbol Obey , Money 500 Symbol AnonMask , Symbol AnonMask , Symbol AnonMask , Money 250 Symbol AnonMask , Symbol AnonMask , Symbol Eye , Money 250 Symbol Ramen , Symbol Ramen , Symbol Ramen , Money 100 Symbol Ramen , Symbol Ramen , Symbol Eye , Money 100 Symbol Sushi , Symbol Sushi , Any , Money 50 Symbol Pizza , Any , Any , Money 20 |] let totalPerReel (reel : SlotSymbol -> int) = List.sumBy reel symbols let calculateOdds prizeIndex = match prizeTable.[prizeIndex] with | Symbol s1 , Symbol s2 , Symbol s3 , _ -> s1.reel1Count * s2.reel2Count * s3.reel3Count | Symbol s1 , Symbol s2 , Any , _ -> s1.reel1Count * s2.reel2Count * totalPerReel (fun s -> s.reel3Count) | Symbol s1 , Any , Any , _ -> s1.reel1Count * totalPerReel (fun s -> s.reel2Count) * totalPerReel (fun s -> s.reel3Count) | _ -> 0 let getTotalCombinations () = (List.sumBy (fun s -> s.reel1Count) symbols) * (List.sumBy (fun s -> s.reel2Count) symbols) * (List.sumBy (fun s -> s.reel3Count) symbols) let getOddsForPrize prizeIndex = let odds = calculateOdds prizeIndex let total = getTotalCombinations () $"{odds} in {total }" //getOddsForPrize 0 //getOddsForPrize 1 //getOddsForPrize 5 //getOddsForPrize 8 let getTotalWaysOfWinning () = [0..prizeTable.Length - 1] |> List.sumBy calculateOdds //totalPerReel (fun s -> s.reel1Count) //totalPerReel (fun s -> s.reel2Count) //totalPerReel (fun s -> s.reel3Count) let reel1 = getReel (fun s -> s.reel1Count) let reel2 = getReel (fun s -> s.reel2Count) let reel3 = getReel (fun s -> s.reel3Count) let slots = [| "https://s7.gifyu.com/images/aneye.png" "https://s7.gifyu.com/images/anonmask.png" "https://s7.gifyu.com/images/circuitboard.png" "https://s7.gifyu.com/images/obey.png" "https://s7.gifyu.com/images/oldtv.png" "https://s7.gifyu.com/images/pills.png" "https://s7.gifyu.com/images/pizza0d47578733961746.png" "https://s7.gifyu.com/images/ramen0515f00869e1f4eb.png" "https://s7.gifyu.com/images/rat69609f842a0eb9f5.png" "https://s7.gifyu.com/images/alcohol.png" "https://s7.gifyu.com/images/bigbrother.png" "https://s7.gifyu.com/images/sushi.png" |] // [| "https://s7.gifyu.com/images/A-bottle-of-pills0a3006d0170e08df.png" // "https://s7.gifyu.com/images/an-eyec362d8152ae2382b.png" // "https://s7.gifyu.com/images/anon-face-mask6c7624821c89fc08.png" // "https://s7.gifyu.com/images/a-piece-of-sushi77071d30f60a89c6.png" // "https://s7.gifyu.com/images/Circuit-board89056017b80f1d13.png" // "https://s7.gifyu.com/images/OBEYf2a8234109836c03.png" // "https://s7.gifyu.com/images/old-tv-screendc6bc9d4b6c1fd65.png" // "https://s7.gifyu.com/images/pizza030ffc00ff50da0e.png" // "https://s7.gifyu.com/images/ramen08336d448018c98f.png" // "https://s7.gifyu.com/images/rat14f65f54f0d75036.png" |] let slotEmojiNames = [| "sushi" "bigbrother" "pizza" "ramen" "circuitboard" "obey" "pills" "oldtv" "rat" "aneye" "alcohol" "anonmask" |] let PlayPricex1 = 5 let PlayPricex2 = 10 let PlayPricex3 = 20 let BaseJackpotAmount = 0 let sleepTime = 1500 let mutable guildEmojis : Map option = None let mutable anyEmoji : DiscordEmoji option = None let getJackpotAmount () = GuildEnvironment.connectionString |> Sql.connect |> Sql.query "SELECT stock FROM item WHERE symbol = 'JACKPOT'" |> Sql.executeRowAsync (fun read -> (read.int "stock") * 1) |> Async.AwaitTask let incrementJackpot amount = GuildEnvironment.connectionString |> Sql.connect |> Sql.parameters [ ( "amount" , Sql.int (int amount) ) ] |> Sql.query "UPDATE item SET stock = stock + @amount WHERE symbol = 'JACKPOT'" |> Sql.executeNonQueryAsync |> Async.AwaitTask let resetJackpot amount = GuildEnvironment.connectionString |> Sql.connect |> Sql.parameters [ ( "amount" , Sql.int (int amount) ) ] |> Sql.query "UPDATE item SET stock = amount WHERE symbol = 'JACKPOT'" |> Sql.executeNonQueryAsync |> Async.AwaitTask let handlePrizeTable (ctx : IDiscordContext) = task { do! Messaging.defer ctx let embed = DiscordEmbedBuilder() match guildEmojis , anyEmoji with | Some emojis , Some any -> let folder acc elem = let s1,s2,s3,prize = elem let prizeTxt = match prize with | Money m -> $"**{m}** $GBT" | Jackpot -> $"**JACKPOT**" let line = match s1 , s2 , s3 with | Symbol s1' , Symbol s2' , Symbol s3' -> $"{Formatter.Emoji(emojis.[s1'.emojiName])}{Formatter.Emoji(emojis.[s2'.emojiName])}{Formatter.Emoji(emojis.[s3'.emojiName])}" | Symbol s1' , Symbol s2' , Any -> $"{Formatter.Emoji(emojis.[s1'.emojiName])}{Formatter.Emoji(emojis.[s2'.emojiName])}{Formatter.Emoji(any)}" | Symbol s1' , Any , Any -> $"{Formatter.Emoji(emojis.[s1'.emojiName])}{Formatter.Emoji(any)}{Formatter.Emoji(any)}" | _ -> "" $"{acc}\n{line} ⠀⠀|⠀⠀ {prizeTxt}" let! jackpot = getJackpotAmount () let rows = Array.fold folder "" prizeTable embed.Color <- DiscordColor.Green embed.Title <- "Degenz Slots Prize Table" embed.Description <- $"Current Jackpot At: **{jackpot}** $GBT\n\n**Combo** ⠀⠀⠀⠀⠀⠀⠀ **Prize**\n{rows}" do! ctx.FollowUp(DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(embed)) do! Analytics.prizeTableViewed (ctx.GetDiscordMember()) | _ , _ -> return () } :> Task let spinEmojis (builder : DiscordFollowupMessageBuilder) (results : SlotSymbol array) (itx : DiscordInteraction) = async { builder.Content <- "Spinning!" let! followUp = itx.CreateFollowupMessageAsync(builder) |> Async.AwaitTask match guildEmojis with | Some emojis -> let e1 = Formatter.Emoji(emojis.[results.[0].emojiName]) let e2 = Formatter.Emoji(emojis.[results.[1].emojiName]) let e3 = Formatter.Emoji(emojis.[results.[2].emojiName]) do! Async.Sleep sleepTime let content = $"{e1}" let! _ = itx.EditFollowupMessageAsync(followUp.Id, DiscordWebhookBuilder().WithContent(content)) |> Async.AwaitTask do! Async.Sleep sleepTime let content = $"{e1}{e2}" let! _ = itx.EditFollowupMessageAsync(followUp.Id, DiscordWebhookBuilder().WithContent(content)) |> Async.AwaitTask do! Async.Sleep sleepTime let content = $"{e1}{e2}{e3}" let! _ = itx.EditFollowupMessageAsync(followUp.Id, DiscordWebhookBuilder().WithContent(content)) |> Async.AwaitTask return followUp , content | None -> let! _ = itx.EditFollowupMessageAsync(followUp.Id, DiscordWebhookBuilder().WithContent("An error occurred, please contact an Admin")) |> Async.AwaitTask return followUp , "" } let spin multiplier (ctx : IDiscordContext) = let playAmount = match multiplier with | 1 -> PlayPricex1 | 2 -> PlayPricex2 | _ -> PlayPricex3 let execute player = async { do! DbService.updatePlayerCurrency -playAmount player |> Async.Ignore let random = Random(Guid.NewGuid().GetHashCode()) let symbols = [| reel1.[random.Next(0, reel1.Length)] ; reel2.[random.Next(0, reel2.Length)] ; reel3.[random.Next(0, reel3.Length)] |] let prize = prizeTable |> Array.tryPick (fun (s1,s2,s3,prize) -> match s1 , s2 , s3 with | Symbol s1' , Symbol s2' , Symbol s3' when s1'.index = symbols.[0].index && s2'.index = symbols.[1].index && s3'.index = symbols.[2].index -> Some prize | Symbol s1' , Symbol s2' , Any when s1'.index = symbols.[0].index && s2'.index = symbols.[1].index -> Some prize | Symbol s1' , Any , Any when s1'.index = symbols.[0].index -> Some prize | _ -> None) let builder = DiscordFollowupMessageBuilder() builder.IsEphemeral <- true let itx = ctx.GetInteraction() let! followUpMessage , slotsContent = spinEmojis builder symbols itx do! Async.Sleep 2000 let embed = DiscordEmbedBuilder() embed.Author <- DiscordEmbedBuilder.EmbedAuthor() embed.Author.Name <- player.Name embed.Author.IconUrl <- ctx.GetDiscordMember().AvatarUrl embed.Title <- "Slots Results" let addGBTField (embed : DiscordEmbedBuilder) prize = let sym = if prize > 0 then "+" else "-" embed.AddField("New 💰$GBT Balance", $"`💰` {player.Bank} ⋙ `💰` {player.Bank + prize} `({sym}{abs prize} $GBT)`") |> ignore let! result , prizeAmount = async { match prize with | Some (Money amount) -> let prizeWithMultiplier = amount * multiplier do! DbService.updatePlayerCurrency prizeWithMultiplier player |> Async.Ignore embed.Color <- DiscordColor.Green embed.Description <- $"You win! You got **{prizeWithMultiplier}** GBT 🎉" // embed.AddField("Result", $"{slotsContent}", true) |> ignore embed.AddField("Bet", $"{playAmount}", true) |> ignore embed.AddField("Prize", $"{prizeWithMultiplier}") |> ignore addGBTField embed prizeWithMultiplier return "WON" , prizeWithMultiplier | Some (Jackpot) -> let! jackpot = getJackpotAmount () embed.Color <- DiscordColor.Purple embed.Description <- $"YOU HIT THE JACKPOT!!!" // embed.AddField("Result", $"{slotsContent}", true) |> ignore embed.AddField("Bet", $"{playAmount}", true) |> ignore embed.AddField("Prize", $"{jackpot}") |> ignore addGBTField embed jackpot do! DbService.updatePlayerCurrency jackpot player |> Async.Ignore do! resetJackpot BaseJackpotAmount |> Async.Ignore return "JACKPOT" , jackpot | None -> do! incrementJackpot playAmount |> Async.Ignore embed.Description <- $"Better luck next time! You paid {playAmount} $GBT" embed.Color <- DiscordColor.Red embed.AddField("Bet", $"{playAmount}", true) |> ignore // embed.AddField("New JACKPOT", $"`💰` {jackpot} `$GBT`", true) |> ignore // embed.AddField("Result", $"{slotsContent}", true) |> ignore addGBTField embed -playAmount return "LOST" , 0 } let dwb = DiscordWebhookBuilder() let betText mult = if mult = multiplier then "Spin Again" else "Bet" let button1 = DiscordButtonComponent(ButtonStyle.Success, $"spin-1x", $"{betText 1} {PlayPricex1} $GBT 🎰") :> DiscordComponent let button2 = DiscordButtonComponent(ButtonStyle.Success, $"spin-2x", $"{betText 2} {PlayPricex2} $GBT 🎰") :> DiscordComponent let button3 = DiscordButtonComponent(ButtonStyle.Success, $"spin-3x", $"{betText 3} {PlayPricex3} $GBT 🎰") :> DiscordComponent dwb.AddComponents([| button1 ; button2 ; button3 |]).AddEmbed(embed).WithContent(slotsContent) |> ignore do! itx.EditFollowupMessageAsync(followUpMessage.Id, dwb) |> Async.AwaitTask |> Async.Ignore do! Analytics.slotPlayed (ctx.GetDiscordMember()) playAmount result prizeAmount } PlayerInteractions.executePlayerAction ctx (fun player -> if player.Bank >= playAmount then execute player else Messaging.sendFollowUpMessage ctx "You do not have sufficient funds to play") let handleButton (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = let ctx = DiscordEventContext event match event.Id with | "spin-1x" -> spin 1 ctx | "spin-2x" -> spin 2 ctx | "spin-3x" -> spin 3 ctx | "prizes" -> handlePrizeTable ctx | _ -> printfn "Wrong Spin ID" Task.CompletedTask |> Async.AwaitTask |> Async.Start Task.CompletedTask let handleGuildDownloadCompleted (_ : DiscordClient) (event : GuildDownloadCompletedEventArgs) = task { let ( result , guild ) = event.Guilds.TryGetValue(GuildEnvironment.guildId) guildEmojis <- guild.Emojis |> Seq.map (fun kvp -> kvp.Value) |> Seq.toArray |> Seq.filter (fun de -> Array.contains de.Name slotEmojiNames) |> Seq.distinctBy (fun de -> de.Name) |> Seq.map (fun de -> ( de.Name , de )) |> Map.ofSeq |> Some anyEmoji <- guild.Emojis |> Seq.tryPick (fun kvp -> if kvp.Value.Name = "any" then Some kvp.Value else None) return () } :> Task let sendInitialEmbed (ctx : IDiscordContext) = async { try let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelSlots) let builder = DiscordMessageBuilder() let embed = DiscordEmbedBuilder() embed.Title <- "Degenz Slot Machine" embed.Description <- "Want to try your luck?" embed.ImageUrl <- "https://s7.gifyu.com/images/ezgif.com-gif-maker-268ecb6e4d28bd55a0.gif" builder.AddEmbed(embed) |> ignore let button1 = DiscordButtonComponent(ButtonStyle.Success, $"spin-1x", $"Bet 1x - {PlayPricex1} $GBT 🎰") :> DiscordComponent let button2 = DiscordButtonComponent(ButtonStyle.Success, $"spin-2x", $"Bet 2x - {PlayPricex2} $GBT 🎰") :> DiscordComponent let button3 = DiscordButtonComponent(ButtonStyle.Success, $"spin-3x", $"Bet 3x - {PlayPricex3} $GBT 🎰") :> DiscordComponent let button4 = DiscordButtonComponent(ButtonStyle.Primary, $"prizes", $"Show Prizes") :> DiscordComponent builder.AddComponents([| button1 ; button2 ; button3 ; button4 |]) |> ignore do! GuildEnvironment.botClientSlots.Value.SendMessageAsync(channel, builder) |> Async.AwaitTask |> Async.Ignore with e -> printfn $"Error trying to get channel Training Dojo\n\n{e.Message}" } |> Async.RunSynchronously