Ai framework and basic bot AI

This commit is contained in:
orcook 2018-03-26 16:51:39 -05:00
parent 8e75b2a025
commit c173b5facd
7 changed files with 277 additions and 20 deletions

View File

@ -42,7 +42,7 @@ export class GameComponent implements OnInit {
}
if (json.players) {
for (let player of json.players) {
if (player.isAlive) {
if (player.alive) {
this.drawPlayer(player);
}
}

View File

@ -0,0 +1,149 @@
/**
*
*/
package org.libertybikes.game.bot;
import java.util.Random;
import org.libertybikes.game.core.AIPlayer;
import org.libertybikes.game.core.DIRECTION;
import org.libertybikes.game.core.GameBoard;
public class Hal extends AIPlayer {
private int ticksTillRandomMove, ticksTillMove, numOfRandomMoves;
private final static int BOARD_SIZE = 121;
static Random ran = new Random();
private final static int CD = 2;
private final static int BD = 6;
public Hal(String id, String name, short playerNum) {
super(id, name, playerNum);
ticksTillRandomMove = 20;
ticksTillMove = 4;
}
@Override
public void broadCastBoard(short[][] board) {
if (--ticksTillRandomMove < 1 && numOfRandomMoves < 10) {
setDirection(DIRECTION.values()[ran.nextInt(4)]);
ticksTillRandomMove = 35;
numOfRandomMoves++;
} else {
if (--ticksTillMove > 1) {
return;
}
ticksTillMove = 2;
switch (direction) {
case DOWN:
if (ran.nextBoolean()) {
if (y + BD > BOARD_SIZE || checkCollision(board, x, y + CD)) {
if (checkCollision(board, x + CD, y + CD)) {
direction = DIRECTION.LEFT;
}
direction = DIRECTION.RIGHT;
}
} else {
if (y + BD > BOARD_SIZE || checkCollision(board, x, y + CD)) {
if (checkCollision(board, x - CD, y + CD)) {
direction = DIRECTION.RIGHT;
}
direction = DIRECTION.LEFT;
}
}
break;
case LEFT:
if (ran.nextBoolean()) {
if (x - BD < 0 || checkCollision(board, x - CD, y)) {
if (checkCollision(board, x - CD, y - CD)) {
direction = DIRECTION.DOWN;
}
direction = DIRECTION.UP;
}
} else {
if (x - BD < 0 || checkCollision(board, x - CD, y)) {
if (checkCollision(board, x - CD, y + CD)) {
direction = DIRECTION.UP;
}
direction = DIRECTION.DOWN;
}
}
break;
case RIGHT:
if (ran.nextBoolean()) {
if (x + BD > BOARD_SIZE || checkCollision(board, x + CD, y)) {
if (checkCollision(board, x + CD, y - CD)) {
direction = DIRECTION.DOWN;
}
direction = DIRECTION.UP;
}
} else {
if (x + BD > BOARD_SIZE || checkCollision(board, x + CD, y)) {
if (checkCollision(board, x + CD, y + CD)) {
direction = DIRECTION.UP;
}
direction = DIRECTION.DOWN;
}
}
break;
case UP:
if (ran.nextBoolean()) {
if (y - BD < 0 || checkCollision(board, x, y - CD)) {
if (checkCollision(board, x + CD, y - CD)) {
direction = DIRECTION.LEFT;
}
direction = DIRECTION.RIGHT;
}
} else {
if (y - BD < 0 || checkCollision(board, x, y - CD)) {
if (checkCollision(board, x - CD, y - CD)) {
direction = DIRECTION.RIGHT;
}
direction = DIRECTION.LEFT;
}
}
break;
}
}
switch (direction) {
case DOWN:
if (y + BD > BOARD_SIZE || checkCollision(board, x, y + CD)) {
setDirection(DIRECTION.UP);
}
break;
case LEFT:
if (x - BD < 0 || checkCollision(board, x - CD, y)) {
setDirection(DIRECTION.RIGHT);
}
break;
case RIGHT:
if (x + BD > BOARD_SIZE || checkCollision(board, x + CD, y)) {
setDirection(DIRECTION.LEFT);
}
break;
case UP:
if (y - BD < 0 || checkCollision(board, x, y - CD)) {
setDirection(DIRECTION.DOWN);
}
break;
}
}
private boolean checkCollision(short[][] board, int x, int y) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (board[x + i][y + j] == GameBoard.PLAYER_SPOT_TAKEN + playerNum || board[x + i][y + j] == GameBoard.SPOT_AVAILABLE) {
} else {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,14 @@
/**
*
*/
package org.libertybikes.game.core;
public abstract class AIPlayer extends Player {
public AIPlayer(String id, String name, short playerNum) {
super(id, name, playerNum);
}
public void broadCastBoard(short[][] board) {}
}

View File

@ -9,6 +9,8 @@ import java.util.Set;
import javax.json.bind.annotation.JsonbTransient;
import org.libertybikes.game.bot.Hal;
public class GameBoard {
public static final int BOARD_SIZE = 121;
@ -20,6 +22,7 @@ public class GameBoard {
public final Set<Obstacle> obstacles = new HashSet<>();
public final Set<MovingObstacle> movingObstacles = new HashSet<>();
public final Set<Player> players = new HashSet<>();
private final Set<AIPlayer> ai = new HashSet<>();
private final boolean[] takenPlayerSlots = new boolean[Player.MAX_PLAYERS];
public GameBoard() {
@ -139,4 +142,58 @@ public class GameBoard {
return true;
}
public boolean broadcastToAI() {
if (ai.isEmpty()) {
return false;
}
for (AIPlayer p : ai) {
try {
p.broadCastBoard(board);
} catch (Exception e) {
// bad ai
}
}
return true;
}
/**
*
*/
public void addAI() {
// Find first open player slot to fill, which determines position
short playerNum = -1;
for (short i = 0; i < takenPlayerSlots.length; i++) {
if (!takenPlayerSlots[i]) {
playerNum = i;
takenPlayerSlots[i] = true;
System.out.println("Player slot " + i + " taken");
break;
}
}
// Initialize Player
AIPlayer p = new Hal("Hal", "Hal", playerNum);
if (p.x > BOARD_SIZE || p.y > BOARD_SIZE)
throw new IllegalArgumentException("Player does not fit on board: " + p);
for (int i = 0; i < p.width; i++) {
for (int j = 0; j < p.height; j++) {
board[p.x + i][p.y + j] = (short) (PLAYER_SPOT_TAKEN + playerNum);
}
}
players.add(p);
ai.add(p);
}
public boolean removeAI(Player p) {
takenPlayerSlots[p.getPlayerNum()] = false;
players.remove(p);
return ai.remove(p);
}
}

View File

@ -69,10 +69,23 @@ public class GameRound implements Runnable {
public GameRound(String id) {
this.id = id;
nextRoundId = getRandomId();
board.addObstacle(new MovingObstacle(10, 5, 60, 60, 0, -1, 5));
board.addObstacle(new MovingObstacle(10, 5, 60, 65, 0, 1));
board.addObstacle(new MovingObstacle(5, 5, GameBoard.BOARD_SIZE / 2, GameBoard.BOARD_SIZE / 3, -1, -1, 1));
board.addObstacle(new MovingObstacle(5, 5, GameBoard.BOARD_SIZE / 2, GameBoard.BOARD_SIZE / 3 * 2, 1, 1));
// board.addObstacle(new MovingObstacle(10, 5, 60, 60, 0, -1, 5));
// board.addObstacle(new MovingObstacle(10, 5, 60, 65, 0, 1));
board.addObstacle(new MovingObstacle(5, 5, GameBoard.BOARD_SIZE / 2 - 10, GameBoard.BOARD_SIZE / 3, -1, -1));
board.addObstacle(new MovingObstacle(5, 5, GameBoard.BOARD_SIZE / 2 + 10, (GameBoard.BOARD_SIZE / 3 * 2) - 5, 1, 1));
// TL
board.addObstacle(new Obstacle(15, 1, GameBoard.BOARD_SIZE / 8, GameBoard.BOARD_SIZE / 8));
board.addObstacle(new Obstacle(1, 14, GameBoard.BOARD_SIZE / 8, GameBoard.BOARD_SIZE / 8 + 1));
// TR
board.addObstacle(new Obstacle(15, 1, ((GameBoard.BOARD_SIZE / 8) * 7) - 14, GameBoard.BOARD_SIZE / 8));
board.addObstacle(new Obstacle(1, 14, (GameBoard.BOARD_SIZE / 8) * 7, GameBoard.BOARD_SIZE / 8 + 1));
// BL
board.addObstacle(new Obstacle(15, 1, GameBoard.BOARD_SIZE / 8, (GameBoard.BOARD_SIZE / 8) * 7));
board.addObstacle(new Obstacle(1, 14, GameBoard.BOARD_SIZE / 8, ((GameBoard.BOARD_SIZE / 8) * 7) - 14));
// BR
board.addObstacle(new Obstacle(15, 1, ((GameBoard.BOARD_SIZE / 8) * 7) - 14, (GameBoard.BOARD_SIZE / 8) * 7));
board.addObstacle(new Obstacle(1, 14, (GameBoard.BOARD_SIZE / 8) * 7, ((GameBoard.BOARD_SIZE / 8) * 7) - 14));
}
public GameBoard getBoard() {
@ -110,6 +123,20 @@ public class GameRound implements Runnable {
broadcastGameBoard();
}
public void addAI() {
if (gameState != State.OPEN) {
return;
}
if (getPlayers().size() + 1 >= Player.MAX_PLAYERS) {
gameState = State.FULL;
}
board.addAI();
broadcastPlayerList();
broadcastGameBoard();
}
public void addSpectator(Session s) {
System.out.println("A spectator has joined.");
clients.put(s, new Client(s));
@ -205,6 +232,8 @@ public class GameRound implements Runnable {
return;
}
board.broadcastToAI();
boolean boardUpdated = board.moveObjects();
boolean death = false;
@ -212,7 +241,7 @@ public class GameRound implements Runnable {
boolean playerStatusChange = false;
boolean playersMoved = false;
for (Player p : getPlayers()) {
if (p.isAlive) {
if (p.isAlive()) {
if (p.movePlayer(board.board)) {
playersMoved = true;
} else {
@ -266,7 +295,7 @@ public class GameRound implements Runnable {
int alivePlayers = 0;
Player alive = null;
for (Player cur : getPlayers()) {
if (cur.isAlive) {
if (cur.isAlive()) {
alivePlayers++;
alive = cur;
}
@ -285,6 +314,10 @@ public class GameRound implements Runnable {
if (gameState != State.OPEN && gameState != State.FULL)
return;
while (gameState == State.OPEN) {
addAI();
}
// Issue a countdown to all of the clients
gameState = State.STARTING;

View File

@ -9,7 +9,7 @@ import java.util.Queue;
import javax.json.bind.annotation.JsonbPropertyOrder;
import javax.json.bind.annotation.JsonbTransient;
@JsonbPropertyOrder({ "id", "name", "color", "status", "isAlive", "x", "y", "width", "height", "oldX", "oldY", "trailPosX", "trailPosY", "trailPosX2", "trailPosY2" })
@JsonbPropertyOrder({ "id", "name", "color", "status", "alive", "x", "y", "width", "height", "oldX", "oldY", "trailPosX", "trailPosY", "trailPosX2", "trailPosY2" })
public class Player {
public static enum STATUS {
@ -51,12 +51,12 @@ public class Player {
public int oldY, y;
public final int width = 3;
public final int height = 3;
public boolean isAlive = true;
private boolean isAlive = true;
public int trailPosX, trailPosY, trailPosX2, trailPosY2;
private STATUS playerStatus = STATUS.Connected;
private final short playerNum;
private DIRECTION direction;
protected final short playerNum;
protected DIRECTION direction;
private DIRECTION lastDirection = null;
private DIRECTION desiredNextDirection = null;
@ -110,7 +110,7 @@ public class Player {
*
* @return True if the player is still alive after moving forward one space. False otherwise.
*/
public boolean movePlayer(short[][] s) {
public final boolean movePlayer(short[][] s) {
oldX = x;
oldY = y;
@ -126,28 +126,28 @@ public class Player {
case UP:
if (y - 1 < 0 || checkCollision(s, x, y - 1)) {
setStatus(STATUS.Dead);
return isAlive;
return isAlive();
}
moveUp(s);
break;
case DOWN:
if (y + height + 1 >= GameBoard.BOARD_SIZE || checkCollision(s, x, y + 1)) {
setStatus(STATUS.Dead);
return isAlive;
return isAlive();
}
moveDown(s);
break;
case RIGHT:
if (x + width + 1 >= GameBoard.BOARD_SIZE || checkCollision(s, x + 1, y)) {
setStatus(STATUS.Dead);
return isAlive;
return isAlive();
}
moveRight(s);
break;
case LEFT:
if (x - 1 < 0 || checkCollision(s, x - 1, y)) {
setStatus(STATUS.Dead);
return isAlive;
return isAlive();
}
moveLeft(s);
break;
@ -174,7 +174,7 @@ public class Player {
lastDirection = direction;
return isAlive;
return isAlive();
}
private boolean checkCollision(short[][] board, int x, int y) {
@ -226,7 +226,7 @@ public class Player {
setStatus(STATUS.Disconnected);
}
public void setStatus(STATUS newState) {
public final void setStatus(STATUS newState) {
if (newState == STATUS.Dead || newState == STATUS.Disconnected)
this.isAlive = false;
if (newState == STATUS.Dead && this.playerStatus == STATUS.Winner)
@ -242,4 +242,8 @@ public class Player {
public int getPlayerNum() {
return this.playerNum;
}
public boolean isAlive() {
return isAlive;
}
}

View File

@ -68,7 +68,7 @@ public class JsonDataTest {
assertEquals("{\"movingObstacles\":[],\"obstacles\":[" + obstacleJson + "],\"players\":[]}", jsonb.toJson(board));
// @JsonbPropertyOrder({ "id", "name", "color", "status", "isAlive", "x", "y", "width", "height", "oldX", "oldY", "trailPosX", "trailPosY", "trailPosX2", "trailPosY2" })
String bobJson = "{\"id\":\"1234\",\"name\":\"Bob\",\"color\":\"#DF740C\",\"status\":\"Connected\",\"isAlive\":true,\"x\":9,\"y\":9,\"width\":3,\"height\":3,\"oldX\":9,\"oldY\":9,\"trailPosX\":10,\"trailPosY\":10,\"trailPosX2\":10,\"trailPosY2\":10}";
String bobJson = "{\"id\":\"1234\",\"name\":\"Bob\",\"color\":\"#DF740C\",\"status\":\"Connected\",\"alive\":true,\"x\":9,\"y\":9,\"width\":3,\"height\":3,\"oldX\":9,\"oldY\":9,\"trailPosX\":10,\"trailPosY\":10,\"trailPosX2\":10,\"trailPosY2\":10}";
board.addPlayer("1234", "Bob");
assertEquals("{\"movingObstacles\":[],\"obstacles\":[" + obstacleJson + "],\"players\":[" + bobJson + "]}",
jsonb.toJson(board));
@ -98,7 +98,7 @@ public class JsonDataTest {
@Test
public void testPlayerList() {
PlayerList list = new OutboundMessage.PlayerList(Collections.singleton(new Player("123", "Bob", (short) 1)));
assertEquals("{\"playerlist\":[{\"id\":\"123\",\"name\":\"Bob\",\"color\":\"#FF0000\",\"status\":\"Connected\",\"isAlive\":true,\"x\":9,\"y\":110,\"width\":3,\"height\":3,\"oldX\":9,\"oldY\":110,\"trailPosX\":10,\"trailPosY\":111,\"trailPosX2\":10,\"trailPosY2\":111}]}",
assertEquals("{\"playerlist\":[{\"id\":\"123\",\"name\":\"Bob\",\"color\":\"#FF0000\",\"status\":\"Connected\",\"alive\":true,\"x\":9,\"y\":110,\"width\":3,\"height\":3,\"oldX\":9,\"oldY\":110,\"trailPosX\":10,\"trailPosY\":111,\"trailPosX2\":10,\"trailPosY2\":111}]}",
jsonb.toJson(list));
}