Welcome to the code repository of the Serpentine team that competed in the AI Snakes competition in 2020. The team consisted of: Bram Grooten, Imre Schilstra, Wolf van der Hert, and Dik van Genuchten.
The final bot we submitted is called AdderBoaCobra, which is a hint to the ABC of AlphaBeta Circle (the main tactics that we use).
You can find its code in the folder: src/finalBot/AdderBoaCobra.java
.
Beware that it uses 300 ms per turn, instead of the full 1 second we were given for computation time (see the rules here).
If you'd like it to run faster or slower, you can easily adjust the time per turn by setting the variable TIME_PER_TURN
at the top of the file of AdderBoaCobra
.
There is a full explanation of how to run the game below, but we'll give a quick summary here. Open this project in IntelliJ.
Click Edit configurations
in the top right. For main class, put snakes.SnakesUIMain
. As program arguments put: humans.Human finalBot.AdderBoaCobra
.
Then you'll be able to play with the arrow keys. For JRE you need 1.8.0_241
.
If you just want to see two bots competing against each other, change the program arguments to finalBot.AdderBoaCobra imre.baseCode
for instance.
You can search for more (earlier) versions of our bot in the folders within src/
.
Extra information:
- In the file
src/snakes/SnakesWindow
you can change the time per game. Now it's set at a maximum of 3 minutes, just like it was during the competition. - In the file
src/snakes/SnakesUIMain
you can set the number of games to more than our default 1. Search for the linestart_tournament_n_times(1, bots);
(with ctrl+F) and increase the 1 to 5 for example.
Below we left the readme file of the original competition repository, which further explains how to set up.
Good luck!
original readme starting here
Snakes AI has got a very simple interface for bot creation. The minimum you need to implement in order to make a functioning bot is a class, that implements the Bot interface.
- JDK 8
- IntelliJ Idea (optional, recommended)
Clone the repo using Git:
// ssh
git clone [email protected]:BeLuckyDaf/snakes-game-tutorial.git
// https (you don't know why the one above doesn't work)
git clone https://github.com/BeLuckyDaf/snakes-game-tutorial.git
or simply download the zip file.
Open snakes.ipr in IntelliJ Idea or any other IDE.
-
Set up JDK
a. Open Project Structure
b. Choose the correct JDK version and project language level, it is level 8.
-
Set up a configuration
a. Open the configuration settings
b. Set correct JRE version for Main
c. Add or change your bot name. (return here after you've finished the tutorial)
The sample bot is already set up in the project, simply compile and launch the game by pressing Shift+F10 or Run > Run 'Main'
in the menu.
First, let's create a new package for you. Name it however you want, for the sake of this tutorial, we'll call it student.
Our bot class must implement the Bot interface, otherwise, we won't be able to tell the game what it does.
package student
public class MyBot implements Bot {
}
That also means, that we must implement the following method, specified in the interface.
public Direction chooseDirection(Snake snake,
Snake opponent,
Coordinate mazeSize,
Coordinate apple)
In fact, this is the only method that we are required to implement in order to make our bot work, so let's make a simple bot, the only function of which would be not dying.
So our code now looks like this:
package student
public class MyBot implements Bot {
@Override
public Direction chooseDirection(Snake snake, Snake opponent, Coordinate mazeSize, Coordinate apple) {
return null;
}
}
The code above is not going to work yet, so at least for it to make sense and for the sake of simplicity, we will now make the snake always go upwards.
@Override
public Direction chooseDirection(Snake snake, Snake opponent, Coordinate mazeSize, Coordinate apple) {
return Direction.UP;
}
You can try it out now, see how to run your bot.
Now to be able to make some randomness we'll also define a list of all directions that we could possible go in as an array.
private static final Direction[] DIRECTIONS = new Direction[] {Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT};
And pick one random direction.
package student
import java.util.Random;
public class MyBot implements Bot {
private static final Direction[] DIRECTIONS = new Direction[] {Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT};
@Override
public Direction chooseDirection(Snake snake, Snake opponent, Coordinate mazeSize, Coordinate apple) {
Random random = new Random();
Direction randomDir = DIRECTIONS[random.nextInt(DIRECTIONS.length)]
return randomDir;
}
}
See how to run your bot.
Now we have a randomly moving bot, but it's not enough, it'll never achieve anything by just randomly moving in different directions, moreover, the snake can't move on all four directions, since there is no way it would go backwards, let's dig into this.
To achieve a more advanced AI, first take look at what information do we possess:
- Our own snake
- The opponent snake
- The size of the maze
- The coordinate of the apple
It would help us if we knew where our own snake's head is located, so let's add that.
Coordinate head = snake.getHead();
We can move in any direction, except going backwards, so we should find the coordinate of "backwards". We will just take the second coordinate from our snake list of body parts.
Coordinate afterHeadNotFinal = null;
if (snake.body.size() >= 2) {
Iterator<Coordinate> it = snake.body.iterator();
it.next();
afterHeadNotFinal = it.next();
}
final Coordinate afterHead = afterHeadNotFinal;
Now remove the backwards direction from the list of our possible moves.
Direction[] validMoves = Arrays.stream(DIRECTIONS)
.filter(d -> !head.moveTo(d).equals(afterHead))
.sorted()
.toArray(Direction[]::new);
Since our bot doesn't want to die, filter out all directions which might cause that.
Direction[] notLosing = Arrays.stream(validMoves)
.filter(d -> head.moveTo(d).inBounds(mazeSize)) // maze bounds
.filter(d -> !opponent.elements.contains(head.moveTo(d))) // opponent body
.filter(d -> !snake.elements.contains(head.moveTo(d))) // and yourself
.sorted()
.toArray(Direction[]::new);
Now choose which move to take. We could add some randomness here, but it is not important now. So if we can move without losing, do it, otherwise, take any valid move that is not backwards, since there is no way to not lose.
if (notLosing.length > 0) return notLosing[0];
else return validMoves[0];
So here is what we came up with, this is included with the repository, you find it as johndoe.SampleBot.
package student
public class MyBot implements Bot {
private static final Direction[] DIRECTIONS = new Direction[] {Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT};
@Override
/* choose the direction (stupidly) */
public Direction chooseDirection(Snake snake, Snake opponent, Coordinate mazeSize, Coordinate apple) {
Coordinate head = snake.getHead();
/* Get the coordinate of the second element of the snake's body
* to prevent going backwards */
Coordinate afterHeadNotFinal = null;
if (snake.body.size() >= 2) {
Iterator<Coordinate> it = snake.body.iterator();
it.next();
afterHeadNotFinal = it.next();
}
final Coordinate afterHead = afterHeadNotFinal;
/* The only illegal move is going backwards. Here we are checking for not doing it */
Direction[] validMoves = Arrays.stream(DIRECTIONS)
.filter(d -> !head.moveTo(d).equals(afterHead)) // Filter out the backwards move
.sorted()
.toArray(Direction[]::new);
/* Just naïve greedy algorithm that tries not to die at each moment in time */
Direction[] notLosing = Arrays.stream(validMoves)
.filter(d -> head.moveTo(d).inBounds(mazeSize)) // Don't leave maze
.filter(d -> !opponent.elements.contains(head.moveTo(d))) // Don't collide with opponent...
.filter(d -> !snake.elements.contains(head.moveTo(d))) // and yourself
.sorted()
.toArray(Direction[]::new);
if (notLosing.length > 0) return notLosing[0];
else return validMoves[0];
/* ^^^ Cannot avoid losing here */
}
}
In order to use your own bot, you must pass your package name and the name of the class as program arguments to the game.
You must pass two bots, in order for the game to work, those could be the same.
Let's use your newly written bot with the one, provided by us. Even though, they actually are the same.
java snakes.SnakesUIMain johndoe.SampleBot student.MyBot
Note: this command is executed in the folder with already compiled .class files, not in the src directory. You do not need to worry about this if you are using an IDE, such as IntelliJ Idea.
If you are using any IDE, you could add those program arguments to be added automatically whenever you want to run or debug the program.
Try to make the bot go towards the apple, it's basically the point of the game, but remember that you are not the only snake on the field.
We're going to let you out on a journey of bot creation now, good luck and have fun!