commit fbba18fd490c10219b67d783f1e85fc304be34dd
Author: Valentijn van der Jagt <1119935@hr.nl>
Date: Mon Nov 24 21:36:30 2025 +0100
Initial Commit
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..1fac4d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,43 @@
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+.kotlin
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100755
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100755
index 0000000..2a65317
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100755
index 0000000..d15a481
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100755
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100755
index 0000000..db96adc
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,27 @@
+plugins {
+ application
+ id("com.gradleup.shadow") version "8.3.1"
+}
+application.mainClass = "nl.herpiederpiee.discordbot.Bot"
+group = "nl.herpiederpiee.discordbot"
+version = "1.0"
+
+val jdaVersion = "6.1.1" //
+
+
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation("net.dv8tion:JDA:$jdaVersion")
+}
+
+tasks.withType {
+ options.encoding = "UTF-8"
+ options.isIncremental = true
+
+
+ sourceCompatibility = "1.8"
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..249e583
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..348defa
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Nov 12 17:27:14 CET 2025
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..1b6c787
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100755
index 0000000..ac1b06f
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100755
index 0000000..b8b4147
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "DiscordBot"
\ No newline at end of file
diff --git a/src/main/java/nl/herpiederpiee/discordbot/BlackjackCommand.java b/src/main/java/nl/herpiederpiee/discordbot/BlackjackCommand.java
new file mode 100755
index 0000000..e44f185
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/discordbot/BlackjackCommand.java
@@ -0,0 +1,211 @@
+package nl.herpiederpiee.discordbot;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.components.actionrow.ActionRow;
+import net.dv8tion.jda.api.components.buttons.Button;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
+
+import java.awt.*;
+import java.util.ArrayList;
+
+public class BlackjackCommand extends ListenerAdapter {
+ private ArrayList playerCards;
+ private ArrayList dealerCards;
+ public Deck deck;
+
+
+ private String getPlayerCardString(){
+ String playerCardsString = "";
+ for (String card : playerCards){
+ playerCardsString += deck.decodeCardName(card) + "\n";
+ }
+ return playerCardsString;
+ }
+
+ private String getDealerCardString(boolean revealed){
+ String cardString = "";
+ if (!revealed){
+ return deck.decodeCardName(dealerCards.get(0)) + "\n????\n";
+ }
+ else {
+ for (String card : dealerCards){
+ cardString += deck.decodeCardName(card) + "\n";
+ }
+ return cardString;
+ }
+ }
+
+ private EmbedBuilder getBaseEmbed(String username){
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setTitle("Blackjack!");
+ embedBuilder.setAuthor(username);
+ embedBuilder.addBlankField(false);
+ embedBuilder.setColor(new Color(0,255,0));
+ return embedBuilder;
+ }
+
+ private Integer getCardsValue(ArrayList cards){
+ Integer total = 0;
+ for (String card : cards){
+ if (card.startsWith("A")){
+ total += 11;
+ continue;
+ }
+
+ if (card.startsWith("J") || card.startsWith("Q") || card.startsWith("K") || card.startsWith("10")){
+ total += 10;
+ continue;
+ }
+
+ try {
+ total += Integer.parseInt(card.substring(0,1));
+ } catch (NumberFormatException e){
+ System.out.println("[ERROR] "+e);
+ }
+ }
+
+ if (total <= 21){
+ return total;
+ }
+
+ for (String card : cards){
+ if (card.startsWith("A")){
+ total -= 10; // make the ace (11) a 1, by subtracting 10.
+ if (total <= 21){
+ return total; // return when total gets lower then 21, so not too many aces get cut if you got multiple
+ }
+ }
+ }
+ return total; // return (busted) total
+ }
+
+ @Override
+ public void onSlashCommandInteraction(SlashCommandInteractionEvent event){
+ if (!event.getName().equals("blackjack")) return;
+
+ // only instansiate variables on command (so only start game, since rest of interaction is with buttons).
+ deck = new Deck();
+ playerCards = new ArrayList();
+ dealerCards = new ArrayList();
+
+ // create the main blackjack message
+ EmbedBuilder embedBuilder = getBaseEmbed(event.getUser().getName());
+
+ // pull all initial cards
+ for (int i = 0; i < 2; i++){
+ String playerPulledCard = deck.pullCard();
+ playerCards.add(playerPulledCard);
+
+ String dealerPulledCard = deck.pullCard();
+ dealerCards.add(dealerPulledCard);
+ }
+
+ String playerCardsString = getPlayerCardString();
+ String dealerCardsString = getDealerCardString(false); // only show one of the dealers cards
+
+
+ embedBuilder.addField("Your Cards ("+getCardsValue(playerCards).toString()+")", playerCardsString, false);
+ embedBuilder.addBlankField(false);
+ embedBuilder.addField("Dealer Cards:", dealerCardsString, false);
+
+ Button hitButton = Button.primary("blackjack_hit", "Hit");
+ Button standButton = Button.danger("blackjack_stand", "Stand");
+
+ event.replyEmbeds(embedBuilder.build())
+ .addComponents(ActionRow.of(
+ hitButton,
+ standButton
+ )).queue();
+
+
+ }
+
+ private void sendMessage(ButtonInteractionEvent event, String message, Color color){
+ event.getMessage().delete().queue();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setTitle(message);
+ embedBuilder.addBlankField(false);
+ embedBuilder.addField("Your Cards (Value: "+getCardsValue(playerCards)+"):", getPlayerCardString(), false);
+ embedBuilder.addBlankField(false);
+ embedBuilder.addField("Dealers Cards (Value: "+getCardsValue(dealerCards)+"):", getDealerCardString(true), false);
+ embedBuilder.setColor(color);
+
+
+ event.getChannel().sendMessageEmbeds(embedBuilder.build()).queue();
+ }
+
+ @Override
+ public void onButtonInteraction(ButtonInteractionEvent event) {
+ if (event.getButton().getCustomId().equals("blackjack_hit")) {
+ String playerPulledCard = deck.pullCard();
+ playerCards.add(playerPulledCard);
+
+ EmbedBuilder embedBuilder = getBaseEmbed(event.getUser().getName());
+
+ String playerCardsString = getPlayerCardString();
+ String dealerCardsString = getDealerCardString(false);
+
+ embedBuilder.addField("Your Cards (" + getCardsValue(playerCards).toString() + ")", playerCardsString, false);
+ embedBuilder.addBlankField(false);
+ embedBuilder.addField("Dealer Cards:", dealerCardsString, false);
+
+ event.editMessageEmbeds(embedBuilder.build()).queue();
+
+ if (getCardsValue(playerCards) > 21) {
+ sendMessage(event,"You Busted :upside_down:", Color.RED);
+ }
+ }
+
+ if (event.getButton().getCustomId().equals("blackjack_stand")){
+ // remove buttons
+ event.editComponents().queue();
+ // reveal dealer cards
+ EmbedBuilder embedBuilder = getBaseEmbed(event.getUser().getName());
+
+ String playerCardsString = getPlayerCardString();
+ String dealerCardsString = getDealerCardString(true);
+
+ embedBuilder.addField("Your Cards (" + getCardsValue(playerCards).toString() + ")", playerCardsString, false);
+ embedBuilder.addBlankField(false);
+ embedBuilder.addField("Dealer Cards ("+ getCardsValue(dealerCards).toString() +"):", dealerCardsString, false);
+
+ event.editMessageEmbeds(embedBuilder.build()).queue();
+
+
+ // while dealer cards worth < 17, dealer hits,
+ while (getCardsValue(dealerCards) < 17) {
+ String pulledCard = deck.pullCard();
+ dealerCards.add(pulledCard);
+ }
+
+ Integer dealerCardValue = getCardsValue(dealerCards);
+ Integer playerCardValue = getCardsValue(playerCards);
+
+ if (dealerCardValue > 21 && playerCardValue <= 21){
+ sendMessage(event,"Dealer busted, You WIN!!!!!", Color.GREEN);
+ return;
+ }
+
+
+ // if both player and dealer are<= 21, check who is higher, that one wins.
+ if (dealerCardValue <= 21 && playerCardValue <= 21) {
+ if (playerCardValue > dealerCardValue){
+ sendMessage(event,"You WIN!!!!!!!!!!!!!", Color.GREEN);
+ return;
+ } else {
+ sendMessage(event,"Dealer WINs :upside_down:", Color.RED);
+ return;
+ }
+ }
+
+ if (dealerCardValue == playerCardValue){
+ sendMessage(event, "Its a tie :neutral_face:", Color.BLUE);
+ return;
+ }
+
+ return /*wtf happened*/;
+ }
+ }
+}
diff --git a/src/main/java/nl/herpiederpiee/discordbot/Bot.java b/src/main/java/nl/herpiederpiee/discordbot/Bot.java
new file mode 100755
index 0000000..d27b4f3
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/discordbot/Bot.java
@@ -0,0 +1,28 @@
+package nl.herpiederpiee.discordbot;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.JDABuilder;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.Commands;
+import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
+
+public class Bot {
+ public static void main(String[] args) {
+ JDA api = JDABuilder.createDefault(System.getenv("DISCORD_TOKEN")).build();
+
+ api.updateCommands().addCommands(
+ Commands.slash("ping", "pong :)"),
+ Commands.slash("blackjack", "play blackjack :)"),
+ Commands.slash("roulette", "play roulette :)").addOptions(
+ new OptionData(OptionType.STRING, "roulette_type", "The type of bet you'll make", true, true),
+ new OptionData(OptionType.STRING, "roulette_choice", "The thing you bet on", true, true),
+ new OptionData(OptionType.INTEGER, "roulette_bet", "The amount you bet", true)
+ )
+ ).queue();
+
+ api.addEventListener(new PingCommand());
+ api.addEventListener(new BlackjackCommand());
+ api.addEventListener(new RouletteCommand());
+ }
+}
diff --git a/src/main/java/nl/herpiederpiee/discordbot/Deck.java b/src/main/java/nl/herpiederpiee/discordbot/Deck.java
new file mode 100755
index 0000000..170b8de
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/discordbot/Deck.java
@@ -0,0 +1,93 @@
+package nl.herpiederpiee.discordbot;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class Deck {
+ public ArrayList cards = new ArrayList();
+
+ public Deck() {
+ fillDeck();
+ }
+
+ private String numberToSuit(int num){
+ switch(num){
+ case 0:
+ return "H"; // hearts
+ case 1:
+ return "S"; // spades
+ case 2:
+ return "D"; //diamonds
+ case 3:
+ return "C"; //clubs
+ default:
+ System.out.println("Number out of suit range");
+ return "";
+ }
+ }
+
+ private void fillDeck(){
+ for (int suit = 0; suit < 4; suit++) {
+ for (int i = 2; i < 11; i++) {
+ Integer number = new Integer(i);
+ cards.add(number.toString() + numberToSuit(suit));
+ }
+ cards.add("A" + numberToSuit(suit));
+ cards.add("J" + numberToSuit(suit));
+ cards.add("Q" + numberToSuit(suit));
+ cards.add("K" + numberToSuit(suit));
+ }
+ System.out.println("Deck Filled:"+cards.toString());
+ }
+
+ public String pullCard(){
+ Collections.shuffle(cards);
+ String pulledCard = cards.get(0);
+ cards.remove(0); // remove pulled card
+ return pulledCard;
+ }
+
+ public String decodeCardName(String card){
+ char firstChar = card.charAt(0);
+ char lastChar = card.charAt(card.length() -1); // get last char
+ String finalString = "";
+
+ switch (firstChar){
+ case 'A':
+ finalString += "Ace";
+ break;
+ case 'J':
+ finalString += "Jack";
+ break;
+ case 'Q':
+ finalString += "Queen";
+ break;
+ case 'K':
+ finalString += "King";
+ break;
+ default:
+ if (firstChar == '1'){finalString += "10";}
+ else {finalString += String.valueOf(firstChar);}
+ break;
+ }
+
+ switch (lastChar){
+ case 'H':
+ finalString += " of Hearts";
+ break;
+ case 'S':
+ finalString += " of Spades";
+ break;
+ case 'D':
+ finalString += " of Diamonds";
+ break;
+ case 'C':
+ finalString += " of Clubs";
+ break;
+ default:
+ break;
+ }
+
+ return finalString;
+ }
+}
diff --git a/src/main/java/nl/herpiederpiee/discordbot/PingCommand.java b/src/main/java/nl/herpiederpiee/discordbot/PingCommand.java
new file mode 100755
index 0000000..c0bb28f
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/discordbot/PingCommand.java
@@ -0,0 +1,13 @@
+package nl.herpiederpiee.discordbot;
+
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+
+public class PingCommand extends ListenerAdapter {
+ @Override
+ public void onSlashCommandInteraction(SlashCommandInteractionEvent event){
+ if (event.getName().equals("ping")){
+ event.reply("Pong!").queue();
+ }
+ }
+}
diff --git a/src/main/java/nl/herpiederpiee/discordbot/RouletteCommand.java b/src/main/java/nl/herpiederpiee/discordbot/RouletteCommand.java
new file mode 100755
index 0000000..c1166c0
--- /dev/null
+++ b/src/main/java/nl/herpiederpiee/discordbot/RouletteCommand.java
@@ -0,0 +1,240 @@
+package nl.herpiederpiee.discordbot;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.interactions.commands.Command;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class RouletteCommand extends ListenerAdapter {
+ private String[] typeChoices = new String[]{"number", "color", "evenodd"};
+ private String[] numberChoices = new String[]{
+ "0",
+ "1","2","3","4","5","6","7","8","9","10",
+ "11","12","13","14","15","16","17","18","19","20",
+ "21","22","23","24","25","26","27","28","29","30",
+ "31","32","33","34","35","36"
+ };
+ private String[] colorChoices = new String[] {"red", "black" };
+ private String[] evenOddChoices = new String[] {"even", "odd"};
+
+ public Map numberColors = new HashMap<>();
+
+ public RouletteCommand(){
+ numberColors.put(0, "green");
+
+ int[] redNumbers = {1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36};
+
+ for (int num : redNumbers) {
+ numberColors.put(num, "red");
+ }
+
+ for (int i = 1; i <= 36; i++) {
+ numberColors.putIfAbsent(i, "black");
+ }
+ }
+
+ private String getColorForNumber(int num){
+ if (num < 0 || num > 36) return null;
+
+ return numberColors.get(num);
+ }
+
+ private void sendAutocompletion(CommandAutoCompleteInteractionEvent event, String[] array){
+ List options = Stream.of(array)
+ .filter(item -> item.startsWith(event.getFocusedOption().getValue())) // only display words that start with the user's current input
+ .map(item -> new Command.Choice(item, item)) // map the words to choices
+ .collect(Collectors.toList());
+
+ //delete everyting after the 25th item cuz of discord limits
+ while (options.size() > 25){
+ options.removeLast();
+ }
+ event.replyChoices(options).queue();
+ }
+
+
+ private boolean isItemInList(String[] list, String item){
+ for (String temp : list){
+ if (temp.equals(item)){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void sendErrorEmbed(SlashCommandInteractionEvent event, String errorMessage){
+ EmbedBuilder embed = new EmbedBuilder()
+ .setTitle("Error")
+ .setDescription(errorMessage)
+ .setColor(Color.RED);
+ event.replyEmbeds(embed.build()).queue();
+ return;
+ }
+
+ @Override
+ public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event) {
+ if (!event.getName().equals("roulette")){return;}
+
+ if (event.getFocusedOption().getName().equals("roulette_type")) {
+ sendAutocompletion(event, typeChoices);
+ return;
+ }
+
+ if (event.getFocusedOption().getName().equals("roulette_choice")) {
+ String chosenType = event.getOption("roulette_type").getAsString();;
+
+ /* Check is the chosen type option is valid */
+ boolean isValid = isItemInList(typeChoices, chosenType);
+ if (!isValid) return;
+
+ // if number, autocomplete numbers
+ if (chosenType.equals("number")) {
+ sendAutocompletion(event, numberChoices);
+ return;
+ }
+
+ if (chosenType.equals("color")) {
+ sendAutocompletion(event, colorChoices);
+ return;
+ }
+
+ if (chosenType.equals("evenodd")) {
+ sendAutocompletion(event, evenOddChoices);
+ return;
+ }
+ }
+
+
+ }
+
+ @Override
+ public void onSlashCommandInteraction(SlashCommandInteractionEvent event){
+ if (!event.getName().equals("roulette")){
+ return;
+ }
+ String type = event.getOption("roulette_type").getAsString();
+ String choice = event.getOption("roulette_choice").getAsString();
+ Integer betAmount = event.getOption("roulette_bet").getAsInt();
+
+
+ // 1. Validate the input "type":
+ boolean validType = isItemInList(typeChoices, type);
+ if (!validType){
+ sendErrorEmbed(event, "Invalid Bet Type");
+ return;
+ }
+
+ // 2. Validate "choice" based on the selected type:
+ boolean validChoice = false;
+ if (type.equals("number")){
+ try {
+ Integer number = Integer.parseInt(choice);
+ if (number >= 0 && number <= 36){
+ validChoice = true;
+ }
+ } catch(NumberFormatException e){
+ validChoice = false;
+ }
+ }
+ if (type.equals("color")) {
+ if (choice.equals("red") || choice.equals("black")){
+ validChoice = true;
+ }
+ }
+ if (type.equals("evenodd")) {
+ if (choice.equals("even") || choice.equals("odd")){
+ validChoice = true;
+ }
+ }
+ if (!validChoice){
+ sendErrorEmbed(event, "Invalid Bet Choice");
+ return;
+ }
+
+
+ // 3. Validate that betAmount is an integer greater than 0
+ if (betAmount < 0){
+ sendErrorEmbed(event, "Invalid Bet Amount");
+ return;
+ }
+
+ // 4. Generate the roulette result number:
+ // - pick a random integer from 0 to 36 (inclusive)
+ Random random = new Random();
+ Integer rouletteResultValue = random.nextInt(0,37); // origin included, bound excluded
+
+ // 5. Determine the result color:
+
+ String rouletteResultColor = getColorForNumber(rouletteResultValue);
+
+ // 6. Determine if resultNumber is even, odd, or neither:
+
+ String rouletteResultEvenOdd = ((rouletteResultValue & 1) == 0) ? "even" : "odd";
+
+ // 7. Determine win or loss based on the selected type:
+ // 8. Assign payout multipliers:
+
+ int payoutMultiplier = 0;
+ if (type.equals("number")){
+ Integer chosenNumber = Integer.parseInt(choice); // this gets validated earlier, so its safe.
+ if (chosenNumber.equals(rouletteResultValue)) {
+ payoutMultiplier = 35;
+ }
+ }
+ if (type.equals("color")){
+ if (choice.equals(rouletteResultColor)){
+ payoutMultiplier = 1;
+ }
+ }
+ if (type.equals("evenodd")){
+ if (choice.equals(rouletteResultEvenOdd)){
+ payoutMultiplier = 1;
+ }
+ }
+
+ // 9. Calculate money outcome:
+ Integer grossReturn = 0;
+ Integer netProfit = -betAmount; // by defaulkt lose everyting
+
+ if (payoutMultiplier > 0) { // indicates win
+ grossReturn = betAmount * (payoutMultiplier + 1);
+ netProfit = betAmount * payoutMultiplier;
+ }
+
+ // 10. Prepare and return final result details, including:
+ // - spun number
+ // - resulting color
+ // - resulting even/odd value
+ // - win or loss result
+ // - payout multiplier
+ // - grossReturn (total returned to player)
+ // - netProfit (total gained or lost)
+ EmbedBuilder resultEmbed = new EmbedBuilder();
+ resultEmbed.setTitle("Roulette Result:")
+ .setDescription(
+ "Spun number: **"+rouletteResultValue+"**\n" +
+ "Color: **"+rouletteResultColor+"**\n" +
+ "Even or Odd: **"+rouletteResultEvenOdd+"**"
+ );
+
+ resultEmbed.addBlankField(false);
+
+ if (payoutMultiplier > 0) {
+ resultEmbed.setColor(Color.GREEN);
+ resultEmbed.addField("YOU WON!", "You got paid out "+grossReturn+", totalling a profit of "+netProfit, false);
+ } else {
+ resultEmbed.setColor(Color.RED);
+ resultEmbed.addField("YOU LOST!", "You lost "+Math.abs(netProfit) +", AKA all your money :)", false);
+ }
+
+
+ event.replyEmbeds(resultEmbed.build()).queue();
+ }
+}