discord-bot-game/Program.fs

182 lines
7.3 KiB
Forth

open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.SlashCommands
type Move =
| Rock
| Paper
| Scissor
type RoundResult =
| P1Win
| P2Win
| Draw
type Player =
{ name : string
health : string }
type Turn =
| WaitingForBoth
| WaitingForOne of Move
| WaitingForTwo of Move
| BothCompleted of Move * Move
type Match =
{ player1 : DiscordUser
player2 : DiscordUser
round : int
scorePlayer1 : int
scorePlayer2 : int
turn : Turn }
type JoeBot() =
inherit ApplicationCommandModule ()
static let mutable currentMatch : Match option = None
[<SlashCommand("match", "Start the match")>]
member _.StartMatch (ctx : InteractionContext, [<Option("player1", "first player")>] player1 : DiscordUser, [<Option("player2", "seceond player")>] player2 : DiscordUser) =
async {
currentMatch <- Some {
player1 = player1
player2 = player2
round = 0
scorePlayer1 = 0
scorePlayer2 = 0
turn = WaitingForBoth
}
do! ctx.CreateResponseAsync (sprintf "Match started between %s and %s. May the best hacker win!" player1.Username player2.Username)
|> Async.AwaitTask
|> Async.Ignore
} |> Async.StartAsTask
:> Task
[<SlashCommand("move", "Challenge another member to combat")>]
member _.SendMove (ctx : InteractionContext, [<Option("move", "choose between rock, paper, scissors")>] moveString : string) =
async {
let move =
match moveString.ToLower() with
| "rock" -> Some Rock
| "paper" -> Some Paper
| "scissors" -> Some Scissor
| _ -> None
match currentMatch , move with
| Some mtc , Some move ->
let updatedTurn =
match mtc.turn with
| WaitingForBoth ->
match ctx.User with
| mem when mem = mtc.player1 -> WaitingForTwo move
| mem when mem = mtc.player2 -> WaitingForOne move
| _ -> mtc.turn
| WaitingForOne p1m ->
match ctx.User with
| mem when mem = mtc.player1 -> BothCompleted ( p1m , move )
| mem when mem = mtc.player2 -> mtc.turn
| _ -> mtc.turn
| WaitingForTwo p2m ->
match ctx.User with
| mem when mem = mtc.player1 -> mtc.turn
| mem when mem = mtc.player2 -> BothCompleted ( p2m , move )
| _ -> mtc.turn
| _ -> mtc.turn
match updatedTurn with
| BothCompleted ( p1m , p2m ) ->
let result =
match p1m , p2m with
| Rock , Paper -> P2Win
| Rock , Scissor -> P1Win
| Paper , Rock -> P1Win
| Paper , Scissor -> P2Win
| Scissor , Rock -> P2Win
| Scissor , Paper -> P1Win
| _ -> Draw
match result with
| P1Win | P2Win ->
let winner = match result with P1Win -> mtc.player1 | P2Win -> mtc.player2 | Draw -> mtc.player1
let winningMove = match result with P1Win -> p1m | P2Win -> p2m | Draw -> p1m
let losingMove = match result with P1Win -> p2m | P2Win -> p1m | Draw -> p1m
let message = $"{winningMove} beats {losingMove}! {winner.Username} takes the round!"
currentMatch <- Some
{ mtc with
round = mtc.round + 1
scorePlayer1 = mtc.scorePlayer1 + (match result with P1Win -> 1 | _ -> 0)
scorePlayer2 = mtc.scorePlayer2 + (match result with P2Win -> 1 | _ -> 0)
turn = WaitingForBoth
}
if mtc.round >= 3 then
let winnerScore = match result with P1Win -> mtc.scorePlayer1 | P2Win -> mtc.scorePlayer2 | Draw -> 0
let loserScore = match result with P1Win -> mtc.scorePlayer2 | P2Win -> mtc.scorePlayer1 | Draw -> 0
currentMatch <- None
do! ctx.CreateResponseAsync $"{winner} wins the match {winnerScore} to {loserScore}! Awarding a 100 genz!"
|> Async.AwaitTask
else
do! ctx.CreateResponseAsync message |> Async.AwaitTask
| Draw ->
currentMatch <- Some { mtc with turn = WaitingForBoth }
do! ctx.CreateResponseAsync $"{mtc.player1.Username} and {mtc.player2.Username} both did {p1m}. Round was a draw! Go again!"
|> Async.AwaitTask
| _ ->
if updatedTurn <> mtc.turn then
currentMatch <- Some { mtc with turn = updatedTurn }
let builder = DiscordInteractionResponseBuilder()
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder.WithContent(($"Move received by {ctx.Member.DisplayName}!")))
|> Async.AwaitTask
else
do! async{ return () }
| None , _ ->
do! ctx.CreateResponseAsync ("No match has been found, please use the '/start-match' command and mention the two players")
|> Async.AwaitTask
|> Async.Ignore
| _ , None ->
do! ctx.CreateResponseAsync $"Could not recognize move '{moveString}', please try again. Valid moves are 'rock', 'paper', or 'scissor'"
|> Async.AwaitTask
|> Async.Ignore
} |> Async.StartAsTask
:> Task
[<SlashCommand("status", "Status of the match")>]
member _.Status (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder.WithContent(sprintf "%A" currentMatch))
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let config = DiscordConfiguration()
config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk"
config.TokenType <- TokenType.Bot
let client = new DiscordClient(config)
let slash = client.UseSlashCommands();
slash.RegisterCommands<JoeBot>();
client.ConnectAsync ()
|> Async.AwaitTask
|> Async.RunSynchronously
Task.Delay(-1)
|> Async.AwaitTask
|> Async.RunSynchronously
client.DisconnectAsync ()
|> Async.AwaitTask
|> Async.RunSynchronously