From 58ab0b459f1a6ee27ab319b5d699741aa7f8731e Mon Sep 17 00:00:00 2001 From: Gabriel Moreira Mattos Date: Sat, 19 Oct 2024 01:34:02 -0300 Subject: [PATCH] wip: add jackpot logic --- .env.example | 1 + app/Entities/JackpotEntity.php | 179 +++++++++++++++ app/Entities/JackpotPlayerEntity.php | 51 +++++ app/Models/Jackpot.php | 20 ++ app/Models/JackpotPlayer.php | 27 +++ app/SlashCommands/JackpotCommand.php | 103 +++++++++ app/SlashCommands/JokenpoCommand.php | 4 +- .../Traits/Jackpot/PlayJackpot.php | 211 ++++++++++++++++++ .../Traits/Jokenpo/PlayJokenpo.php | 90 ++++---- ...2024_10_19_040320_create_jackpot_table.php | 27 +++ 10 files changed, 666 insertions(+), 47 deletions(-) create mode 100644 app/Entities/JackpotEntity.php create mode 100644 app/Entities/JackpotPlayerEntity.php create mode 100644 app/Models/Jackpot.php create mode 100644 app/Models/JackpotPlayer.php create mode 100644 app/SlashCommands/JackpotCommand.php create mode 100644 app/SlashCommands/Traits/Jackpot/PlayJackpot.php create mode 100644 database/migrations/2024_10_19_040320_create_jackpot_table.php diff --git a/.env.example b/.env.example index c9c822f..2e4ee5d 100644 --- a/.env.example +++ b/.env.example @@ -38,3 +38,4 @@ PICASSO_IMAGE_GENERATION_MODEL=dall-e-2 PICASSO_IMAGE_QTY=1 JOKENPO_TIMER=15 +JACKPOT_TIMER=15 diff --git a/app/Entities/JackpotEntity.php b/app/Entities/JackpotEntity.php new file mode 100644 index 0000000..8ea1801 --- /dev/null +++ b/app/Entities/JackpotEntity.php @@ -0,0 +1,179 @@ + 0, + 'pinguin' => 1, + 'borboleta' => 2, + 'macaco' => 3, + 'coruja' => 4 + ]; + + /** + * The game probabilities. + * + * @var array $probabilities + */ + public $probabilities = [ + 'zebra' => [0, 45], // 45% of chance to win + 'pinguin' => [45, 65], // 20% of chance to win + 'borboleta' => [65, 85], // 20% of chance to win + 'macaco' => [85, 95], // 10% of chance to win + 'coruja' => [95, 100] // 5% of chance to win + ]; + + /** + * The possible labels. + * + * @var array $labels + */ + public $labels = [ + 'zebra' => '🦓', + 'pinguin' => '🐧', + 'borboleta' => '🦋', + 'macaco' => '🐒', + 'coruja' => '🦉' + ]; + + /** + * The possible messages. + * + * @var array $messages + */ + public $messages = [ + 'zebra' => 'Vish, deu zebra! Perdeu suas coins! 💸', + 'pinguin' => 'Opa deu pinguin! Você manteve suas coins para o proximo jogo!', + 'borboleta' => 'Borboleta chegando dobrando suas coins! 💵💵', + 'macaco' => 'Macaco chegou pulando e com o rolex no pulso **triplicando suas coins**! 💰💰', + 'coruja' => '💲😎 Você conseguiu a maior recompensa! **Suas coins x4** 😎💲' + ]; + + /** + * The possible options. + * + * @var array $options + */ + public $options = ['pinguin', 'borboleta', 'macaco', 'coruja']; + + /** + * The game result. + * + * @var string $gameResult + */ + public $gameResult; + + /** + * Movements of all players + * @var JackpotPlayerEntity[] $players + */ + public $players = []; + + public function __construct(public int $gameId) {} + + /** + * Get the game ID. + * + * @return int + */ + public function getId() + { + return $this->gameId; + } + + /** + * Get the emoji for a bet. + * + * @param string $move + * @return string + */ + public function getEmoji(string $bet): string + { + return $this->labels[$bet]; + } + + /** + * Set the player's bet. + * + * @param JackpotPlayerEntity $player + * @return false|array + */ + public function setPlayerBet(JackpotPlayerEntity $player): bool + { + foreach ($this->players as $bet) { + if ($bet->getDiscordId() === $player->getDiscordId()) { + return false; + } + } + + $this->players[] = $player; + return true; + } + + /** + * Get the players. + * + * @return array + */ + public function getPlayers(): array + { + return $this->players; + } + + public function getPlayerMultiplier($player): string + { + $playerBet = $player->getBet(); + $gameMultiplier = $this->multipliers[$player->getBet()]; + return $playerBet * $gameMultiplier; + } + + public function getGameResult(): string + { + return $this->gameResult; + } + + public function getGameMultiplier(): string + { + return $this->multipliers[$this->gameResult]; + } + + /** + * Set the game result. + * + * @return string + */ + public function setGameResult() + { + $result = ''; + + // generate a random number between 0 and 99 + $randomNumber = mt_rand(0, 99); + + // loop through the probabilities and check if the random number is less than the probability + foreach ($this->probabilities as $label => $probability) { + if ($randomNumber >= $probability[0] && $randomNumber < $probability[1]) { + $result = $label; + } + } + + $this->gameResult = $result; + } + + public function getGameMessage(): string + { + return $this->messages[$this->gameResult]; + } +} diff --git a/app/Entities/JackpotPlayerEntity.php b/app/Entities/JackpotPlayerEntity.php new file mode 100644 index 0000000..fa8b05d --- /dev/null +++ b/app/Entities/JackpotPlayerEntity.php @@ -0,0 +1,51 @@ +discordId; + } + + /** + * Get the result of the game. + * + * @return string + */ + public function getResult() + { + return $this->result; + } + + /** + * Set the result of the game. + * + * @param string $result + */ + public function setResult($result) + { + $this->result = $result; + } +} diff --git a/app/Models/Jackpot.php b/app/Models/Jackpot.php new file mode 100644 index 0000000..559d73e --- /dev/null +++ b/app/Models/Jackpot.php @@ -0,0 +1,20 @@ +belongsTo(User::class, 'created_by'); + } +} diff --git a/app/Models/JackpotPlayer.php b/app/Models/JackpotPlayer.php new file mode 100644 index 0000000..0f38143 --- /dev/null +++ b/app/Models/JackpotPlayer.php @@ -0,0 +1,27 @@ +belongsTo(Jackpot::class, 'jackpot_id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } +} diff --git a/app/SlashCommands/JackpotCommand.php b/app/SlashCommands/JackpotCommand.php new file mode 100644 index 0000000..6422544 --- /dev/null +++ b/app/SlashCommands/JackpotCommand.php @@ -0,0 +1,103 @@ +setGame($game); + $this->setCounter($game, env('JACKPOT_TIMER', 30)); + + $interaction->respondWithMessage( + $this->buildGameMessage($game) + )->then(fn() => $this->startCounter($interaction, $game)); + } + + /** + * The command interaction routes. + */ + public function interactions(): array + { + return [ + 'action:{gameId}' => fn(Interaction $interaction, int $gameId) => $this->playJackpot($interaction, $gameId), + ]; + } +} diff --git a/app/SlashCommands/JokenpoCommand.php b/app/SlashCommands/JokenpoCommand.php index fd36db6..b91e0aa 100644 --- a/app/SlashCommands/JokenpoCommand.php +++ b/app/SlashCommands/JokenpoCommand.php @@ -114,10 +114,10 @@ public function handle($interaction) /** * The command interaction routes. */ - public function interactions() : array + public function interactions(): array { return [ - 'action:{type}:{gameId}' => fn (Interaction $interaction, string $type, int $gameId) => $this->playJokenpo($interaction, $type, $gameId), + 'action:{type}:{gameId}' => fn(Interaction $interaction, string $type, int $gameId) => $this->playJokenpo($interaction, $type, $gameId), ]; } } diff --git a/app/SlashCommands/Traits/Jackpot/PlayJackpot.php b/app/SlashCommands/Traits/Jackpot/PlayJackpot.php new file mode 100644 index 0000000..e7bad25 --- /dev/null +++ b/app/SlashCommands/Traits/Jackpot/PlayJackpot.php @@ -0,0 +1,211 @@ +games[$game->getId()] = $game; + } + + /** + * @param int $gameId + * @return JackpotEntity + */ + public function getGame(int $gameId): JackpotEntity + { + return $this->games[$gameId]; + } + + /** + * @param int $gameId + * @return bool + */ + public function hasGame(int $gameId): bool + { + return isset($this->games[$gameId]); + } + + /** + * @param JackpotEntity $game + * @return void + */ + public function updateGame(JackpotEntity $game): void + { + $this->games[$game->getId()] = $game; + } + + /** + * @param int $gameId + * @return void + */ + public function deleteGame(int $gameId): void + { + unset($this->games[$gameId]); + } + + /** + * @param int $gameId + * @return int + */ + public function setCounter(JackpotEntity $game, int $counter): void + { + $this->counters[$game->getId()] = $counter; + } + + /** + * @param JackpotEntity $game + * @return int + */ + public function getCounter(JackpotEntity $game): int + { + return $this->counters[$game->getId()]; + } + + /** + * @param int $gameId + * @return void + */ + public function decreaseCounter(int $gameId): void + { + $this->counters[$gameId]--; + } + + /** + * @param int $gameId + * @return void + */ + public function deleteCounter(int $gameId): void + { + unset($this->counters[$gameId]); + } + + /** + * @param Interaction $interaction + * @return void + */ + public function startCounter(Interaction $interaction, JackpotEntity $game): void + { + $timer = $this->bot->getLoop()->addPeriodicTimer(1.5, function () use (&$timer, $interaction, $game) { + $counter = $this->getCounter($game); + $game = $this->getGame($game->getId()); + + if ($counter === 0) { + $this->bot->getLoop()->cancelTimer($timer); + $game->setGameResult(); + + $interaction->updateOriginalResponse( + $this->buildGameResults($game) + ); + return; + } + + $interaction->updateOriginalResponse( + $this->buildGameMessage($game) + ); + + $this->decreaseCounter($game->getId()); + }); + } + + /** + * @param \Discord\Parts\Interactions\Interaction $interaction + * @param string $type + * @param int $gameId + * @return void + */ + public function playJackpot(Interaction $interaction, int $gameId): void + { + $discordId = $interaction->member->user->id; + $game = $this->getGame($gameId); + $counter = $this->getCounter($game); + + if ($counter <= 1) { + $interaction->respondWithMessage( + $this + ->message('O tempo para jogar acabou!') + ->build(), + true + ); + return; + } + + $playerBet = $game->setPlayerBet(new JackpotPlayerEntity($discordId)); + + if (!$playerBet) { + $interaction->respondWithMessage( + $this + ->message('Você já fez sua jogada!') + ->build(), + true + ); + return; + } + + $this->updateGame($game); + + $interaction->updateMessage( + $this->buildGameMessage($game) + ); + } + + /** + * @return \Discord\Builders\MessageBuilder + */ + public function buildGameMessage(JackpotEntity $game): MessageBuilder + { + $players = implode("\n", array_map(fn($player) => sprintf("<@%s>", $player->getDiscordId()), $game->getPlayers())); + + $counter = $this->getCounter($game); + $id = $game->getId(); + + return $this + ->message("Custo: :coin: 200\nBora jogar que o tempo ja vai acabar!\n\n⏰ **$counter**") + ->title("JACKPOT! ($id)") + ->button("Jogar", route: "action:$id", style: 'primary') + ->fields([ + 'Jogador' => $players, + ]) + ->build(); + } + + /** + * @return \Discord\Builders\MessageBuilder + */ + public function buildGameResults(JackpotEntity $game): MessageBuilder + { + $message = $game->getGameMessage(); + $players = implode("\n", array_map(fn($player) => sprintf("<@%s>", $player->getDiscordId()), $game->getPlayers())); + $id = $game->getId(); + + $this->deleteGame($id); + $this->deleteCounter($id); + + $emoji = $game->getEmoji($game->getGameResult()); + $label = ucwords($game->getGameResult()); + $multiplier = $game->getGameMultiplier() * 200; + + return $this + ->message("**$emoji $label**\n$message\n\nCusto: :coin: 200\nResultado: :coin: $multiplier\n\n") + ->title("Jackpot! ($id)") + ->fields([ + 'Jogador' => $players, + ]) + ->build(); + } +} diff --git a/app/SlashCommands/Traits/Jokenpo/PlayJokenpo.php b/app/SlashCommands/Traits/Jokenpo/PlayJokenpo.php index c8334e0..4edd0a3 100644 --- a/app/SlashCommands/Traits/Jokenpo/PlayJokenpo.php +++ b/app/SlashCommands/Traits/Jokenpo/PlayJokenpo.php @@ -23,7 +23,7 @@ trait PlayJokenpo * @param JokenpoEntity $game * @return void */ - public function setGame(JokenpoEntity $game) : void + public function setGame(JokenpoEntity $game): void { $this->games[$game->getId()] = $game; } @@ -32,7 +32,7 @@ public function setGame(JokenpoEntity $game) : void * @param int $gameId * @return JokenpoEntity */ - public function getGame(int $gameId) : JokenpoEntity + public function getGame(int $gameId): JokenpoEntity { return $this->games[$gameId]; } @@ -41,7 +41,7 @@ public function getGame(int $gameId) : JokenpoEntity * @param int $gameId * @return bool */ - public function hasGame(int $gameId) : bool + public function hasGame(int $gameId): bool { return isset($this->games[$gameId]); } @@ -50,7 +50,7 @@ public function hasGame(int $gameId) : bool * @param JokenpoEntity $game * @return void */ - public function updateGame(JokenpoEntity $game) : void + public function updateGame(JokenpoEntity $game): void { $this->games[$game->getId()] = $game; } @@ -59,7 +59,7 @@ public function updateGame(JokenpoEntity $game) : void * @param int $gameId * @return void */ - public function deleteGame(int $gameId) : void + public function deleteGame(int $gameId): void { unset($this->games[$gameId]); } @@ -68,7 +68,7 @@ public function deleteGame(int $gameId) : void * @param int $gameId * @return int */ - public function setCounter(JokenpoEntity $game, int $counter) : void + public function setCounter(JokenpoEntity $game, int $counter): void { $this->counters[$game->getId()] = $counter; } @@ -77,7 +77,7 @@ public function setCounter(JokenpoEntity $game, int $counter) : void * @param JokenpoEntity $game * @return int */ - public function getCounter(JokenpoEntity $game) : int + public function getCounter(JokenpoEntity $game): int { return $this->counters[$game->getId()]; } @@ -86,7 +86,7 @@ public function getCounter(JokenpoEntity $game) : int * @param int $gameId * @return void */ - public function decreaseCounter(int $gameId) : void + public function decreaseCounter(int $gameId): void { $this->counters[$gameId]--; } @@ -95,7 +95,7 @@ public function decreaseCounter(int $gameId) : void * @param int $gameId * @return void */ - public function deleteCounter(int $gameId) : void + public function deleteCounter(int $gameId): void { unset($this->counters[$gameId]); } @@ -104,7 +104,7 @@ public function deleteCounter(int $gameId) : void * @param Interaction $interaction * @return void */ - public function startCounter(Interaction $interaction, JokenpoEntity $game) : void + public function startCounter(Interaction $interaction, JokenpoEntity $game): void { $timer = $this->bot->getLoop()->addPeriodicTimer(1.5, function () use (&$timer, $interaction, $game) { $counter = $this->getCounter($game); @@ -139,7 +139,7 @@ public function startCounter(Interaction $interaction, JokenpoEntity $game) : vo * @param int $gameId * @return void */ - public function playJokenpo(Interaction $interaction, string $type, int $gameId) : void + public function playJokenpo(Interaction $interaction, string $type, int $gameId): void { $discordId = $interaction->member->user->id; $userCoinHistoryRepository = new UserCoinHistoryRepository; @@ -150,8 +150,8 @@ public function playJokenpo(Interaction $interaction, string $type, int $gameId) if ($counter <= 1) { $interaction->respondWithMessage( $this - ->message('O tempo para jogar acabou!') - ->build(), + ->message('O tempo para jogar acabou!') + ->build(), true ); return; @@ -160,8 +160,8 @@ public function playJokenpo(Interaction $interaction, string $type, int $gameId) if ($userCoinHistoryRepository->hasAvailableCoins($discordId, 200.0) === false) { $interaction->respondWithMessage( $this - ->message('Você não tem coins suficientes para jogar!') - ->build(), + ->message('Você não tem coins suficientes para jogar!') + ->build(), true ); return; @@ -172,8 +172,8 @@ public function playJokenpo(Interaction $interaction, string $type, int $gameId) if (!$playerMove) { $interaction->respondWithMessage( $this - ->message('Você já fez sua jogada!') - ->build(), + ->message('Você já fez sua jogada!') + ->build(), true ); return; @@ -206,31 +206,31 @@ public function playJokenpo(Interaction $interaction, string $type, int $gameId) /** * @return \Discord\Builders\MessageBuilder */ - public function buildGameMessage(JokenpoEntity $game) : MessageBuilder + public function buildGameMessage(JokenpoEntity $game): MessageBuilder { $players = implode("\n", array_map(fn($player) => sprintf("<@%s>", $player->getDiscordId()), $game->getPlayers())); $choices = implode("\n", array_map(fn($player) => sprintf("%s %s", $game->getEmoji($player->getMove()), ucwords($player->getMove())), $game->getPlayers())); return $this - ->message(sprintf("Ado, ado, ado quem perder é...ruim!\n\nCusto: :coin: 200\nPrêmio: :coin: 400\n\n⏰: **%s**", $this->getCounter($game))) - ->title(sprintf('JO-KEN-PÔ! (%s)', $game->getId())) - ->image(config('butecobot.images.jokenpo')) - ->fields([ - 'Jogador' => $players, - 'Escolheu' => $choices, - ]) - ->button("Pedra", route: 'action:pedra:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('pedra')) - ->button("Papel", route: 'action:papel:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('papel')) - ->button("Tesoura", route: 'action:tesoura:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('tesoura')) - ->button("Lagarto", route: 'action:lagarto:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('lagarto')) - ->button("Spock", route: 'action:spock:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('spock')) - ->build(); + ->message(sprintf("Ado, ado, ado quem perder é...ruim!\n\nCusto: :coin: 200\nPrêmio: :coin: 400\n\n⏰: **%s**", $this->getCounter($game))) + ->title(sprintf('JO-KEN-PÔ! (%s)', $game->getId())) + ->image(config('butecobot.images.jokenpo')) + ->fields([ + 'Jogador' => $players, + 'Escolheu' => $choices, + ]) + ->button("Pedra", route: 'action:pedra:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('pedra')) + ->button("Papel", route: 'action:papel:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('papel')) + ->button("Tesoura", route: 'action:tesoura:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('tesoura')) + ->button("Lagarto", route: 'action:lagarto:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('lagarto')) + ->button("Spock", route: 'action:spock:' . $game->getId(), style: 'secondary', emoji: $game->getEmoji('spock')) + ->build(); } /** * @return \Discord\Builders\MessageBuilder */ - public function buildGameResults(JokenpoEntity $game) : MessageBuilder + public function buildGameResults(JokenpoEntity $game): MessageBuilder { $userCoinHistoryRepository = new UserCoinHistoryRepository; $userRepository = new UserRepository; @@ -269,18 +269,18 @@ public function buildGameResults(JokenpoEntity $game) : MessageBuilder $this->deleteCounter($game->getId()); return $this - ->message( - sprintf( - "Eu escolhi: **%s**\n\nCusto: :coin: 200\nPrêmio: :coin: 400\n\n", - sprintf("%s %s", $game->getEmoji($game->getBotMove()), ucwords($game->getBotMove())) + ->message( + sprintf( + "Eu escolhi: **%s**\n\nCusto: :coin: 200\nPrêmio: :coin: 400\n\n", + sprintf("%s %s", $game->getEmoji($game->getBotMove()), ucwords($game->getBotMove())) + ) ) - ) - ->title(sprintf('JO-KEN-PÔ! (%s)', $game->getId())) - ->fields([ - 'Jogador' => $players, - 'Escolheu' => $choices, - 'Resultado' => $results, - ]) - ->build(); + ->title(sprintf('JO-KEN-PÔ! (%s)', $game->getId())) + ->fields([ + 'Jogador' => $players, + 'Escolheu' => $choices, + 'Resultado' => $results, + ]) + ->build(); } -} \ No newline at end of file +} diff --git a/database/migrations/2024_10_19_040320_create_jackpot_table.php b/database/migrations/2024_10_19_040320_create_jackpot_table.php new file mode 100644 index 0000000..b86596b --- /dev/null +++ b/database/migrations/2024_10_19_040320_create_jackpot_table.php @@ -0,0 +1,27 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jackpot'); + } +};