Summary
The project was constructed using Vue3.0 and JAVA Springboot framework. It is a two-player game: each user (bot) controls a snake in order to win the game.
GitHub link: https://github.com/bobliu96/kob
Game Design
In a 13 * 14 2D grid, two snakes start from the bottom left [1,1] or top right [13,14] corner. There are 20 random blocks generated from the map grid. Users can use “W” (up), “A” (left), “S” (down), or “D” (right) to control the direction of their snake. The initial length of the snake is 1 and grows to 10 within the first 10 steps. Starting from step 11, snake length increase by 1 in every 3 steps. The first snake to hit the edge, blocks, or opponent will lose the game. If the two snakes lose at the same time, they will tie the game.

System Design

There are three main components in the system: backend server, matching-system server, and Bot-running system server. In the backend server, it uses WebSocket to interact with clients (both server and client can send requests).
The matching-system server, adds all the potential users into a Matching Pool, and matches users based on their rankings. The system will try to match users with ranking differences of less than 10. When time increases, if no suitable users are found, the ranking difference allowance will increase by 10 within each second. For example, a user with a score of 1600 will take 10 seconds to match a user with a score of 1500, given there are no other users in the pool. The pool will send the matching result back to the backend server.
The Bot-running system, adds all the playing bots, code, and game info into the bot pool. JOOR will compile bot JAVA code and send a command for the bot to run. Note that if a user is playing, it will bypass the Bot-running system, since the user will calculate the route on their own.
Interaction
User: Use “W” (up), “A” (left), “S” (down), or “D” (right)
Bot: Use a simple algorithm to avoid obstacles.
import java.util.ArrayList;
import java.util.List;
public class Bot implements com.kob.botrunningsystem.utils.BotInterface {
static class Cell {
public int x,y;
public Cell(int x, int y) {
this.x = x;
this.y = y;
}
}
private boolean checkTailIncreasing(int step) { //Check rounds
if (step <= 10) return true;
return step % 3 == 1;
}
public List<Cell> getCells(int sx, int sy, String steps) {
steps = steps.substring(1,steps.length()-1);
List<Cell> res = new ArrayList<>();
int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
int x = sx, y = sy;
int step = 0;
res.add(new Cell(x,y));
for (int i=0; i<steps.length(); i++) {
int d = steps.charAt(i) - '0';
x += dx[d];
y += dy[d];
res.add(new Cell(x,y));
if (!checkTailIncreasing( ++ step)) {
res.remove(0);
}
}
return res;
}
@Override
public Integer nextMove(String input) {
String[] strs = input.split("#");
int[][] grid = new int[13][14];
for (int i=0, k=0; i<13; i++) {
for (int j=0; j<14; j++, k++) {
if (strs[0].charAt(k) == '1') {
grid[i][j] = 1;
}
}
}
int aSx = Integer.parseInt(strs[1]), aSy = Integer.parseInt(strs[2]);
int bSx = Integer.parseInt(strs[4]), bSy = Integer.parseInt(strs[5]);
List<Cell> aCells = getCells(aSx, aSy, strs[3]);
List<Cell> bCells = getCells(bSx, bSy, strs[6]);
for (Cell c:aCells) grid[c.x][c.y] = 1;
for (Cell c:bCells) grid[c.x][c.y] = 1;
int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
for (int i=0;i<4; i++) {
int x = aCells.get(aCells.size()-1).x + dx[i];
int y = aCells.get(aCells.size()-1).y + dy[i];
if (x >= 0 && x < 13 && y >= 0 && y < 14 && grid[x][y] == 0) {
return i;
}
}
return 0;
}
}
Deliverables
- CREATE USER ACCOUNT
- USER LOGIN
- UPDATE USER PROFILE
- ADD BOTS
- UPDATE BOTS
- DELETE BOTS
- GET BOTS
- BOT MATCHING SYSTEM
- BOT RUNNING SYSTEM
- MATCH REPLAY
Screenshots







Core Algorithms
Check Connectivity
By using the flood fill algorithm, to make sure the game map is connected. Sometimes the randomly generated blocks will create a real “wall” so that two snakes are not able to meet each other.
private boolean checkConnectivity(int sx, int sy, int tx, int ty) {
if (sx == tx && sy == ty) return true;
grid[sx][sy] = 1;
for (int i = 0; i < 4; i ++) {
int x = sx + dx[i], y = sy + dy[i];
if (x >= 0 && x < this.rows && y >=0 && y < this.cols && grid[x][y] == 0) {
if (checkConnectivity(x,y,tx,ty)) {
grid[sx][sy] = 0;
return true;
}
}
}
grid[sx][sy] = 0;
return false;
}