Skip to content

Commit

Permalink
snake vision
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentin authored and valentin-stamate committed Aug 15, 2021
1 parent eacce1b commit d94b452
Show file tree
Hide file tree
Showing 19 changed files with 567 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
build
*.jar
/library
/gradle
9 changes: 3 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'java'
}

group 'org.perosal'
group 'com.valentinstamate'
version '1.0'

repositories {
Expand All @@ -11,13 +11,10 @@ repositories {

dependencies {
implementation 'org.processing:core:3.3.7'
implementation 'org.apache.httpcomponents:httpclient:4.5'
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.11.1'
implementation fileTree("library")

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
}

test {
Expand Down
5 changes: 0 additions & 5 deletions gradle/wrapper/gradle-wrapper.properties

This file was deleted.

4 changes: 2 additions & 2 deletions src/main/java/MainClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

public class MainClass {
public static void main(String... args) {
SecondaryWindow secondaryWindow = new SecondaryWindow();
PApplet.runSketch(new String[]{""}, secondaryWindow);
// SecondaryWindow secondaryWindow = new SecondaryWindow();
// PApplet.runSketch(new String[]{""}, secondaryWindow);

MainWindow mainWindow = new MainWindow();
PApplet.runSketch(new String[]{""}, mainWindow);
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/board/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import util.Config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Board {
Expand Down Expand Up @@ -38,6 +39,9 @@ public void setOnRefreshListener(Observer observer) {
void makeSnakeStep() {
for (Snake snake : snakeList) {
snake.makeStep();
int[] vision = snake.getVision();
System.out.println(Arrays.toString(vision));

}
}

Expand Down Expand Up @@ -65,6 +69,19 @@ void drawSnakes() {
}
}

public boolean allSnakesFinished() {
int n = snakeList.size();

int snakesFinished = 0;
for (Snake snake : snakeList) {
if (snake.isFinished()) {
snakesFinished++;
}
}

return n == snakesFinished;
}

void drawBoard() {
for (int i = 0; i < Config.BOARD_ROWS; i++) {
for (int j = 0; j < Config.BOARD_COLUMNS; j++) {
Expand Down
123 changes: 119 additions & 4 deletions src/main/java/board/snake/Snake.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import board.CellType;
import board.snake.food.Food;
import controller.controlers.genetic.GeneticAiUtil;
import observer.Observer;
import observer.OnFinishObserver;
import observer.OnFoodEaten;
Expand All @@ -11,6 +12,7 @@

public class Snake {

/* dir[0] -> i, dir[1] -> j */
private final int[] direction = new int[]{0, 1};
private final int[][] boardMatrix;

Expand Down Expand Up @@ -155,9 +157,14 @@ private void onSnakeCollide() {
for (OnFinishObserver observer : onFinishObservers) {
observer.update(snakeData);
}

snakeFinished = true;
}

public boolean checkFoodCollision(int i, int j) {
return food.getI() == i && food.getJ() == j;
}

/* FOOD LOGIC */
public void repositionFood() {
List<Integer> freeCells = new ArrayList<>();
Expand All @@ -178,10 +185,6 @@ public void repositionFood() {
food.updateFoodPosition(i, j);
}

public boolean checkFoodCollision(int i, int j) {
return food.getI() == i && food.getJ() == j;
}

public void addPoint() {
snakeIncrease = true;
score+= 5;
Expand Down Expand Up @@ -230,6 +233,118 @@ public int[] getDirection() {
return new int[]{direction[0], direction[1]};
}

/* Gets a 24 double vector */
public int[] getVision() {

// degrees are in reverse because the coords are in reverse
int[] head = direction;
int[] west = GeneticAiUtil.reverse(GeneticAiUtil.rotateVectorInt(head[1], head[0], -90));
int[] south = GeneticAiUtil.reverse(GeneticAiUtil.rotateVectorInt(head[1], head[0], -180));
int[] east = GeneticAiUtil.reverse(GeneticAiUtil.rotateVectorInt(head[1], head[0], -270));

double[] rotationDiag = GeneticAiUtil.reverse(GeneticAiUtil.rotateVector(head[1], head[0], -45));

int[] northWest = GeneticAiUtil.crushedDiagonalsToDirection(rotationDiag);
int[] southWest = GeneticAiUtil.reverse(GeneticAiUtil.rotateVectorInt(northWest[1], northWest[0], -90));
int[] southEast = GeneticAiUtil.reverse(GeneticAiUtil.rotateVectorInt(northWest[1], northWest[0], -180));
int[] northEast = GeneticAiUtil.reverse(GeneticAiUtil.rotateVectorInt(northWest[1], northWest[0], -270));

int i = getHeadI();
int j = getHeadJ();

int[] headData = getDirectionData(i, j, head);
int[] westData = getDirectionData(i, j, west);
int[] southData = getDirectionData(i, j, south);
int[] eastData = getDirectionData(i, j, east);

int[] northWestData = getDirectionData(i, j, northWest);
int[] southWestData = getDirectionData(i, j, southWest);
int[] southEastData = getDirectionData(i, j, southEast);
int[] northEastData = getDirectionData(i, j, northEast);

int[] vision = new int[8 * 3];

System.arraycopy(headData, 0, vision, 0, 3);
System.arraycopy(northWestData, 0, vision, 3, 3);
System.arraycopy(westData, 0, vision, 6, 3);
System.arraycopy(southWestData, 0, vision, 9, 3);
System.arraycopy(southData, 0, vision, 12, 3);
System.arraycopy(southEastData, 0, vision, 15, 3);
System.arraycopy(eastData, 0, vision, 18, 3);
System.arraycopy(northEastData, 0, vision, 21, 3);

return vision;
}

public double[] getNormalizedVision() {
return normalizeDistances(getVision());
}

private int[] getDirectionData(int i, int j, int[] direction) {
int rows = boardMatrix.length;
int cols = boardMatrix[0].length;

boolean foodFound = false;
boolean tailFound = false;

int foodDistance = 0;
int tailDistance = 0;
int wallDistance = 0;

for (; i < rows && i >= 0 && j < cols && j >= 0;
i += direction[0], j += direction[1]) {

if (!foodFound) {
foodDistance++;
}

wallDistance++;

if (!tailFound) {
tailDistance++;
}

if (checkFoodCollision(i, j)) {
foodFound = true;
}

if (!tailFound && checkTailCollision(i + direction[0], j + direction[1])) {
tailFound = true;
}

}

int maxDistance = Math.max(rows, cols);

if (!foodFound) {
foodDistance = maxDistance;
}

if (!tailFound) {
tailDistance = maxDistance;
}

if (tailFound) {
wallDistance = maxDistance;
}

return new int[]{foodDistance, tailDistance, wallDistance};
}

private double[] normalizeDistances(int[] distances) {
int n = distances.length;

double[] normalizedDistance = new double[n];

double max = Math.max(boardMatrix.length, boardMatrix[0].length);

for (int i = 0; i < n; i++) {
normalizedDistance[i] = 1.0 * distances[i] / max;
}

return normalizedDistance;
}

/* CHANGING DIRECTION */
public void moveUp() {
if (direction[0] == 1 || !canChangeDirection) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/controller/SnakeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public abstract class SnakeController {
protected final List<Snake> snakeList;
protected final PApplet pApplet;

SnakeController(PApplet pApplet) {
public SnakeController(PApplet pApplet) {
this.pApplet = pApplet;
this.snakeList = new ArrayList<>();
this.board = new Board(pApplet, snakeList);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package controller;
package controller.controlers;

import board.snake.Snake;
import controller.SnakeController;
import controller.node.Node;
import observer.OnUpdateObserver;
import processing.core.PApplet;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package controller;
package controller.controlers;

import board.snake.Snake;
import controller.SnakeController;
import processing.core.PApplet;
import processing.event.KeyEvent;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package controller;
package controller.controlers;

import board.snake.Snake;
import controller.SnakeController;
import processing.core.PApplet;
import processing.event.KeyEvent;

public class TwoPlayersController extends SnakeController{
public class TwoPlayersController extends SnakeController {

private final Snake snakeA;
private final Snake snakeB;
Expand Down
108 changes: 108 additions & 0 deletions src/main/java/controller/controlers/genetic/GeneticAiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package controller.controlers.genetic;

import board.snake.Snake;
import controller.SnakeController;
import ga.AbstractGeneticAlgorithm;
import ga.config.GaConfig;
import ga.lambda.BeforeEvaluationEvent;
import ga.member.AbstractMember;
import ga.operators.crossover.AbstractCrossover;
import ga.operators.crossover.OnePointCrossover;
import ga.operators.mutation.AbstractMutation;
import ga.operators.mutation.SimpleMutation;
import ga.operators.selection.AbstractSelection;
import ga.operators.selection.TournamentSelection;
import neural_network.NeuralNetwork;
import processing.core.PApplet;

import java.util.ArrayList;
import java.util.List;

public class GeneticAiController extends SnakeController {

public GeneticAiController(PApplet pApplet) {
super(pApplet);
}

@Override
public void run() {

int[] layerSize = new int[]{3, 4, 5, 3};
int geneLength = SmartSnake.calculateGeneLength(layerSize);

NeuralNetwork neuralNetwork = new NeuralNetwork(layerSize);

GaConfig gaConfig = GaConfig.initializeWithParameters(
100,
1000,
1,
geneLength,
0.0015,
0.02
);

AbstractMutation abstractMutation = new SimpleMutation();
AbstractCrossover abstractCrossover = new OnePointCrossover();
AbstractSelection abstractSelection = new TournamentSelection();

AbstractGeneticAlgorithm abstractGeneticAlgorithm = new GeneticAlgorithm(
gaConfig,
abstractMutation,
abstractCrossover,
abstractSelection
);

List<SmartSnake> smartSnakes = new ArrayList<>();

BeforeEvaluationEvent beforeEvaluationEvent = (population) -> {
for (AbstractMember abstractMember : population) {
short[] gene = abstractMember.getGeneCopy();

Snake snake = new Snake(super.board.getBoardMatrix());
SmartSnake smartSnake = new SmartSnake(snake, gene, layerSize);

smartSnakes.add(smartSnake);

snakeList.clear();
snakeList.add(smartSnake.getSnake());
}

while (!board.allSnakesFinished()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

for (int i = 0; i < snakeList.size(); i++) {
Snake snake = snakeList.get(i);
AbstractMember abstractMember = population.get(i);

((Member) abstractMember).setScore(snake.getScore());
}

};

new Thread(abstractGeneticAlgorithm::start).start();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

super.board.start();

while (!board.allSnakesFinished()) {
for (SmartSnake smartSnake : smartSnakes) {
Snake snake = smartSnake.getSnake();

if (!snake.isFinished()) {
smartSnake.predictNext();
}
}
}

}
}
Loading

0 comments on commit d94b452

Please sign in to comment.