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,12 +60,13 @@ module Game =
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
let executePlayerActionWithTargetId (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
let executePlayerActionWithTargetId defer (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Content"
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
if defer then
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
let! players =
[ tryFindPlayer (ctx.GetDiscordMember().Id)
tryFindPlayer targetId ]

View File

@ -211,7 +211,7 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
| id when id.StartsWith("Defend") -> handleDefense eventCtx
| id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task
| 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 {
let builder = DiscordInteractionResponseBuilder()

View File

@ -4,45 +4,137 @@ open System
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Messaging
type Move =
| Rock
| Paper
| Scissor
| Scissors
type RoundResult =
| P1Win
| P2Win
| 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
| Rock , Paper -> P2Win
| Rock , Scissor -> P1Win
| Rock , Scissors -> P1Win
| Paper , Rock -> P1Win
| Paper , Scissor -> P2Win
| Scissor , Rock -> P2Win
| Scissor , Paper -> 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 =
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
let buttons =
[ DiscordButtonComponent(ButtonStyle.Primary, $"RPS-rock-{defender.DiscordId}-{defender.Name}", "🪨 Rock")
DiscordButtonComponent(ButtonStyle.Primary, $"RPS-paper", "📜 Paper")
DiscordButtonComponent(ButtonStyle.Primary, $"RPS-scissors", "✂ Scissors") ]
|> Seq.cast<DiscordComponent>
let builder = DiscordFollowupMessageBuilder()
builder.AddComponents(buttons) |> ignore
builder.IsEphemeral <- true
do! ctx.FollowUp builder |> Async.AwaitTask
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() =
inherit ApplicationCommandModule ()

View File

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

View File

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