diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..04ba73a Binary files /dev/null and b/.DS_Store differ diff --git a/Asteroid.pde b/Asteroid.pde index 528ec89..cabda25 100644 --- a/Asteroid.pde +++ b/Asteroid.pde @@ -71,6 +71,10 @@ public class Asteroid{ if (distance < size/2 + locals.player.getSize()){ locals.player.setHit(); } + + if (distance < 100 + (size/2) && locals.player.accelerate){ + locals.player.avoids = locals.player.avoids + 1; + } } public void explode(){ diff --git a/GameScene.pde b/GameScene.pde index 0a13ff7..6f04c7c 100644 --- a/GameScene.pde +++ b/GameScene.pde @@ -4,14 +4,16 @@ public class GameScene{ boolean online; Locals locals; Simple_NEAT n; + Grid gameGrid; public GameScene(Locals l){ locals = l; - locals.level = 4; + locals.level = 1; resetAstroids(locals.level); + gameGrid = new Grid(50, l); locals.player = new Ship(locals); - n = new Simple_NEAT(33,4); - Network temp = Network.loadFromFile("C:/Users/WALTR/Downloads/CODE/Processing/Asteroids_Train/best.net"); + n = new Simple_NEAT(gameGrid.getWidth() * gameGrid.getWidth() + 1, 4); + Network temp = Network.loadFromFile("/home/sam/Documents/CIS_365/AiProject/newRepo/Asteroids_Train/best.net"); n.addAgent(temp); n.setCurrentAgent(0); } @@ -73,9 +75,10 @@ public class GameScene{ showText(); // locals.player.showBullets(); locals.player.show(); - runNetwork(); + runNetwork2(); showAsteroids(); checkLevel(); + gameGrid.show(); } public void runNetwork(){ @@ -105,7 +108,49 @@ public class GameScene{ if (outputs[2] >= 0.5){ locals.player.turnRight(); } - if (outputs[3] >= 0.5){ + if (outputs[3] <= 0.5){ + locals.player.accelerate = true; + locals.player.accelerate(); + } + else{ + locals.player.accelerate = false; + } + + String outs = ""; + for(float f : outputs){ + outs +=(f + ", "); + } + textSize(20); + text(outs, width/2, height-50); + + } + + public void runNetwork2(){ + float[] inputs = new float[gameGrid.getWidth() * gameGrid.getWidth() + 1]; + inputs[0] = (float) locals.player.getAngle() / (2*3.14159265359); + int c = 1; + for (int x = 0; x < gameGrid.getGrid().length; x++){ + for (int y = 0; y < gameGrid.getGrid().length; y++){ + inputs[c] = (float) gameGrid.getGrid()[x][y]; + // System.out.println(c + " - " + (float) gameGrid.getGrid()[x][y]); + c++; + } + } + + n.runCurrent(inputs); + + float[] outputs = n.getCurOutput(); + + if (outputs[0] > 0.0){ + locals.player.shoot(); + } + if (outputs[1] > 0.0){ + locals.player.turnLeft(); + } + if (outputs[2] > 0.0){ + locals.player.turnRight(); + } + if (outputs[3] > 0.0){ locals.player.accelerate = true; locals.player.accelerate(); } diff --git a/Grid.pde b/Grid.pde new file mode 100644 index 0000000..743219f --- /dev/null +++ b/Grid.pde @@ -0,0 +1,96 @@ +public class Grid{ + double[][] grid; + Locals l; + int cellWidth; + + + //CellWidth should be an even multiple of the width and height of the screen. + public Grid(int cellWidth, Locals l){ + this.l = l; + this.cellWidth = cellWidth; + int xSize = (int) l.width / cellWidth; + int ySize = (int) l.height / cellWidth; + grid = new double[xSize][ySize]; + resetGrid(); + } + + private void resetGrid(){ + for (int x = 0; x < grid.length; x++){ + for (int y = 0; y < grid.length; y++){ + grid[x][y] = 0.0; + } + } + } + + private void setAsteroids(){ + resetGrid(); + for (Asteroid a : l.asteroids){ + int xCell = (int) (a.getX() / cellWidth); + int yCell = (int) (a.getY() / cellWidth); + + //Cap all coordinates + if (xCell >= grid.length) + xCell = grid.length-1; + if (xCell < 0) + xCell = 0; + if (yCell >= grid[0].length) + yCell = grid[0].length-1; + if (yCell < 0) + yCell = 0; + + grid[xCell][yCell] = 1.0; + } + + int xCell = (int) (l.player.getX() / cellWidth); + int yCell = (int) (l.player.getY() / cellWidth); + + //Cap all coordinates + if (xCell >= grid.length) + xCell = grid.length-1; + if (xCell < 0) + xCell = 0; + if (yCell >= grid[0].length) + yCell = grid[0].length-1; + if (yCell < 0) + yCell = 0; + + grid[xCell][yCell] = -1; + } + + public void show(){ + float squareSize = 200; + + stroke(255); + fill(0, 0, 0); + rect(10, 10, squareSize, squareSize); + + float subSize = squareSize / grid.length; + + setAsteroids(); + for (int x = 0; x < grid.length; x++){ + for (int y = 0; y < grid.length; y++){ + if (grid[x][y] > 0){ + fill(255); + noStroke(); + rectMode(CORNER); + rect(10 + subSize * x, 10 + subSize * y, subSize, subSize); + } + if (grid[x][y] < 0){ + fill(255, 0, 0); + noStroke(); + rectMode(CORNER); + rect(10 + subSize * x, 10 + subSize * y, subSize, subSize); + } + } + } + } + + public int getWidth(){ + return grid.length; + } + + public double[][] getGrid(){ + return grid; + } + +} diff --git a/Network.java b/Network.java index 2e84b3f..9a3f1f5 100644 --- a/Network.java +++ b/Network.java @@ -175,6 +175,15 @@ public void mutateWeight(){ c.randomizeWeight(); } + public void mutateBiasWeight(){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + validConnections.addAll(outputs); + + Neuron n = validConnections.get(r.nextInt(validConnections.size())); + n.randomizeBiasWeight(); + } + public void remvoveRandConnection(){ ArrayList validConnections = new ArrayList(); validConnections.addAll(hidden); diff --git a/NetworkView.pde b/NetworkView.pde new file mode 100644 index 0000000..8809d24 --- /dev/null +++ b/NetworkView.pde @@ -0,0 +1,28 @@ +public class NetworkView{ + Network net; + ArrayList nodes; + + public NetworkView(Network net){ + this.net = net; + } + + private void initNodes(){ + // for (Neuron n : net.getInputs()){ + // break; + // } + } + + public void show(){ + return; + } +} + + +class Node{ + float x, y; + + public Node(float x, float y){ + this.x = x; + this.y = y; + } +} diff --git a/Neuron.java b/Neuron.java index 0ff38a1..21256e6 100644 --- a/Neuron.java +++ b/Neuron.java @@ -62,8 +62,10 @@ public void setInput(float val){ } public void activate(){ - float d = (float) Math.pow((double) Math.exp(1.0),(double) sum()); - value = (float) (1.0/(1+d)); + float sum = sum(); + float n = (float) Math.pow((double) Math.exp(1.0), (double) sum) - (float) Math.pow((double) Math.exp(1.0), (double) -sum); + float d = (float) Math.pow((double) Math.exp(1.0), (double) sum) + (float) Math.pow((double) Math.exp(1.0), (double) -sum); + value = (float) (n/d); activated = true; } @@ -130,6 +132,10 @@ public void removeConnection(Neuron n){ } } + public void randomizeBiasWeight(){ + this.biasWeight = r.nextFloat(); + } + public static void main(String[] args){ Neuron n0 = new Neuron(1, 0); Neuron n1 = new Neuron(1, 1); diff --git a/Ship.pde b/Ship.pde index 35018cd..ba8fc87 100644 --- a/Ship.pde +++ b/Ship.pde @@ -9,7 +9,7 @@ public class Ship{ boolean turn, accelerate, dead, noHit; long timeStamp; int k; - int lives, maxLives, score; + int lives, maxLives, score, avoids; Vector velocity; final double PI = 3.14159265359; Locals locals; @@ -35,6 +35,7 @@ public class Ship{ this.maxLives = 1; this.lives = maxLives; this.score = 0; + this.avoids = 0; //ArrayList for the current pressed characters. //(Mainly used for making turning less janky.) this.pressedChars = new ArrayList(); @@ -105,6 +106,13 @@ public class Ship{ turnRight(); } } + + if (angle > 2*PI){ + angle = angle - (2*PI); + } + if (angle < 0){ + angle = (2*PI) + angle; + } } private void turnLeft(){ @@ -253,7 +261,7 @@ public class Ship{ pushMatrix(); //Display the bullets showBullets(); - showSensors(); + //showSensors(); //Edit pos of the ship. turn(); diff --git a/Simple_NEAT.java b/Simple_NEAT.java index 5a485b1..d17756e 100644 --- a/Simple_NEAT.java +++ b/Simple_NEAT.java @@ -22,7 +22,7 @@ public Simple_NEAT(int nI, int nO){ public void addAgent(){ agents.add(new Network(numInputs, numOutputs)); } - + public void addAgent(Network n){ agents.add(n); } @@ -119,10 +119,10 @@ public void mutate(){ //Should we mutate this agent? if (mutationRate >= num){ num = ThreadLocalRandom.current().nextDouble(0,1); - if (num <= 1/3){ + if (num <= .33){ a.addRandHiddenNode(); } - else if(num > 1/3 && num <= 2/3){ + else if(num > .33 && num <= .66){ a.addRandConnection(); } else{ diff --git a/Train.sh b/Train.sh new file mode 100755 index 0000000..2cd07b4 --- /dev/null +++ b/Train.sh @@ -0,0 +1,6 @@ +#!/bin/bash +javac ./*.java +javac ./Train_Files/*.java +cd ./Train_Files +java Asteroids_Train $1 +cd .. diff --git a/Train_Files/.DS_Store b/Train_Files/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/Train_Files/.DS_Store differ diff --git a/Train_Files/Asteroid.class b/Train_Files/Asteroid.class index a7f418c..fef68f2 100644 Binary files a/Train_Files/Asteroid.class and b/Train_Files/Asteroid.class differ diff --git a/Train_Files/Asteroid.java b/Train_Files/Asteroid.java index d4e8638..5252945 100644 --- a/Train_Files/Asteroid.java +++ b/Train_Files/Asteroid.java @@ -72,6 +72,10 @@ public void checkHit(){ //System.out.println(distance); if (distance < size/2 + locals.player.getSize()){ locals.player.setHit(); + } + + if (distance < 100 + (size/2) && locals.player.accelerate){ + locals.player.avoids = locals.player.avoids + 1; } } diff --git a/Train_Files/Asteroids_Train.class b/Train_Files/Asteroids_Train.class index c8ebd1d..f8d4315 100644 Binary files a/Train_Files/Asteroids_Train.class and b/Train_Files/Asteroids_Train.class differ diff --git a/Train_Files/Asteroids_Train.java b/Train_Files/Asteroids_Train.java index 4b6f0e8..381495e 100644 --- a/Train_Files/Asteroids_Train.java +++ b/Train_Files/Asteroids_Train.java @@ -17,7 +17,7 @@ public class Asteroids_Train{ public Asteroids_Train(){ setup(); numAgents = 80; - neat = new Simple_NEAT(33, 4); + neat = new Simple_NEAT((18 * 18) + 1, 4); locals.neat = neat; for (int i = 0; i < numAgents; i++){ neat.addAgent(); @@ -43,23 +43,30 @@ private void draw(){ } private void runAll(){ - int totalFrames = 180 * 60; + int totalFrames = 45 * 60; int c = 0; for (int i = 0; i < numAgents; i++){ - locals.GS.resetAstroids(4); + locals.GS.resetAstroids(1); neat.setCurrentAgent(i); int frameCount = 0; + boolean longLive = false; while(!locals.player.dead){ locals.GS.show(); frameCount++; - if (frameCount > totalFrames) + if (frameCount > totalFrames){ + longLive = true; break; + } } double curFit = locals.player.getScore(); //System.out.println("Accuracy: " + locals.player.getAccuracy() + " Score: " + curFit); curFit = curFit * locals.player.getAccuracy(); + if(longLive){ + curFit *= 2; + } + //curFit += locals.player.avoids * 2; // if (totalFrames - frameCount < 5) // curFit *= 2; neat.setFitness(i, curFit); @@ -90,8 +97,8 @@ public void printBest(int gen, int total){ removeLine(3); } int percent = (int) (gen / (total * 1.0) * 100); - System.out.println(String.format("Generation: %d / best fit: %f", gen ,neat.getBestFit().getFitness())); - System.out.println("Progress: " + Colors.GREEN + percent + "%" + Colors.RESET); + System.out.println(String.format("Generation: %d / AVG fit: %f", gen ,neat.getAvgFit())); + System.out.println("Progress: " + Colors.GREEN + percent + "%" + Colors.RESET); System.out.println("Time Remaining: " + Colors.YELLOW + timeRemain(gen , total) + Colors.RESET); } @@ -102,11 +109,11 @@ public void removeLine(int amt){ } } - public String timeRemain(int gen, int total){ + public String timeRemain(int gen, int total){ long tempStamp = System.currentTimeMillis(); long passed = tempStamp - stamp; timeSum += passed; - + if (gen % 10 == 0){ curAvgTime = (double) timeSum / 10.0; timeSum = 0; diff --git a/Train_Files/Colors.class b/Train_Files/Colors.class index 00b1fda..70d8da1 100644 Binary files a/Train_Files/Colors.class and b/Train_Files/Colors.class differ diff --git a/Train_Files/Connection.class b/Train_Files/Connection.class index a247b77..3b12ab6 100644 Binary files a/Train_Files/Connection.class and b/Train_Files/Connection.class differ diff --git a/Train_Files/GameScene.class b/Train_Files/GameScene.class index 71d02c3..f1ff73c 100644 Binary files a/Train_Files/GameScene.class and b/Train_Files/GameScene.class differ diff --git a/Train_Files/GameScene.java b/Train_Files/GameScene.java index e7490e7..e21f9ce 100644 --- a/Train_Files/GameScene.java +++ b/Train_Files/GameScene.java @@ -3,11 +3,13 @@ public class GameScene{ boolean online; Locals locals; + Grid gameGrid; public GameScene(Locals l){ locals = l; resetAstroids(locals.level); locals.player = new Ship(locals); + gameGrid = new Grid(50, l); } /** @@ -70,36 +72,34 @@ public void show(){ locals.player.show(); showAsteroids(); checkLevel(); + gameGrid.show(); } public void runNetwork(){ - float[] inputs = new float[33]; - int c = 0; - for (Sensor s : locals.player.getSensors()){ - inputs[c] = (float) s.getWeightValue(); - c++; + float[] inputs = new float[gameGrid.getWidth() * gameGrid.getWidth() + 1]; + inputs[0] = (float) locals.player.getAngle() / (float) (2*3.14159265359); + int c = 1; + for (int x = 0; x < gameGrid.getGrid().length; x++){ + for (int y = 0; y < gameGrid.getGrid().length; y++){ + inputs[c] = (float) gameGrid.getGrid()[x][y]; + c++; + } } - for (Sensor s : locals.player.getSensors()){ - inputs[c] = (float) s.getLevelValue(); - c++; - } - - inputs[32] = (float) locals.player.bullets.size() / 4; locals.neat.runCurrent(inputs); float[] outputs = locals.neat.getCurOutput(); - if (outputs[0] > 0.5){ + if (outputs[0] > 0.0){ locals.player.shoot(); } - if (outputs[1] >=0.5){ + if (outputs[1] > 0.0){ locals.player.turnLeft(); } - if (outputs[2] >= 0.5){ + if (outputs[2] > 0.0){ locals.player.turnRight(); } - if (outputs[3] >= 0.5){ + if (outputs[3] > 0.0){ locals.player.accelerate = true; locals.player.accelerate(); } diff --git a/Train_Files/Grid.class b/Train_Files/Grid.class new file mode 100644 index 0000000..e605648 Binary files /dev/null and b/Train_Files/Grid.class differ diff --git a/Train_Files/Grid.java b/Train_Files/Grid.java new file mode 100644 index 0000000..e3b2864 --- /dev/null +++ b/Train_Files/Grid.java @@ -0,0 +1,73 @@ +public class Grid{ + double[][] grid; + Locals l; + int cellWidth; + + + //CellWidth should be an even multiple of the width and height of the screen. + public Grid(int cellWidth, Locals l){ + this.l = l; + this.cellWidth = cellWidth; + int xSize = (int) l.width / cellWidth; + int ySize = (int) l.height / cellWidth; + grid = new double[xSize][ySize]; + resetGrid(); + } + + private void resetGrid(){ + for (int x = 0; x < grid.length; x++){ + for (int y = 0; y < grid.length; y++){ + grid[x][y] = 0.0; + } + } + } + + private void setAsteroids(){ + resetGrid(); + //System.out.println(l.asteroids.size()); + for (Asteroid a : l.asteroids){ + int xCell = (int) (a.getX() / cellWidth); + int yCell = (int) (a.getY() / cellWidth); + + //Cap all coordinates + if (xCell >= grid.length) + xCell = grid.length-1; + if (xCell < 0) + xCell = 0; + if (yCell >= grid[0].length) + yCell = grid[0].length-1; + if (yCell < 0) + yCell = 0; + + grid[xCell][yCell] = 1.0; + } + + int xCell = (int) (l.player.getX() / cellWidth); + int yCell = (int) (l.player.getY() / cellWidth); + + //Cap all coordinates + if (xCell >= grid.length) + xCell = grid.length-1; + if (xCell < 0) + xCell = 0; + if (yCell >= grid[0].length) + yCell = grid[0].length-1; + if (yCell < 0) + yCell = 0; + + grid[xCell][yCell] = -1; + } + + public void show(){ + setAsteroids(); + } + + public int getWidth(){ + return grid.length; + } + + public double[][] getGrid(){ + return grid; + } + +} diff --git a/Train_Files/Network.class b/Train_Files/Network.class index 67cab1c..27c1709 100644 Binary files a/Train_Files/Network.class and b/Train_Files/Network.class differ diff --git a/Train_Files/Network.java b/Train_Files/Network.java index 2e84b3f..eeac06d 100644 --- a/Train_Files/Network.java +++ b/Train_Files/Network.java @@ -170,9 +170,21 @@ public void mutateWeight(){ validConnections.addAll(outputs); Neuron n = validConnections.get(r.nextInt(validConnections.size())); - Connection c = n.getConnections().get(r.nextInt(n.getConnections().size())); + + if(n.getConnections().size() > 0){ + Connection c = n.getConnections().get(r.nextInt(n.getConnections().size())); + + c.randomizeWeight(); + } + } + + public void mutateBiasWeight(){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + validConnections.addAll(outputs); - c.randomizeWeight(); + Neuron n = validConnections.get(r.nextInt(validConnections.size())); + n.randomizeBiasWeight(); } public void remvoveRandConnection(){ diff --git a/Train_Files/Neuron.class b/Train_Files/Neuron.class index ce9cf95..5e107d9 100644 Binary files a/Train_Files/Neuron.class and b/Train_Files/Neuron.class differ diff --git a/Train_Files/Neuron.java b/Train_Files/Neuron.java index 0ff38a1..21256e6 100644 --- a/Train_Files/Neuron.java +++ b/Train_Files/Neuron.java @@ -62,8 +62,10 @@ public void setInput(float val){ } public void activate(){ - float d = (float) Math.pow((double) Math.exp(1.0),(double) sum()); - value = (float) (1.0/(1+d)); + float sum = sum(); + float n = (float) Math.pow((double) Math.exp(1.0), (double) sum) - (float) Math.pow((double) Math.exp(1.0), (double) -sum); + float d = (float) Math.pow((double) Math.exp(1.0), (double) sum) + (float) Math.pow((double) Math.exp(1.0), (double) -sum); + value = (float) (n/d); activated = true; } @@ -130,6 +132,10 @@ public void removeConnection(Neuron n){ } } + public void randomizeBiasWeight(){ + this.biasWeight = r.nextFloat(); + } + public static void main(String[] args){ Neuron n0 = new Neuron(1, 0); Neuron n1 = new Neuron(1, 1); diff --git a/Train_Files/Ship.class b/Train_Files/Ship.class index cd84113..d3aba52 100644 Binary files a/Train_Files/Ship.class and b/Train_Files/Ship.class differ diff --git a/Train_Files/Ship.java b/Train_Files/Ship.java index 4e409b5..2366205 100644 --- a/Train_Files/Ship.java +++ b/Train_Files/Ship.java @@ -8,7 +8,7 @@ public class Ship{ ArrayList sensors; boolean turn, accelerate, dead, noHit; long timeStamp; - int k, numShots, numHits; + int k, numShots, numHits, avoids; int lives, maxLives, score; Vector velocity; final double PI = 3.14159265359; @@ -37,6 +37,7 @@ public Ship(Locals l){ this.maxLives = 1; this.lives = maxLives; this.score = 0; + this.avoids = 0; //ArrayList for the current pressed characters. //(Mainly used for making turning less janky.) this.pressedChars = new ArrayList(); diff --git a/Train_Files/Simple_NEAT.class b/Train_Files/Simple_NEAT.class index 2b70b3d..f84955b 100644 Binary files a/Train_Files/Simple_NEAT.class and b/Train_Files/Simple_NEAT.class differ diff --git a/Train_Files/Simple_NEAT.java b/Train_Files/Simple_NEAT.java index 8ad8e5d..d68fab2 100644 --- a/Train_Files/Simple_NEAT.java +++ b/Train_Files/Simple_NEAT.java @@ -4,7 +4,7 @@ public class Simple_NEAT{ - private ArrayList agents; + private ArrayList agents; private int numInputs, numOutputs, genNum; private Network curAgent; private boolean keepBest; @@ -15,8 +15,8 @@ public Simple_NEAT(int nI, int nO){ numOutputs = nO; genNum = 0; keepBest = true; - agents = new ArrayList(); - mutationRate = 0.60; + agents = new ArrayList(); + mutationRate = 0.60; } public void addAgent(){ @@ -116,15 +116,12 @@ public void mutate(){ //Should we mutate this agent? if (mutationRate >= num){ num = ThreadLocalRandom.current().nextDouble(0,1); - if (num <= 1/4){ + if (num <= .33){ a.addRandHiddenNode(); } - else if(num > 1/4 && num <= 2/4){ + else if(num > .33 && num <= .66){ a.addRandConnection(); } - else if(num > 2/4 && num <= 3/4){ - a.remvoveRandConnection(); - } else{ a.mutateWeight(); } @@ -182,5 +179,5 @@ public static void main(String args[]){ // } // System.out.println("\n------------------------------------------"); // n.breed(); - } + } } diff --git a/Train_Files/best.net b/Train_Files/best.net index 62a2078..2f8ad95 100644 Binary files a/Train_Files/best.net and b/Train_Files/best.net differ diff --git a/Visualize.sh b/Visualize.sh new file mode 100755 index 0000000..ffc8f5e --- /dev/null +++ b/Visualize.sh @@ -0,0 +1,2 @@ +#!/bin/bash +processing-java --sketch=../Asteroids_Train --run \ No newline at end of file diff --git a/best.net b/best.net index 60dd8c6..f3956b3 100644 Binary files a/best.net and b/best.net differ diff --git a/build/Asteroids_Train$Asteroid.class b/build/Asteroids_Train$Asteroid.class new file mode 100644 index 0000000..6ccaf95 Binary files /dev/null and b/build/Asteroids_Train$Asteroid.class differ diff --git a/build/Asteroids_Train$Bullet.class b/build/Asteroids_Train$Bullet.class new file mode 100644 index 0000000..44f0231 Binary files /dev/null and b/build/Asteroids_Train$Bullet.class differ diff --git a/build/Asteroids_Train$GameScene.class b/build/Asteroids_Train$GameScene.class new file mode 100644 index 0000000..ad763f1 Binary files /dev/null and b/build/Asteroids_Train$GameScene.class differ diff --git a/build/Asteroids_Train$Grid.class b/build/Asteroids_Train$Grid.class new file mode 100644 index 0000000..ad2318f Binary files /dev/null and b/build/Asteroids_Train$Grid.class differ diff --git a/build/Asteroids_Train$Locals.class b/build/Asteroids_Train$Locals.class new file mode 100644 index 0000000..0d58140 Binary files /dev/null and b/build/Asteroids_Train$Locals.class differ diff --git a/build/Asteroids_Train$NetworkView.class b/build/Asteroids_Train$NetworkView.class new file mode 100644 index 0000000..d7191fd Binary files /dev/null and b/build/Asteroids_Train$NetworkView.class differ diff --git a/build/Asteroids_Train$Node.class b/build/Asteroids_Train$Node.class new file mode 100644 index 0000000..3b64fed Binary files /dev/null and b/build/Asteroids_Train$Node.class differ diff --git a/build/Asteroids_Train$Sensor.class b/build/Asteroids_Train$Sensor.class new file mode 100644 index 0000000..74653cb Binary files /dev/null and b/build/Asteroids_Train$Sensor.class differ diff --git a/build/Asteroids_Train$Ship.class b/build/Asteroids_Train$Ship.class new file mode 100644 index 0000000..5f44d1d Binary files /dev/null and b/build/Asteroids_Train$Ship.class differ diff --git a/build/Asteroids_Train.class b/build/Asteroids_Train.class new file mode 100644 index 0000000..91a530a Binary files /dev/null and b/build/Asteroids_Train.class differ diff --git a/build/Connection.class b/build/Connection.class new file mode 100644 index 0000000..a96b8a3 Binary files /dev/null and b/build/Connection.class differ diff --git a/build/Network.class b/build/Network.class new file mode 100644 index 0000000..63aeddc Binary files /dev/null and b/build/Network.class differ diff --git a/build/Neuron.class b/build/Neuron.class new file mode 100644 index 0000000..3a1c664 Binary files /dev/null and b/build/Neuron.class differ diff --git a/build/Simple_NEAT.class b/build/Simple_NEAT.class new file mode 100644 index 0000000..91e830c Binary files /dev/null and b/build/Simple_NEAT.class differ diff --git a/build/Vector.class b/build/Vector.class new file mode 100644 index 0000000..9fdfa6c Binary files /dev/null and b/build/Vector.class differ diff --git a/build/source/Asteroids_Train.java b/build/source/Asteroids_Train.java new file mode 100644 index 0000000..797da2e --- /dev/null +++ b/build/source/Asteroids_Train.java @@ -0,0 +1,1077 @@ +import processing.core.*; +import processing.data.*; +import processing.event.*; +import processing.opengl.*; + +import java.util.ArrayList; +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; +import java.lang.Math; +import java.lang.Math; +import java.util.concurrent.ThreadLocalRandom; +import java.util.ArrayList; +import java.lang.Math; +import java.util.ArrayList; +import java.util.concurrent.ThreadLocalRandom; + +import java.util.HashMap; +import java.util.ArrayList; +import java.io.File; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +public class Asteroids_Train extends PApplet { + + + +GameScene GS; +Locals locals; + +public void setup(){ + locals = new Locals(); + + frameRate(60); + locals.player = new Ship(locals); + locals.asteroids = new ArrayList(); + GS = new GameScene(locals); + // locals.level = 10; +} + +public void draw(){ + GS.show(); + // System.out.println(player.score + " - " + player.dead); +} + +/** + * Button released handle. + */ +public void keyReleased(){ + int code; + if (keyCode > 40){ + code = PApplet.parseInt(Character.toLowerCase(key)); + } + else{ + code = keyCode; + } + locals.player.processButtonReleased(PApplet.parseInt(Character.toLowerCase(code))); +} + +/** + * Button pressed handle. + */ +public void keyPressed(){ + // char k = key; + //System.out.println(keyCode); + int code; + if (keyCode > 40){ + code = PApplet.parseInt(Character.toLowerCase(key)); + } + else{ + code = keyCode; + } + + + locals.player.processButtonPress(code); +} + + + + + +public class Asteroid{ + double x, y, size, angle, maxLevel; + int level; + Vector velocity; + final double PI = 3.14159265359f; + Locals locals; + + /** + * Constructor for the Asteroid class. + * @param x X pos of the asteroid + * @param y Y pos of the asteroid + * @param level Level of asteroid (1-3) + */ + public Asteroid(double x, double y, int level, Locals l){ + locals = l; + this.x = x; + this.y = y; + this.size = 40 * level; + this.level = level; + this.maxLevel = 3; + this.angle = ThreadLocalRandom.current().nextDouble(-PI, PI); + this.velocity = Vector.fromAngle(angle); + this.velocity.mult( (float) ((maxLevel+1) - level) * 0.8f); + } + + public Asteroid(double x, double y, double a, int level, Locals l){ + locals = l; + this.x = x; + this.y = y; + this.size = 30 * level; + this.level = level; + this.maxLevel = 3; + this.angle = a; + this.velocity = Vector.fromAngle(angle); + this.velocity.mult(((maxLevel+1) - level) * 0.8f); + } + + + /** + * Bound the Asteroid to stay inside of the screen. + */ + public void bound(){ + if (x + size < 0){ + x = width + size/2; + } + else if (x - size > width){ + x = 0 - size/2; + } + + if (y + size < 0){ + y = height + size/2; + } + else if (y - size > height){ + y = 0 - size/2; + } + } + + public void travel(){ + x += velocity.x; + y += velocity.y; + } + + public void checkHit(){ + double distance = dist(x, y, locals.player.getX(), locals.player.getY()); + //System.out.println(distance); + if (distance < size/2 + locals.player.getSize()){ + locals.player.setHit(); + } + + if (distance < 100 + (size/2) && locals.player.accelerate){ + locals.player.avoids = locals.player.avoids + 1; + } + } + + public void explode(){ + if (level > 1){ + + for (int i = 0; i < 2; i++){ + Asteroid newAsteroid = new Asteroid(x, y, level - 1, locals); + locals.asteroids.add(newAsteroid); + } + locals.asteroids.remove(this); + } + else{ + locals.asteroids.remove(this); + } + + } + + public int getScore(){ + if (level == 1) + return 100; + else if (level == 2) + return 50; + else + return 20; + + } + + + public void show(){ + pushMatrix(); + checkHit(); + travel(); + bound(); + noFill(); + stroke(255); + ellipseMode(CENTER); + translate((float) x, (float) y); + ellipse(0, 0, (float) size, (float) size); + popMatrix(); + } + + public double getX(){ + return x; + } + + public double getY(){ + return y; + } + + public int getLevel(){ + return level; + } + + public double getAngle(){ + return angle; + } + + public void setAngle(double a){ + angle = a; + } + + public double getSize(){ + return size; + } + + private double dist(double x1, double y1, double x2, double y2){ + return Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2)); + } +} + + + +public class Bullet{ + double x, y, angle, size; + int count; + boolean owner; + Vector velocity; + final double PI = 3.14159265359f; + Locals locals; + + public Bullet(double x, double y, double angle, Locals l){ + locals = l; + this.x = x; + this.y = y; + this.angle = angle - PI/2; + this.size = 5; + this.count = 0; + this.owner = true; + this.velocity = Vector.fromAngle(this.angle); + this.velocity.mult(8); + } + + public void travel(){ + x += velocity.x; + y += velocity.y; + } + + public void setOwner(boolean b){ + owner = b; + } + + public boolean isOwner(){ + return owner; + } + + public boolean bound(){ + if (x + size < 0){ + x = width + size; + } + else if (x - size > width){ + x = 0 - size; + } + + if (y + size < 0){ + y = height + size; + } + else if (y - size > height){ + y = 0 - size; + } + + if(count > 900){ + return true; + } + else{ + count += 8; + return false; + } + } + + public void checkHit(){ + for (Asteroid a : locals.asteroids){ + double distance = dist(a.getX(), a.getY(), x, y); + if (distance < a.getSize()/2 + size/2){ + locals.player.addScore(a.getScore()); + a.explode(); + count = 1000; + break; + } + } + } + + public void show(){ + pushMatrix(); + ellipseMode(CENTER); + translate((float) x, (float) y); + travel(); + noStroke(); + fill(255); + ellipse(0, 0, (float) size, (float) size); + popMatrix(); + if (owner) + checkHit(); + } + + public double getX(){ + return x; + } + + public double getY(){ + return y; + } + + private double dist(double x1, double y1, double x2, double y2){ + return Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2)); + } + +} + + +public class GameScene{ + boolean online; + Locals locals; + Simple_NEAT n; + Grid gameGrid; + + public GameScene(Locals l){ + locals = l; + locals.level = 1; + resetAstroids(locals.level); + gameGrid = new Grid(50, l); + locals.player = new Ship(locals); + n = new Simple_NEAT(gameGrid.getWidth() * gameGrid.getWidth() + 1, 4); + Network temp = Network.loadFromFile("/Users/ryanwalt/Downloads/CODE/Java/Processing/Asteroids_Train/best.net"); + n.addAgent(temp); + n.setCurrentAgent(0); + } + + /** + * Show the text of the scene + */ + private void showText(){ + textAlign(CENTER); + textSize(30); + String levelString = "Level " + locals.level + "\n" + locals.player.getScore(); + fill(255); + textSize(30); + text(levelString, width/2, 50); + String liveString = ""; + for (int i = 0; i < locals.player.getLives(); i++){ + liveString += " | "; + } + text(liveString, width - 50, 50); + } + + private void resetAstroids(int level){ + locals.asteroids.clear(); + int num = 2 + (level * 2); + for (int i = 0; i < num; i++){ + float tempX = ThreadLocalRandom.current().nextInt(50, locals.width - 50); + float tempY = ThreadLocalRandom.current().nextInt(50, locals.height/2 - 150) + ((locals.height/2 + 150) * ((ThreadLocalRandom.current().nextInt(0, 2)))); + locals.asteroids.add(new Asteroid(tempX, tempY, 3, locals)); + locals.player.resetPos(); + locals.player.clearBullets(); + } + } + + /** + * Display all of the asteroids to the screen. + */ + private void showAsteroids(){ + try{ + for (Asteroid a : locals.asteroids){ + a.show(); + } + } + catch(Exception e){ + System.out.println("Exception in showAsteroids (gameScene): " + e); + } + } + + private boolean checkLevel(){ + if (locals.asteroids.size() < 1){ + locals.level++; + resetAstroids(locals.level); + return true; + } + return false; + } + + public void show(){ + background(0); + showText(); + // locals.player.showBullets(); + locals.player.show(); + runNetwork2(); + showAsteroids(); + checkLevel(); + gameGrid.show(); + } + + public void runNetwork(){ + float[] inputs = new float[33]; + int c = 0; + for (Sensor s : locals.player.getSensors()){ + inputs[c] = (float) s.getWeightValue(); + c++; + } + for (Sensor s : locals.player.getSensors()){ + inputs[c] = (float) s.getLevelValue(); + c++; + } + + inputs[32] = (float) locals.player.bullets.size() / 4; + + n.runCurrent(inputs); + + float[] outputs = n.getCurOutput(); + + if (outputs[0] > 0.5f){ + locals.player.shoot(); + } + if (outputs[1] >=0.5f){ + locals.player.turnLeft(); + } + if (outputs[2] >= 0.5f){ + locals.player.turnRight(); + } + if (outputs[3] <= 0.5f){ + locals.player.accelerate = true; + locals.player.accelerate(); + } + else{ + locals.player.accelerate = false; + } + + String outs = ""; + for(float f : outputs){ + outs +=(f + ", "); + } + textSize(20); + text(outs, width/2, height-50); + + } + + public void runNetwork2(){ + float[] inputs = new float[gameGrid.getWidth() * gameGrid.getWidth() + 1]; + inputs[0] = (float) locals.player.getAngle() / (2*3.14159265359f); + int c = 1; + for (int x = 0; x < gameGrid.getGrid().length; x++){ + for (int y = 0; y < gameGrid.getGrid().length; y++){ + inputs[c] = (float) gameGrid.getGrid()[x][y]; + // System.out.println(c + " - " + (float) gameGrid.getGrid()[x][y]); + c++; + } + } + + n.runCurrent(inputs); + + float[] outputs = n.getCurOutput(); + + if (outputs[0] > 0.0f){ + locals.player.shoot(); + } + if (outputs[1] > 0.0f){ + locals.player.turnLeft(); + } + if (outputs[2] > 0.0f){ + locals.player.turnRight(); + } + if (outputs[3] > 0.0f){ + locals.player.accelerate = true; + locals.player.accelerate(); + } + else{ + locals.player.accelerate = false; + } + + String outs = ""; + for(float f : outputs){ + outs +=(f + ", "); + } + textSize(20); + text(outs, width/2, height-50); + + } +} +public class Grid{ + double[][] grid; + Locals l; + int cellWidth; + + + //CellWidth should be an even multiple of the width and height of the screen. + public Grid(int cellWidth, Locals l){ + this.l = l; + this.cellWidth = cellWidth; + int xSize = (int) l.width / cellWidth; + int ySize = (int) l.height / cellWidth; + grid = new double[xSize][ySize]; + resetGrid(); + } + + private void resetGrid(){ + for (int x = 0; x < grid.length; x++){ + for (int y = 0; y < grid.length; y++){ + grid[x][y] = 0.0f; + } + } + } + + private void setAsteroids(){ + resetGrid(); + for (Asteroid a : l.asteroids){ + int xCell = (int) (a.getX() / cellWidth); + int yCell = (int) (a.getY() / cellWidth); + + //Cap all coordinates + if (xCell >= grid.length) + xCell = grid.length-1; + if (xCell < 0) + xCell = 0; + if (yCell >= grid[0].length) + yCell = grid[0].length-1; + if (yCell < 0) + yCell = 0; + + grid[xCell][yCell] = 1.0f; + } + + int xCell = (int) (l.player.getX() / cellWidth); + int yCell = (int) (l.player.getY() / cellWidth); + + //Cap all coordinates + if (xCell >= grid.length) + xCell = grid.length-1; + if (xCell < 0) + xCell = 0; + if (yCell >= grid[0].length) + yCell = grid[0].length-1; + if (yCell < 0) + yCell = 0; + + grid[xCell][yCell] = -1; + } + + public void show(){ + float squareSize = 200; + + stroke(255); + fill(0, 0, 0); + rect(10, 10, squareSize, squareSize); + + float subSize = squareSize / grid.length; + + setAsteroids(); + for (int x = 0; x < grid.length; x++){ + for (int y = 0; y < grid.length; y++){ + if (grid[x][y] > 0){ + fill(255); + noStroke(); + rectMode(CORNER); + rect(10 + subSize * x, 10 + subSize * y, subSize, subSize); + } + if (grid[x][y] < 0){ + fill(255, 0, 0); + noStroke(); + rectMode(CORNER); + rect(10 + subSize * x, 10 + subSize * y, subSize, subSize); + } + } + } + } + + public int getWidth(){ + return grid.length; + } + + public double[][] getGrid(){ + return grid; + } + +} + + +public class Locals{ + Ship player; + ArrayList asteroids; + GameScene GS; + int level; + int width; + int height; + + public Locals(){ + width = 900; + height = 900; + } +} +public class NetworkView{ + Network net; + ArrayList nodes; + + public NetworkView(Network net){ + this.net = net; + } + + private void initNodes(){ + // for (Neuron n : net.getInputs()){ + // break; + // } + } + + public void show(){ + return; + } +} + + +class Node{ + float x, y; + + public Node(float x, float y){ + this.x = x; + this.y = y; + } +} + + +public class Sensor{ + private double length, angle, weightValue, levelValue; + private Locals locals; + + public Sensor(double angle, Locals l){ + this.length = 350; + this.angle = angle; + this.weightValue = 0.0f; + this.levelValue = 0.0f; + locals = l; + } + + public void show(double x, double y, double angle){ + double x2 = x + (Math.cos(angle + this.angle) * length); + double y2 = y + (Math.sin(angle + this.angle) * length); + // System.out.println(angle); + strokeWeight(2); + stroke(255); + line((float) x, (float) y, (float) x2, (float) y2); + calculateIntersection(x,y,x2,y2); + } + + public void calculateIntersection(double x0, double y0, double x1, double y1){ + double a = square(x1 - x0) + square(y1 - y0); + + double iX = 0.0f; + double iY = 0.0f; + weightValue = 0; + levelValue = 0; + + for (Asteroid ast : locals.asteroids){ + double b = 2 * (x1-x0) * (x0 - ast.getX()) + 2 * (y1-y0) * (y0 - ast.getY()); + double c = square(x0-ast.getX()) + square(y0-ast.getY()) - square(ast.getSize()/2.0f); + double t = (2*c) / ((-1*b) + Math.sqrt(square(b) - (4 * a * c))); + + if(t > 0 && t < 1 && square(b) - (4 * a * c) > 0){ + if (1 - t > weightValue){ + weightValue = 1-t; + levelValue = ast.getLevel() * 0.333f; + iX = intersectX(x0, x1, t); + iY = intersectY(y0, y1, t); + } + } + } + if (weightValue > 0.0f){ + fill(255, 0, 0); + noStroke(); + ellipse((float) iX, (float) iY, 15, 15); + } + // fill(255, 0, 0); + // ellipse(iX, iY, 10, 10); + } + + public double intersectX(double x0, double x1, double t){ + return (x1-x0) * t + x0; + } + + public double intersectY(double y0, double y1, double t){ + return (y1-y0) * t + y0; + } + + + private double square(double x){ + return x * x; + } + + public double getWeightValue(){ + return weightValue; + } + + public double getLevelValue(){ + return levelValue; + } +} + + + +public class Ship{ + double x, y, size, angle, turnRadius, deRate; + ArrayList pressedChars; + ArrayList bullets; + ArrayList sensors; + boolean turn, accelerate, dead, noHit; + long timeStamp; + int k; + int lives, maxLives, score, avoids; + Vector velocity; + final double PI = 3.14159265359f; + Locals locals; + + /** + * Constuctor for the ship class. + * @param a Asteroids in the game for the ship to reference. + */ + public Ship(Locals l){ + locals = l; + //X and Y for the ship. + this.x = width/2; + this.y = height/2; + //Size of the ship. + this.size = 20; + this.angle = 0; + this.turnRadius = 0.1f; + this.deRate = 0.05f; + this.turn = false; + this.accelerate = false; + this.dead = false; + this.noHit = false; + this.maxLives = 1; + this.lives = maxLives; + this.score = 0; + this.avoids = 0; + //ArrayList for the current pressed characters. + //(Mainly used for making turning less janky.) + this.pressedChars = new ArrayList(); + this.bullets = new ArrayList(); + this.velocity = new Vector(); + this.sensors = new ArrayList(); + sensors.add(new Sensor(0, locals)); + sensors.add(new Sensor(PI, locals)); + sensors.add(new Sensor(3*PI/2, locals)); + sensors.add(new Sensor(PI/2, locals)); + sensors.add(new Sensor(PI/4, locals)); + sensors.add(new Sensor(3*PI/4, locals)); + sensors.add(new Sensor(7*PI/4, locals)); + sensors.add(new Sensor(5*PI/4, locals)); + sensors.add(new Sensor(22.5f*(PI/180), locals)); + sensors.add(new Sensor(-22.5f*(PI/180), locals)); + sensors.add(new Sensor(-67.5f*(PI/180), locals)); + sensors.add(new Sensor(67.5f*(PI/180), locals)); + sensors.add(new Sensor(112.5f*(PI/180), locals)); + sensors.add(new Sensor(-112.5f*(PI/180), locals)); + sensors.add(new Sensor(-157.5f*(PI/180), locals)); + sensors.add(new Sensor(157.5f*(PI/180), locals)); + + + + } + + /** + * Removes one of the current pressed characters from the Array. + * If there are no characters left then we stop turning. + * @param k key entered by the user. + */ + private void freezeTurn(int code){ + + //Loop through and find characters we should remove. + for (int i = pressedChars.size() - 1; i >= 0; i--){ + if (pressedChars.get(i) == code){ + pressedChars.remove(i); + } + } + + //If there are no more charaters then we should stop turning. + if(pressedChars.size() < 1) + turn = false; + } + + /** + * Tell the ship to start to turn in the appropriate direction. + * @param k key entered by the user. + */ + private void setTurn(int code){ + //System.out.println("Turn"); + turn = true; + this.k = code; + pressedChars.add(this.k); + } + + /** + * Turn the ship X radians. + */ + private void turn(){ + //Make sure we are in the scene and we should be turning. + if (turn){ + if (k == 'a' || k == 37){ + turnLeft(); + } + if (k == 'd' || k == 39){ + turnRight(); + } + } + + if (angle > 2*PI){ + angle = angle - (2*PI); + } + if (angle < 0){ + angle = (2*PI) + angle; + } + } + + private void turnLeft(){ + angle -= turnRadius; + } + + private void turnRight(){ + angle += turnRadius; + } + + /** + * Bound the player to stay inside of the screen. + */ + private void bound(){ + if (x + size < 0){ + x = width + size; + } + else if (x - size > width){ + x = 0 - size; + } + + if (y + size < 0){ + y = height + size; + } + else if (y - size > height){ + y = 0 - size; + } + } + + public void resetPos(){ + x = width/2; + y = height/2; + velocity.mult(0); + angle = 0; + } + + public void resetVars(){ + lives = maxLives; + dead = false; + score = 0; + bullets.clear(); + } + + public void clearBullets(){ + bullets.clear(); + } + + /** + * Move in the direction of the current velocity. + */ + private void move(){ + x += velocity.x; + y += velocity.y; + + //Adds "friction" to slow down the ship. + velocity.mult(0.99f); + } + + private void hyperDrive(){ + x = ThreadLocalRandom.current().nextInt(20, width - 20); + y = ThreadLocalRandom.current().nextInt(20, height - 20); + } + + /** + * Accelerates the ship in the current faced direction. + */ + private void accelerate(){ + if (accelerate){ + Vector force = Vector.fromAngle(angle - PI/2); + //Limit how strong the force is. + force.mult(0.08f); + velocity.add(force); + } + } + + /** + * Display the bullets and remove if out of the screen. + */ + private void showBullets(){ + for (int i = bullets.size() - 1; i >= 0; i--){ + Bullet b = bullets.get(i); + b.show(); + + //If the bullet is out of the screen then we want to remove it. + if (b.bound()){ + bullets.remove(i); + } + } + } + + public void setHit(){ + if (!noHit){ + lives--; + if (lives < 1){ + dead = true; + } + else{ + noHit = true; + timeStamp = millis(); + resetPos(); + } + } + } + + public void setAlive(){ + dead = false; + } + + private void checkNoHit(){ + if(noHit){ + if (millis() - timeStamp > 3000) + noHit = false; + } + } + + private void shoot(){ + if (bullets.size() < 4 && !dead){ + addBullet(new Bullet(x, y, angle, locals)); + } + } + + public void addScore(int s){ + score += s; + } + + public void addBullet(Bullet b){ + bullets.add(b); + } + + public long millis(){ + return System.currentTimeMillis() % 1000; + } + + public void showSensors(){ + for (Sensor s : sensors){ + s.show(x, y, angle); + } + } + + /** + * Display the ship to the screen. + */ + public boolean show(){ + if (!dead){ + checkNoHit(); + pushMatrix(); + //Display the bullets + showBullets(); + //showSensors(); + + //Edit pos of the ship. + turn(); + move(); + accelerate(); + bound(); + + noFill(); + stroke(255, 255, 102); + if (noHit) + stroke(56, 252, 159); + strokeWeight(3); + translate((float)x, (float)y); + rotate((float)angle); + + triangle((float)-size, (float)size, 0, (float)-size - 5, (float)size, (float)size); + + popMatrix(); + return true; + } + // resetVars(); + resetPos(); + return false; + } + + /** + * Handle the button pressed by the user. + * @param k key entered by the user. + */ + public void processButtonPress(int code){ + if (code == 97 || code == 100 || code == 37 || code == 39){ + setTurn(code); + } + + if (code == 119 || code == 38){ + accelerate = true; + } + + if (code == 32){ + shoot(); + } + + if (code == 115 || code == 40){ + hyperDrive(); + } + } + + /** + * Handle the button released by the user. + * @param k key entered by the user. + */ + public void processButtonReleased(int code){ + if (code == 97 || code == 100 || code == 37 || code == 39){ + freezeTurn(code); + } + if (code == 119 || code == 38){ + accelerate = false; + } + } + + public void processClick(){ + shoot(); + } + + public ArrayList getSensors(){ + return sensors; + } + + public double getX(){ + return x; + } + + public double getY(){ + return y; + } + + public double getAngle(){ + return angle; + } + + public double getSize(){ + return size; + } + + public int getLives(){ + return lives; + } + + public void setLives(int l){ + lives = l; + maxLives = l; + } + + public int getScore(){ + return score; + } +} + public void settings() { size(900, 900, OPENGL); } + static public void main(String[] passedArgs) { + String[] appletArgs = new String[] { "Asteroids_Train" }; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } + } +} diff --git a/build/source/Network.java b/build/source/Network.java new file mode 100644 index 0000000..b4d2fc7 --- /dev/null +++ b/build/source/Network.java @@ -0,0 +1,371 @@ +import java.util.ArrayList; +import java.util.Random; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + + +public class Network implements Serializable, Comparable{ + ArrayList inputs, outputs, hidden; + int idCount; + float bias; + Random r; + double fitness, genFitness; + + public Network(int numInputs, int numOutputs){ + idCount = 0; + bias = 1.0f; + fitness = 0.0; + inputs = new ArrayList(); + outputs = new ArrayList(); + hidden = new ArrayList(); + r = new Random(); + + //Add Neurons for inputs + for (int x = 0; x < numInputs; x++){ + Neuron in = new Neuron(bias, idCount); + inputs.add(in); + idCount++; + } + + for (int x = 0; x < numOutputs; x++){ + Neuron out = new Neuron(bias, idCount); + // out.setLayer(0); + outputs.add(out); + idCount++; + } + + initConnections(); + } + + public Network(ArrayList inputs, ArrayList hidden, ArrayList outputs, float bias, int idCount){ + this.inputs = inputs; + this.outputs = outputs; + this.hidden = hidden; + + this.bias = bias; + this.idCount = idCount; + fitness = 0.0; + r = new Random(); + + } + + private void initConnections(){ + for (Neuron inputN : inputs){ + for (Neuron outputN : outputs){ + outputN.addConnection(inputN); + } + } + } + + public void addRandHiddenNode(){ + //Create a new hidden node. + Neuron tempHidden = new Neuron(bias, idCount); + idCount++; + + //Combine all nodes that are valid for adding a connection (hidden and output) + ArrayList validNodes = new ArrayList(); + validNodes.addAll(outputs); + validNodes.addAll(hidden); + + // System.out.println("add node: " + validNodes.size()); + + //Grab a random node to manipulate connection. + int index = r.nextInt(validNodes.size()); + Neuron randNode = validNodes.get(index); + + //Grab random connection from this node. + index = r.nextInt(randNode.getConnections().size()); + Connection randConnection = randNode.getConnections().get(index); + + //Add from the new hidden to the old. + tempHidden.addConnection(randConnection.neuron, 1); + //Remove old connection. + randNode.removeConnection(randConnection); + //Create new connection with the new hidden node. + randNode.addConnection(tempHidden, randConnection.weight); + hidden.add(tempHidden); + + // System.out.println("Adding node " + tempHidden.getID() + " --> " + randConnection.neuron.getID() + " - " + randNode.getID()); + + //Set the layer of this node to its output + 1 + // tempHidden.setLayer(randNode.getLayer() + 1); + } + + public void addRandConnection(Neuron node){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + + // System.out.println(validConnections); + if (validConnections.size() < 1) + return; + + //Grab a hidden to make a new input. + int index = r.nextInt(validConnections.size()); + Neuron randHidden = validConnections.get(index); + + //Grab random hidden to connect to. + while(true){ + if (!checkRepeat(node, randHidden)){ + node.addConnection(randHidden); + if (!checkDeadlock(node, node)){ + // System.out.println("Setting connection: " + randHidden.getID() + " --> " + node.getID()); + break; + } + else{ + node.removeConnection(randHidden); + } + } + + validConnections.remove(randHidden); + if (validConnections.size() < 1){ + // System.out.println("No valid connection to make for: " + node.getID()); + break; + } + + index = r.nextInt(validConnections.size()); + randHidden = validConnections.get(index); + } + } + + public void addRandConnection(){ + ArrayList validConnections = getConnectionNodes(); + Neuron n = validConnections.get(r.nextInt(validConnections.size())); + addRandConnection(n); + } + + public ArrayList getConnectionNodes(){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + validConnections.addAll(outputs); + return validConnections; + } + + public boolean checkDeadlock(Neuron node, Neuron cur){ + for (Connection c : cur.getConnections()){ + if (node == c.neuron){ + return true; + } + if (checkDeadlock(node, c.neuron)){ + return true; + } + } + return false; + } + + public boolean checkRepeat(Neuron mainNode, Neuron connection){ + for (Connection c : mainNode.getConnections()){ + if (c.neuron == connection){ + return true; + } + } + return false; + } + + public void mutateWeight(){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + validConnections.addAll(outputs); + + Neuron n = validConnections.get(r.nextInt(validConnections.size())); + Connection c = n.getConnections().get(r.nextInt(n.getConnections().size())); + + c.randomizeWeight(); + } + + public void mutateBiasWeight(){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + validConnections.addAll(outputs); + + Neuron n = validConnections.get(r.nextInt(validConnections.size())); + n.randomizeBiasWeight(); + } + + public void remvoveRandConnection(){ + ArrayList validConnections = new ArrayList(); + validConnections.addAll(hidden); + validConnections.addAll(outputs); + Neuron n = validConnections.get(r.nextInt(validConnections.size())); + + Connection c = n.getConnections().get(r.nextInt(n.getConnections().size())); + n.removeConnection(c); + + } + + public void runNetwork(float[] initInputs){ + if (initInputs.length != inputs.size()){ + System.out.println("Error: Input mismatch..."); + return; + } + //Set inputs for the input layer. + for (int i = 0; i < inputs.size(); i++){ + Neuron inputN = inputs.get(i); + inputN.setInput(initInputs[i]); + } + + //Run network and get activated values. + for (Neuron n : outputs){ + n.feed(); + } + + //Reset each hidden node to unactivated. + for (Neuron n : hidden){ + n.setActivation(false); + } + } + + public float[] getSimpleOutput(){ + float[] nums = new float[outputs.size()]; + + for (int i = 0; i < outputs.size(); i++){ + nums[i] = outputs.get(i).getValue(); + } + + return nums; + } + + public double getFitness(){ + return fitness; + } + + public void setFitness(double d){ + fitness = d; + } + + public Network copy(){ + ArrayList tempInputs = new ArrayList(); + ArrayList tempHidden = new ArrayList(); + ArrayList tempOutputs = new ArrayList(); + + for (Neuron n : inputs){ + tempInputs.add(n.copy()); + } + + for (Neuron n : hidden){ + tempHidden.add(n.copy()); + } + + for (Neuron n : outputs){ + tempOutputs.add(n.copy()); + } + + ArrayList validNodes = new ArrayList(); + validNodes.addAll(tempInputs); + validNodes.addAll(tempHidden); + + for (int i = 0; i < hidden.size(); i++){ + Neuron n = hidden.get(i); + for (Connection c : n.getConnections()){ + int id = c.neuron.getID(); + Neuron connectionNeuron = null; + for (Neuron x : validNodes){ + if (x.getID() == id){ + connectionNeuron = x; + } + } + tempHidden.get(i).addConnection(connectionNeuron, c.weight); + } + } + + for (int i = 0; i < outputs.size(); i++){ + Neuron n = outputs.get(i); + for (Connection c : n.getConnections()){ + int id = c.neuron.getID(); + Neuron connectionNeuron = null; + for (Neuron x : validNodes){ + if (x.getID() == id){ + connectionNeuron = x; + } + } + tempOutputs.get(i).addConnection(connectionNeuron, c.weight); + } + } + + return new Network(tempInputs, tempHidden, tempOutputs, this.bias, this.idCount); + } + + public boolean saveToFile(String s){ + try{ + FileOutputStream fos = new FileOutputStream(s); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(this); + oos.close(); + return true; + } + catch(Exception e){ + System.out.println(e); + return false; + } + } + + public static Network loadFromFile(String s){ + try{ + FileInputStream fis = new FileInputStream(s); + ObjectInputStream ois = new ObjectInputStream(fis); + Network n = (Network) ois.readObject(); + ois.close(); + return n; + } + catch(Exception e){ + System.out.println(e); + return null; + } + } + + public void setGenFitness(double f){ + genFitness = f; + } + + public double getGenFitness(){ + return genFitness; + } + + @Override + public int compareTo(Network o) { + if (this.genFitness > o.getGenFitness()){ + return -1; + } + if (this.genFitness < o.getGenFitness()){ + return 1; + } + return 0; + } + + public static void main(String[] args){ + // Locals l = new Locals(); + // Network n = new Network(3, 2, l); + // + // + // float[] inputs = {0.4f, 0.9f, 0.33f}; + // n.runNetwork(inputs); + // for (float i : n.getSimpleOutput()){ + // System.out.print(i + ", "); + // } + // System.out.println("\n------------------------------------------"); + // n.saveToFile("test"); + // + // Network nCopy = Network.loadFromFile("test"); + // + // for (float i : nCopy.getSimpleOutput()){ + // System.out.print(i + ", "); + // } + // System.out.println(""); + + // Network n = new Network(3, 2); + // float[] inputs = {0.4f, 0.9f, 0.33f}; + // for (int i = 0; i < 5; i++){ + // n.addRandHiddenNode(); + // } + // Random r = new Random(); + // for (int i = 0; i < 4; i++){ + // n.addRandConnection(n.outputs.get(r.nextInt(n.outputs.size()))); + // } + // n.runNetwork(inputs); + // for (float i : n.getSimpleOutput()){ + // System.out.println(i); + // } + } +} \ No newline at end of file diff --git a/build/source/Neuron.java b/build/source/Neuron.java new file mode 100644 index 0000000..513f3b6 --- /dev/null +++ b/build/source/Neuron.java @@ -0,0 +1,177 @@ +import java.util.Random; +import java.util.ArrayList; +import java.io.Serializable; + + +public class Neuron implements Serializable{ + + private float bias, biasWeight; + private ArrayList inputConnections; + private boolean activated; + private float value; + private Random r; + private int id, layer; + + public Neuron(float bias, int id){ + this.bias = bias; + r = new Random(); + this.biasWeight = r.nextFloat(); + inputConnections = new ArrayList(); + activated = false; + value = 0.0f; + this.id = id; + layer = -1; + } + + public Neuron(float bias, int id, float biasWeight){ + this.bias = bias; + r = new Random(); + this.biasWeight = biasWeight; + inputConnections = new ArrayList(); + activated = false; + value = 0.0f; + this.id = id; + layer = -1; + } + + public float sum(){ + float sum = 0; + for (Connection c : inputConnections){ + // System.out.println("Val: " + c.neuron.getValue() + " - weight: " + c.weight); + sum += c.neuron.getValue() * c.weight; + } + // System.out.println("BeforeBias: " + sum); + sum += bias * biasWeight; + // System.out.println("FinalSum: " + sum); + return sum; + } + + public void addConnection(Neuron n){ + inputConnections.add(new Connection(n)); + } + + public void addConnection(Neuron n, float w){ + inputConnections.add(new Connection(n, w)); + } + + //Use for input nodes. + public void setInput(float val){ + activated = true; + value = val; + // System.out.println("Setting input --> " + value); + } + + public void activate(){ + float sum = sum(); + float n = (float) Math.pow((double) Math.exp(1.0), (double) sum) - (float) Math.pow((double) Math.exp(1.0), (double) -sum); + float d = (float) Math.pow((double) Math.exp(1.0), (double) sum) + (float) Math.pow((double) Math.exp(1.0), (double) -sum); + value = (float) (n/d); + activated = true; + } + + public void feed(){ + for (Connection c : inputConnections){ + if (!c.neuron.activated){ + // System.out.println("Feeding: " + c.neuron + " / " + c.neuron.getID()); + c.neuron.feed(); + } + } + activate(); + } + + public ArrayList getConnections(){ + return inputConnections; + } + + public float getValue(){ + return value; + } + + public void setActivation(boolean b){ + activated = b; + } + + public int getID(){ + return id; + } + + public void setLayer(int l){ + layer = l; + for (Connection c : inputConnections){ + c.neuron.setLayer(layer + 1); + } + } + + public int getLayer(){ + return layer; + } + + public void removeConnection(Connection c1){ + for (Connection c2 : inputConnections){ + if (c1 == c2){ + inputConnections.remove(c2); + return; + } + } + } + + public Neuron copy(){ + // Neuron copy = new Neuron(bias, id, biasWeight); + // for (Connection c : inputConnections){ + // copy.inputConnections.add(c.copy()); + // } + return new Neuron(bias, id, biasWeight); + } + + public void removeConnection(Neuron n){ + for (Connection c : inputConnections){ + if (c.neuron == n){ + inputConnections.remove(c); + return; + } + } + } + + public void randomizeBiasWeight(){ + this.biasWeight = r.nextFloat(); + } + + public static void main(String[] args){ + Neuron n0 = new Neuron(1, 0); + Neuron n1 = new Neuron(1, 1); + Neuron n2 = new Neuron(1, 2); + Neuron n3 = new Neuron(1, 3); + + n0.addConnection(n1); + n0.addConnection(n2); + Neuron copy = n0.copy(); + System.out.println(n0.bias + " --> " + n0.biasWeight); + System.out.println(copy.bias + " --> " + copy.biasWeight); + + } +} + +class Connection implements Serializable{ + public Neuron neuron; + public float weight; + private Random r; + + public Connection(Neuron n){ + neuron = n; + r = new Random(); + randomizeWeight(); + } + + public Connection(Neuron n, float w){ + neuron = n; + weight = w; + r = new Random(); + } + + public void randomizeWeight(){ + weight = r.nextFloat(); + //yea this sucks but I am lazy at the moment. + if (r.nextFloat() > 0.5) + weight = weight * -1; + } +} \ No newline at end of file diff --git a/build/source/Simple_NEAT.java b/build/source/Simple_NEAT.java new file mode 100644 index 0000000..ef4ec4a --- /dev/null +++ b/build/source/Simple_NEAT.java @@ -0,0 +1,177 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.ThreadLocalRandom; + + +public class Simple_NEAT{ + private ArrayList agents; + private int numInputs, numOutputs, genNum; + private Network curAgent; + private boolean keepBest; + private double mutationRate; + + public Simple_NEAT(int nI, int nO){ + numInputs = nI; + numOutputs = nO; + genNum = 0; + keepBest = false; + agents = new ArrayList(); + mutationRate = 0.60; + } + + public void addAgent(){ + agents.add(new Network(numInputs, numOutputs)); + } + + public void addAgent(Network n){ + agents.add(n); + } + + public void setCurrentAgent(int index){ + curAgent = agents.get(index); + } + + /** + * Run the entire generation with the same input(s) + * @param inputs Network input(s) + */ + public void runAll(float[] inputs){ + for (Network n : agents){ + n.runNetwork(inputs); + } + } + + /** + * Run the currently set agent + * @param inputs Network input(s) + */ + public void runCurrent(float[] inputs){ + curAgent.runNetwork(inputs); + } + + public float[] getCurOutput(){ + return curAgent.getSimpleOutput(); + } + + public Network getAgent(int i){ + return agents.get(i); + } + + public float[] getAgentOutput(int i){ + return agents.get(i).getSimpleOutput(); + } + + public void setFitness(int i, double d){ + Network n = getAgent(i); + n.setFitness(d); + } + + public void breed(){ + int popSize = 0; + ArrayList nextGen = new ArrayList(); + if (keepBest){ + popSize = agents.size() - 1; + } + else + popSize = agents.size(); + + double sum = 0.0; + for (Network a : agents){ + sum += a.getFitness(); + } + //Normalize the fitness values + for (Network a : agents){ + a.setGenFitness(a.getFitness()/sum); + } + + Collections.sort(agents); + for (Network a : agents){ + // System.out.println(a + " - " + a.getGenFitness()); + } + // System.out.println("------------------------------"); + + //Accumulate normalized fitness + sum = 0.0; + for (Network a : agents){ + double temp = a.getGenFitness(); + a.setGenFitness(temp + sum); + sum += temp; + } + + for (int i = 0; i < popSize; i++){ + double num = ThreadLocalRandom.current().nextDouble(0,1); + for (Network a : agents){ + if (a.getGenFitness() >= num){ + nextGen.add(a.copy()); + break; + } + } + } + + genNum++; + agents = nextGen; + mutate(); + } + + public void mutate(){ + for (Network a : agents){ + double num = ThreadLocalRandom.current().nextDouble(0,1); + //Should we mutate this agent? + if (mutationRate >= num){ + num = ThreadLocalRandom.current().nextDouble(0,1); + if (num <= 1/3){ + a.addRandHiddenNode(); + } + else if(num > 1/3 && num <= 2/3){ + a.addRandConnection(); + } + else{ + a.mutateWeight(); + } + } + } + } + + public Network getBestFit(){ + double max = 0; + Network best = null; + + for (Network n : agents){ + if (n.getFitness() > max){ + best = n; + max = n.getFitness(); + } + } + return best; + } + + public Network getNetwork(int i){ + return agents.get(i); + } + + public static void main(String args[]){ + // Simple_NEAT n = new Simple_NEAT(3, 2); + // Locals l = new Locals(); + // for (int i =0; i < 1; i++){ + // Ship s = new Ship(l); + // n.addAgent(s); + // n.setCurrentAgent(s); + // // n.setFitness(s, ThreadLocalRandom.current().nextInt(0, 200)); + // } + // + // float[] inputs = {0.4f, 0.9f, 0.33f}; + // n.runCurrent(inputs); + // for (float i : n.getCurOutput()){ + // System.out.print(i + ", "); + // } + // System.out.println("\n------------------------------------------"); + // + // float[] inputs1 = {0.8f, 0.1f, 0.73f}; + // n.runCurrent(inputs1); + // for (float i : n.getCurOutput()){ + // System.out.print(i + ", "); + // } + // System.out.println("\n------------------------------------------"); + // n.breed(); + } +} \ No newline at end of file diff --git a/build/source/Vector.java b/build/source/Vector.java new file mode 100644 index 0000000..e279471 --- /dev/null +++ b/build/source/Vector.java @@ -0,0 +1,27 @@ +public class Vector{ + public float x,y; + + public Vector(float x, float y){ + this.x = x; + this.y = y; + } + + public Vector(){ + this.x = 0; + this.y = 0; + } + + public static Vector fromAngle(double a){ + return new Vector((float) Math.cos(a), (float) Math.sin(a)); + } + + public void mult(double n){ + this.x *= n; + this.y *= n; + } + + public void add(Vector v){ + this.x += v.x; + this.y += v.y; + } +} \ No newline at end of file