Skip to content

Commit

Permalink
genetic ai mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentin authored and valentin-stamate committed Aug 16, 2021
1 parent d94b452 commit b4a2542
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 95 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
build
*.jar
/library
/gradle
/gradle
ga/
neural_network/
11 changes: 4 additions & 7 deletions src/main/java/board/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
import observer.OnUpdateObserver;
import processing.core.PApplet;
import util.Config;

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

public class Board {
Expand Down Expand Up @@ -37,11 +35,9 @@ public void setOnRefreshListener(Observer observer) {

/* SNAKES CONTROL */
void makeSnakeStep() {
for (Snake snake : snakeList) {
for (int i = 0; i < snakeList.size(); i++) {
Snake snake = snakeList.get(i);
snake.makeStep();
int[] vision = snake.getVision();
System.out.println(Arrays.toString(vision));

}
}

Expand All @@ -64,7 +60,8 @@ public void draw() {
}

void drawSnakes() {
for (Snake snake : snakeList) {
for (int i = 0; i < snakeList.size(); i++) {
Snake snake = snakeList.get(i);
snake.drawSnake();
}
}
Expand Down
35 changes: 23 additions & 12 deletions src/main/java/board/snake/Snake.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ public class Snake {

private boolean canChangeDirection = true;
private boolean snakeIncrease = true;
private boolean snakeFinished = false;
protected boolean snakeFinished = false;

private final Food food;
private int score;
protected int score;
protected int steps;

private final List<OnFinishObserver> onFinishObservers;
private final List<Observer> observerList;
Expand All @@ -35,7 +36,9 @@ public Snake(int[][] boardMatrix) {
this.snakeCellsHashSet = new HashSet<>();
this.onFinishObservers = new ArrayList<>();
this.observerList = new ArrayList<>();

this.score = 0;
this.steps = 0;

this.food = new Food(boardMatrix);
initialize();
Expand All @@ -49,7 +52,7 @@ public void initialize() {
direction[0] = Util.generateRandom(-1, 2);
direction[1] = 0;
if (direction[0] == 0) {
direction[1] = Util.generateRandom(-1, 2);
direction[1] = 1;
}

initializeSnakePosition();
Expand All @@ -62,8 +65,11 @@ public void initialize() {
}

private void initializeSnakePosition() {
int currentI = Config.BOARD_ROWS / 2 + Util.generateRandom(-7, 7);
int currentJ = Config.BOARD_COLUMNS / 2 + Util.generateRandom(-7, 7);
// int currentI = Config.BOARD_ROWS / 2 + Util.generateRandom(-7, 7);
// int currentJ = Config.BOARD_COLUMNS / 2 + Util.generateRandom(-7, 7);

int currentI = Config.BOARD_ROWS / 2;
int currentJ = Config.BOARD_COLUMNS / 2;

if (Config.BOARD_COLUMNS < 10) {
currentI = 2;
Expand Down Expand Up @@ -129,6 +135,8 @@ public void makeStep() {
}

canChangeDirection = true;

steps++;
}

/* DRAWING LOGIC */
Expand Down Expand Up @@ -187,7 +195,7 @@ public void repositionFood() {

public void addPoint() {
snakeIncrease = true;
score+= 5;
score++;

for (Observer observer : observerList) {
if (observer instanceof OnFoodEaten) {
Expand Down Expand Up @@ -235,7 +243,6 @@ public int[] getDirection() {

/* 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));
Expand Down Expand Up @@ -264,10 +271,10 @@ public int[] getVision() {

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(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);
Expand Down Expand Up @@ -336,7 +343,7 @@ private double[] normalizeDistances(int[] distances) {

double[] normalizedDistance = new double[n];

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

for (int i = 0; i < n; i++) {
normalizedDistance[i] = 1.0 * distances[i] / max;
Expand Down Expand Up @@ -385,4 +392,8 @@ public void moveDown() {
direction[1] = 0;
canChangeDirection = false;
}

public int getSteps() {
return steps;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
import ga.AbstractGeneticAlgorithm;
import ga.config.GaConfig;
import ga.lambda.BeforeEvaluationEvent;
import ga.lambda.observers.OnNewGeneration;
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 util.Config;

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

public class GeneticAiController extends SnakeController {

Expand All @@ -27,17 +27,14 @@ public GeneticAiController(PApplet pApplet) {
@Override
public void run() {

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

NeuralNetwork neuralNetwork = new NeuralNetwork(layerSize);
int geneLength = SmartSnake.calculateGeneLength(Config.LAYER_SIZE);

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

Expand All @@ -52,21 +49,18 @@ public void run() {
abstractSelection
);

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

BeforeEvaluationEvent beforeEvaluationEvent = (population) -> {
for (AbstractMember abstractMember : population) {
BeforeEvaluationEvent beforeEvaluationEvent = (copyReferencePopulation) -> {
/* Put the population on the board */
snakeList.clear();
for (AbstractMember abstractMember : copyReferencePopulation) {
short[] gene = abstractMember.getGeneCopy();

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

smartSnakes.add(smartSnake);
SmartSnake smartSnake = new SmartSnake(super.board.getBoardMatrix(), gene, Config.LAYER_SIZE);

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

/* Wait until the all of them are done */
while (!board.allSnakesFinished()) {
try {
Thread.sleep(100);
Expand All @@ -75,34 +69,31 @@ public void run() {
}
}

/* TODO: implement a method in GA to print all scores */

/* Calculate the score (= fitness) */
for (int i = 0; i < snakeList.size(); i++) {
Snake snake = snakeList.get(i);
AbstractMember abstractMember = population.get(i);
AbstractMember abstractMember = copyReferencePopulation.get(i);

((Member) abstractMember).setScore(snake.getScore());
((Member) abstractMember).setScore(snake.getScore() * 50 + snake.getSteps() / 10 + 1);
abstractMember.calculateFitness();
}

};

new Thread(abstractGeneticAlgorithm::start).start();
OnNewGeneration onNewGeneration = ((copyPopulation, generation) -> {
copyPopulation.sort(Collections.reverseOrder());

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Generation %d. Best fitness %6.2f\n", generation, copyPopulation.get(0).getFitness());
});

super.board.start();
abstractGeneticAlgorithm.addPopulationObserver(onNewGeneration);

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

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

new Thread(abstractGeneticAlgorithm::start).start();
}
}
10 changes: 10 additions & 0 deletions src/main/java/controller/controlers/genetic/GeneticAiUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,14 @@ public static int[] crushedDiagonalsToDirection(double[] dir) {
return newDir;
}

public static double getMax(double[] vector) {
double max = 0;

for (double x : vector) {
max = Math.max(x, max);
}

return max;
}

}
46 changes: 44 additions & 2 deletions src/main/java/controller/controlers/genetic/GeneticAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

import ga.AbstractGeneticAlgorithm;
import ga.config.GaConfig;
import ga.conversion.RangeDoubleToInterval;
import ga.member.AbstractMember;
import ga.operators.crossover.AbstractCrossover;
import ga.operators.mutation.AbstractMutation;
import ga.operators.selection.AbstractSelection;
import neural_network.NeuralNetwork;
import util.Config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class GeneticAlgorithm extends AbstractGeneticAlgorithm {
Expand All @@ -16,11 +22,47 @@ public GeneticAlgorithm(GaConfig gaConfig, AbstractMutation abstractMutation, Ab

@Override
public List<AbstractMember> generatePopulation() {
return null;
List<AbstractMember> population = new ArrayList<>();

for (int i = 0; i < gaConfig.populationSize; i++) {
NeuralNetwork neuralNetwork = new NeuralNetwork(Config.LAYER_SIZE);

double[][][] brain = neuralNetwork.getBrainReference();
double[] vector = GeneticAiUtil.brainToVector(brain, Config.LAYER_SIZE);
short[] bitmap = RangeDoubleToInterval.toBitMapVector(vector, Config.START, Config.END, Config.PRECISION);

population.add(new Member(bitmap));
}

return population;
}

@Override
public List<AbstractMember> getAbstractMembersFromGene(List<short[]> list, GaConfig gaConfig, AbstractMutation abstractMutation) {
return null;
List<AbstractMember> members = new ArrayList<>();

for (short[] gene : list) {
members.add(new Member(gene));
}

return members;
}

@Override
public void selectPopulation(List<AbstractMember> population) {
List<AbstractMember> bestNThMembers = new ArrayList<>();
List<AbstractMember> copyPopulation = getPopulationCopy(population);

super.selectPopulation(population);

copyPopulation.sort(Collections.reverseOrder());

int min = Math.min(5, population.size());

for (int i = 0; i < min; i++) {
bestNThMembers.add(copyPopulation.get(i));
}

population.addAll(bestNThMembers);
}
}
9 changes: 7 additions & 2 deletions src/main/java/controller/controlers/genetic/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ public void calculateScore() {

}

public void setScore(int score) {
public void setScore(double score) {
super.score = score;
}

@Override
public AbstractMember getCopy() {
return new Member(Arrays.copyOf(gene, gene.length));
Member member = new Member(Arrays.copyOf(gene, gene.length));

member.setScore(score);
member.calculateFitness();

return member;
}
}
Loading

0 comments on commit b4a2542

Please sign in to comment.