Rock Paper Scissors

This commit is contained in:
Joseph Ferano 2022-02-14 12:01:04 +07:00
parent 537145dae6
commit 9f756540f1
5 changed files with 119 additions and 23 deletions

View File

@ -60,11 +60,12 @@ module Game =
else do! Messaging.sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command" else do! Messaging.sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
} |> Async.StartAsTask :> Task } |> Async.StartAsTask :> Task
let executePlayerActionWithTargetId (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) = let executePlayerActionWithTargetId defer (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
async { async {
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true builder.IsEphemeral <- true
builder.Content <- "Content" builder.Content <- "Content"
if defer then
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
let! players = let! players =
[ tryFindPlayer (ctx.GetDiscordMember().Id) [ tryFindPlayer (ctx.GetDiscordMember().Id)

View File

@ -211,7 +211,7 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
| id when id.StartsWith("Defend") -> handleDefense eventCtx | id when id.StartsWith("Defend") -> handleDefense eventCtx
| id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task
| id when id.StartsWith("Steal") -> Thief.handleSteal eventCtx | id when id.StartsWith("Steal") -> Thief.handleSteal eventCtx
| id when id.StartsWith("Steal") -> RockPaperScissors.handleRPS eventCtx | id when id.StartsWith("RPS") -> RockPaperScissors.handleRPS eventCtx
| _ -> | _ ->
task { task {
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()

View File

@ -4,45 +4,137 @@ open System
open System.Threading.Tasks open System.Threading.Tasks
open DSharpPlus open DSharpPlus
open DSharpPlus.Entities open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
open Degenz.Messaging open Degenz.Messaging
type Move = type Move =
| Rock | Rock
| Paper | Paper
| Scissor | Scissors
type RoundResult = type RoundResult =
| P1Win | P1Win
| P2Win | P2Win
| Draw | Draw
let player1Won p1m p2m = let moveFromString = function
| "rock" -> Rock
| "paper" -> Paper
| "scissors" -> Scissors
| _ -> failwith "Unknown RPS move"
let getWinner p1m p2m =
match p1m , p2m with match p1m , p2m with
| Rock , Paper -> P2Win | Rock , Paper -> P2Win
| Rock , Scissor -> P1Win | Rock , Scissors -> P1Win
| Paper , Rock -> P1Win | Paper , Rock -> P1Win
| Paper , Scissor -> P2Win | Paper , Scissors -> P2Win
| Scissor , Rock -> P2Win | Scissors , Rock -> P2Win
| Scissor , Paper -> P1Win | Scissors , Paper -> P1Win
| _ , _ -> Draw | _ , _ -> Draw
let playRPS target ctx = let leftGif = function
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async { | 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 = let buttons =
[ DiscordButtonComponent(ButtonStyle.Primary, $"RPS-rock-{defender.DiscordId}-{defender.Name}", "🪨 Rock") [ DiscordButtonComponent(ButtonStyle.Primary, buttonId "rock", "🪨 Rock", disabled)
DiscordButtonComponent(ButtonStyle.Primary, $"RPS-paper", "📜 Paper") DiscordButtonComponent(ButtonStyle.Primary, buttonId "paper", "📜 Paper", disabled)
DiscordButtonComponent(ButtonStyle.Primary, $"RPS-scissors", "✂ Scissors") ] DiscordButtonComponent(ButtonStyle.Primary, buttonId "scissors", "✂ Scissors", disabled) ]
|> Seq.cast<DiscordComponent> |> Seq.cast<DiscordComponent>
let builder = DiscordFollowupMessageBuilder() let embed =
builder.AddComponents(buttons) |> ignore DiscordEmbedBuilder()
builder.IsEphemeral <- true .WithTitle("Rock Paper Scissors")
.AddField("Opponent", opponent.Name)
.WithImageUrl("https://s10.gifyu.com/images/222e9b8f5459e5d3a7d.png")
( buttons , embed )
do! ctx.FollowUp builder |> Async.AwaitTask 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 =
Game.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 = async.Zero() |> Async.StartAsTask let handleRPS (ctx : IDiscordContext) =
let split = ctx.GetInteractionId().Split("-")
let move = split.[1]
let targetId = uint64 split.[2]
let isResponse = split.[4] = "True"
Game.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 split.[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() = type RPSGame() =
inherit ApplicationCommandModule () inherit ApplicationCommandModule ()

View File

@ -171,7 +171,7 @@ let handleSteal (ctx : IDiscordContext) =
} }
if answer = "yes" then if answer = "yes" then
let targetId = uint64 split.[2] let targetId = uint64 split.[2]
Game.executePlayerActionWithTargetId targetId ctx (fun attacker defender -> async { Game.executePlayerActionWithTargetId true targetId ctx (fun attacker defender -> async {
do! attacker do! attacker
|> Player.removeExpiredActions false |> Player.removeExpiredActions false
|> checkVictimStealingCooldown defender |> checkVictimStealingCooldown defender

View File

@ -129,6 +129,7 @@ module Messaging =
abstract member GetGuild : unit -> DiscordGuild abstract member GetGuild : unit -> DiscordGuild
abstract member GetInteractionId : unit -> string abstract member GetInteractionId : unit -> string
abstract member GetChannel : unit -> DiscordChannel abstract member GetChannel : unit -> DiscordChannel
abstract member GetContext : unit -> obj
type DiscordInteractionContext(ctx : InteractionContext) = type DiscordInteractionContext(ctx : InteractionContext) =
interface IDiscordContext with interface IDiscordContext with
@ -144,6 +145,7 @@ module Messaging =
member this.GetGuild() = ctx.Guild member this.GetGuild() = ctx.Guild
member this.GetInteractionId() = string ctx.InteractionId member this.GetInteractionId() = string ctx.InteractionId
member this.GetChannel() = ctx.Channel member this.GetChannel() = ctx.Channel
member this.GetContext() = ctx
type DiscordEventContext(ctx : ComponentInteractionCreateEventArgs) = type DiscordEventContext(ctx : ComponentInteractionCreateEventArgs) =
interface IDiscordContext with interface IDiscordContext with
@ -159,6 +161,7 @@ module Messaging =
member this.GetGuild() = ctx.Guild member this.GetGuild() = ctx.Guild
member this.GetInteractionId() = ctx.Id member this.GetInteractionId() = ctx.Id
member this.GetChannel() = ctx.Channel member this.GetChannel() = ctx.Channel
member this.GetContext() = ctx
let getTimeText isCooldown (timespan : TimeSpan) timestamp = let getTimeText isCooldown (timespan : TimeSpan) timestamp =
let span = let span =