commit 21cecccc2e3136345691d4dbd9bad6fbfb806085 Author: PocketMars <51007356+PocketMars@users.noreply.github.com> Date: Fri Jun 12 18:19:23 2020 -0500 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..371c402 --- /dev/null +++ b/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '5.2.0' +} + +group 'com.polymars' +version '1.0' + +mainClassName = "Bot" +sourceCompatibility = 1.8; + +repositories { + mavenCentral() + jcenter() +} + +dependencies { + compile 'net.dv8tion:JDA:4.1.1_162' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f3d88b1 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 100644 index 0000000..9459f64 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jun 11 09:36:06 CDT 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$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 "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9618d8d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,100 @@ +@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 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 init + +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 init + +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 + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +: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 %CMD_LINE_ARGS% + +: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 b/settings.gradle new file mode 100644 index 0000000..2f98175 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'Sokobot' + diff --git a/src/main/java/Bot.java b/src/main/java/Bot.java new file mode 100644 index 0000000..2587883 --- /dev/null +++ b/src/main/java/Bot.java @@ -0,0 +1,23 @@ +import net.dv8tion.jda.api.AccountType; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Activity; + +import javax.security.auth.login.LoginException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class Bot { + static String prefix = "!"; + + public static void main(String[] args) throws LoginException, IOException { + JDABuilder builder = new JDABuilder(AccountType.BOT); + String token = new String(Files.readAllBytes(Paths.get("token.txt"))); + builder.setToken(token); + builder.setStatus(OnlineStatus.ONLINE); + builder.setActivity(Activity.playing("!play to play Sokoban!")); + builder.addEventListeners(new Commands()); + builder.build(); + } +} \ No newline at end of file diff --git a/src/main/java/Box.java b/src/main/java/Box.java new file mode 100644 index 0000000..c39dfaa --- /dev/null +++ b/src/main/java/Box.java @@ -0,0 +1,70 @@ +public class Box +{ + int x = 0; + int y = 0; + int originalX = 0; //in case player messes up and wants to reset + int originalY = 0; + Grid currentGrid; + public Box(int x, int y, Grid currentGrid) + { + this.x = x; + this.y = y; + originalX = x; + originalY = y; + this.currentGrid = currentGrid; + } + public void reset() + { + x = originalX; + y = originalY; + } + public int getX() + { + return x; + } + public int getY() + { + return y; + } + public boolean moveUp() + { + if (!currentGrid.isWall(x, y - 1) && !currentGrid.isBox(x, y - 1)) + { + y -= 1; + return true; + } + return false; + } + public boolean moveDown() + { + if (!currentGrid.isWall(x, y + 1) && !currentGrid.isBox(x, y + 1)) + { + y += 1; + return true; + } + return false; + } + public boolean moveLeft() + { + if (!currentGrid.isWall(x - 1, y) && !currentGrid.isBox(x - 1, y)) + { + x -= 1; + return true; + } + return false; + } + public boolean moveRight() + { + if (!currentGrid.isWall(x + 1, y) && !currentGrid.isBox(x + 1, y)) + { + x += 1; + return true; + } + return false; + } + public boolean onDestination() + { + return currentGrid.isDestination(x, y); + } + +} diff --git a/src/main/java/Commands.java b/src/main/java/Commands.java new file mode 100644 index 0000000..03b7a2d --- /dev/null +++ b/src/main/java/Commands.java @@ -0,0 +1,105 @@ +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class Commands extends ListenerAdapter { + HashMap games = new HashMap(); + ArrayList commands = new ArrayList(Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r", Bot.prefix + "play", Bot.prefix + "continue", Bot.prefix + "stop")); + public void onGuildMessageReceived(GuildMessageReceivedEvent event) + { + if (event.getAuthor().isBot() && event.getMessage().getEmbeds().get(0).getTitle().charAt(0) == 'L') + { + event.getMessage().addReaction("U+2B05").queue(); + event.getMessage().addReaction("U+27A1").queue(); + event.getMessage().addReaction("U+2B06").queue(); + event.getMessage().addReaction("U+2B07").queue(); + event.getMessage().addReaction("U+1F504").queue(); + return; + } + String[] args = event.getMessage().getContentRaw().split("\\s+"); + + if (args[0].toLowerCase().equals(Bot.prefix + "info")) + { + event.getChannel().sendMessage(info().build()).queue(); + } + else if (commands.contains(args[0].toLowerCase())) + { + if (!games.containsKey(event.getAuthor())) + { + games.put(event.getAuthor(), new Game()); + } + String userInput = args[0].toLowerCase(); + if (Character.toString(userInput.charAt(0)).equals(Bot.prefix)) + { + userInput = userInput.substring(1, userInput.length()); + } + games.get(event.getAuthor()).run(event.getChannel(), userInput); + } + } + public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) { + if (event.getMember().getUser().isBot()) + { + return; + } + if (!games.containsKey(event.getMember().getUser())) + { + games.put(event.getMember().getUser(), new Game()); + } + String userInput = ""; + switch (event.getReactionEmote().toString()) + { + case "RE:U+2b05": + userInput = "left"; + break; + case "RE:U+27a1": + userInput = "right"; + break; + case "RE:U+2b06": + userInput = "up"; + break; + case "RE:U+2b07": + userInput = "down"; + break; + case "RE:U+1f504": + userInput = "r"; + break; + } + games.get(event.getMember().getUser()).run(event.getChannel(), userInput); + } + EmbedBuilder info() + { + EmbedBuilder info = new EmbedBuilder(); + info.setTitle("Sokobot"); + info.setThumbnail("https://cdn.discordapp.com/avatars/713635251703906336/4094ba90942077c27549cccbd54cecd4.png?size=128"); + info.setDescription("Sokobot is a bot that lets you play Sokoban, the classic box-pushing puzzle game."); + info.setColor(0xdd2e53); + info.addField("How to Play", "You are a **Sokoban** :flushed:.\nYour job is to push **boxes** :brown_square: on top of their **destinations** :negative_squared_cross_mark:.", false); + info.addField("Features", ":white_small_square:**Infinite levels**\nThe fun never ends thanks to endless, randomly generated maps of increasing difficulty!\n:white_small_square:**Varied controls**\nSokobot has multiple control options to improve the player's experience, including reactions and wasd commands!\n:white_small_square:**Simultaneous games**\nThanks to the power of Java HashMaps:tm:, multiple users can use the bot at the same time without interfering with eachothers' games.", false); + info.addField("Commands", ("``" + Bot.prefix + "play`` can be used to start a game if you are not currently in one.\n``" + Bot.prefix + "stop`` can be used to stop your active game at any time.\n``" + Bot.prefix + "info`` provides some useful details about the bot."), false); + info.addField("Source code", "https://github.com/polymarsdev", false); + info.setFooter("created by PolyMars", "https://avatars0.githubusercontent.com/u/51007356?s=460&u=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4"); + return info; + } + public static void sendGameEmbed(MessageChannel channel, String level, String game) + { + EmbedBuilder embed = new EmbedBuilder(); + embed.setTitle("Level " + level); + embed.setDescription(game); + embed.setFooter("Enter direction (up, down, left, right, or wasd) or r to reset"); + channel.sendMessage(embed.build()).queue(); + } + public static void sendWinEmbed(MessageChannel channel, String level) + { + EmbedBuilder embed = new EmbedBuilder(); + embed.setTitle("You win!"); + embed.setDescription("Type ``" + Bot.prefix + "continue`` to continue to Level " + level + " or ``" + Bot.prefix + "stop`` to quit "); + channel.sendMessage(embed.build()).queue(); + } +} diff --git a/src/main/java/Destination.java b/src/main/java/Destination.java new file mode 100644 index 0000000..fd9f93d --- /dev/null +++ b/src/main/java/Destination.java @@ -0,0 +1,24 @@ +public class Destination +{ + int x = 0; + int y = 0; + Grid currentGrid; + public Destination(int x, int y, Grid currentGrid) + { + this.x = x; + this.y = y; + this.currentGrid = currentGrid; + } + public boolean hasBox(Grid currentGrid) + { + return currentGrid.isWall(x, y); + } + public int getX() + { + return x; + } + public int getY() + { + return y; + } +} diff --git a/src/main/java/Game.java b/src/main/java/Game.java new file mode 100644 index 0000000..11ed665 --- /dev/null +++ b/src/main/java/Game.java @@ -0,0 +1,73 @@ +import net.dv8tion.jda.api.entities.MessageChannel; + +public class Game { + boolean gameActive = false; + public int level = 1; + int width = 9; + int height = 6; + Grid grid = new Grid(width, height, level); + public void newGame(MessageChannel channel) + { + if (!gameActive) + { + level = 1; + width = 9; + height = 6; + grid = new Grid(width, height, level); + + gameActive = true; + Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString()); + } + } + public void run(MessageChannel channel, String userInput) + { + if (userInput.equals("stop") && gameActive) + { + channel.sendMessage("Thanks for playing!").queue(); + gameActive = false; + } + + if (userInput.equals("play") && !gameActive) + { + newGame(channel); + } + else if (gameActive) + { + if (!grid.hasWon()) { + + String direction = userInput; + if (direction.equals("up") || direction.equals("w")) + { + grid.getPlayer().moveUp(); + } else if (direction.equals("down") || direction.equals("s")) + { + grid.getPlayer().moveDown(); + } else if (direction.equals("left") || direction.equals("a")) + { + grid.getPlayer().moveLeft(); + } else if (direction.equals("right") || direction.equals("d")) + { + grid.getPlayer().moveRight(); + } else if (direction.equals("r")) + { + grid.reset(); + } + if (!grid.hasWon()) { //need to check again + Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString()); + } + } + if (grid.hasWon()) { + level += 1; + if (width < 13) { + width += 2; + } + if (height < 8) + { + height += 1; + } + Commands.sendWinEmbed(channel, String.valueOf(level)); + grid = new Grid(width, height, level); + } + } + } +} diff --git a/src/main/java/Grid.java b/src/main/java/Grid.java new file mode 100644 index 0000000..91b5128 --- /dev/null +++ b/src/main/java/Grid.java @@ -0,0 +1,214 @@ +public class Grid +{ + final int GROUND = 0; + final int WALL = 1; + final int BOX = 2; + final int DESTINATION = 3; + final int PLAYER = 4; + final int MAX_BOXES = 8; + Tile[][] grid; + Box[] boxes; + Destination[] destinations; + int boxCount; + int height = 0; + int width = 0; + int color = 0; + Player player; + Randomizer rand = new Randomizer(); + public Grid(int width, int height, int boxCount) //create a random grid with specific width, height, and number of boxes + { + player = new Player(2, 2, this); + if (boxCount > MAX_BOXES) + { + boxCount = MAX_BOXES; + } + this.boxCount = boxCount; + boxes = new Box[boxCount]; + destinations = new Destination[boxCount]; + this.height = height; + this.width = width; + grid = new Tile[width][height]; + createBoxes(); + createDestinations(); + updateGrid(); + } + public Player getPlayer() + { + return player; + } + public void reset() + { + player.setPosition(2, 2); + for (int i = 0; i < boxCount; i++) + { + boxes[i].reset(); + } + updateGrid(); + } + public void setStatus(int x, int y, int status) + { + grid[x][y].setStatus(status); + } + public int getStatus(int x, int y) + { + return grid[x][y].getStatus(); + } + public boolean isWall(int x, int y) + { + return grid[x][y].getStatus() == WALL; + } + public boolean isBox(int x, int y) + { + return grid[x][y].getStatus() == BOX; + } + public boolean isBoxRaw(int x, int y) //allows you to check if a box is at a position before grid is set up + { + for (int i = 0; i < boxCount; i++) + { + if (x == boxes[i].getX() && y == boxes[i].getY()) + { + return true; + } + } + return false; + } + public boolean isDestination(int x, int y) + { + return grid[x][y].getStatus() == DESTINATION; + + } + public Box getBox(int x, int y) + { + for (int i = 0; i < boxCount; i++) + { + if (x == boxes[i].getX() && y == boxes[i].getY()) + { + return boxes[i]; + } + } + return null; + } + public void createBoxes() + { + color = rand.nextInt(6); //runs after each level + for (int i = 0; i < boxCount; i++) + { + int x = rand.nextInt(width - 4) + 2; + int y = rand.nextInt(height - 4) + 2; + for (int j = 0; j < i; j++) + { + while ((x == boxes[j].getX() && y == boxes[j].getY()) || (x == 2 && y == 2)) + { + x = rand.nextInt(width - 4) + 2; + y = rand.nextInt(height - 4) + 2; + } + } + boxes[i] = new Box(x, y, this); + } + } + public void createDestinations() + { + for (int i = 0; i < boxCount; i++) + { + int x = rand.nextInt(width - 2) + 1; + int y = rand.nextInt(height - 2) + 1; + for (int j = 0; j < i; j++) + { + while (((x == destinations[j].getX() && y == destinations[j].getY())) || isBoxRaw(x, y)) + { + x = rand.nextInt(width - 2) + 1; + y = rand.nextInt(height - 2) + 1; + } + } + destinations[i] = new Destination(x, y, this); + } + } + public void updateGrid() + { + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + grid[j][i] = new Tile(GROUND); + if (j == 0 || j == width - 1 || i == 0 || i == height - 1) + { + grid[j][i] = new Tile(WALL, color); + } + for (int k = 0; k < boxCount; k++) + { + if (destinations[k].getX() == j && destinations[k].getY() == i) + { + grid[j][i] = new Tile(DESTINATION); + } + } + if (player.getX() == j && player.getY() == i) + { + grid[j][i] = new Tile(PLAYER); + } + for (int k = 0; k < boxCount; k++) + { + if (boxes[k].getX() == j && boxes[k].getY() == i) + { + if (boxes[k].onDestination()) + { + grid[j][i] = new Tile(WALL, color); + } else { + grid[j][i] = new Tile(BOX); + } + + } + } + } + } + } + public boolean hasWon() + { + for (int i = 0; i < boxCount; i++) + { + if (!destinations[i].hasBox(this)) + { + return false; + } + } + return true; + } + public Tile[][] getGrid() + { + return grid; + } + public Box[] getBoxes() + { + return boxes; + } + public Destination[] getDestinations() + { + return destinations; + } + public int getBoxCount() + { + return boxCount; + } + public int getHeight() + { + return height; + } + public int getWidth() + { + return width; + } + public String toString() + { + updateGrid(); + String result = ""; + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + result += grid[j][i]; + } + result += "\n"; + } + return result; + } + +} diff --git a/src/main/java/Player.java b/src/main/java/Player.java new file mode 100644 index 0000000..aea126d --- /dev/null +++ b/src/main/java/Player.java @@ -0,0 +1,98 @@ +public class Player +{ + int x = 0; + int y = 0; + Grid currentGrid; + public Player(int x, int y, Grid currentGrid) + { + this.x = x; + this.y = y; + this.currentGrid = currentGrid; + } + public int getX() + { + return x; + } + public int getY() + { + return y; + } + public void setPosition(int x, int y) + { + this.x = x; + this.y = y; + } + public boolean moveUp() + { + if (!currentGrid.isWall(x, y - 1)) + { + if (currentGrid.isBox(x, y - 1)) + { + if (currentGrid.getBox(x, y - 1).moveUp()) + { + y -= 1; + return true; + } + return false; + } + y -= 1; + return true; + } + return false; + } + public boolean moveDown() + { + if (!currentGrid.isWall(x, y + 1)) + { + if (currentGrid.isBox(x, y + 1)) + { + if (currentGrid.getBox(x, y + 1).moveDown()) + { + y += 1; + return true; + } + return false; + } + y += 1; + return true; + } + return false; + } + public boolean moveLeft() + { + if (!currentGrid.isWall(x - 1, y)) + { + if (currentGrid.isBox(x - 1, y)) + { + if (currentGrid.getBox(x - 1, y).moveLeft()) + { + x -= 1; + return true; + } + return false; + } + x -= 1; + return true; + } + return false; + } + public boolean moveRight() + { + if (!currentGrid.isWall(x + 1, y)) + { + if (currentGrid.isBox(x + 1, y)) + { + if (currentGrid.getBox(x + 1, y).moveRight()) + { + x += 1; + return true; + } + return false; + } + x += 1; + return true; + } + return false; + } + +} diff --git a/src/main/java/Randomizer.java b/src/main/java/Randomizer.java new file mode 100644 index 0000000..ad762ad --- /dev/null +++ b/src/main/java/Randomizer.java @@ -0,0 +1,91 @@ +import java.util.*; + +public class Randomizer{ + + public static Random theInstance = null; + + public Randomizer(){ + + } + + public static Random getInstance(){ + if(theInstance == null){ + theInstance = new Random(); + } + return theInstance; + } + + /** + * Return a random boolean value. + * @return True or false value simulating a coin flip. + */ + public static boolean nextBoolean(){ + return Randomizer.getInstance().nextBoolean(); + } + + /** + * This method simulates a weighted coin flip which will return + * true with the probability passed as a parameter. + * + * @param probability The probability that the method returns true, + * a value between 0 to 1 inclusive. + * @return True or false value simulating a weighted coin flip. + */ + public static boolean nextBoolean(double probability){ + return Randomizer.nextDouble() < probability; + } + + /** + * This method returns a random integer. + * @return A random integer. + */ + public static int nextInt(){ + return Randomizer.getInstance().nextInt(); + } + + /** + * This method returns a random integer between 0 and n, exclusive. + * @param n The maximum value for the range. + * @return A random integer between 0 and n, exclusive. + */ + public static int nextInt(int n){ + return Randomizer.getInstance().nextInt(n); + } + + /** + * Return a number between min and max, inclusive. + * @param min The minimum integer value of the range, inclusive. + * @param max The maximum integer value in the range, inclusive. + * @return A random integer between min and max. + */ + public static int nextInt(int min, int max){ + return min + Randomizer.nextInt(max - min + 1); + } + + /** + * Return a random double between 0 and 1. + * @return A random double between 0 and 1. + */ + public static double nextDouble(){ + return Randomizer.getInstance().nextDouble(); + } + + /** + * Return a random double between min and max. + * @param min The minimum double value in the range. + * @param max The maximum double value in the rang. + * @return A random double between min and max. + */ + public static double nextDouble(double min, double max){ + return min + (max - min) * Randomizer.nextDouble(); + } + + /** + * Return a random color. + * @return A random hex string that represents a color. + */ + public static String nextColor(){ + String randomNum = Integer.toHexString(Randomizer.nextInt(0, 16777216)); + return "'#" + randomNum + "'"; + } +} diff --git a/src/main/java/Tile.java b/src/main/java/Tile.java new file mode 100644 index 0000000..9cbd192 --- /dev/null +++ b/src/main/java/Tile.java @@ -0,0 +1,65 @@ +public class Tile +{ + final int GROUND = 0; + final int WALL = 1; + final int BOX = 2; + final int DESTINATION = 3; + final int PLAYER = 4; + int color = 0; + int status = 0; + public Tile(int status) + { + this.status = status; + } + public Tile(int status, int color) + { + this.status = status; + this.color = color; + } + public void setStatus(int status) + { + this.status = status; + } + public void setStatus(int status, int color) + { + this.status = status; + this.color = color; + } + public int getStatus() + { + return this.status; + } + public String toString() + { + if (status == GROUND) + { + return ":black_large_square:"; + } + if (status == WALL) + { + switch (color) { + case 0: + return ":red_square:"; + case 1: + return ":orange_square:"; + case 2: + return ":yellow_square:"; + case 3: + return ":green_square:"; + case 4: + return ":blue_square:"; + default: + return ":purple_square:"; + } + } + if (status == BOX) + { + return ":brown_square:"; + } + if (status == DESTINATION) + { + return ":negative_squared_cross_mark:"; + } + return ":flushed:"; + } +} \ No newline at end of file diff --git a/src/main/resources/token.txt b/src/main/resources/token.txt new file mode 100644 index 0000000..2755fc0 --- /dev/null +++ b/src/main/resources/token.txt @@ -0,0 +1 @@ +replace this with your Discord bot token (https://discord.com/developers/applications) \ No newline at end of file