AI Dino Game Development
AI Dino Game Development
Dino game
//Globals
Population pop;
int upToGen = 0;
Player genPlayerTemp;
//images
PImage dinoRun1;
PImage dinoRun2;
PImage dinoJump;
PImage dinoDuck;
PImage dinoDuck1;
PImage smallCactus;
PImage manySmallCactus;
PImage bigCactus;
PImage bird;
PImage bird1;
int randomAddition = 0;
int groundCounter = 0;
//-----------------------------------------------------------------------------------------------------------------------------------
---------------
void setup() {
frameRate(60);
fullScreen();
//size(900,700);
dinoRun1 = loadImage("dinorun0000.png");
dinoRun2 = loadImage("dinorun0001.png");
dinoJump = loadImage("dinoJump0000.png");
dinoDuck = loadImage("dinoduck0000.png");
dinoDuck1 = loadImage("dinoduck0001.png");
smallCactus = loadImage("cactusSmall0000.png");
bigCactus = loadImage("cactusBig0000.png");
manySmallCactus = loadImage("cactusSmallMany0000.png");
bird = loadImage("berd.png");
bird1 = loadImage("berd2.png");
//-----------------------------------------------------------------------------------------------------------------------------------
---------------------
void draw() {
drawToScreen();
genPlayerTemp.updateLocalObstacles();
genPlayerTemp.look();
genPlayerTemp.think();
genPlayerTemp.update();
genPlayerTemp.show();
upToGen ++;
if (upToGen >= pop.genPlayers.size()) {//if at the end then return to the start and stop doing it
upToGen= 0;
showBestEachGen = false;
} else {//if not at the end then get the next generation
genPlayerTemp = pop.genPlayers.get(upToGen).cloneForReplay();
updateObstacles();
pop.updateAlive();
} else {//all dead
//genetic algorithm
pop.naturalSelection();
resetObstacles();
//-----------------------------------------------------------------------------------------------------------------------------------
----------------------
void drawToScreen() {
if (!showNothing) {
background(250);
stroke(0);
strokeWeight(2);
drawBrain();
writeInfo();
//-----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------
int w = 600;
int h = 400;
if (showBestEachGen) {
if (!pop.pop.get(i).dead) {
break;
//-----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------
void writeInfo() {
fill(200);
textAlign(LEFT);
textSize(40);
if (showBestEachGen) { //if showing the best for each gen then write the applicable info
textAlign(RIGHT);
textSize(20);
int x = 580;
text("Speed", x, 18+5*44.44444);
text("Bias", x, 18+8*44.44444);
textAlign(LEFT);
textAlign(RIGHT);
textSize(20);
int x = 580;
text("Speed", x, 18+5*44.44444);
text("Bias", x, 18+8*44.44444);
textAlign(LEFT);
}
//-----------------------------------------------------------------------------------------------------------------------------------
---------------
void keyPressed() {
switch(key) {
frameSpeed += 10;
frameRate(frameSpeed);
println(frameSpeed);
break;
frameSpeed -= 10;
frameRate(frameSpeed);
println(frameSpeed);
break;
showBestEachGen = !showBestEachGen;
upToGen = 0;
genPlayerTemp = pop.genPlayers.get(upToGen).cloneForReplay();
break;
showNothing = !showNothing;
break;
switch(keyCode) {
if (showBestEachGen) {//if showing the best player each generation then move on to the next
generation
upToGen++;
if (upToGen >= pop.genPlayers.size()) {//if reached the current generation then exit out of the
showing generations mode
showBestEachGen = false;
} else {
genPlayerTemp = pop.genPlayers.get(upToGen).cloneForReplay();
break;
break;
//-----------------------------------------------------------------------------------------------------------------------------------
----------------------
void updateObstacles() {
obstacleTimer ++;
speed += 0.002;
addObstacle();
groundCounter ++;
groundCounter =0;
grounds.add(new Ground());
moveObstacles();//move everything
showObstacles();
}
}
//-----------------------------------------------------------------------------------------------------------------------------------
----------------------
void moveObstacles() {
println(speed);
obstacles.get(i).move(speed);
obstacles.remove(i);
i--;
birds.get(i).move(speed);
birds.remove(i);
i--;
grounds.get(i).move(speed);
grounds.remove(i);
i--;
//-----------------------------------------------------------------------------------------------------------------------------------
-------------------------
//every so often add an obstacle
void addObstacle() {
int tempInt;
if (lifespan > 1000 && random(1) < 0.15) { // 15% of the time add a bird
tempInt = floor(random(3));
birds.add(temp);
tempInt = floor(random(3));
obstacles.add(temp);
tempInt+=3;
obstacleHistory.add(tempInt);
randomAddition = floor(random(50));
randomAdditionHistory.add(randomAddition);
obstacleTimer = 0;
//-----------------------------------------------------------------------------------------------------------------------------------
----------------------
void showObstacles() {
grounds.get(i).show();
obstacles.get(i).show();
}
for (int i = 0; i< birds.size(); i++) {
birds.get(i).show();
//-----------------------------------------------------------------------------------------------------------------------------------
--------
void resetObstacles() {
obstacleTimer = 0;
randomAddition = 0;
groundCounter = 0;
speed = 10;
Bird
class Bird {
float w = 60;
float h = 50;
float posX;
float posY;
int flapCount = 0;
int typeOfBird;
//-----------------------------------------------------------------------------------------------------------------------------------
-------------------
//constructor
Bird(int type) {
posX = width;
typeOfBird = type;
switch(type) {
posY = 10 + h/2;
break;
posY = 100;
break;
posY = 180;
break;
//-----------------------------------------------------------------------------------------------------------------------------------
-------------------
void show() {
flapCount++;
} else {
flapCount = -15;
}
//-----------------------------------------------------------------------------------------------------------------------------------
-------------------
posX -= speed;
//-----------------------------------------------------------------------------------------------------------------------------------
-------------------
if ((playerLeft<= thisRight && playerRight >= thisLeft ) || (thisLeft <= playerRight && thisRight >=
playerLeft)) {
return true;
return false;
Genome
class Genome {
int inputs;
int outputs;
int nextNode = 0;
int biasNode;
ArrayList<Node> network = new ArrayList<Node>();//a list of the nodes in the order that they need
to be considered in the NN
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
inputs = in;
outputs = out;
nodes.add(new Node(i));
nextNode ++;
nodes.get(i).layer =0;
nodes.add(new Node(i+inputs));
nodes.get(i+inputs).layer = 1;
nextNode++;
}
nodes.add(new Node(nextNode));//bias node
biasNode = nextNode;
nextNode++;
nodes.get(biasNode).layer = 0;
//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------
if (nodes.get(i).number == nodeNumber) {
return nodes.get(i);
return null;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
//adds the conenctions going out of a node to that node so that it can acess the next node during
feeding forward
void connectNodes() {
nodes.get(i).outputConnections.clear();
}
for (int i = 0; i < genes.size(); i++) {//for each connectionGene
genes.get(i).fromNode.outputConnections.add(genes.get(i));//add it to node
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
nodes.get(i).outputValue = inputValues[i];
for (int i = 0; i< network.size(); i++) {//for each node in the network engage it(see node class for
what this does)
network.get(i).engage();
for (int i = 0; i < nodes.size(); i++) {//reset all the nodes for the next feed forward
nodes.get(i).inputSum = 0;
return outs;
}
//----------------------------------------------------------------------------------------------------------------------------------
------
void generateNetwork() {
connectNodes();
//for each layer add the node in that layer, since layers cannot connect to themselves there is no
need to order the nodes within a layer
network.add(nodes.get(i));
//----------------------------------------------------------------------------------------------------------------------------------
-------
//it does this by picking a random connection and disabling it then 2 new connections are added
//1 between the input node of the disabled connection and the new node
//and the other between the new node and the output of the disabled connection
if (genes.size() ==0) {
addConnection(innovationHistory);
return;
}
int randomConnection = floor(random(genes.size()));
randomConnection = floor(random(genes.size()));
genes.get(randomConnection).enabled = false;//disable it
nodes.add(new Node(newNodeNo));
nextNode ++;
genes.add(new connectionGene(genes.get(randomConnection).fromNode,
getNode(newNodeNo), 1, connectionInnovationNumber));
//add a new connection from the new node with a weight the same as the disabled connection
//more accurately the layer numbers of all layers equal to or greater than this new node need to
be incrimented
if (getNode(newNodeNo).layer == genes.get(randomConnection).toNode.layer) {
for (int i = 0; i< nodes.size() -1; i++) {//dont include this newest node
nodes.get(i).layer ++;
layers ++;
connectNodes();
//------------------------------------------------------------------------------------------------------------------
if (fullyConnected()) {
println("connection failed");
return;
randomNode1 = floor(random(nodes.size()));
randomNode2 = floor(random(nodes.size()));
int temp;
temp =randomNode2 ;
randomNode2 = randomNode1;
randomNode1 = temp;
//this will be a new number if no identical genome has mutated in the same way
connectNodes();
//----------------------------------------------------------------------------------------------------------------------------------
---------
if (nodes.get(r1).layer == nodes.get(r2).layer) return true; // if the nodes are in the same layer
return false;
//----------------------------------------------------------------------------------------------------------------------------------
---------
//returns the innovation number for the new mutation
//if this mutation has never been seen before then it will be given a new unique innovation number
//if this mutation matches a previous mutation then it will be given the same innovation number as
the previous one
break;
if (isNew) {//if the mutation is new then create an arrayList of integers representing the current
state of the genome
innoNumbers.add(genes.get(i).innovationNo);
nextConnectionNo++;
return connectionInnovationNumber;
//----------------------------------------------------------------------------------------------------------------------------------
------
//returns whether the network is fully connected or not
boolean fullyConnected() {
int maxConnections = 0;
int[] nodesInLayers = new int[layers];//array which stored the amount of nodes in each layer
//populate array
nodesInLayers[nodes.get(i).layer] +=1;
//for each layer the maximum amount of connections is the number in this layer * the number of
nodes infront of it
//so lets add the max for each layer together and then we will get the maximum amount of
connections in the network
int nodesInFront = 0;
for (int j = i+1; j < layers; j++) {//for each layer infront of this layer
if (maxConnections == genes.size()) {//if the number of connections is equal to the max number of
connections possible then it is full
return true;
return false;
//-------------------------------------------------------------------------------------------------------------------------------
//mutates the genome
if (genes.size() ==0) {
addConnection(innovationHistory);
genes.get(i).mutateWeight();
if (rand2<0.08) {
addConnection(innovationHistory);
if (rand3<0.02) {
addNode(innovationHistory);
//---------------------------------------------------------------------------------------------------------------------------------
child.genes.clear();
child.nodes.clear();
child.layers = layers;
child.nextNode = nextNode;
child.biasNode = biasNode;
if (random(1) < 0.75) {//75% of the time disabel the childs gene
setEnabled = false;
if (rand<0.5) {
childGenes.add(genes.get(i));
} else {
childGenes.add(parent2.genes.get(parent2gene));
childGenes.add(genes.get(i));
setEnabled = genes.get(i).enabled;
}
isEnabled.add(setEnabled);
//since all excess and disjoint genes are inherrited from the more fit parent (this Genome) the
childs structure is no different from this parent | with exception of dormant connections being
enabled but this wont effect nodes
child.nodes.add(nodes.get(i).clone());
//clone all the connections so that they connect the childs new nodes
child.genes.add(childGenes.get(i).clone(child.getNode(childGenes.get(i).fromNode.number),
child.getNode(childGenes.get(i).toNode.number)));
child.genes.get(i).enabled = isEnabled.get(i);
child.connectNodes();
return child;
//----------------------------------------------------------------------------------------------------------------------------------
------
inputs = in;
outputs = out;
}
//----------------------------------------------------------------------------------------------------------------------------------
------
//returns whether or not there is a gene matching the input innovation number in the input
genome
if (parent2.genes.get(i).innovationNo == innovationNumber) {
return i;
//----------------------------------------------------------------------------------------------------------------------------------
------
void printGenome() {
println("nodes");
print(nodes.get(i).number + ",");
println("Genes");
"is enabled " +genes.get(i).enabled, "from layer " + genes.get(i).fromNode.layer, "to layer " +
genes.get(i).toNode.layer, "weight: " + genes.get(i).weight);
println();
}
//----------------------------------------------------------------------------------------------------------------------------------
------
Genome clone() {
clone.nodes.add(nodes.get(i).clone());
//copy all the connections so that they connect the clone new nodes
clone.genes.add(genes.get(i).clone(clone.getNode(genes.get(i).fromNode.number),
clone.getNode(genes.get(i).toNode.number)));
clone.layers = layers;
clone.nextNode = nextNode;
clone.biasNode = biasNode;
clone.connectNodes();
return clone;
//----------------------------------------------------------------------------------------------------------------------------------
------
//i know its ugly but it works (and is not that important) so I'm not going to mess with it
//for each layer add the position of the node on the screen to the node posses arraylist
fill(255, 0, 0);
for (int j = 0; j< allNodes.get(i).size(); j++) {//for the position in the layer
nodeNumbers.add(allNodes.get(i).get(j).number);
println(i,j,x,y);
}
}
//draw connections
stroke(0);
strokeWeight(2);
if (genes.get(i).enabled) {
stroke(0);
} else {
stroke(100);
PVector from;
PVector to;
from = nodePoses.get(nodeNumbers.indexOf(genes.get(i).fromNode.number));
to = nodePoses.get(nodeNumbers.indexOf(genes.get(i).toNode.number));
if (genes.get(i).weight > 0) {
stroke(255, 0, 0);
} else {
stroke(0, 0, 255);
strokeWeight(map(abs(genes.get(i).weight), 0, 1, 0, 5));
fill(255);
stroke(0);
strokeWeight(1);
textSize(10);
fill(0);
textAlign(CENTER, CENTER);
Ground
class Ground{
int w = floor(random(1,10));
Ground(){}
void show(){
stroke(0);
strokeWeight(3);
posX -= speed;
}
Node
class Node {
int number;
int layer = 0;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
//constructor
Node(int no) {
number = no;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
//the node sends its output to the inputs of the nodes its connected to
void engage() {
outputValue = sigmoid(inputSum);
outputConnections.get(i).toNode.inputSum += outputConnections.get(i).weight *
outputValue;//add the weighted output to the sum of the inputs of whatever node this node is
connected to
//-----------------------------------------------------------------------------------------------------------------------------------
-----
//not used
float stepFunction(float x) {
if (x < 0) {
return 0;
} else {
return 1;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
float sigmoid(float x) {
return y;
//----------------------------------------------------------------------------------------------------------------------------------
------------------------
return false;
//you get it
if (node.outputConnections.get(i).toNode == this) {
return true;
} else {
for (int i = 0; i < outputConnections.size(); i++) {
if (outputConnections.get(i).toNode == node) {
return true;
return false;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
Node clone() {
clone.layer = layer;
return clone;
Obstacle
class Obstacle {
float posX;
int w ;
int h ;
int type;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------
//constructor
Obstacle(int t) {
posX = width;
type = t;
switch(type) {
case 0://small cactus
w = 40;
h = 80;
break;
w = 60;
h = 120;
break;
w = 120;
h = 80;
break;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------
void show() {
fill(0);
rectMode(CENTER);
switch(type) {
case 0:
break;
case 1:
break;
case 2:
break;
}
//----------------------------------------------------------------------------------------------------------------------------------
--------------------
posX -= speed;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------
if ((playerLeft<= thisRight && playerRight >= thisLeft ) || (thisLeft <= playerRight && thisRight >=
playerLeft)) {
float thisUp = h;
return true;
return false;
Player
class Player {
float fitness;
Genome brain;
float unadjustedFitness;
boolean dead;
int score;
int gen = 0;
int genomeInputs = 7;
int genomeOutputs = 3;
float[] vision = new float[genomeInputs];//t he input array fed into the neuralNet
//-------------------------------------
float posY = 0;
float velY = 0;
int historyCounter = 0;
int localObstacleTimer = 0;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
//constructor
Player() {
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void show() {
if (runCount < 0) {
} else {
} else
if (posY ==0) {
if (runCount < 0) {
} else {
} else {
runCount++;
if (runCount > 5) {
runCount = -5;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void incrementCounters() {
lifespan++;
if (lifespan % 3 ==0) {
score+=1;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
//checks for collisions and if this is a replay move all the obstacles
void move() {
posY += velY;
if (posY >0) {
velY -= gravity;
} else {
velY = 0;
posY = 0;
}
if (!replay) {
dead = true;
dead = true;
} else {
dead = true;
dead = true;
}
for (int i = 0; i< replayBirds.size(); i++) {
dead = true;
} else {
dead = true;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
if (posY ==0) {
if (bigJump) {
gravity = 1;
velY = 20;
} else {
gravity = 1.2;
velY = 16;
}
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
gravity = 3;
duck = isDucking;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void update() {
incrementCounters();
move();
//----------------------------------------------------------------------------------------------------------------------------------
------------------------
void look() {
if (!replay) {
float temp = 0;
minIndex = i;
}
}
minIndex = i;
berd = true;
vision[4] = speed;
vision[5] = posY;
vision[0] = 0;
vision[1] = 0;
vision[2] = 0;
vision[3] = 0;
vision[6] = 0;
} else {
vision[0] = 1.0/(min/10.0);
if (berd) {
vision[1] = birds.get(minIndex).h;
vision[2] = birds.get(minIndex).w;
if (birds.get(minIndex).typeOfBird == 0) {
vision[3] = 0;
} else {
vision[3] = birds.get(minIndex).posY;
}
} else {
vision[1] = obstacles.get(minIndex).h;
vision[2] = obstacles.get(minIndex).w;
vision[3] = 0;
//vision 6 is the gap between the this obstacle and the next one
min = 10000;
minIndex = -1;
minIndex = i;
minIndex = i;
}
if (minIndex == -1) {//if there is only one obejct on the screen
vision[6] = 0;
} else {
float temp = 0;
minIndex = i;
minIndex = i;
berd = true;
vision[4] = localSpeed;
vision[5] = posY;
if (minIndex == -1) {//if there are no replayObstacles
vision[0] = 0;
vision[1] = 0;
vision[2] = 0;
vision[3] = 0;
vision[6] = 0;
} else {
vision[0] = 1.0/(min/10.0);
if (berd) {
vision[1] = replayBirds.get(minIndex).h;
vision[2] = replayBirds.get(minIndex).w;
if (replayBirds.get(minIndex).typeOfBird == 0) {
vision[3] = 0;
} else {
vision[3] = replayBirds.get(minIndex).posY;
} else {
vision[1] = replayObstacles.get(minIndex).h;
vision[2] = replayObstacles.get(minIndex).w;
vision[3] = 0;
//vision 6 is the gap between the this obstacle and the next one
min = 10000;
minIndex = -1;
minIndex = i;
minIndex = i;
vision[6] = 0;
} else {
}
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void think() {
float max = 0;
int maxIndex = 0;
decision = brain.feedForward(vision);
max = decision[i];
maxIndex = i;
ducking(false);
return;
switch(maxIndex) {
case 0:
jump(false);
break;
case 1:
jump(true);
break;
case 2:
ducking(true);
break;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
Player clone() {
clone.brain = brain.clone();
clone.fitness = fitness;
clone.brain.generateNetwork();
clone.gen = gen;
clone.bestScore = score;
return clone;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------
//since there is some randomness in games sometimes when we want to replay the game we need
to remove that randomness
Player cloneForReplay() {
clone.brain = brain.clone();
clone.fitness = fitness;
clone.brain.generateNetwork();
clone.gen = gen;
clone.bestScore = score;
clone.replay = true;
if (replay) {
clone.localObstacleHistory = (ArrayList)localObstacleHistory.clone();
clone.localRandomAdditionHistory = (ArrayList)localRandomAdditionHistory.clone();
} else {
clone.localObstacleHistory = (ArrayList)obstacleHistory.clone();
clone.localRandomAdditionHistory = (ArrayList)randomAdditionHistory.clone();
return clone;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void calculateFitness() {
fitness = score*score;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
child.brain = brain.crossover(parent2.brain);
child.brain.generateNetwork();
return child;
//----------------------------------------------------------------------------------------------------------------------------------
----------------------
void updateLocalObstacles() {
localObstacleTimer ++;
localSpeed += 0.002;
addLocalObstacle();
groundCounter ++;
groundCounter =0;
grounds.add(new Ground());
moveLocalObstacles();
showLocalObstacles();
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void moveLocalObstacles() {
replayObstacles.get(i).move(localSpeed);
replayObstacles.remove(i);
i--;
replayBirds.get(i).move(localSpeed);
replayBirds.remove(i);
i--;
}
}
grounds.get(i).move(localSpeed);
grounds.remove(i);
i--;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------
void addLocalObstacle() {
localRandomAddition = localRandomAdditionHistory.get(historyCounter);
historyCounter ++;
if (tempInt < 3) {
replayBirds.add(new Bird(tempInt));
} else {
localObstacleTimer = 0;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void showLocalObstacles() {
grounds.get(i).show();
replayObstacles.get(i).show();
}
for (int i = 0; i< replayBirds.size(); i++) {
replayBirds.get(i).show();
Population
class Player {
float fitness;
Genome brain;
float unadjustedFitness;
boolean dead;
int score;
int gen = 0;
int genomeInputs = 7;
int genomeOutputs = 3;
float[] vision = new float[genomeInputs];//t he input array fed into the neuralNet
//-------------------------------------
float posY = 0;
float velY = 0;
int historyCounter = 0;
int localObstacleTimer = 0;
int localRandomAddition = 0;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
//constructor
Player() {
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void show() {
if (runCount < 0) {
} else {
}
} else
if (posY ==0) {
if (runCount < 0) {
} else {
} else {
runCount++;
if (runCount > 5) {
runCount = -5;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void incrementCounters() {
lifespan++;
if (lifespan % 3 ==0) {
score+=1;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
//checks for collisions and if this is a replay move all the obstacles
void move() {
posY += velY;
if (posY >0) {
velY -= gravity;
} else {
velY = 0;
posY = 0;
if (!replay) {
dead = true;
dead = true;
} else {
dead = true;
dead = true;
dead = true;
} else {
dead = true;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
if (posY ==0) {
if (bigJump) {
gravity = 1;
velY = 20;
} else {
gravity = 1.2;
velY = 16;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
gravity = 3;
duck = isDucking;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void update() {
incrementCounters();
move();
//----------------------------------------------------------------------------------------------------------------------------------
------------------------
void look() {
if (!replay) {
float temp = 0;
minIndex = i;
minIndex = i;
berd = true;
vision[4] = speed;
vision[5] = posY;
vision[0] = 0;
vision[1] = 0;
vision[2] = 0;
vision[3] = 0;
vision[6] = 0;
} else {
vision[0] = 1.0/(min/10.0);
if (berd) {
vision[1] = birds.get(minIndex).h;
vision[2] = birds.get(minIndex).w;
if (birds.get(minIndex).typeOfBird == 0) {
vision[3] = 0;
} else {
vision[3] = birds.get(minIndex).posY;
} else {
vision[1] = obstacles.get(minIndex).h;
vision[2] = obstacles.get(minIndex).w;
vision[3] = 0;
//vision 6 is the gap between the this obstacle and the next one
min = 10000;
minIndex = -1;
minIndex = i;
}
for (int i = 0; i< birds.size(); i++) {
minIndex = i;
vision[6] = 0;
} else {
float temp = 0;
minIndex = i;
minIndex = i;
berd = true;
vision[4] = localSpeed;
vision[5] = posY;
vision[0] = 0;
vision[1] = 0;
vision[2] = 0;
vision[3] = 0;
vision[6] = 0;
} else {
vision[0] = 1.0/(min/10.0);
if (berd) {
vision[1] = replayBirds.get(minIndex).h;
vision[2] = replayBirds.get(minIndex).w;
if (replayBirds.get(minIndex).typeOfBird == 0) {
vision[3] = 0;
} else {
vision[3] = replayBirds.get(minIndex).posY;
} else {
vision[1] = replayObstacles.get(minIndex).h;
vision[2] = replayObstacles.get(minIndex).w;
vision[3] = 0;
//vision 6 is the gap between the this obstacle and the next one
min = 10000;
minIndex = -1;
minIndex = i;
minIndex = i;
} else {
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void think() {
float max = 0;
int maxIndex = 0;
decision = brain.feedForward(vision);
max = decision[i];
maxIndex = i;
return;
switch(maxIndex) {
case 0:
jump(false);
break;
case 1:
jump(true);
break;
case 2:
ducking(true);
break;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
Player clone() {
clone.brain = brain.clone();
clone.fitness = fitness;
clone.brain.generateNetwork();
clone.gen = gen;
clone.bestScore = score;
return clone;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------
//since there is some randomness in games sometimes when we want to replay the game we need
to remove that randomness
Player cloneForReplay() {
clone.brain = brain.clone();
clone.fitness = fitness;
clone.brain.generateNetwork();
clone.gen = gen;
clone.bestScore = score;
clone.replay = true;
if (replay) {
clone.localObstacleHistory = (ArrayList)localObstacleHistory.clone();
clone.localRandomAdditionHistory = (ArrayList)localRandomAdditionHistory.clone();
} else {
clone.localObstacleHistory = (ArrayList)obstacleHistory.clone();
clone.localRandomAdditionHistory = (ArrayList)randomAdditionHistory.clone();
return clone;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void calculateFitness() {
fitness = score*score;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
Player crossover(Player parent2) {
child.brain = brain.crossover(parent2.brain);
child.brain.generateNetwork();
return child;
//----------------------------------------------------------------------------------------------------------------------------------
----------------------
void updateLocalObstacles() {
localObstacleTimer ++;
localSpeed += 0.002;
addLocalObstacle();
groundCounter ++;
groundCounter =0;
grounds.add(new Ground());
moveLocalObstacles();
showLocalObstacles();
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void moveLocalObstacles() {
replayObstacles.get(i).move(localSpeed);
replayObstacles.remove(i);
i--;
replayBirds.get(i).move(localSpeed);
replayBirds.remove(i);
i--;
grounds.get(i).move(localSpeed);
grounds.remove(i);
i--;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------
void addLocalObstacle() {
localRandomAddition = localRandomAdditionHistory.get(historyCounter);
historyCounter ++;
if (tempInt < 3) {
replayBirds.add(new Bird(tempInt));
} else {
localObstacleTimer = 0;
}
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
void showLocalObstacles() {
grounds.get(i).show();
replayObstacles.get(i).show();
replayBirds.get(i).show();
Species
class Species {
float bestFitness = 0;
Player champ;
float averageFitness = 0;
int staleness = 0;//how many generations the species has gone without an improvement
Genome rep;
//--------------------------------------------
float excessCoeff = 1;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------
//empty constructor
Species() {
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------
Species(Player p) {
players.add(p);
bestFitness = p.fitness;
rep = p.brain.clone();
champ = p.cloneForReplay();
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------
boolean sameSpecies(Genome g) {
float compatibility;
float excessAndDisjoint = getExcessDisjoint(g, rep);//get the number of excess and disjoint genes
between this player and the current species rep
if (largeGenomeNormaliser<1) {
largeGenomeNormaliser =1;
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------
void addToSpecies(Player p) {
players.add(p);
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------
//returns the number of excess and disjoint genes between the 2 input genomes
if (brain1.genes.get(i).innovationNo == brain2.genes.get(j).innovationNo) {
matching ++;
break;
}
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------
//returns the avereage weight difference between matching genes in the input genomes
return 0;
float matching = 0;
float totalDiff= 0;
if (brain1.genes.get(i).innovationNo == brain2.genes.get(j).innovationNo) {
matching ++;
break;
return 100;
return totalDiff/matching;
//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------
void sortSpecies() {
float max = 0;
int maxIndex = 0;
max = players.get(j).fitness;
maxIndex = j;
temp.add(players.get(maxIndex));
players.remove(maxIndex);
i--;
players = (ArrayList)temp.clone();
if (players.size() == 0) {
print("fucking");
staleness = 200;
return;
staleness = 0;
bestFitness = players.get(0).fitness;
rep = players.get(0).brain.clone();
champ = players.get(0).cloneForReplay();
staleness ++;
}
//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------
//simple stuff
void setAverage() {
float sum = 0;
sum += players.get(i).fitness;
averageFitness = sum/players.size();
//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------
Player baby;
if (random(1) < 0.25) {//25% of the time there is no crossover and the child is simply a clone of a
random(ish) player
baby = selectPlayer().clone();
//the crossover function expects the highest fitness parent to be the object and the lowest as the
argument
baby = parent2.crossover(parent1);
} else {
baby = parent1.crossover(parent2);
return baby;
//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------
Player selectPlayer() {
float fitnessSum = 0;
fitnessSum += players.get(i).fitness;
float runningSum = 0;
runningSum += players.get(i).fitness;
return players.get(i);
return players.get(0);
//----------------------------------------------------------------------------------------------------------------------------------
--------
void cull() {
if (players.size() > 2) {
players.remove(i);
i--;
//----------------------------------------------------------------------------------------------------------------------------------
--------
//in order to protect unique players, the fitnesses of each player is divided by the number of
players in the species that that player belongs to
void fitnessSharing() {
players.get(i).fitness/=players.size();
connectionGene
class connectionGene {
Node fromNode;
Node toNode;
float weight;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
//constructor
toNode = to;
weight = w;
innovationNo = inno;
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------
void mutateWeight() {
if (rand2 < 0.1) {//10% of the time completely change the weight
weight += randomGaussian()/50;
weight = 1;
weight = -1;
//----------------------------------------------------------------------------------------------------------
clone.enabled = enabled;
return clone;
connectionHistory
class connectionHistory {
int fromNode;
int toNode;
int innovationNumber;
//this represents the genome and allows us to test if another genoeme is the same
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
//constructor
fromNode = from;
toNode = to;
innovationNumber = inno;
innovationNumbers = (ArrayList)innovationNos.clone();
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
//returns whether the genome matches the original genome and the connection is between the
same nodes
//next check if all the innovation numbers match from the genome
if (!innovationNumbers.contains(genome.genes.get(i).innovationNo)) {
return false;
//if reached this far then the innovationNumbers match the genes innovation numbers and the
connection is between the same nodes
return true;
return false;