0% found this document useful (0 votes)
139 views78 pages

AI Dino Game Development

This document contains code for a dinosaur game AI simulation. It defines classes like Population, Player, Obstacle, and Bird. The draw() function handles the simulation loop, updating players each frame if alive, or performing genetic algorithm operations if all players are dead. Additional functions handle drawing the display, brain visualization, obstacle movement/addition, and user input. The goal is to evolve neural networks that can control a dinosaur to avoid obstacles and maximize its score/lifetime in the game.

Uploaded by

Kim Akirida
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
139 views78 pages

AI Dino Game Development

This document contains code for a dinosaur game AI simulation. It defines classes like Population, Player, Obstacle, and Bird. The draw() function handles the simulation loop, updating players each frame if alive, or performing genetic algorithm operations if all players are dead. Additional functions handle drawing the display, brain visualization, obstacle movement/addition, and user input. The goal is to evolve neural networks that can control a dinosaur to avoid obstacles and maximize its score/lifetime in the game.

Uploaded by

Kim Akirida
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 78

DINOgame AI neuro network

Dino game

//Globals

int nextConnectionNo = 1000;

Population pop;

int frameSpeed = 60;

boolean showBestEachGen = false;

int upToGen = 0;

Player genPlayerTemp;

boolean showNothing = false;

//images

PImage dinoRun1;

PImage dinoRun2;

PImage dinoJump;

PImage dinoDuck;

PImage dinoDuck1;

PImage smallCactus;

PImage manySmallCactus;

PImage bigCactus;

PImage bird;

PImage bird1;

ArrayList<Obstacle> obstacles = new ArrayList<Obstacle>();

ArrayList<Bird> birds = new ArrayList<Bird>();

ArrayList<Ground> grounds = new ArrayList<Ground>();


int obstacleTimer = 0;

int minimumTimeBetweenObstacles = 60;

int randomAddition = 0;

int groundCounter = 0;

float speed = 10;

int groundHeight = 250;

int playerXpos = 150;

ArrayList<Integer> obstacleHistory = new ArrayList<Integer>();

ArrayList<Integer> randomAdditionHistory = new ArrayList<Integer>();

//-----------------------------------------------------------------------------------------------------------------------------------
---------------

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");

pop = new Population(500); //<<number of dinosaurs in each generation

//-----------------------------------------------------------------------------------------------------------------------------------
---------------------

void draw() {

drawToScreen();

if (showBestEachGen) {//show the best of each gen

if (!genPlayerTemp.dead) {//if current gen player is not dead then update it

genPlayerTemp.updateLocalObstacles();

genPlayerTemp.look();

genPlayerTemp.think();

genPlayerTemp.update();

genPlayerTemp.show();

} else {//if dead move on to the next generation

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();

} else {//if just evolving normally

if (!pop.done()) {//if any players are alive then update them

updateObstacles();

pop.updateAlive();
} else {//all dead

//genetic algorithm

pop.naturalSelection();

resetObstacles();

//-----------------------------------------------------------------------------------------------------------------------------------
----------------------

//draws the display screen

void drawToScreen() {

if (!showNothing) {

background(250);

stroke(0);

strokeWeight(2);

line(0, height - groundHeight - 30, width, height - groundHeight - 30);

drawBrain();

writeInfo();

//-----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------

void drawBrain() { //show the brain of whatever genome is currently showing

int startX = 600;

int startY = 10;

int w = 600;

int h = 400;

if (showBestEachGen) {

genPlayerTemp.brain.drawGenome(startX, startY, w, h);


} else {

for (int i = 0; i< pop.pop.size(); i++) {

if (!pop.pop.get(i).dead) {

pop.pop.get(i).brain.drawGenome(startX, startY, w, h);

break;

//-----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------

//writes info about the current player

void writeInfo() {

fill(200);

textAlign(LEFT);

textSize(40);

if (showBestEachGen) { //if showing the best for each gen then write the applicable info

text("Score: " + genPlayerTemp.score, 30, height - 30);

//text(, width/2-180, height-30);

textAlign(RIGHT);

text("Gen: " + (genPlayerTemp.gen +1), width -40, height-30);

textSize(20);

int x = 580;

text("Distace to next obstacle", x, 18+44.44444);

text("Height of obstacle", x, 18+2*44.44444);

text("Width of obstacle", x, 18+3*44.44444);

text("Bird height", x, 18+4*44.44444);

text("Speed", x, 18+5*44.44444);

text("Players Y position", x, 18+6*44.44444);

text("Gap between obstacles", x, 18+7*44.44444);

text("Bias", x, 18+8*44.44444);
textAlign(LEFT);

text("Small Jump", 1220, 118);

text("Big Jump", 1220, 218);

text("Duck", 1220, 318);

} else { //evolving normally

text("Score: " + floor(pop.populationLife/3.0), 30, height - 30);

//text(, width/2-180, height-30);

textAlign(RIGHT);

text("Gen: " + (pop.gen +1), width -40, height-30);

textSize(20);

int x = 580;

text("Distace to next obstacle", x, 18+44.44444);

text("Height of obstacle", x, 18+2*44.44444);

text("Width of obstacle", x, 18+3*44.44444);

text("Bird height", x, 18+4*44.44444);

text("Speed", x, 18+5*44.44444);

text("Players Y position", x, 18+6*44.44444);

text("Gap between obstacles", x, 18+7*44.44444);

text("Bias", x, 18+8*44.44444);

textAlign(LEFT);

text("Small Jump", 1220, 118);

text("Big Jump", 1220, 218);

text("Duck", 1220, 318);

}
//-----------------------------------------------------------------------------------------------------------------------------------
---------------

void keyPressed() {

switch(key) {

case '+'://speed up frame rate

frameSpeed += 10;

frameRate(frameSpeed);

println(frameSpeed);

break;

case '-'://slow down frame rate

if (frameSpeed > 10) {

frameSpeed -= 10;

frameRate(frameSpeed);

println(frameSpeed);

break;

case 'g'://show generations

showBestEachGen = !showBestEachGen;

upToGen = 0;

genPlayerTemp = pop.genPlayers.get(upToGen).cloneForReplay();

break;

case 'n'://show absolutely nothing in order to speed up computation

showNothing = !showNothing;

break;

case CODED://any of the arrow keys

switch(keyCode) {

case RIGHT://right is used to move through the generations

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;

//-----------------------------------------------------------------------------------------------------------------------------------
----------------------

//called every frame

void updateObstacles() {

obstacleTimer ++;

speed += 0.002;

if (obstacleTimer > minimumTimeBetweenObstacles + randomAddition) { //if the obstacle timer is


high enough then add a new obstacle

addObstacle();

groundCounter ++;

if (groundCounter> 10) { //every 10 frames add a ground bit

groundCounter =0;

grounds.add(new Ground());

moveObstacles();//move everything

if (!showNothing) {//show everything

showObstacles();

}
}

//-----------------------------------------------------------------------------------------------------------------------------------
----------------------

//moves obstacles to the left based on the speed of the game

void moveObstacles() {

println(speed);

for (int i = 0; i< obstacles.size(); i++) {

obstacles.get(i).move(speed);

if (obstacles.get(i).posX < -playerXpos) {

obstacles.remove(i);

i--;

for (int i = 0; i< birds.size(); i++) {

birds.get(i).move(speed);

if (birds.get(i).posX < -playerXpos) {

birds.remove(i);

i--;

for (int i = 0; i < grounds.size(); i++) {

grounds.get(i).move(speed);

if (grounds.get(i).posX < -playerXpos) {

grounds.remove(i);

i--;

//-----------------------------------------------------------------------------------------------------------------------------------
-------------------------
//every so often add an obstacle

void addObstacle() {

int lifespan = pop.populationLife;

int tempInt;

if (lifespan > 1000 && random(1) < 0.15) { // 15% of the time add a bird

tempInt = floor(random(3));

Bird temp = new Bird(tempInt);//floor(random(3)));

birds.add(temp);

} else {//otherwise add a cactus

tempInt = floor(random(3));

Obstacle temp = new Obstacle(tempInt);//floor(random(3)));

obstacles.add(temp);

tempInt+=3;

obstacleHistory.add(tempInt);

randomAddition = floor(random(50));

randomAdditionHistory.add(randomAddition);

obstacleTimer = 0;

//-----------------------------------------------------------------------------------------------------------------------------------
----------------------

//what do you think this does?

void showObstacles() {

for (int i = 0; i< grounds.size(); i++) {

grounds.get(i).show();

for (int i = 0; i< obstacles.size(); i++) {

obstacles.get(i).show();

}
for (int i = 0; i< birds.size(); i++) {

birds.get(i).show();

//-----------------------------------------------------------------------------------------------------------------------------------
--------

//resets all the obstacles after every dino has died

void resetObstacles() {

randomAdditionHistory = new ArrayList<Integer>();

obstacleHistory = new ArrayList<Integer>();

obstacles = new ArrayList<Obstacle>();

birds = new ArrayList<Bird>();

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) {

case 0://flying low

posY = 10 + h/2;

break;

case 1://flying middle

posY = 100;

break;

case 2://flying high

posY = 180;

break;

//-----------------------------------------------------------------------------------------------------------------------------------
-------------------

//show the birf

void show() {

flapCount++;

if (flapCount < 0) {//flap the berd

image(bird,posX-bird.width/2,height - groundHeight - (posY + bird.height-20));

} else {

image(bird1,posX-bird1.width/2,height - groundHeight - (posY + bird1.height-20));

if(flapCount > 15){

flapCount = -15;

}
//-----------------------------------------------------------------------------------------------------------------------------------
-------------------

//move the bard

void move(float speed) {

posX -= speed;

//-----------------------------------------------------------------------------------------------------------------------------------
-------------------

//returns whether or not the bird collides with the player

boolean collided(float playerX, float playerY, float playerWidth, float playerHeight) {

float playerLeft = playerX - playerWidth/2;

float playerRight = playerX + playerWidth/2;

float thisLeft = posX - w/2 ;

float thisRight = posX + w/2;

if ((playerLeft<= thisRight && playerRight >= thisLeft ) || (thisLeft <= playerRight && thisRight >=
playerLeft)) {

float playerUp = playerY + playerHeight/2;

float playerDown = playerY - playerHeight/2;

float thisUp = posY + h/2;

float thisDown = posY - h/2;

if (playerDown <= thisUp && playerUp >= thisDown) {

return true;

return false;

Genome
class Genome {

ArrayList<connectionGene> genes = new ArrayList<connectionGene>();//a list of connections


between nodes which represent the NN

ArrayList<Node> nodes = new ArrayList<Node>();//list of nodes

int inputs;

int outputs;

int layers =2;

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

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

Genome(int in, int out) {

//set input number and output number

inputs = in;

outputs = out;

//create input nodes

for (int i = 0; i<inputs; i++) {

nodes.add(new Node(i));

nextNode ++;

nodes.get(i).layer =0;

//create output nodes

for (int i = 0; i < outputs; i++) {

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;

//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------

//returns the node with a matching number

//sometimes the nodes will not be in order

Node getNode(int nodeNumber) {

for (int i = 0; i < nodes.size(); i++) {

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() {

for (int i = 0; i< nodes.size(); i++) {//clear the connections

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

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//feeding in input values into the NN and returning output array

float[] feedForward(float[] inputValues) {

//set the outputs of the input nodes

for (int i =0; i < inputs; i++) {

nodes.get(i).outputValue = inputValues[i];

nodes.get(biasNode).outputValue = 1;//output of bias is 1

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();

//the outputs are nodes[inputs] to nodes [inputs+outputs-1]

float[] outs = new float[outputs];

for (int i = 0; i < outputs; i++) {

outs[i] = nodes.get(inputs + i).outputValue;

for (int i = 0; i < nodes.size(); i++) {//reset all the nodes for the next feed forward

nodes.get(i).inputSum = 0;

return outs;
}

//----------------------------------------------------------------------------------------------------------------------------------
------

//sets up the NN as a list of nodes in order to be engaged

void generateNetwork() {

connectNodes();

network = new ArrayList<Node>();

//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

for (int l = 0; l< layers; l++) {//for each layer

for (int i = 0; i< nodes.size(); i++) {//for each node

if (nodes.get(i).layer == l) {//if that node is in that layer

network.add(nodes.get(i));

//----------------------------------------------------------------------------------------------------------------------------------
-------

//mutate the NN by adding a new node

//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

void addNode(ArrayList<connectionHistory> innovationHistory) {

//pick a random connection to create a node between

if (genes.size() ==0) {

addConnection(innovationHistory);

return;

}
int randomConnection = floor(random(genes.size()));

while (genes.get(randomConnection).fromNode == nodes.get(biasNode) && genes.size() !=1 )


{//dont disconnect bias

randomConnection = floor(random(genes.size()));

genes.get(randomConnection).enabled = false;//disable it

int newNodeNo = nextNode;

nodes.add(new Node(newNodeNo));

nextNode ++;

//add a new connection to the new node with a weight of 1

int connectionInnovationNumber = getInnovationNumber(innovationHistory,


genes.get(randomConnection).fromNode, getNode(newNodeNo));

genes.add(new connectionGene(genes.get(randomConnection).fromNode,
getNode(newNodeNo), 1, connectionInnovationNumber));

connectionInnovationNumber = getInnovationNumber(innovationHistory, getNode(newNodeNo),


genes.get(randomConnection).toNode);

//add a new connection from the new node with a weight the same as the disabled connection

genes.add(new connectionGene(getNode(newNodeNo), genes.get(randomConnection).toNode,


genes.get(randomConnection).weight, connectionInnovationNumber));

getNode(newNodeNo).layer = genes.get(randomConnection).fromNode.layer +1;

connectionInnovationNumber = getInnovationNumber(innovationHistory, nodes.get(biasNode),


getNode(newNodeNo));

//connect the bias to the new node with a weight of 0

genes.add(new connectionGene(nodes.get(biasNode), getNode(newNodeNo), 0,


connectionInnovationNumber));
//if the layer of the new node is equal to the layer of the output node of the old connection then a
new layer needs to be created

//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

if (nodes.get(i).layer >= getNode(newNodeNo).layer) {

nodes.get(i).layer ++;

layers ++;

connectNodes();

//------------------------------------------------------------------------------------------------------------------

//adds a connection between 2 nodes which aren't currently connected

void addConnection(ArrayList<connectionHistory> innovationHistory) {

//cannot add a connection to a fully connected network

if (fullyConnected()) {

println("connection failed");

return;

//get random nodes

int randomNode1 = floor(random(nodes.size()));

int randomNode2 = floor(random(nodes.size()));

while (randomConnectionNodesAreShit(randomNode1, randomNode2)) {//while the random


nodes are no good

//get new ones

randomNode1 = floor(random(nodes.size()));
randomNode2 = floor(random(nodes.size()));

int temp;

if (nodes.get(randomNode1).layer > nodes.get(randomNode2).layer) {//if the first random node is


after the second then switch

temp =randomNode2 ;

randomNode2 = randomNode1;

randomNode1 = temp;

//get the innovation number of the connection

//this will be a new number if no identical genome has mutated in the same way

int connectionInnovationNumber = getInnovationNumber(innovationHistory,


nodes.get(randomNode1), nodes.get(randomNode2));

//add the connection with a random array

genes.add(new connectionGene(nodes.get(randomNode1), nodes.get(randomNode2), random(-1,


1), connectionInnovationNumber));//changed this so if error here

connectNodes();

//----------------------------------------------------------------------------------------------------------------------------------
---------

boolean randomConnectionNodesAreShit(int r1, int r2) {

if (nodes.get(r1).layer == nodes.get(r2).layer) return true; // if the nodes are in the same layer

if (nodes.get(r1).isConnectedTo(nodes.get(r2))) return true; //if the nodes are already connected

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

int getInnovationNumber(ArrayList<connectionHistory> innovationHistory, Node from, Node to) {

boolean isNew = true;

int connectionInnovationNumber = nextConnectionNo;

for (int i = 0; i < innovationHistory.size(); i++) {//for each previous mutation

if (innovationHistory.get(i).matches(this, from, to)) {//if match found

isNew = false;//its not a new mutation

connectionInnovationNumber = innovationHistory.get(i).innovationNumber; //set the


innovation number as the innovation number of the match

break;

if (isNew) {//if the mutation is new then create an arrayList of integers representing the current
state of the genome

ArrayList<Integer> innoNumbers = new ArrayList<Integer>();

for (int i = 0; i< genes.size(); i++) {//set the innovation numbers

innoNumbers.add(genes.get(i).innovationNo);

//then add this mutation to the innovationHistory

innovationHistory.add(new connectionHistory(from.number, to.number,


connectionInnovationNumber, innoNumbers));

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

for (int i =0; i< nodes.size(); i++) {

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

for (int i = 0; i < layers-1; i++) {

int nodesInFront = 0;

for (int j = i+1; j < layers; j++) {//for each layer infront of this layer

nodesInFront += nodesInLayers[j];//add up nodes

maxConnections += nodesInLayers[i] * nodesInFront;

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

void mutate(ArrayList<connectionHistory> innovationHistory) {

if (genes.size() ==0) {

addConnection(innovationHistory);

float rand1 = random(1);

if (rand1<0.8) { // 80% of the time mutate weights

for (int i = 0; i< genes.size(); i++) {

genes.get(i).mutateWeight();

//5% of the time add a new connection

float rand2 = random(1);

if (rand2<0.08) {

addConnection(innovationHistory);

//1% of the time add a node

float rand3 = random(1);

if (rand3<0.02) {

addNode(innovationHistory);

//---------------------------------------------------------------------------------------------------------------------------------

//called when this Genome is better that the other parent

Genome crossover(Genome parent2) {

Genome child = new Genome(inputs, outputs, true);

child.genes.clear();
child.nodes.clear();

child.layers = layers;

child.nextNode = nextNode;

child.biasNode = biasNode;

ArrayList<connectionGene> childGenes = new ArrayList<connectionGene>();//list of genes to be


inherrited form the parents

ArrayList<Boolean> isEnabled = new ArrayList<Boolean>();

//all inherrited genes

for (int i = 0; i< genes.size(); i++) {

boolean setEnabled = true;//is this node in the chlid going to be enabled

int parent2gene = matchingGene(parent2, genes.get(i).innovationNo);

if (parent2gene != -1) {//if the genes match

if (!genes.get(i).enabled || !parent2.genes.get(parent2gene).enabled) {//if either of the


matching genes are disabled

if (random(1) < 0.75) {//75% of the time disabel the childs gene

setEnabled = false;

float rand = random(1);

if (rand<0.5) {

childGenes.add(genes.get(i));

//get gene from this fucker

} else {

//get gene from parent2

childGenes.add(parent2.genes.get(parent2gene));

} else {//disjoint or excess gene

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

//so all the nodes can be inherrited from this parent

for (int i = 0; i < nodes.size(); i++) {

child.nodes.add(nodes.get(i).clone());

//clone all the connections so that they connect the childs new nodes

for ( int i =0; i<childGenes.size(); i++) {

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;

//----------------------------------------------------------------------------------------------------------------------------------
------

//create an empty genome

Genome(int in, int out, boolean crossover) {

//set input number and output number

inputs = in;

outputs = out;
}

//----------------------------------------------------------------------------------------------------------------------------------
------

//returns whether or not there is a gene matching the input innovation number in the input
genome

int matchingGene(Genome parent2, int innovationNumber) {

for (int i =0; i < parent2.genes.size(); i++) {

if (parent2.genes.get(i).innovationNo == innovationNumber) {

return i;

return -1; //no matching gene found

//----------------------------------------------------------------------------------------------------------------------------------
------

//prints out info about the genome to the console

void printGenome() {

println("Print genome layers:", layers);

println("bias node: " + biasNode);

println("nodes");

for (int i = 0; i < nodes.size(); i++) {

print(nodes.get(i).number + ",");

println("Genes");

for (int i = 0; i < genes.size(); i++) {//for each connectionGene

println("gene " + genes.get(i).innovationNo, "From node " + genes.get(i).fromNode.number, "To


node " + genes.get(i).toNode.number,

"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();

}
//----------------------------------------------------------------------------------------------------------------------------------
------

//returns a copy of this genome

Genome clone() {

Genome clone = new Genome(inputs, outputs, true);

for (int i = 0; i < nodes.size(); i++) {//copy nodes

clone.nodes.add(nodes.get(i).clone());

//copy all the connections so that they connect the clone new nodes

for ( int i =0; i<genes.size(); i++) {//copy genes

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;

//----------------------------------------------------------------------------------------------------------------------------------
------

//draw the genome on the screen

void drawGenome(int startX, int startY, int w, int h) {

//i know its ugly but it works (and is not that important) so I'm not going to mess with it

ArrayList<ArrayList<Node>> allNodes = new ArrayList<ArrayList<Node>>();


ArrayList<PVector> nodePoses = new ArrayList<PVector>();

ArrayList<Integer> nodeNumbers= new ArrayList<Integer>();

//get the positions on the screen that each node is supposed to be in

//split the nodes into layers

for (int i = 0; i< layers; i++) {

ArrayList<Node> temp = new ArrayList<Node>();

for (int j = 0; j< nodes.size(); j++) {//for each node

if (nodes.get(j).layer == i ) {//check if it is in this layer

temp.add(nodes.get(j)); //add it to this layer

allNodes.add(temp);//add this layer to all nodes

//for each layer add the position of the node on the screen to the node posses arraylist

for (int i = 0; i < layers; i++) {

fill(255, 0, 0);

float x = startX + (float)((i)*w)/(float)(layers-1);

for (int j = 0; j< allNodes.get(i).size(); j++) {//for the position in the layer

float y = startY + ((float)(j + 1.0) * h)/(float)(allNodes.get(i).size() + 1.0);

nodePoses.add(new PVector(x, y));

nodeNumbers.add(allNodes.get(i).get(j).number);

if(i == layers -1){

println(i,j,x,y);

}
}

//draw connections

stroke(0);

strokeWeight(2);

for (int i = 0; i< genes.size(); i++) {

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));

line(from.x, from.y, to.x, to.y);

//draw nodes last so they appear ontop of the connection lines

for (int i = 0; i < nodePoses.size(); i++) {

fill(255);

stroke(0);

strokeWeight(1);

ellipse(nodePoses.get(i).x, nodePoses.get(i).y, 20, 20);

textSize(10);
fill(0);

textAlign(CENTER, CENTER);

text(nodeNumbers.get(i), nodePoses.get(i).x, nodePoses.get(i).y);

Ground

//this class is useless it just shows some dots on the ground

class Ground{

float posX = width;

float posY = height -floor(random(groundHeight - 20,groundHeight +30));

int w = floor(random(1,10));

Ground(){}

void show(){

stroke(0);

strokeWeight(3);

line(posX,posY, posX + w, posY);

void move(float speed) {

posX -= speed;

}
Node

class Node {

int number;

float inputSum = 0;//current sum i.e. before activation

float outputValue = 0; //after activation function is applied

ArrayList<connectionGene> outputConnections = new ArrayList<connectionGene>();

int layer = 0;

PVector drawPos = new PVector();

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//constructor

Node(int no) {

number = no;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//the node sends its output to the inputs of the nodes its connected to

void engage() {

if (layer!=0) {//no sigmoid for the inputs and bias

outputValue = sigmoid(inputSum);

for (int i = 0; i< outputConnections.size(); i++) {//for each connection

if (outputConnections.get(i).enabled) {//dont do shit if not enabled

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;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//sigmoid activation function

float sigmoid(float x) {

float y = 1 / (1 + pow((float)Math.E, -4.9*x));

return y;

//----------------------------------------------------------------------------------------------------------------------------------
------------------------

//returns whether this node connected to the parameter node

//used when adding a new connection

boolean isConnectedTo(Node node) {

if (node.layer == layer) {//nodes in the same layer cannot be connected

return false;

//you get it

if (node.layer < layer) {

for (int i = 0; i < node.outputConnections.size(); i++) {

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;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//returns a copy of this node

Node clone() {

Node clone = new Node(number);

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;

case 1://big cactus

w = 60;

h = 120;

break;

case 2://small cacti

w = 120;

h = 80;

break;

//----------------------------------------------------------------------------------------------------------------------------------
--------------------

//show the cactus

void show() {

fill(0);

rectMode(CENTER);

switch(type) {

case 0:

image(smallCactus, posX - smallCactus.width/2, height - groundHeight - smallCactus.height);

break;

case 1:

image(bigCactus, posX - bigCactus.width/2, height - groundHeight - bigCactus.height);

break;

case 2:

image(manySmallCactus, posX - manySmallCactus.width/2, height - groundHeight -


manySmallCactus.height);

break;
}

//----------------------------------------------------------------------------------------------------------------------------------
--------------------

// move the obstacle

void move(float speed) {

posX -= speed;

//----------------------------------------------------------------------------------------------------------------------------------
--------------------

//returns whether or not the player collides with this obstacle

boolean collided(float playerX, float playerY, float playerWidth, float playerHeight) {

float playerLeft = playerX - playerWidth/2;

float playerRight = playerX + playerWidth/2;

float thisLeft = posX - w/2 ;

float thisRight = posX + w/2;

if ((playerLeft<= thisRight && playerRight >= thisLeft ) || (thisLeft <= playerRight && thisRight >=
playerLeft)) {

float playerDown = playerY - playerHeight/2;

float thisUp = h;

if (playerDown <= thisUp) {

return true;

return false;

Player
class Player {

float fitness;

Genome brain;

boolean replay = false;

float unadjustedFitness;

int lifespan = 0;//how long the player lived for fitness

int bestScore =0;//stores the score achieved used for replay

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[] decision = new float[genomeOutputs]; //the out put of the NN

//-------------------------------------

float posY = 0;

float velY = 0;

float gravity =1.2;

int runCount = -5;

int size = 20;

ArrayList<Obstacle> replayObstacles = new ArrayList<Obstacle>();

ArrayList<Bird> replayBirds = new ArrayList<Bird>();

ArrayList<Integer> localObstacleHistory = new ArrayList<Integer>();

ArrayList<Integer> localRandomAdditionHistory = new ArrayList<Integer>();

int historyCounter = 0;

int localObstacleTimer = 0;

float localSpeed = 10;


int localRandomAddition = 0;

boolean duck= false;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//constructor

Player() {

brain = new Genome(genomeInputs, genomeOutputs);

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//show the dino

void show() {

if (duck && posY == 0) {

if (runCount < 0) {

image(dinoDuck, playerXpos - dinoDuck.width/2, height - groundHeight - (posY +


dinoDuck.height));

} else {

image(dinoDuck1, playerXpos - dinoDuck1.width/2, height - groundHeight - (posY +


dinoDuck1.height));

} else

if (posY ==0) {

if (runCount < 0) {

image(dinoRun1, playerXpos - dinoRun1.width/2, height - groundHeight - (posY +


dinoRun1.height));

} else {

image(dinoRun2, playerXpos - dinoRun2.width/2, height - groundHeight - (posY +


dinoRun2.height));
}

} else {

image(dinoJump, playerXpos - dinoJump.width/2, height - groundHeight - (posY +


dinoJump.height));

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) {

for (int i = 0; i< obstacles.size(); i++) {

if (obstacles.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

for (int i = 0; i< birds.size(); i++) {

if (duck && posY ==0) {

if (birds.get(i).collided(playerXpos, posY + dinoDuck.height/2, dinoDuck.width*0.8,


dinoDuck.height)) {

dead = true;

} else {

if (birds.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

} else {//if replayign then move local obstacles

for (int i = 0; i< replayObstacles.size(); i++) {

if (replayObstacles.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

}
for (int i = 0; i< replayBirds.size(); i++) {

if (duck && posY ==0) {

if (replayBirds.get(i).collided(playerXpos, posY + dinoDuck.height/2, dinoDuck.width*0.8,


dinoDuck.height)) {

dead = true;

} else {

if (replayBirds.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//what could this do????

void jump(boolean bigJump) {

if (posY ==0) {

if (bigJump) {

gravity = 1;

velY = 20;

} else {

gravity = 1.2;

velY = 16;

}
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//if parameter is true and is in the air increase gravity

void ducking(boolean isDucking) {

if (posY != 0 && isDucking) {

gravity = 3;

duck = isDucking;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//called every frame

void update() {

incrementCounters();

move();

//----------------------------------------------------------------------------------------------------------------------------------
------------------------

//get inputs for Neural network

void look() {

if (!replay) {

float temp = 0;

float min = 10000;

int minIndex = -1;

boolean berd = false;

for (int i = 0; i< obstacles.size(); i++) {

if (obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2) < min &&


obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2) > 0) {//if the distance
between the left of the player and the right of the obstacle is the least

min = obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

}
}

for (int i = 0; i< birds.size(); i++) {

if (birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2) < min &&


birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2) > 0) {//if the distance between
the left of the player and the right of the obstacle is the least

min = birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

berd = true;

vision[4] = speed;

vision[5] = posY;

if (minIndex == -1) {//if there are no obstacles

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

int bestIndex = minIndex;

float closestDist = min;

min = 10000;

minIndex = -1;

for (int i = 0; i< obstacles.size(); i++) {

if ((berd || i != bestIndex) && obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos -


dinoRun1.width/2) < min && obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos -
dinoRun1.width/2) > 0) {//if the distance between the left of the player and the right of the obstacle
is the least

min = obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

for (int i = 0; i< birds.size(); i++) {

if ((!berd || i != bestIndex) && birds.get(i).posX + birds.get(i).w/2 - (playerXpos -


dinoRun1.width/2) < min && birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2) >
0) {//if the distance between the left of the player and the right of the obstacle is the least

min = birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

}
if (minIndex == -1) {//if there is only one obejct on the screen

vision[6] = 0;

} else {

vision[6] = 1/(min - closestDist);

} else {//if replaying then use local shit

float temp = 0;

float min = 10000;

int minIndex = -1;

boolean berd = false;

for (int i = 0; i< replayObstacles.size(); i++) {

if (replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2) <


min && replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2)
> 0) {//if the distance between the left of the player and the right of the obstacle is the least

min = replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos -


dinoRun1.width/2);

minIndex = i;

for (int i = 0; i< replayBirds.size(); i++) {

if (replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2) < min &&


replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2) > 0) {//if the
distance between the left of the player and the right of the obstacle is the least

min = replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

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

int bestIndex = minIndex;

float closestDist = min;

min = 10000;
minIndex = -1;

for (int i = 0; i< replayObstacles.size(); i++) {

if ((berd || i != bestIndex) && replayObstacles.get(i).posX + replayObstacles.get(i).w/2 -


(playerXpos - dinoRun1.width/2) < min && replayObstacles.get(i).posX + replayObstacles.get(i).w/2
- (playerXpos - dinoRun1.width/2) > 0) {//if the distance between the left of the player and the right
of the obstacle is the least

min = replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos -


dinoRun1.width/2);

minIndex = i;

for (int i = 0; i< replayBirds.size(); i++) {

if ((!berd || i != bestIndex) && replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos -


dinoRun1.width/2) < min && replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos -
dinoRun1.width/2) > 0) {//if the distance between the left of the player and the right of the obstacle
is the least

min = replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

if (minIndex == -1) {//if there is only one obejct on the screen

vision[6] = 0;

} else {

vision[6] = 1/(min - closestDist);

}
//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//gets the output of the brain then converts them to actions

void think() {

float max = 0;

int maxIndex = 0;

//get the output of the neural network

decision = brain.feedForward(vision);

for (int i = 0; i < decision.length; i++) {

if (decision[i] > max) {

max = decision[i];

maxIndex = i;

if (max < 0.7) {

ducking(false);

return;

switch(maxIndex) {

case 0:

jump(false);

break;

case 1:

jump(true);
break;

case 2:

ducking(true);

break;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//returns a clone of this player with the same brian

Player clone() {

Player clone = new Player();

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

//this fuction does that

Player cloneForReplay() {

Player clone = new Player();

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;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//fot Genetic algorithm

void calculateFitness() {

fitness = score*score;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

Player crossover(Player parent2) {

Player child = new Player();

child.brain = brain.crossover(parent2.brain);

child.brain.generateNetwork();

return child;

//----------------------------------------------------------------------------------------------------------------------------------
----------------------

//if replaying then the dino has local obstacles

void updateLocalObstacles() {

localObstacleTimer ++;
localSpeed += 0.002;

if (localObstacleTimer > minimumTimeBetweenObstacles + localRandomAddition) {

addLocalObstacle();

groundCounter ++;

if (groundCounter > 10) {

groundCounter =0;

grounds.add(new Ground());

moveLocalObstacles();

showLocalObstacles();

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

void moveLocalObstacles() {

for (int i = 0; i< replayObstacles.size(); i++) {

replayObstacles.get(i).move(localSpeed);

if (replayObstacles.get(i).posX < -100) {

replayObstacles.remove(i);

i--;

for (int i = 0; i< replayBirds.size(); i++) {

replayBirds.get(i).move(localSpeed);

if (replayBirds.get(i).posX < -100) {

replayBirds.remove(i);

i--;

}
}

for (int i = 0; i < grounds.size(); i++) {

grounds.get(i).move(localSpeed);

if (grounds.get(i).posX < -100) {

grounds.remove(i);

i--;

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------

void addLocalObstacle() {

int tempInt = localObstacleHistory.get(historyCounter);

localRandomAddition = localRandomAdditionHistory.get(historyCounter);

historyCounter ++;

if (tempInt < 3) {

replayBirds.add(new Bird(tempInt));

} else {

replayObstacles.add(new Obstacle(tempInt -3));

localObstacleTimer = 0;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

void showLocalObstacles() {

for (int i = 0; i< grounds.size(); i++) {

grounds.get(i).show();

for (int i = 0; i< replayObstacles.size(); i++) {

replayObstacles.get(i).show();

}
for (int i = 0; i< replayBirds.size(); i++) {

replayBirds.get(i).show();

Population

class Player {

float fitness;

Genome brain;

boolean replay = false;

float unadjustedFitness;

int lifespan = 0;//how long the player lived for fitness

int bestScore =0;//stores the score achieved used for replay

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[] decision = new float[genomeOutputs]; //the out put of the NN

//-------------------------------------

float posY = 0;

float velY = 0;

float gravity =1.2;

int runCount = -5;

int size = 20;


ArrayList<Obstacle> replayObstacles = new ArrayList<Obstacle>();

ArrayList<Bird> replayBirds = new ArrayList<Bird>();

ArrayList<Integer> localObstacleHistory = new ArrayList<Integer>();

ArrayList<Integer> localRandomAdditionHistory = new ArrayList<Integer>();

int historyCounter = 0;

int localObstacleTimer = 0;

float localSpeed = 10;

int localRandomAddition = 0;

boolean duck= false;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//constructor

Player() {

brain = new Genome(genomeInputs, genomeOutputs);

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//show the dino

void show() {

if (duck && posY == 0) {

if (runCount < 0) {

image(dinoDuck, playerXpos - dinoDuck.width/2, height - groundHeight - (posY +


dinoDuck.height));

} else {

image(dinoDuck1, playerXpos - dinoDuck1.width/2, height - groundHeight - (posY +


dinoDuck1.height));

}
} else

if (posY ==0) {

if (runCount < 0) {

image(dinoRun1, playerXpos - dinoRun1.width/2, height - groundHeight - (posY +


dinoRun1.height));

} else {

image(dinoRun2, playerXpos - dinoRun2.width/2, height - groundHeight - (posY +


dinoRun2.height));

} else {

image(dinoJump, playerXpos - dinoJump.width/2, height - groundHeight - (posY +


dinoJump.height));

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) {

for (int i = 0; i< obstacles.size(); i++) {

if (obstacles.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

for (int i = 0; i< birds.size(); i++) {

if (duck && posY ==0) {

if (birds.get(i).collided(playerXpos, posY + dinoDuck.height/2, dinoDuck.width*0.8,


dinoDuck.height)) {

dead = true;

} else {

if (birds.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

} else {//if replayign then move local obstacles

for (int i = 0; i< replayObstacles.size(); i++) {


if (replayObstacles.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,
dinoRun1.height)) {

dead = true;

for (int i = 0; i< replayBirds.size(); i++) {

if (duck && posY ==0) {

if (replayBirds.get(i).collided(playerXpos, posY + dinoDuck.height/2, dinoDuck.width*0.8,


dinoDuck.height)) {

dead = true;

} else {

if (replayBirds.get(i).collided(playerXpos, posY +dinoRun1.height/2, dinoRun1.width*0.5,


dinoRun1.height)) {

dead = true;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//what could this do????

void jump(boolean bigJump) {

if (posY ==0) {

if (bigJump) {

gravity = 1;

velY = 20;
} else {

gravity = 1.2;

velY = 16;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//if parameter is true and is in the air increase gravity

void ducking(boolean isDucking) {

if (posY != 0 && isDucking) {

gravity = 3;

duck = isDucking;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//called every frame

void update() {

incrementCounters();

move();

//----------------------------------------------------------------------------------------------------------------------------------
------------------------

//get inputs for Neural network

void look() {

if (!replay) {

float temp = 0;

float min = 10000;

int minIndex = -1;


boolean berd = false;

for (int i = 0; i< obstacles.size(); i++) {

if (obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2) < min &&


obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2) > 0) {//if the distance
between the left of the player and the right of the obstacle is the least

min = obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

for (int i = 0; i< birds.size(); i++) {

if (birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2) < min &&


birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2) > 0) {//if the distance between
the left of the player and the right of the obstacle is the least

min = birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

berd = true;

vision[4] = speed;

vision[5] = posY;

if (minIndex == -1) {//if there are no obstacles

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

int bestIndex = minIndex;

float closestDist = min;

min = 10000;

minIndex = -1;

for (int i = 0; i< obstacles.size(); i++) {

if ((berd || i != bestIndex) && obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos -


dinoRun1.width/2) < min && obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos -
dinoRun1.width/2) > 0) {//if the distance between the left of the player and the right of the obstacle
is the least

min = obstacles.get(i).posX + obstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

}
for (int i = 0; i< birds.size(); i++) {

if ((!berd || i != bestIndex) && birds.get(i).posX + birds.get(i).w/2 - (playerXpos -


dinoRun1.width/2) < min && birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2) >
0) {//if the distance between the left of the player and the right of the obstacle is the least

min = birds.get(i).posX + birds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

if (minIndex == -1) {//if there is only one obejct on the screen

vision[6] = 0;

} else {

vision[6] = 1/(min - closestDist);

} else {//if replaying then use local shit

float temp = 0;

float min = 10000;

int minIndex = -1;

boolean berd = false;

for (int i = 0; i< replayObstacles.size(); i++) {

if (replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2) <


min && replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos - dinoRun1.width/2)
> 0) {//if the distance between the left of the player and the right of the obstacle is the least

min = replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos -


dinoRun1.width/2);

minIndex = i;

for (int i = 0; i< replayBirds.size(); i++) {


if (replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2) < min &&
replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2) > 0) {//if the
distance between the left of the player and the right of the obstacle is the least

min = replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

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

int bestIndex = minIndex;

float closestDist = min;

min = 10000;

minIndex = -1;

for (int i = 0; i< replayObstacles.size(); i++) {

if ((berd || i != bestIndex) && replayObstacles.get(i).posX + replayObstacles.get(i).w/2 -


(playerXpos - dinoRun1.width/2) < min && replayObstacles.get(i).posX + replayObstacles.get(i).w/2
- (playerXpos - dinoRun1.width/2) > 0) {//if the distance between the left of the player and the right
of the obstacle is the least

min = replayObstacles.get(i).posX + replayObstacles.get(i).w/2 - (playerXpos -


dinoRun1.width/2);

minIndex = i;

for (int i = 0; i< replayBirds.size(); i++) {

if ((!berd || i != bestIndex) && replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos -


dinoRun1.width/2) < min && replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos -
dinoRun1.width/2) > 0) {//if the distance between the left of the player and the right of the obstacle
is the least

min = replayBirds.get(i).posX + replayBirds.get(i).w/2 - (playerXpos - dinoRun1.width/2);

minIndex = i;

if (minIndex == -1) {//if there is only one obejct on the screen


vision[6] = 0;

} else {

vision[6] = 1/(min - closestDist);

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//gets the output of the brain then converts them to actions

void think() {

float max = 0;

int maxIndex = 0;

//get the output of the neural network

decision = brain.feedForward(vision);

for (int i = 0; i < decision.length; i++) {

if (decision[i] > max) {

max = decision[i];

maxIndex = i;

if (max < 0.7) {


ducking(false);

return;

switch(maxIndex) {

case 0:

jump(false);

break;

case 1:

jump(true);

break;

case 2:

ducking(true);

break;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//returns a clone of this player with the same brian

Player clone() {

Player clone = new Player();

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

//this fuction does that

Player cloneForReplay() {

Player clone = new Player();

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;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//fot Genetic algorithm

void calculateFitness() {

fitness = score*score;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------
Player crossover(Player parent2) {

Player child = new Player();

child.brain = brain.crossover(parent2.brain);

child.brain.generateNetwork();

return child;

//----------------------------------------------------------------------------------------------------------------------------------
----------------------

//if replaying then the dino has local obstacles

void updateLocalObstacles() {

localObstacleTimer ++;

localSpeed += 0.002;

if (localObstacleTimer > minimumTimeBetweenObstacles + localRandomAddition) {

addLocalObstacle();

groundCounter ++;

if (groundCounter > 10) {

groundCounter =0;

grounds.add(new Ground());

moveLocalObstacles();

showLocalObstacles();

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

void moveLocalObstacles() {

for (int i = 0; i< replayObstacles.size(); i++) {

replayObstacles.get(i).move(localSpeed);

if (replayObstacles.get(i).posX < -100) {

replayObstacles.remove(i);
i--;

for (int i = 0; i< replayBirds.size(); i++) {

replayBirds.get(i).move(localSpeed);

if (replayBirds.get(i).posX < -100) {

replayBirds.remove(i);

i--;

for (int i = 0; i < grounds.size(); i++) {

grounds.get(i).move(localSpeed);

if (grounds.get(i).posX < -100) {

grounds.remove(i);

i--;

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------

void addLocalObstacle() {

int tempInt = localObstacleHistory.get(historyCounter);

localRandomAddition = localRandomAdditionHistory.get(historyCounter);

historyCounter ++;

if (tempInt < 3) {

replayBirds.add(new Bird(tempInt));

} else {

replayObstacles.add(new Obstacle(tempInt -3));

localObstacleTimer = 0;
}

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

void showLocalObstacles() {

for (int i = 0; i< grounds.size(); i++) {

grounds.get(i).show();

for (int i = 0; i< replayObstacles.size(); i++) {

replayObstacles.get(i).show();

for (int i = 0; i< replayBirds.size(); i++) {

replayBirds.get(i).show();

Species

class Species {

ArrayList<Player> players = new ArrayList<Player>();

float bestFitness = 0;

Player champ;

float averageFitness = 0;

int staleness = 0;//how many generations the species has gone without an improvement

Genome rep;

//--------------------------------------------

//coefficients for testing compatibility

float excessCoeff = 1;

float weightDiffCoeff = 0.5;


float compatibilityThreshold = 3;

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------

//empty constructor

Species() {

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------

//constructor which takes in the player which belongs to the species

Species(Player p) {

players.add(p);

//since it is the only one in the species it is by default the best

bestFitness = p.fitness;

rep = p.brain.clone();

champ = p.cloneForReplay();

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------

//returns whether the parameter genome is in this species

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

float averageWeightDiff = averageWeightDiff(g, rep);//get the average weight difference between


matching genes

float largeGenomeNormaliser = g.genes.size() - 20;

if (largeGenomeNormaliser<1) {
largeGenomeNormaliser =1;

compatibility = (excessCoeff* excessAndDisjoint/largeGenomeNormaliser) + (weightDiffCoeff*


averageWeightDiff);//compatablilty formula

return (compatibilityThreshold > compatibility);

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------

//add a player to the species

void addToSpecies(Player p) {

players.add(p);

//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------

//returns the number of excess and disjoint genes between the 2 input genomes

//i.e. returns the number of genes which dont match

float getExcessDisjoint(Genome brain1, Genome brain2) {

float matching = 0.0;

for (int i =0; i <brain1.genes.size(); i++) {

for (int j = 0; j < brain2.genes.size(); j++) {

if (brain1.genes.get(i).innovationNo == brain2.genes.get(j).innovationNo) {

matching ++;

break;

return (brain1.genes.size() + brain2.genes.size() - 2*(matching));//return no of excess and disjoint


genes

}
//----------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------

//returns the avereage weight difference between matching genes in the input genomes

float averageWeightDiff(Genome brain1, Genome brain2) {

if (brain1.genes.size() == 0 || brain2.genes.size() ==0) {

return 0;

float matching = 0;

float totalDiff= 0;

for (int i =0; i <brain1.genes.size(); i++) {

for (int j = 0; j < brain2.genes.size(); j++) {

if (brain1.genes.get(i).innovationNo == brain2.genes.get(j).innovationNo) {

matching ++;

totalDiff += abs(brain1.genes.get(i).weight - brain2.genes.get(j).weight);

break;

if (matching ==0) {//divide by 0 error

return 100;

return totalDiff/matching;

//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------

//sorts the species by fitness

void sortSpecies() {

ArrayList<Player> temp = new ArrayList<Player>();


//selection short

for (int i = 0; i < players.size(); i ++) {

float max = 0;

int maxIndex = 0;

for (int j = 0; j< players.size(); j++) {

if (players.get(j).fitness > max) {

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;

//if new best player

if (players.get(0).fitness > bestFitness) {

staleness = 0;

bestFitness = players.get(0).fitness;

rep = players.get(0).brain.clone();

champ = players.get(0).cloneForReplay();

} else {//if no new best player

staleness ++;

}
//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------

//simple stuff

void setAverage() {

float sum = 0;

for (int i = 0; i < players.size(); i ++) {

sum += players.get(i).fitness;

averageFitness = sum/players.size();

//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------

//gets baby from the players in this species

Player giveMeBaby(ArrayList<connectionHistory> innovationHistory) {

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();

} else {//75% of the time do crossover

//get 2 random(ish) parents

Player parent1 = selectPlayer();

Player parent2 = selectPlayer();

//the crossover function expects the highest fitness parent to be the object and the lowest as the
argument

if (parent1.fitness < parent2.fitness) {

baby = parent2.crossover(parent1);

} else {
baby = parent1.crossover(parent2);

baby.brain.mutate(innovationHistory);//mutate that baby brain

return baby;

//----------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------

//selects a player based on it fitness

Player selectPlayer() {

float fitnessSum = 0;

for (int i =0; i<players.size(); i++) {

fitnessSum += players.get(i).fitness;

float rand = random(fitnessSum);

float runningSum = 0;

for (int i = 0; i<players.size(); i++) {

runningSum += players.get(i).fitness;

if (runningSum > rand) {

return players.get(i);

//unreachable code to make the parser happy

return players.get(0);

//----------------------------------------------------------------------------------------------------------------------------------
--------

//kills off bottom half of the species

void cull() {
if (players.size() > 2) {

for (int i = players.size()/2; i<players.size(); i++) {

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() {

for (int i = 0; i< players.size(); i++) {

players.get(i).fitness/=players.size();

connectionGene

//a connection between 2 nodes

class connectionGene {

Node fromNode;

Node toNode;

float weight;

boolean enabled = true;

int innovationNo;//each connection is given a innovation number to compare genomes

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//constructor

connectionGene(Node from, Node to, float w, int inno) {


fromNode = from;

toNode = to;

weight = w;

innovationNo = inno;

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------

//changes the weight

void mutateWeight() {

float rand2 = random(1);

if (rand2 < 0.1) {//10% of the time completely change the weight

weight = random(-1, 1);

} else {//otherwise slightly change it

weight += randomGaussian()/50;

//keep weight between bounds

if(weight > 1){

weight = 1;

if(weight < -1){

weight = -1;

//----------------------------------------------------------------------------------------------------------

//returns a copy of this connectionGene

connectionGene clone(Node from, Node to) {

connectionGene clone = new connectionGene(from, to, weight, innovationNo);

clone.enabled = enabled;
return clone;

connectionHistory

class connectionHistory {

int fromNode;

int toNode;

int innovationNumber;

ArrayList<Integer> innovationNumbers = new ArrayList<Integer>();//the innovation Numbers from


the connections of the genome which first had this mutation

//this represents the genome and allows us to test if another genoeme is the same

//this is before this connection was added

//----------------------------------------------------------------------------------------------------------------------------------
-----------------------

//constructor

connectionHistory(int from, int to, int inno, ArrayList<Integer> innovationNos) {

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

boolean matches(Genome genome, Node from, Node to) {

if (genome.genes.size() == innovationNumbers.size()) { //if the number of connections are


different then the genoemes aren't the same
if (from.number == fromNode && to.number == toNode) {

//next check if all the innovation numbers match from the genome

for (int i = 0; i< genome.genes.size(); i++) {

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

//so it does match

return true;

return false;

You might also like