301 lines
13 KiB
Forth
301 lines
13 KiB
Forth
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<GBT>
|
||
| Jackpot
|
||
|
||
type Slot =
|
||
| Symbol of SlotSymbol
|
||
| Any
|
||
|
||
let prizeTable =
|
||
[| Symbol BigBrother , Symbol BigBrother , Symbol BigBrother , Jackpot
|
||
Symbol Eye , Symbol Eye , Symbol Eye , Money 500<GBT>
|
||
Symbol Eye , Symbol Eye , Symbol Obey , Money 500<GBT>
|
||
Symbol AnonMask , Symbol AnonMask , Symbol AnonMask , Money 250<GBT>
|
||
Symbol AnonMask , Symbol AnonMask , Symbol Eye , Money 250<GBT>
|
||
Symbol Ramen , Symbol Ramen , Symbol Ramen , Money 100<GBT>
|
||
Symbol Ramen , Symbol Ramen , Symbol Eye , Money 100<GBT>
|
||
Symbol Sushi , Symbol Sushi , Any , Money 50<GBT>
|
||
Symbol Pizza , Any , Any , Money 20<GBT> |]
|
||
|
||
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 PlayPrice = 5<GBT>
|
||
let sleepTime = 1500
|
||
let mutable guildEmojis : Map<string, DiscordEmoji> 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 "price") * 1<GBT>)
|
||
|
||
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
|
||
|
||
let spinEmojis (results : SlotSymbol array) (ctx : IDiscordContext) =
|
||
async {
|
||
let itx = ctx.GetInteraction()
|
||
let builder = DiscordFollowupMessageBuilder()
|
||
|
||
builder.Content <- "Spinning!"
|
||
builder.IsEphemeral <- true
|
||
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 ()
|
||
| None -> return ()
|
||
}
|
||
|
||
let spin spinType (ctx : IDiscordContext) =
|
||
PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||
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)
|
||
|
||
match spinType with
|
||
| "Emojis" -> do! spinEmojis symbols ctx
|
||
// | "Files" -> do! spinFiles symbols ctx
|
||
| _ -> ()
|
||
|
||
do! Async.Sleep 2000
|
||
let builder = DiscordFollowupMessageBuilder()
|
||
builder.IsEphemeral <- true
|
||
let embed4 = DiscordEmbedBuilder()
|
||
embed4.Title <- "Results"
|
||
match prize with
|
||
| Some (Money amount) ->
|
||
do! DbService.updatePlayerCurrency amount player |> Async.Ignore
|
||
embed4.ImageUrl <- "https://s7.gifyu.com/images/youwin.png"
|
||
embed4.Description <- $"You win **{amount}** GBT!"
|
||
| Some (Jackpot) ->
|
||
embed4.ImageUrl <- "https://s7.gifyu.com/images/jackpot2ac30c9823f6a91c.png"
|
||
embed4.Description <- $"YOU HIT THE JACKPOT!!!"
|
||
| None ->
|
||
do! DbService.updatePlayerCurrency -PlayPrice player |> Async.Ignore
|
||
do! incrementJackpot PlayPrice |> Async.AwaitTask |> Async.Ignore
|
||
embed4.Description <- $"Better luck next time! You paid {PlayPrice} $GBT"
|
||
let button1 = DiscordButtonComponent(ButtonStyle.Success, $"spin", $"Spin Again {PlayPrice} $GBT") :> DiscordComponent
|
||
let button2 = DiscordButtonComponent(ButtonStyle.Success, $"prizes", $"Show Prizes") :> DiscordComponent
|
||
builder.AddComponents [| button1 ; button2 |] |> ignore
|
||
builder.AddEmbed(embed4) |> ignore
|
||
do! ctx.FollowUp(builder) |> Async.AwaitTask |> Async.Ignore
|
||
|
||
return ()
|
||
})
|
||
|
||
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}"
|
||
let builder = DiscordFollowupMessageBuilder()
|
||
builder.IsEphemeral <- true
|
||
do! ctx.FollowUp(builder.AddEmbed(embed))
|
||
| _ , _ -> return ()
|
||
} :> Task
|
||
|
||
let handleButton (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
|
||
let ctx = DiscordEventContext event
|
||
task {
|
||
match event.Id with
|
||
| "spin" -> do! spin "Emojis" ctx
|
||
| "prizes" -> do! handlePrizeTable ctx
|
||
| _ ->
|
||
printfn "Wrong Spin ID"
|
||
return ()
|
||
} :> Task
|
||
|
||
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 <- "Hello I am an embed"
|
||
embed.ImageUrl <- "https://i.kym-cdn.com/photos/images/original/001/169/608/a43.gif"
|
||
|
||
builder.AddEmbed(embed) |> ignore
|
||
|
||
let button1 = DiscordButtonComponent(ButtonStyle.Success, $"spin", $"Spin {PlayPrice} $GBT") :> DiscordComponent
|
||
let button2 = DiscordButtonComponent(ButtonStyle.Success, $"prizes", $"Show Prizes") :> DiscordComponent
|
||
builder.AddComponents [| button1 ; button2 |] |> 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
|