Skip to content

Commit

Permalink
Snailrace Core (#30)
Browse files Browse the repository at this point in the history
* feat: Setting up snailrace core

* feat: More work on core

* feat: Untested Snailrace Core Package

* feat: Base renderer for snailrace state

* feat: Attempting to allow people to join via application command

* feat: Snailrace Join a hosted Race working

* feat: Snailrace Place a Quickbet

* feat: Snailrace Racing working

* feat: Racing finished, payouts, cancelled races and tweaked track display. Almost ready to be merged.

* feat: Altered Race Mechanics and Trading Cards Implemented

* refactor: Moved common snail usage const to the App file

* fix: The use card returns a specific error when there is no usages left
  • Loading branch information
lcox74 authored Apr 26, 2024
1 parent 9390e5d commit 808cc79
Show file tree
Hide file tree
Showing 28 changed files with 2,226 additions and 13 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ WORKDIR /app
COPY . .

# Download the dependencies
RUN go mod download
# RUN go mod download

# Build the application
RUN CGO_ENABLED=0 go build -o tony .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o tony .

FROM scratch

Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ docker build -t tony .
sudo docker run \
--env-file .env \
--network tony-network \
tony \
\
tony
```

Ensure your `.env` file includes the necessary credentials:
Expand Down
16 changes: 14 additions & 2 deletions applications/blackjack/app.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package blackjackApp
package blackjack_app

import (
"slices"
Expand Down Expand Up @@ -178,7 +178,19 @@ func OnJoin(ctx framework.EventContext) {
}

// Charge the user's balance
wallet.Debit(ctx.Database(), ctx.GetUser().ID, int64(betInt), "Blackjack bet", "blackjack")
err = wallet.Debit(ctx.Database(), ctx.GetUser().ID, int64(betInt), "Blackjack bet", "blackjack")
if err != nil {
// You can react to button presses with no data and it doesn't error or send a message
ctx.Logger().WithError(err).Error("Failed to charge user")
ctx.Session().InteractionRespond(ctx.Interaction(), &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "**Error**: " + err.Error(),
},
})
return
}

// You can react to button presses with no data and it doesn't error or send a message
ctx.Session().InteractionRespond(ctx.Interaction(), &discordgo.InteractionResponse{
Expand Down
2 changes: 1 addition & 1 deletion applications/blackjack/render.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package blackjackApp
package blackjack_app

import (
"fmt"
Expand Down
2 changes: 1 addition & 1 deletion applications/blackjack/trading_cards.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package blackjackApp
package blackjack_app

import (
"github.com/aussiebroadwan/tony/pkg/blackjack"
Expand Down
48 changes: 48 additions & 0 deletions applications/snailrace/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package snailrace_app

import (
"github.com/aussiebroadwan/tony/framework"
"github.com/aussiebroadwan/tony/pkg/snailrace"
"github.com/bwmarrin/discordgo"
)

const CommonSnailUsage = 25

func RegisterSnailraceApp(bot *framework.Bot) framework.Route {
return framework.NewRoute(bot, "snailrace",
// snailrace
&Snailrace{}, // [NOP]

framework.NewRoute(bot, "host", &SnailraceHostSubCommand{}),
framework.NewRoute(bot, "bet", &SnailraceBetSubCommand{}),
)
}

type Snailrace struct {
framework.ApplicationCommand
framework.ApplicationMountable
}

func (s Snailrace) GetType() framework.AppType {
return framework.AppTypeCommand | framework.AppTypeMountable
}

func (s Snailrace) OnMount(ctx framework.MountContext) {
snailrace.SetupSnailraceDB(ctx.Database())
}

func (s Snailrace) GetDefinition() *discordgo.ApplicationCommand {
return &discordgo.ApplicationCommand{
Name: "snailrace",
Description: "Let's race snails!",
Options: []*discordgo.ApplicationCommandOption{
{
Type: discordgo.ApplicationCommandOptionSubCommand,
Name: "host",
Description: "Host a snailrace",
},
},
}
}

func (s Snailrace) OnCommand(ctx framework.CommandContext) {}
42 changes: 42 additions & 0 deletions applications/snailrace/render/bet_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package render

import (
"fmt"
"time"

"github.com/aussiebroadwan/tony/pkg/snailrace"
"github.com/bwmarrin/discordgo"
"github.com/sirupsen/logrus"
)

// bettingMessage generates the message and componenets required for the
// betting stage of a race.
func bettingMessage(state snailrace.RaceState) (string, []discordgo.MessageComponent) {
description := fmt.Sprintf(
"Bets are now open to everyone, do you feel lucky? To place a quick bet you can select the snail via the drop down. \n\n```\nRace ID: %s\nStarting: %s\n\nEntrants:\n",
state.Race.Id,
state.Race.StartAt.Format(time.DateTime),
)

menuOptions := make([]discordgo.SelectMenuOption, len(state.Snails))
for index, snail := range state.Snails {
logrus.Infof("Calculating odds for snail %s, race_pool %d, pool %d", snail.Name, state.Race.Pool, state.Race.Snails[index].Pool)
odds := snailrace.CalculateOdds(state.Race.Pool, state.Race.Snails[index].Pool)
description += fmt.Sprintf("[%d]: %s @ %.02f\n", index, snail.Name, odds)

menuOptions[index] = discordgo.SelectMenuOption{
Label: fmt.Sprintf("%s @ %.02f", snail.Name, odds),
Value: fmt.Sprintf("%d", index),
Default: false,
}
}
description += "```"

return description, []discordgo.MessageComponent{
discordgo.SelectMenu{
CustomID: "snailrace.bet:win_request:" + state.Race.Id,
Placeholder: "Select a Quickbet",
Options: menuOptions,
},
}
}
20 changes: 20 additions & 0 deletions applications/snailrace/render/cancel_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package render

import (
"github.com/aussiebroadwan/tony/pkg/snailrace"
"github.com/bwmarrin/discordgo"
)

func cancelledMessage(state snailrace.RaceState) (string, []discordgo.MessageComponent) {

description := "Race has been cancelled due to not enough players.\n"

return description, []discordgo.MessageComponent{
discordgo.Button{
Label: "Concluded",
Disabled: true,
Style: discordgo.SuccessButton,
CustomID: "snailrace.host:" + state.Race.Id,
},
}
}
51 changes: 51 additions & 0 deletions applications/snailrace/render/finish_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package render

import (
"fmt"

"github.com/aussiebroadwan/tony/pkg/snailrace"
"github.com/bwmarrin/discordgo"
)

func finishedMessage(state snailrace.RaceState, creditUser func(string, int64)) (string, []discordgo.MessageComponent) {

description := fmt.Sprintf("```\nRace ID: %s\n\n%s\n", state.Race.Id, buildTrack(state))
entrants := "Results:\n"

// Display the results
for index, snail := range state.Snails {
entrants += fmt.Sprintf("[%d]: %s ", index, snail.Name)
if place, ok := state.Place[index]; ok {
switch place {
case 1:
entrants += "🥇"
case 2:
entrants += "🥈"
case 3:
entrants += "🥉"
}
}
entrants += "\n"
}
description += entrants + "```"

// Payout the winners
for _, userBet := range state.Race.UserBets {
if place, ok := state.Place[userBet.SnailIndex]; ok {
if place == 1 {
odds := snailrace.CalculateOdds(state.Race.Pool, state.Race.Snails[userBet.SnailIndex].Pool)
win := int64(float64(userBet.Amount) * odds)
creditUser(userBet.UserId, win)
}
}
}

return description, []discordgo.MessageComponent{
discordgo.Button{
Label: "Concluded",
Disabled: true,
Style: discordgo.SuccessButton,
CustomID: "snailrace.host:" + state.Race.Id,
},
}
}
36 changes: 36 additions & 0 deletions applications/snailrace/render/join_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package render

import (
"fmt"
"time"

"github.com/aussiebroadwan/tony/pkg/snailrace"
"github.com/bwmarrin/discordgo"
)

// joinMessage generates the join stage message and components.
func joinMessage(state snailrace.RaceState) (string, []discordgo.MessageComponent) {
description := fmt.Sprintf(
"A new race has been hosted!\n\nRace ID: `%s`\nStarting: `%s`\n\n"+
"Click the `Join` button to join with your own snail.\n\n",
state.Race.Id,
state.Race.StartAt.Format(time.DateTime),
)

if len(state.Snails) == 0 {
description += "> No snails have joined yet\n"
} else {
description += "**Entrants:**\n"
for _, snail := range state.Snails {
description += fmt.Sprintf("- %s <@%s>\n", snail.Name, snail.OwnerId)
}
}

return description, []discordgo.MessageComponent{
discordgo.Button{
Label: "Join",
Style: discordgo.SuccessButton,
CustomID: "snailrace.host:join_request:" + state.Race.Id,
},
}
}
29 changes: 29 additions & 0 deletions applications/snailrace/render/race_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package render

import (
"fmt"

"github.com/aussiebroadwan/tony/pkg/snailrace"
"github.com/bwmarrin/discordgo"
)

func raceMessage(state snailrace.RaceState) (string, []discordgo.MessageComponent) {

description := fmt.Sprintf("```\nRace ID: %s\n\n%s\n", state.Race.Id, buildTrack(state))
entrants := "Entrants:\n"

for index, snail := range state.Snails {
odds := snailrace.CalculateOdds(state.Race.Pool, state.Race.Snails[index].Pool)
entrants += fmt.Sprintf("[%d]: %s @ %.02f\n", index, snail.Name, odds)
}
description += entrants + "```"

return description, []discordgo.MessageComponent{
discordgo.Button{
Label: "Racing",
Disabled: true,
Style: discordgo.SuccessButton,
CustomID: "snailrace.host:" + state.Race.Id,
},
}
}
Loading

0 comments on commit 808cc79

Please sign in to comment.