discord-bot-game/Bot/Games/RockPaperScissors.fs

156 lines
6.1 KiB
Forth

module Degenz.RockPaperScissors
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Messaging
type Move =
| Rock
| Paper
| Scissors
type RoundResult =
| P1Win
| P2Win
| Draw
let moveFromString = function
| "rock" -> Rock
| "paper" -> Paper
| "scissors" -> Scissors
| _ -> failwith "Unknown RPS move"
let getWinner p1m p2m =
match p1m , p2m with
| Rock , Paper -> P2Win
| Rock , Scissors -> P1Win
| Paper , Rock -> P1Win
| Paper , Scissors -> P2Win
| Scissors , Rock -> P2Win
| Scissors , Paper -> P1Win
| _ , _ -> Draw
let leftGif = function
| Rock -> "https://s10.gifyu.com/images/Sprite-0001614d8697f32165af.gif"
| Paper -> "https://s10.gifyu.com/images/Sprite-0001e7e8ac379d1c5112.gif"
| Scissors -> "https://s10.gifyu.com/images/Sprite-00012c83b50d768a2ed1.gif"
let rightGif = function
| Rock -> "https://s10.gifyu.com/images/Sprite-0001b4002047d9699732.gif"
| Paper -> "https://s10.gifyu.com/images/Sprite-00014072202347b8271e.gif"
| Scissors -> "https://s10.gifyu.com/images/Sprite-0001c8532b0538ed8706.gif"
let rpsEmbed disabled opponentMove opponent =
let buttonId move =
match opponentMove with
| Some m -> $"RPS-{move}-{opponent.DiscordId}-{opponent.Name}-True-{m}"
| None -> $"RPS-{move}-{opponent.DiscordId}-{opponent.Name}-False"
let buttons =
[ DiscordButtonComponent(ButtonStyle.Primary, buttonId "rock", "🪨 Rock", disabled)
DiscordButtonComponent(ButtonStyle.Primary, buttonId "paper", "📜 Paper", disabled)
DiscordButtonComponent(ButtonStyle.Primary, buttonId "scissors", "✂ Scissors", disabled) ]
|> Seq.cast<DiscordComponent>
let embed =
DiscordEmbedBuilder()
.WithTitle("Rock Paper Scissors")
.AddField("Opponent", opponent.Name)
.WithImageUrl("https://s10.gifyu.com/images/222e9b8f5459e5d3a7d.png")
( buttons , embed )
let matchResultsEmbed winner move1 move2 player1 player2 =
let firstEmbed =
DiscordEmbedBuilder()
.WithTitle($"{player1.Name}'s Move")
.WithImageUrl(leftGif move1)
let secondEmbed =
DiscordEmbedBuilder()
.WithTitle($"{player2.Name}'s Move")
.WithImageUrl(rightGif move2)
let description =
match winner with
| P1Win -> $"{player1.Name} wins with {move1}"
| P2Win -> $"{player2.Name} wins with {move2}"
| Draw -> $"Draw! Both players played {move1}"
let thirdEmbed =
DiscordEmbedBuilder()
.WithTitle($"Results")
.WithDescription(description)
[ firstEmbed ; secondEmbed ; thirdEmbed ]
let playRPS target ctx =
PlayerInteractions.executePlayerActionWithTarget target ctx (fun _ defender -> async {
let buttons , embed = rpsEmbed false None defender
let builder =
DiscordFollowupMessageBuilder()
.AddComponents(buttons)
.AddEmbed(embed)
.AsEphemeral(true)
do! ctx.FollowUp (builder) |> Async.AwaitTask
})
let handleRPS (ctx : IDiscordContext) =
let tokens = ctx.GetInteractionId().Split("-")
let move = tokens.[1]
let targetId = uint64 tokens.[2]
let isResponse = tokens.[4] = "True"
PlayerInteractions.executePlayerActionWithTargetId false targetId ctx (fun attacker defender -> async {
if isResponse then
let eventCtx = ctx.GetContext() :?> ComponentInteractionCreateEventArgs
let buttons , embed = rpsEmbed true None attacker
let builder =
DiscordInteractionResponseBuilder()
.AddComponents(buttons)
.AddEmbed(embed)
.AsEphemeral(true)
do! eventCtx.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask
let move1 = moveFromString move
let move2 = moveFromString tokens.[5]
let winner = getWinner move1 move2
let embeds = matchResultsEmbed winner move1 move2 attacker defender |> Seq.map (fun e -> e.Build())
let builder = DiscordMessageBuilder().AddEmbeds(embeds)
do! ctx.GetChannel().SendMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore
else
let eventCtx = ctx.GetContext() :?> ComponentInteractionCreateEventArgs
let buttons , embed = rpsEmbed true None attacker
let builder =
DiscordInteractionResponseBuilder()
.AddComponents(buttons)
.AddEmbed(embed)
.AsEphemeral(true)
do! eventCtx.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask
// do! sendFollowUpMessage ctx $"Sent challenge to {defender.Name}, wait till they respond"
let buttons , embed = rpsEmbed false (Some move) attacker
let builder =
DiscordMessageBuilder()
.WithContent($"<@{defender.DiscordId}>, you have been challenged to a match of Rock, Paper, Scissors. Pick your move!")
.AddComponents(buttons)
.AddEmbed(embed)
do! ctx.GetChannel().SendMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore
})
type RPSGame() =
inherit ApplicationCommandModule ()
let enforceChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) =
match ctx.GetChannel().Id with
| id when id = GuildEnvironment.channelArmory -> storeFn ctx
| _ ->
task {
let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy or sell weapons"
do! Messaging.sendSimpleResponse ctx msg
}
[<SlashCommand("rock-paper-scissors", "Steal some money from another player, but you might go to prison if caught")>]
member this.RPS (ctx : InteractionContext, [<Option("target", "Who do you want to play with?")>] target : DiscordUser) =
// enforceChannel (DiscordInteractionContext ctx) (steal target)
playRPS target (DiscordInteractionContext ctx)