├── .gitignore ├── .replit ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Procfile ├── README.md ├── app.json ├── pom.xml └── src ├── main └── java │ └── com │ └── battlesnake │ └── starter │ └── Snake.java └── test └── java └── com └── battlesnake └── starter └── SnakeTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | target/ 5 | .idea/ 6 | *.iml 7 | .vscode/ 8 | .pmd 9 | bin/ 10 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "java10" 2 | run = "mvn compile exec:exec" -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | cache: 5 | directories: 6 | - $HOME/.m2 -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Battlesnake Code of Conduct 2 | 3 | Please see https://docs.battlesnake.com/policies/conduct 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We welcome contributions from the community! 4 | 5 | Please see https://docs.battlesnake.com/community/contributing 6 | 7 | We track all issues/discussions for all our open source repos at https://github.com/BattlesnakeOfficial/feedback/discussions 8 | Any item tagged with "flag/help-wanted ✋" is a great place to start, as it highlights any issues where we'd love to get some help! 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Battlesnake Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY aCLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -DPORT=$PORT -jar target/starter-snake-java.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A simple [Battlesnake](http://play.battlesnake.com) written in Java. 2 | 3 | This is a basic implementation of the [Battlesnake API](https://docs.battlesnake.com/snake-api). It's a great starting point for anyone wanting to program their first Battlesnake using Java. It comes ready to use with [Replit.com](https://replit.com) and provides instructions below for getting started. It can also be deployed to [Heroku](https://heroku.com), or any other cloud provider you'd like. 4 | 5 | 6 | ### Maintanance 7 | 8 | This is a community maintained Starter Project Battlesnake! 9 | 10 | Contribute to Open Source, and help keep this project up-to-date via pull request. Pull requests will be reviewed and merged by the [Battlesnake Official](https://github.com/BattlesnakeOfficial) team. 11 | 12 | Get involved in the Battlesnake community! 13 | * [Discord](https://play.battlesnake.com/discord) 14 | * [Twitch](https://www.twitch.tv/battlesnakeofficial) 15 | 16 | 17 | ### Technologies 18 | 19 | * [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) 20 | * [Maven](https://maven.apache.org/install.html) 21 | * [Heroku](https://heroku.com) (optional) 22 | 23 | 24 | ## Quickstart 25 | 26 | The [Quick Start Coding Guide](https://docs.battlesnake.com/guides/getting-started) provides the full set of instructions to customize, register, and create your first games with your Battlesnake! While the guide uses [Repl.it](https://repl.it) as an example host, the instructions can be modified to work with any hosting provider. You can also find advice on other hosting providers on our [Hosting Suggestions](https://docs.battlesnake.com/references/hosting-suggestions) page. 27 | 28 | ### Prerequisites 29 | 30 | * A free [Battlesnake Account](https://play.battlesnake.com/?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=homepage) 31 | 32 | --- 33 | 34 | ## Running Your Battlesnake on [replit.com](https://replit.com) 35 | 36 | [![Run on Replit.com](https://replit.com/badge/github/BattlesnakeOfficial/starter-snake-java)](https://replit.com/github/BattlesnakeOfficial/starter-snake-java) 37 | 38 | 1. Login to your [Replit.com](https://replit.com) account. 39 | 40 | 2. Click the 'Run on replit.com' button above, or visit the following URL: https://replit.com/github/BattlesnakeOfficial/starter-snake-java. 41 | 42 | 3. You should see your Repl being initialized - this might take a few moments to complete. 43 | 44 | 4. Once your Repl is ready to run, click `Run ▶️` at the top of the screen. You should see maven (and any other dependencies) being installed. Once installation is complete, your Battlesnake server will start and you should see the following: 45 | 46 | ``` 47 | [Thread-0] INFO org.eclipse.jetty.server.Server - Started @ms 48 | ``` 49 | 50 | 5. Above the terminal window you'll see the live output from your Battlesnake server, including its URL. That URL will be the URL used to create your Battlesnake in the next step. If you visit that URL in your browser, you should see text similar to this: 51 | 52 | ``` 53 | {"apiversion": "1", "author": "", "color": "#888888", "head": "default", "tail": "default"} 54 | ``` 55 | 56 | This means your Battlesnake is running correctly on Replit.com. 57 | 58 | **At this point your Battlesnake is live and ready to enter games!** 59 | 60 | 61 | 62 | ## Customizing Your Battlesnake 63 | 64 | Locate the `index` method inside [Snake.java](src/main/java/com/battlesnake/starter/Snake.java#L104). You should see code that looks like this: 65 | ```java 66 | Map response = new HashMap<>(); 67 | response.put("apiversion", "1"); 68 | response.put("author", ""); // TODO: Your Battlesnake Username 69 | response.put("color", "#888888"); // TODO: Personalize 70 | response.put("head", "default"); // TODO: Personalize 71 | response.put("tail", "default"); // TODO: Personalize 72 | return response; 73 | ``` 74 | 75 | This function is called by the game engine periodically to make sure your Battlesnake is healthy, responding correctly, and to determine how your Battlesnake will appear on the game board. See [Battlesnake Personalization](https://docs.battlesnake.com/references/personalization) for how to customize your Battlesnake's appearance using these values. 76 | 77 | Whenever you update these values, you can refresh your Battlesnake on [your profile page](https://play.battlesnake.com/me/) to use your latest configuration. Your changes should be reflected in the UI, as well as any new games created. 78 | 79 | ## Changing Behavior 80 | 81 | On every turn of each game your Battlesnake receives information about the game board and must decide its next move. 82 | 83 | Locate the `move` method inside [Snake.java](src/main/java/com/battlesnake/starter/Snake.java#L138). 84 | 85 | Possible moves are "up", "down", "left", or "right". To start your Battlesnake will choose a move randomly. Your goal as a developer is to read information sent to you about the board (available in the `moveRequest` variable) and make an intelligent decision about where your Battlesnake should move next. 86 | 87 | See the [Battlesnake Rules](https://docs.battlesnake.com/rules) for more information on playing the game, moving around the board, and improving your algorithm. 88 | 89 | ## (Optional) Running Your Battlesnake Locally 90 | 91 | Eventually you might want to run your Battlesnake server locally for faster testing and debugging. You can do this by installing both [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) and [Maven](https://maven.apache.org/install.html), then running: 92 | 93 | ```shell 94 | mvn compile exec:exec 95 | ``` 96 | 97 | This will start the Battlesnake server on port 8080. 98 | 99 | 100 | **Note:** You cannot create games on [play.battlesnake.com](https://play.battlesnake.com) using a locally running Battlesnake unless you install and use a port forwarding tool like [ngrok](https://ngrok.com/). 101 | 102 | ## Running Unit Tests 103 | 104 | The starter snake is setup with the [JUnit Jupiter](https://junit.org/junit5/docs/current/user-guide/) testing framework, if you are interested in developing unit tests for your Battlesnake. You can find the test cases in [SnakeTest.java](src/test/java/com/battlesnake/starter/SnakeTest.java) 105 | 106 | Run the following to start the tests: 107 | 108 | ```shell 109 | mvn compile test 110 | ``` 111 | 112 | --- 113 | 114 | 115 | ## Playing Battlesnake 116 | 117 | ### Completing Challenges 118 | 119 | If you're looking for the Single Player Mode of Battlesnake, or something to practice with between events, check out [Challenges.](https://docs.battlesnake.com/guides/quick-start-challenges-guide) 120 | 121 | ### Joining a Battlesnake Arena 122 | 123 | Once you've made your Battlesnake behave and survive on its own, you can enter it into the [Global Battlesnake Arena](https://play.battlesnake.com/arena/global) to see how it performs against other Battlesnakes worldwide. 124 | 125 | Arenas will regularly create new games and rank Battlesnakes based on their results. They're a good way to get regular feedback on how well your Battlesnake is performing, and a fun way to track your progress as you develop your algorithm. 126 | 127 | ### Joining a Battlesnake League 128 | 129 | Want to get out there to compete and win prizes? Check out the [Quick Start League Guide](https://docs.battlesnake.com/guides/quick-start-league-guide) for information on the how and when of our competitive seasons. 130 | 131 | --- 132 | 133 | ## (Optional) Deploying Your First Battlesnake with Heroku 134 | 135 | 1. [Fork this repo](https://github.com/BattlesnakeOfficial/starter-snake-java/fork) into your GitHub Account. 136 | 137 | 2. [Clone your forked repo](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) into your local environment. 138 | ```shell 139 | git clone git@github.com:[YOUR-GITHUB-USERNAME]/starter-snake-java.git 140 | ``` 141 | 142 | 3. [Create a new Heroku app](https://devcenter.heroku.com/articles/creating-apps) to run your Battlesnake. 143 | ```shell 144 | heroku create [YOUR-APP-NAME] 145 | ``` 146 | 147 | 4. [Deploy your Battlesnake code to Heroku](https://devcenter.heroku.com/articles/git#deploying-code). 148 | ```shell 149 | git push heroku master 150 | ``` 151 | 152 | 5. Open your new Heroku app in your browser. 153 | ```shell 154 | heroku open 155 | ``` 156 | If everything was successful, you should see the following text: 157 | ``` 158 | {"tailType":"default","color":"#888888","headType":"default","author":"","apiversion":"1"} 159 | ``` 160 | 161 | 6. Optionally, you can view your server logs using the [Heroku logs command](https://devcenter.heroku.com/articles/logging#log-retrieval) `heroku logs --tail`. The `--tail` option will show a live feed of your logs in real-time. 162 | 163 | **At this point your Battlesnake is live and ready to enter games!** 164 | 165 | ### (Optional) Updating Your Battlesnake with Heroku 166 | 167 | After making changes, commit them using git and deploy your changes to Heroku. 168 | ```shell 169 | git add . 170 | git commit -m "update my battlesnake's appearance" 171 | git push heroku master 172 | ``` 173 | 174 | Once Heroku has updated you can [create a new game](https://play.battlesnake.com/account/games/create/) with your Battlesnake to view your latest changes in action. 175 | 176 | **At this point you should feel comfortable making changes to your code and deploying those changes to Heroku!** 177 | 178 | ## (Optional) Using a Cloud Provider 179 | 180 | As your Battlesnake gets more complex, it might make sense to move it to a dedicated hosting provider such as GCP or AWS. We suggest choosing a platform you're familiar with, or one you'd be interested in learning more about. 181 | 182 | If you have questions or ideas, our developer community on [Discord](https://play.battlesnake.com/discord) will be able to help out. 183 | 184 | 185 | 186 | 187 | ## Resources 188 | 189 | All documentation is available at [docs.battlesnake.com](https://docs.battlesnake.com), including detailed Guides, API References, and Tips. 190 | 191 | You can also join the Battlesnake Developer Community on [Discord](https://play.battlesnake.com/discord?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=discord). We have a growing community of Battlesnake developers of all skill levels wanting to help everyone succeed and have fun with Battlesnake :) 192 | 193 | Check out live Battlesnake events on [Twitch](https://www.twitch.tv/battlesnakeofficial) and see what is happening when on the [Calendar.](https://play.battlesnake.com/calendar?utm_source=github&utm_medium=readme&utm_campaign=python_starter&utm_content=calendar) 194 | 195 | Want to contribute to Battlesnake? We have a number of open-source codebases and would love for you to get involved! Check out our page on [Contributing.](https://docs.battlesnake.com/guides/contributing) 196 | 197 | ## Feedback 198 | 199 | * **Do you have an issue or suggestions for this repository?** Head over to our [Feedback Repository](https://play.battlesnake.com/feedback) today and let us know! 200 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Battlesnake Java Starter Snake", 3 | "description": "A sample starter snake for Battlesnake written in Java", 4 | "repository": "https://github.com/battlesnakeofficial/starter-snake-java", 5 | "keywords": [ 6 | "battlesnake", 7 | "java" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | io.battlesnake 5 | starter-snake-java 6 | 0.0.1-SNAPSHOT 7 | starter-snake-java 8 | Battlesnake starter snake written in Java! 9 | 10 | 11 | 1.8 12 | 1.8 13 | UTF-8 14 | 15 | 16 | 17 | 18 | 19 | com.sparkjava 20 | spark-core 21 | 2.9.3 22 | 23 | 24 | 25 | com.fasterxml.jackson.core 26 | jackson-databind 27 | 2.12.4 28 | 29 | 30 | 31 | org.slf4j 32 | slf4j-simple 33 | 1.7.31 34 | 35 | 36 | 37 | org.junit.jupiter 38 | junit-jupiter-api 39 | 5.2.0 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.codehaus.mojo 48 | exec-maven-plugin 49 | 3.0.0 50 | 51 | java 52 | 53 | -classpath 54 | 55 | com.battlesnake.starter.Snake 56 | 57 | 58 | 59 | 60 | maven-surefire-plugin 61 | 2.21.0 62 | 63 | 64 | org.junit.platform 65 | junit-platform-surefire-provider 66 | 1.2.0 67 | 68 | 69 | org.junit.jupiter 70 | junit-jupiter-engine 71 | 5.2.0 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-assembly-plugin 78 | 79 | 80 | package 81 | 82 | single 83 | 84 | 85 | 86 | 87 | 88 | com.battlesnake.starter.Snake 89 | 90 | 91 | 92 | 93 | jar-with-dependencies 94 | 95 | starter-snake-java 96 | false 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/main/java/com/battlesnake/starter/Snake.java: -------------------------------------------------------------------------------- 1 | package com.battlesnake.starter; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import spark.Request; 9 | import spark.Response; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.Random; 16 | 17 | import static spark.Spark.port; 18 | import static spark.Spark.post; 19 | import static spark.Spark.get; 20 | 21 | /** 22 | * This is a simple Battlesnake server written in Java. 23 | * 24 | * For instructions see 25 | * https://github.com/BattlesnakeOfficial/starter-snake-java/README.md 26 | */ 27 | public class Snake { 28 | private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); 29 | private static final Handler HANDLER = new Handler(); 30 | private static final Logger LOG = LoggerFactory.getLogger(Snake.class); 31 | 32 | /** 33 | * Main entry point. 34 | * 35 | * @param args are ignored. 36 | */ 37 | public static void main(String[] args) { 38 | String port = System.getProperty("PORT"); 39 | if (port == null) { 40 | LOG.info("Using default port: {}", port); 41 | port = "8080"; 42 | } else { 43 | LOG.info("Found system provided port: {}", port); 44 | } 45 | port(Integer.parseInt(port)); 46 | get("/", HANDLER::process, JSON_MAPPER::writeValueAsString); 47 | post("/start", HANDLER::process, JSON_MAPPER::writeValueAsString); 48 | post("/move", HANDLER::process, JSON_MAPPER::writeValueAsString); 49 | post("/end", HANDLER::process, JSON_MAPPER::writeValueAsString); 50 | } 51 | 52 | /** 53 | * Handler class for dealing with the routes set up in the main method. 54 | */ 55 | public static class Handler { 56 | 57 | /** 58 | * For the start/end request 59 | */ 60 | private static final Map EMPTY = new HashMap<>(); 61 | 62 | /** 63 | * Generic processor that prints out the request and response from the methods. 64 | * 65 | * @param req 66 | * @param res 67 | * @return 68 | */ 69 | public Map process(Request req, Response res) { 70 | try { 71 | JsonNode parsedRequest = JSON_MAPPER.readTree(req.body()); 72 | String uri = req.uri(); 73 | LOG.info("{} called with: {}", uri, req.body()); 74 | Map snakeResponse; 75 | if (uri.equals("/")) { 76 | snakeResponse = index(); 77 | } else if (uri.equals("/start")) { 78 | snakeResponse = start(parsedRequest); 79 | } else if (uri.equals("/move")) { 80 | snakeResponse = move(parsedRequest); 81 | } else if (uri.equals("/end")) { 82 | snakeResponse = end(parsedRequest); 83 | } else { 84 | throw new IllegalAccessError("Strange call made to the snake: " + uri); 85 | } 86 | 87 | LOG.info("Responding with: {}", JSON_MAPPER.writeValueAsString(snakeResponse)); 88 | 89 | return snakeResponse; 90 | } catch (JsonProcessingException e) { 91 | LOG.warn("Something went wrong!", e); 92 | return null; 93 | } 94 | } 95 | 96 | /** 97 | * This method is called everytime your Battlesnake is entered into a game. 98 | * 99 | * Use this method to decide how your Battlesnake is going to look on the board. 100 | * 101 | * @return a response back to the engine containing the Battlesnake setup 102 | * values. 103 | */ 104 | public Map index() { 105 | Map response = new HashMap<>(); 106 | response.put("apiversion", "1"); 107 | response.put("author", ""); // TODO: Your Battlesnake Username 108 | response.put("color", "#888888"); // TODO: Personalize 109 | response.put("head", "default"); // TODO: Personalize 110 | response.put("tail", "default"); // TODO: Personalize 111 | return response; 112 | } 113 | 114 | /** 115 | * This method is called everytime your Battlesnake is entered into a game. 116 | * 117 | * Use this method to decide how your Battlesnake is going to look on the board. 118 | * 119 | * @param startRequest a JSON data map containing the information about the game 120 | * that is about to be played. 121 | * @return responses back to the engine are ignored. 122 | */ 123 | public Map start(JsonNode startRequest) { 124 | LOG.info("START"); 125 | return EMPTY; 126 | } 127 | 128 | /** 129 | * This method is called on every turn of a game. It's how your snake decides 130 | * where to move. 131 | * 132 | * Use the information in 'moveRequest' to decide your next move. The 133 | * 'moveRequest' variable can be interacted with as 134 | * com.fasterxml.jackson.databind.JsonNode, and contains all of the information 135 | * about the Battlesnake board for each move of the game. 136 | * 137 | * For a full example of 'json', see 138 | * https://docs.battlesnake.com/references/api/sample-move-request 139 | * 140 | * @param moveRequest JsonNode of all Game Board data as received from the 141 | * Battlesnake Engine. 142 | * @return a Map response back to the engine the single move to 143 | * make. One of "up", "down", "left" or "right". 144 | */ 145 | public Map move(JsonNode moveRequest) { 146 | 147 | try { 148 | LOG.info("Data: {}", JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(moveRequest)); 149 | } catch (JsonProcessingException e) { 150 | LOG.error("Error parsing payload", e); 151 | } 152 | 153 | /* 154 | * Example how to retrieve data from the request payload: 155 | * 156 | * String gameId = moveRequest.get("game").get("id").asText(); 157 | * 158 | * int height = moveRequest.get("board").get("height").asInt(); 159 | * 160 | */ 161 | 162 | JsonNode head = moveRequest.get("you").get("head"); 163 | JsonNode body = moveRequest.get("you").get("body"); 164 | 165 | ArrayList possibleMoves = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 166 | 167 | // Don't allow your Battlesnake to move back in on it's own neck 168 | avoidMyNeck(head, body, possibleMoves); 169 | 170 | // TODO: Using information from 'moveRequest', find the edges of the board and 171 | // don't 172 | // let your Battlesnake move beyond them board_height = ? board_width = ? 173 | 174 | // TODO Using information from 'moveRequest', don't let your Battlesnake pick a 175 | // move 176 | // that would hit its own body 177 | 178 | // TODO: Using information from 'moveRequest', don't let your Battlesnake pick a 179 | // move 180 | // that would collide with another Battlesnake 181 | 182 | // TODO: Using information from 'moveRequest', make your Battlesnake move 183 | // towards a 184 | // piece of food on the board 185 | 186 | // Choose a random direction to move in 187 | final int choice = new Random().nextInt(possibleMoves.size()); 188 | final String move = possibleMoves.get(choice); 189 | 190 | LOG.info("MOVE {}", move); 191 | 192 | Map response = new HashMap<>(); 193 | response.put("move", move); 194 | return response; 195 | } 196 | 197 | /** 198 | * Remove the 'neck' direction from the list of possible moves 199 | * 200 | * @param head JsonNode of the head position e.g. {"x": 0, "y": 0} 201 | * @param body JsonNode of x/y coordinates for every segment of a 202 | * Battlesnake. e.g. [ {"x": 0, "y": 0}, {"x": 1, "y": 0}, 203 | * {"x": 2, "y": 0} ] 204 | * @param possibleMoves ArrayList of String. Moves to pick from. 205 | */ 206 | public void avoidMyNeck(JsonNode head, JsonNode body, ArrayList possibleMoves) { 207 | JsonNode neck = body.get(1); 208 | 209 | if (neck.get("x").asInt() < head.get("x").asInt()) { 210 | possibleMoves.remove("left"); 211 | } else if (neck.get("x").asInt() > head.get("x").asInt()) { 212 | possibleMoves.remove("right"); 213 | } else if (neck.get("y").asInt() < head.get("y").asInt()) { 214 | possibleMoves.remove("down"); 215 | } else if (neck.get("y").asInt() > head.get("y").asInt()) { 216 | possibleMoves.remove("up"); 217 | } 218 | } 219 | 220 | /** 221 | * This method is called when a game your Battlesnake was in ends. 222 | * 223 | * It is purely for informational purposes, you don't have to make any decisions 224 | * here. 225 | * 226 | * @param endRequest a map containing the JSON sent to this snake. Use this data 227 | * to know which game has ended 228 | * @return responses back to the engine are ignored. 229 | */ 230 | public Map end(JsonNode endRequest) { 231 | LOG.info("END"); 232 | return EMPTY; 233 | } 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /src/test/java/com/battlesnake/starter/SnakeTest.java: -------------------------------------------------------------------------------- 1 | package com.battlesnake.starter; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.JsonNode; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertEquals; 16 | import static org.junit.jupiter.api.Assertions.assertTrue; 17 | 18 | public class SnakeTest { 19 | 20 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 21 | static { 22 | OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); 23 | } 24 | 25 | private Snake.Handler handler; 26 | 27 | @BeforeEach 28 | void setUp() { 29 | handler = new Snake.Handler(); 30 | } 31 | 32 | @Test 33 | void indexTest() throws IOException { 34 | 35 | Map response = handler.index(); 36 | assertEquals("#888888", response.get("color")); 37 | assertEquals("default", response.get("head")); 38 | assertEquals("default", response.get("tail")); 39 | } 40 | 41 | @Test 42 | void startTest() throws IOException { 43 | JsonNode startRequest = OBJECT_MAPPER.readTree("{}"); 44 | Map response = handler.end(startRequest); 45 | assertEquals(0, response.size()); 46 | 47 | } 48 | 49 | @Test 50 | void moveTest() throws IOException { 51 | JsonNode moveRequest = OBJECT_MAPPER.readTree( 52 | "{\"game\":{\"id\":\"game-00fe20da-94ad-11ea-bb37\",\"ruleset\":{\"name\":\"standard\",\"version\":\"v.1.2.3\"},\"timeout\":500},\"turn\":14,\"board\":{\"height\":11,\"width\":11,\"food\":[{\"x\":5,\"y\":5},{\"x\":9,\"y\":0},{\"x\":2,\"y\":6}],\"hazards\":[{\"x\":3,\"y\":2}],\"snakes\":[{\"id\":\"snake-508e96ac-94ad-11ea-bb37\",\"name\":\"My Snake\",\"health\":54,\"body\":[{\"x\":0,\"y\":0},{\"x\":1,\"y\":0},{\"x\":2,\"y\":0}],\"latency\":\"111\",\"head\":{\"x\":0,\"y\":0},\"length\":3,\"shout\":\"why are we shouting??\",\"squad\":\"\"},{\"id\":\"snake-b67f4906-94ae-11ea-bb37\",\"name\":\"Another Snake\",\"health\":16,\"body\":[{\"x\":5,\"y\":4},{\"x\":5,\"y\":3},{\"x\":6,\"y\":3},{\"x\":6,\"y\":2}],\"latency\":\"222\",\"head\":{\"x\":5,\"y\":4},\"length\":4,\"shout\":\"I'm not really sure...\",\"squad\":\"\"}]},\"you\":{\"id\":\"snake-508e96ac-94ad-11ea-bb37\",\"name\":\"My Snake\",\"health\":54,\"body\":[{\"x\":0,\"y\":0},{\"x\":1,\"y\":0},{\"x\":2,\"y\":0}],\"latency\":\"111\",\"head\":{\"x\":0,\"y\":0},\"length\":3,\"shout\":\"why are we shouting??\",\"squad\":\"\"}}"); 53 | Map response = handler.move(moveRequest); 54 | 55 | List options = new ArrayList(); 56 | options.add("up"); 57 | options.add("down"); 58 | options.add("left"); 59 | options.add("right"); 60 | 61 | assertTrue(options.contains(response.get("move"))); 62 | } 63 | 64 | @Test 65 | void endTest() throws IOException { 66 | JsonNode endRequest = OBJECT_MAPPER.readTree("{}"); 67 | Map response = handler.end(endRequest); 68 | assertEquals(0, response.size()); 69 | } 70 | 71 | @Test 72 | void avoidNeckAllTest() throws IOException { 73 | 74 | JsonNode testHead = OBJECT_MAPPER.readTree("{\"x\": 5, \"y\": 5}"); 75 | JsonNode testBody = OBJECT_MAPPER 76 | .readTree("[{\"x\": 5, \"y\": 5}, {\"x\": 5, \"y\": 5}, {\"x\": 5, \"y\": 5}]"); 77 | ArrayList possibleMoves = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 78 | ArrayList expectedResult = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 79 | 80 | handler.avoidMyNeck(testHead, testBody, possibleMoves); 81 | 82 | assertTrue(possibleMoves.size() == 4); 83 | assertTrue(possibleMoves.equals(expectedResult)); 84 | } 85 | 86 | @Test 87 | void avoidNeckLeftTest() throws IOException { 88 | 89 | JsonNode testHead = OBJECT_MAPPER.readTree("{\"x\": 5, \"y\": 5}"); 90 | JsonNode testBody = OBJECT_MAPPER 91 | .readTree("[{\"x\": 5, \"y\": 5}, {\"x\": 4, \"y\": 5}, {\"x\": 3, \"y\": 5}]"); 92 | ArrayList possibleMoves = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 93 | ArrayList expectedResult = new ArrayList<>(Arrays.asList("up", "down", "right")); 94 | 95 | handler.avoidMyNeck(testHead, testBody, possibleMoves); 96 | 97 | assertTrue(possibleMoves.size() == 3); 98 | assertTrue(possibleMoves.equals(expectedResult)); 99 | } 100 | 101 | @Test 102 | void avoidNeckRightTest() throws IOException { 103 | 104 | JsonNode testHead = OBJECT_MAPPER.readTree("{\"x\": 5, \"y\": 5}"); 105 | JsonNode testBody = OBJECT_MAPPER 106 | .readTree("[{\"x\": 5, \"y\": 5}, {\"x\": 6, \"y\": 5}, {\"x\": 7, \"y\": 5}]"); 107 | ArrayList possibleMoves = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 108 | ArrayList expectedResult = new ArrayList<>(Arrays.asList("up", "down", "left")); 109 | 110 | handler.avoidMyNeck(testHead, testBody, possibleMoves); 111 | 112 | assertTrue(possibleMoves.size() == 3); 113 | assertTrue(possibleMoves.equals(expectedResult)); 114 | } 115 | 116 | @Test 117 | void avoidNeckUpTest() throws IOException { 118 | 119 | JsonNode testHead = OBJECT_MAPPER.readTree("{\"x\": 5, \"y\": 5}"); 120 | JsonNode testBody = OBJECT_MAPPER 121 | .readTree("[{\"x\": 5, \"y\": 5}, {\"x\": 5, \"y\": 6}, {\"x\": 5, \"y\": 7}]"); 122 | ArrayList possibleMoves = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 123 | ArrayList expectedResult = new ArrayList<>(Arrays.asList("down", "left", "right")); 124 | 125 | handler.avoidMyNeck(testHead, testBody, possibleMoves); 126 | 127 | assertTrue(possibleMoves.size() == 3); 128 | assertTrue(possibleMoves.equals(expectedResult)); 129 | } 130 | 131 | @Test 132 | void avoidNeckDownTest() throws IOException { 133 | 134 | JsonNode testHead = OBJECT_MAPPER.readTree("{\"x\": 5, \"y\": 5}"); 135 | JsonNode testBody = OBJECT_MAPPER 136 | .readTree("[{\"x\": 5, \"y\": 5}, {\"x\": 5, \"y\": 4}, {\"x\": 5, \"y\": 3}]"); 137 | ArrayList possibleMoves = new ArrayList<>(Arrays.asList("up", "down", "left", "right")); 138 | ArrayList expectedResult = new ArrayList<>(Arrays.asList("up", "left", "right")); 139 | 140 | handler.avoidMyNeck(testHead, testBody, possibleMoves); 141 | 142 | assertTrue(possibleMoves.size() == 3); 143 | assertTrue(possibleMoves.equals(expectedResult)); 144 | } 145 | } 146 | --------------------------------------------------------------------------------