Skip to content

Commit 1667024

Browse files
Add the /blackjack command
1 parent 0b1ea61 commit 1667024

6 files changed

Lines changed: 417 additions & 4 deletions

File tree

src/main/java/technobot/commands/CommandRegistry.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.jetbrains.annotations.NotNull;
1313
import technobot.TechnoBot;
1414
import technobot.commands.automation.AutoRoleCommand;
15+
import technobot.commands.casino.BlackjackCommand;
1516
import technobot.commands.economy.*;
1617
import technobot.commands.fun.*;
1718
import technobot.commands.greetings.FarewellCommand;
@@ -52,6 +53,9 @@ public class CommandRegistry extends ListenerAdapter {
5253
*/
5354
public CommandRegistry(TechnoBot bot) {
5455
mapCommand(
56+
//Casino commands
57+
new BlackjackCommand(bot),
58+
5559
//Automation commands
5660
new AutoRoleCommand(bot),
5761

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
package technobot.commands.casino;
2+
3+
import net.dv8tion.jda.api.EmbedBuilder;
4+
import net.dv8tion.jda.api.entities.Guild;
5+
import net.dv8tion.jda.api.entities.MessageEmbed;
6+
import net.dv8tion.jda.api.entities.User;
7+
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
8+
import net.dv8tion.jda.api.interactions.commands.OptionType;
9+
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
10+
import net.dv8tion.jda.api.interactions.components.buttons.Button;
11+
import technobot.TechnoBot;
12+
import technobot.commands.Category;
13+
import technobot.commands.Command;
14+
import technobot.data.GuildData;
15+
import technobot.data.cache.Economy;
16+
import technobot.handlers.economy.EconomyHandler;
17+
import technobot.listeners.ButtonListener;
18+
import technobot.util.embeds.EmbedColor;
19+
import technobot.util.embeds.EmbedUtils;
20+
import technobot.util.enums.Cards;
21+
22+
import java.util.*;
23+
24+
/**
25+
* Command that plays a game of blackjack.
26+
*
27+
* @author TechnoVision
28+
*/
29+
public class BlackjackCommand extends Command {
30+
31+
public static final String CARDBACK_EMOJI = "<:cardback:992575657320140801>";
32+
public static final Map<String, Blackjack> games = new HashMap<>();
33+
public static final Stack<Cards> deck = new Stack<>();
34+
35+
public BlackjackCommand(TechnoBot bot) {
36+
super(bot);
37+
this.name = "blackjack";
38+
this.description = "Play a game of blackjack.";
39+
this.category = Category.CASINO;
40+
this.args.add(new OptionData(OptionType.INTEGER, "bet", "The amount you want to wager", true).setMinValue(1));
41+
42+
// Setup deck of cards
43+
for (Cards card : Cards.values()) {
44+
deck.push(card);
45+
}
46+
}
47+
48+
@Override
49+
public void execute(SlashCommandInteractionEvent event) {
50+
// Charge player for bet
51+
User user = event.getUser();
52+
long bet = event.getOption("bet").getAsLong();
53+
EconomyHandler economyHandler = GuildData.get(event.getGuild()).economyHandler;
54+
long balance = economyHandler.getBalance(user.getIdLong());
55+
if (balance < bet) {
56+
String currency = economyHandler.getCurrency() + " **" + balance + "**";
57+
String text = "You don't have enough money for this bet. You currently have " + currency + " in cash.";
58+
event.replyEmbeds(EmbedUtils.createError(text)).setEphemeral(true).queue();
59+
return;
60+
}
61+
economyHandler.removeMoney(user.getIdLong(), bet);
62+
63+
// Shuffle deck
64+
Stack<Cards> shuffledDeck = (Stack<Cards>) deck.clone();
65+
Collections.shuffle(shuffledDeck);
66+
67+
// Create new blackjack game
68+
Cards dealerCard = shuffledDeck.pop();
69+
List<Cards> playerHand = new ArrayList<>();
70+
playerHand.add(shuffledDeck.pop());
71+
playerHand.add(shuffledDeck.pop());
72+
games.put(user.getId(), new Blackjack(shuffledDeck, dealerCard, playerHand));
73+
74+
// Send embed with buttons
75+
int score = calculateValue(playerHand);
76+
MessageEmbed embed = getEmbed(user, score).build();
77+
String uuid = user.getId() + ":" + UUID.randomUUID();
78+
List<Button> buttons = List.of(Button.primary("blackjack:hit:"+uuid+":"+bet, "Hit"), Button.secondary("blackjack:stand:"+uuid+":"+bet, "Stand"));
79+
ButtonListener.buttons.put(uuid, buttons);
80+
event.replyEmbeds(embed).addActionRow(buttons).queue(interactionHook -> ButtonListener.disableButtons(uuid, interactionHook));
81+
}
82+
83+
/**
84+
* Stores and represents a blackjack game.
85+
*
86+
* @param deck the shuffled deck of cards to draw from.
87+
* @param dealerCard the card that the dealer has revealed.
88+
* @param playerHand List of cards that the player has.
89+
*/
90+
public record Blackjack(Stack<Cards> deck, Cards dealerCard, List<Cards> playerHand) {
91+
92+
/**
93+
* Draws a card from the deck and adds it to the player's hand.
94+
*/
95+
public void hit() { playerHand.add(deck.pop()); }
96+
}
97+
98+
/**
99+
* Create a blackjack embed with current hand and score.
100+
*
101+
* @param user the user playing blackjack.
102+
* @param score the current score of the player's hand.
103+
* @return a MessageEmbed for the blackjack game.
104+
*/
105+
public static EmbedBuilder getEmbed(User user, int score) {
106+
Blackjack game = games.get(user.getId());
107+
Cards dealerCard = game.dealerCard();
108+
List<Cards> playerHand = game.playerHand();
109+
110+
String dealerText = dealerCard.emoji + " " + CARDBACK_EMOJI + "\n\nValue: " + dealerCard.value;
111+
String userText = printCards(playerHand) + "\n\nValue: " + score;
112+
return new EmbedBuilder()
113+
.setColor(EmbedColor.DEFAULT.color)
114+
.setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl())
115+
.addField("Your Hand", userText, true)
116+
.addField("Dealer Hand", dealerText, true);
117+
}
118+
119+
/**
120+
* Same as getEmbed except this also shows the dealer's resulting hand.
121+
*
122+
* @param user the user playing blackjack.
123+
* @param dealerHand the revealed cards in the dealer's hand.
124+
* @param playerScore the current score of the player's hand.
125+
* @param dealerScore the score of the cards in the dealer's hand.
126+
* @return a MessageEmbed for the blackjack game with dealer cards revealed.
127+
*/
128+
public static EmbedBuilder getResultEmbed(User user, List<Cards> dealerHand, int playerScore, int dealerScore) {
129+
Blackjack game = games.get(user.getId());
130+
List<Cards> playerHand = game.playerHand();
131+
132+
String userText = printCards(playerHand) + "\n\nValue: " + playerScore;
133+
String dealerText = printCards(dealerHand) + "\n\nValue: " + dealerScore;
134+
return new EmbedBuilder()
135+
.setColor(EmbedColor.DEFAULT.color)
136+
.setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl())
137+
.addField("Your Hand", userText, true)
138+
.addField("Dealer Hand", dealerText, true);
139+
}
140+
141+
/**
142+
* Dealer will draw cards from the deck following standard blackjack rules.
143+
*
144+
* @param game the instance of the blackjack game.
145+
* @param playerScore the current score of the player's hand.
146+
* @return a list of revealed cards in the dealer's hand.
147+
*/
148+
private static List<Cards> revealDealerHand(Blackjack game, int playerScore) {
149+
List<Cards> dealerHand = new ArrayList<>();
150+
dealerHand.add(game.dealerCard());
151+
int dealerScore = game.dealerCard().value;
152+
do {
153+
Cards card = game.deck().pop();
154+
dealerScore += card.value;
155+
dealerHand.add(card);
156+
} while (dealerScore < playerScore && dealerScore < 17);
157+
return dealerHand;
158+
}
159+
160+
/**
161+
* Draws a card from the deck and updates score.
162+
*
163+
* @param user the user playing blackjack.
164+
* @param bet the amount of money the player bet with.
165+
* @param uuid the UUID for the embed buttons.
166+
* @return a MessageEmbed with the updated game status.
167+
*/
168+
public static MessageEmbed hit(Guild guild, User user, long bet, String uuid) {
169+
// Update game stats
170+
games.get(user.getId()).hit();
171+
Blackjack game = games.get(user.getId());
172+
int score = calculateValue(game.playerHand());
173+
174+
// Send updated embed
175+
EmbedBuilder embed;
176+
EconomyHandler economyHandler = GuildData.get(guild).economyHandler;
177+
String currency = economyHandler.getCurrency();
178+
if (score >= 21) {
179+
if (score > 21) {
180+
// Player busted
181+
List<Cards> dealerHand = List.of(game.dealerCard(), game.deck().pop());
182+
int dealerScore = calculateValue(dealerHand);
183+
embed = getResultEmbed(user, dealerHand, score, dealerScore);
184+
embed.setDescription("Result: Bust " + currency + " -" + bet);
185+
embed.setColor(EmbedColor.ERROR.color);
186+
} else {
187+
// Player has 21 cards
188+
List<Cards> dealerHand = revealDealerHand(game, score);
189+
int dealerScore = calculateValue(dealerHand);
190+
embed = getResultEmbed(user, dealerHand, score, dealerScore);
191+
if (dealerScore == 21) {
192+
// Player and dealer tied
193+
embed.setDescription("Result: Push, money back");
194+
embed.setColor(EmbedColor.WARNING.color);
195+
economyHandler.addMoney(user.getIdLong(), bet);
196+
} else {
197+
// Player wins
198+
embed.setDescription("Result: Win " + currency + " " + (2*bet));
199+
embed.setColor(EmbedColor.SUCCESS.color);
200+
economyHandler.addMoney(user.getIdLong(), (2*bet));
201+
}
202+
}
203+
endGame(user.getId(), uuid);
204+
} else {
205+
embed = getEmbed(user, score);
206+
}
207+
return embed.build();
208+
}
209+
210+
/**
211+
* Ends the game and reveals dealer cards.
212+
*
213+
* @param user the user playing blackjack.
214+
* @param bet the amount of money the player bet with.
215+
* @param uuid the UUID for the embed buttons.
216+
* @return a MessageEmbed with the updated game status.
217+
*/
218+
public static MessageEmbed stand(Guild guild, User user, long bet, String uuid) {
219+
// Get player stats
220+
Blackjack game = games.get(user.getId());
221+
int score = calculateValue(game.playerHand());
222+
223+
// Get dealer stats
224+
List<Cards> dealerHand = revealDealerHand(game, score);
225+
int dealerScore = calculateValue(dealerHand);
226+
227+
// Send updated embed
228+
EconomyHandler economyHandler = GuildData.get(guild).economyHandler;
229+
String currency = economyHandler.getCurrency();
230+
EmbedBuilder embed = getResultEmbed(user, dealerHand, score, dealerScore);
231+
if (dealerScore > score) {
232+
if (dealerScore > 21) {
233+
// Dealer busted
234+
embed.setDescription("Result: Dealer bust " + currency + " " + (2*bet));
235+
embed.setColor(EmbedColor.SUCCESS.color);
236+
economyHandler.addMoney(user.getIdLong(), (2*bet));
237+
} else {
238+
// Dealer wins
239+
embed.setDescription("Result: Loss " + currency + " -" + bet);
240+
embed.setColor(EmbedColor.ERROR.color);
241+
}
242+
} else if (dealerScore == score) {
243+
// Player and dealer tie (push)
244+
embed.setDescription("Result: Push, money back");
245+
embed.setColor(EmbedColor.WARNING.color);
246+
economyHandler.addMoney(user.getIdLong(), bet);
247+
} else {
248+
// Player wins
249+
embed.setDescription("Result: Win " + currency + " " + (2*bet));
250+
embed.setColor(EmbedColor.SUCCESS.color);
251+
economyHandler.addMoney(user.getIdLong(), (2*bet));
252+
}
253+
endGame(user.getId(), uuid);
254+
return embed.build();
255+
}
256+
257+
/**
258+
* Ends the game by deleting stats and disabling the embed buttons.
259+
*
260+
* @param userID the ID of the user playing this game.
261+
* @param uuid the UUID for the embed buttons.
262+
*/
263+
public static void endGame(String userID, String uuid) {
264+
games.remove(userID);
265+
List<Button> old = ButtonListener.buttons.get(uuid);
266+
List<Button> components = new ArrayList<>();
267+
components.add(old.get(0).asDisabled());
268+
components.add(old.get(1).asDisabled());
269+
ButtonListener.buttons.put(uuid, components);
270+
}
271+
272+
/**
273+
* Prints playing cards into a string using their respective emojis.
274+
*
275+
* @param hand the list of card enums in this player's hand.
276+
* @return a string with all cards in emoji form.
277+
*/
278+
public static String printCards(List<Cards> hand) {
279+
StringBuilder text = new StringBuilder();
280+
for (Cards card : hand) {
281+
text.append(card.emoji).append(" ");
282+
}
283+
return text.toString();
284+
}
285+
286+
/**
287+
* Calculate the value of the cards in a player's hand.
288+
* Card values are based on the blackjack rule set.
289+
*
290+
* @param hand the list of card enums in this player's hand.
291+
* @return the integer value of the player's cards.
292+
*/
293+
public static int calculateValue(List<Cards> hand) {
294+
int value = 0;
295+
for (Cards card : hand) {
296+
value += card.value;
297+
}
298+
if (value > 21) {
299+
for (Cards card : hand) {
300+
if (card.isAce) {
301+
value -= 10;
302+
break;
303+
}
304+
}
305+
}
306+
return value;
307+
}
308+
}

src/main/java/technobot/data/cache/Item.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import technobot.handlers.economy.EconomyHandler;
99
import technobot.util.embeds.EmbedColor;
1010

11-
import java.util.UUID;
12-
1311
/**
1412
* POJO object that stores a shop item.
1513
*

src/main/java/technobot/handlers/economy/EconomyHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public Economy getProfile(long userID) {
274274
*
275275
* @param amount the amount of money to add.
276276
*/
277-
private void addMoney(long userID, long amount) {
277+
public void addMoney(long userID, long amount) {
278278
Bson filter = Filters.and(guildFilter, Filters.eq("user", userID));
279279
bot.database.economy.updateOne(filter, Updates.inc("balance", amount), UPSERT);
280280
}
@@ -284,7 +284,7 @@ private void addMoney(long userID, long amount) {
284284
*
285285
* @param amount the amount of money to remove.
286286
*/
287-
private void removeMoney(long userID, long amount) {
287+
public void removeMoney(long userID, long amount) {
288288
Bson filter = Filters.and(guildFilter, Filters.eq("user", userID));
289289
bot.database.economy.updateOne(filter, Updates.inc("balance", -1 * amount), UPSERT);
290290
}

src/main/java/technobot/listeners/ButtonListener.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
import net.dv8tion.jda.api.requests.ErrorResponse;
1414
import net.dv8tion.jda.api.requests.restaction.WebhookMessageAction;
1515
import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction;
16+
import technobot.commands.casino.BlackjackCommand;
1617
import technobot.data.GuildData;
1718
import technobot.util.embeds.EmbedUtils;
19+
import technobot.util.enums.Cards;
1820

1921
import java.util.*;
2022
import java.util.concurrent.Executors;
@@ -207,5 +209,15 @@ else if (pressedArgs[0].equals("reset") && storedArgs[0].equals("reset")) {
207209
event.getHook().editOriginalComponents(new ArrayList<>()).setEmbeds(embed).queue();
208210
}
209211
}
212+
else if (pressedArgs[0].equals("blackjack") && storedArgs[0].equals("blackjack")) {
213+
long bet = Long.parseLong(pressedArgs[4]);
214+
MessageEmbed embed = null;
215+
if (pressedArgs[1].equals("hit")) {
216+
embed = BlackjackCommand.hit(event.getGuild(), event.getUser(), bet, uuid);
217+
} else if (pressedArgs[1].equals("stand")) {
218+
embed = BlackjackCommand.stand(event.getGuild(), event.getUser(), bet, uuid);
219+
}
220+
event.editComponents(ActionRow.of(buttons.get(uuid))).setEmbeds(embed).queue();
221+
}
210222
}
211223
}

0 commit comments

Comments
 (0)