The little circles learn over time how to move around and collect food. This essentially is a simple artificial intelligence. I am constantly updating/modifying the code.
import java.util.Scanner; import java.util.Random; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Timer; import java.util.TimerTask; import java.awt.event.*; import java.awt.*; import javax.swing.*; public class NeuralNetwork extends JPanel{ Timer timer; int speed = 2; public static Circle[] foodArray; public static Circle base; public static int rNum = 2; public static int cNum = 5; public static int inNum = 2; public static int outNum = 3; public static int foodNum = 20; public static int brainNum = 20; static Brain[] brains; int count = 0; int maxCount = 15000; int genNum = 0; public void paintComponent(Graphics g){ super.paintComponent(g); //g.drawOval(base.getX(), base.getY(), base.getRadius() * 2, base.getRadius() * 2); double radFov = (20 * Math.PI) / 180; for(int i = 0; i < brains.length; i++){ g.drawOval(brains[i].getX(), brains[i].getY(), brains[i].radius * 2, brains[i].radius * 2); g.drawLine(brains[i].getX() + brains[i].radius, brains[i].getY() + brains[i].radius, brains[i].getX() + brains[i].radius + Math.round((float)(Math.cos(brains[i].rotation)) * 4), brains[i].getY() + brains[i].radius - Math.round((float)(Math.sin(brains[i].rotation)) * 4)); //g.drawLine(brains[i].getX() + brains[i].radius, brains[i].getY() + brains[i].radius, brains[i].getX() + brains[i].radius + Math.round((float)(Math.cos(brains[i].rotation - radFov)) * 1000), brains[i].getY() + brains[i].radius - Math.round((float)(Math.sin(brains[i].rotation - radFov)) * 1000)); } for(int i = 0; i < foodArray.length; i++){ g.drawOval(foodArray[i].getX(), foodArray[i].getY(), foodArray[i].getRadius() * 2, foodArray[i].getRadius() * 2); } } public static void main(String[] args){ brains = new Brain[brainNum]; foodArray = new Circle[foodNum]; base = new Circle(40, (640 / 2) - 40, (480 / 2) - 40); for(int i = 0; i < foodNum; i++){ foodArray[i] = new Circle(10, (int)Math.round(Math.random() * 640), (int)Math.round(Math.random() * 480)); } for(int i = 0; i < brainNum; i++){ String[][] newWeights = new String[rNum][cNum * cNum]; brains[i] = new Brain(rNum, cNum, true, newWeights, 5); } NeuralNetwork content = new NeuralNetwork(); JFrame window = new JFrame(); window.setContentPane(content); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setLocation(120,70); window.setSize(640,480); window.setVisible(true); } class Task extends TimerTask { public void run(){ count++; double[] inputArray = new double[inNum]; double smallestFood = 800; double newFood = 0; int smallNum = 0; if(count > maxCount){ createNextGen(); count = 0; genNum++; System.out.println("Generation: " + genNum); } for(int i = 0; i < brainNum; i++){ for(int u = 0; u < foodNum; u++){ newFood = brains[i].canSee(foodArray[u], 40); if(newFood < smallestFood){ smallestFood = newFood; smallNum = u; } //Tests if food can be eaten int eatTest = brains[i].getDistance(foodArray[u]); if(eatTest < 16 && brains[i].getFoodCount() < 100000){ foodArray[u] = new Circle(10, (int)Math.round(Math.random() * 640), (int)Math.round(Math.random() * 480)); brains[i].setAddedScore(1); brains[i].setFoodCount(brains[i].getFoodCount() + 1); } } inputArray[0] = Math.abs((double)(brains[i].canSee(foodArray[smallNum], 40) - 800) / 800); //System.out.println(inputArray[0]); /* if(brains[i].getDistance(base) < 45 && brains[i].getFoodCount() > 4){ System.out.println("Dropped off food"); int scoreAdded = 2 * brains[i].getFoodCount(); brains[i].setAddedScore(scoreAdded); brains[i].setFoodCount(0); } */ //inputArray[1] = Math.abs((double)(brains[i].canSee(base, 40) - 800) / 800); /* if(brains[i].getFoodCount() > 4){ inputArray[2] = 1; } else{ inputArray[2] = 0; } */ if(inputArray[0] <= 0 ){ inputArray[1] = 1.0; } else{ inputArray[1] = 0; } brains[i].calculateWeights(inputArray); brains[i].calculateMovement(); } repaint(); } } public NeuralNetwork(){ timer = new Timer(); timer.schedule(new Task(), 0, speed); } public Brain breedBrains(Brain brain1, Brain brain2){ Brain newBrain = new Brain(rNum, cNum, false, new String[rNum][cNum * cNum], 5); String newGene = ""; for(int i = 0; i < rNum; i++){ for(int u = 0; u < cNum * cNum; u++){ newGene = ""; int crossoverPoint = Math.round((float)(Math.random() * 14)); for(int z = 1; z < 14; z++){ double random = Math.random(); double mutation = Math.random(); if(z == 1){ if(random > .5){ newGene = newGene + brain1.getWeights(i, u).charAt(0); } else{ newGene = newGene + brain2.getWeights(i, u).charAt(0); } } if(random > .5 && Character.getNumericValue(brain1.getWeights(i, u).charAt(z)) != -1){ if(mutation < .2){ if(Character.getNumericValue(brain1.getWeights(i, u).charAt(z)) == 1){ newGene = newGene + "0"; } else{ newGene = newGene + "1"; } } else{ newGene = newGene + brain1.getWeights(i, u).charAt(z); } } else if(random <= .5 && Character.getNumericValue(brain1.getWeights(i, u).charAt(z)) != -1){ if(mutation < .2){ if(Character.getNumericValue(brain2.getWeights(i, u).charAt(z)) == 1){ newGene = newGene + "0"; } else{ newGene = newGene + "1"; } } else{ newGene = newGene + brain2.getWeights(i, u).charAt(z); } } else{ newGene = newGene + "."; } } newBrain.setWeights(i, u, newGene); } } for(int i = 0; i < rNum * cNum + outNum; i++){ String newFunction = ""; for(int u = 1; u < 14; u++){ if(u == 1){ double negativeRan = Math.random(); if(negativeRan < .5){ newFunction = "" + brain1.getNeuronFunction(i).charAt(0); } else{ newFunction = "" + brain2.getNeuronFunction(i).charAt(0); } } double random = Math.random(); if(random < .5 && Character.getNumericValue(brain1.getNeuronFunction(i).charAt(u)) != -1){ newFunction = newFunction + brain1.getNeuronFunction(i).charAt(u); } else if(random >= .5 && Character.getNumericValue(brain2.getNeuronFunction(i).charAt(u)) != -1){ newFunction = newFunction + brain2.getNeuronFunction(i).charAt(u); } else{ newFunction = newFunction + "."; } } newBrain.setNeuronFunction(i, newFunction); } for(int i = 0; i < rNum * cNum + outNum; i++){ String newPower = ""; for(int u = 1; u < 14; u++){ if(u == 1){ newPower = newPower + "+"; } double random = Math.random(); if(random < .5 && Character.getNumericValue(brain1.getPower(i).charAt(u)) != -1){ newPower = newPower + brain1.getPower(i).charAt(u); } else if(random >= .5 && Character.getNumericValue(brain2.getPower(i).charAt(u)) != -1){ newPower = newPower + brain2.getPower(i).charAt(u); } else{ newPower = newPower+ "."; } } newBrain.setPower(i, newPower); } return newBrain; } public void createNextGen(){ double totalScore = 0; for(int i = 0; i < brainNum; i++){ totalScore += brains[i].getScore(); } for(int i = 0; i < brainNum; i++){ brains[i].setPercent((brains[i].getScore() / totalScore) * 100); } int comScore = 0; for(int i = 0; i < brainNum; i++){ comScore += brains[i].getPercent(); brains[i].setPercent(brains[i].getPercent() + comScore); } boolean done = false; boolean done1 = false; Brain chosenBrain1 = null; Brain chosenBrain2 = null; Brain[] newBrainList = new Brain[brainNum]; for(int z = 0; z < brainNum; z++){ done1 = false; done = false; while(done1 == false){ double random = Math.random() * 100; for(int i = 0; i < brainNum; i++){ if(i == 0){ if(brains[i].getPercent() > random){ chosenBrain1 = brains[i]; System.out.println("Brain 1: " + i); System.out.println("Score: " + chosenBrain1.getScore()); done1 = true; break; } } else if(brains[i].getPercent() >= random && brains[i - 1].getPercent() < random){ chosenBrain1 = brains[i]; System.out.println("Brain 1: " + i); System.out.println("Score: " + chosenBrain1.getScore()); done1 = true; break; } } } while(done == false){ double random1 = Math.random() * 100; for(int i = 0; i < brainNum; i++){ if(i == 0){ if(brains[i].getPercent() > random1){ if(chosenBrain1.equals(brains[i])){ } else{ chosenBrain2 = brains[i]; System.out.println("Brain 2: " + i); System.out.println("Score: " + chosenBrain2.getScore()); done = true; break; } } } else if(brains[i].getPercent() >= random1 && brains[i - 1].getPercent() < random1){ if(chosenBrain1.equals(brains[i])){ } else{ chosenBrain2 = brains[i]; System.out.println("Brain 2: " + i); System.out.println("Score: " + chosenBrain2.getScore()); done = true; break; } } } } newBrainList[z] = breedBrains(chosenBrain1, chosenBrain2); } for(int i = 0; i < brainNum; i++){ brains[i] = newBrainList[i]; } } } class Brain{ public int radius; double percent = 0; private int divFunction = 8; private int divNeuron = 8; private int divPower = 8; private String[][] weights; private double[] neurons; private String[] neuronFunction; private String[] neuronPower; double dx = Math.random() * 638 + 2; double dy = Math.random() * 478 + 2; int score = 0; int divideNum = 1; int speedReduce = 1; int foodCount; int rowNum; int colunmNum; double moveOut, rotationLeftOut, rotationRightOut; double[] outPuts; int outNum = 3; int x, y; double rotation = 0; public Brain(int rows, int columnNumber, boolean first, String[][] weight, int bRadius){ weights = new String[rows][columnNumber * columnNumber]; radius = bRadius; System.arraycopy(weight, 0, weights, 0, weight.length); rowNum = rows; colunmNum = columnNumber; neurons = new double[rowNum * colunmNum]; neuronFunction = new String[neurons.length + NeuralNetwork.outNum]; neuronPower = new String[neuronFunction.length]; outPuts = new double[outNum]; x = 100; y = 100; if(first == true){ generateWeights(); } } private void generateWeights(){ for(int i = 0; i < rowNum; i++){ for(int u = 0; u < colunmNum * colunmNum; u++){ double random = (double)(Math.random() * 16); double negativeRan = Math.random(); if(negativeRan < 0.5){ random = random * -1; } weights[i][u] = toBinary(random); } } for(int i = 0; i < neuronFunction.length; i++){ double random = (double)(Math.random() * 16); double negativeRan = Math.random(); if(negativeRan < 0.5){ random = random * -1; } neuronFunction[i] = toBinary(random); } for(int i = 0; i < neuronFunction.length; i++){ double random = Math.random() * 16; neuronPower[i] = toBinary(random); } } public static String toBinary(double num){ double dNum = Math.abs(num); String sNum = "" + dNum; String bNum = ""; char ch = sNum.charAt(1); int nu = Character.getNumericValue(ch); if(nu == -1){ for(int i = 0; i < sNum.length(); i++){ char c = sNum.charAt(i); int iNum = Character.getNumericValue(c); for(int u = 0; u < 4; u++){ if( iNum == -1){ String binaryDec = "."; bNum = bNum + binaryDec; break; } else{ int binaryNum = iNum % 2; if(binaryNum == 1){ iNum = (iNum - 1) / 2; } else{ iNum = iNum / 2; } bNum = bNum + binaryNum; } } } } else{ int iNum = 0; for(int i = 0; i < sNum.length() - 1; i++){ if(i == 0){ char c1 = sNum.charAt(0); char c2 = sNum.charAt(1); String twoNum = "" + c1 + c2; iNum = Integer.parseInt(twoNum); } char c = sNum.charAt(i + 1); if(i != 0){ iNum = Character.getNumericValue(c); } for(int u = 0; u < 4; u++){ if( iNum == -1){ String binaryDec = "."; bNum = bNum + binaryDec; break; } else{ int binaryNum = iNum % 2; if(binaryNum == 1){ iNum = (iNum - 1) / 2; } else{ iNum = iNum / 2; } bNum = bNum + binaryNum; } } } } if(num < 0){ bNum = "-" + bNum; } else{ bNum = "+" + bNum; } return bNum; } public static double toDecimal(String bNum){ int convertedNum = 0; double finalNum = 0; String numString = ""; for(int u = 1; u < (bNum.length()) / 4; u++){ char c; convertedNum = 0; for(int i = 0; i < 4; i++){ if(u != 0){ c = bNum.charAt(1 + i + u * 4); } else{ c = bNum.charAt(i + u * 4); } int num = Character.getNumericValue(c); if(num == 1){ convertedNum = convertedNum + Math.round((float)Math.pow(2, Math.abs(i))); } } if(u == 1){ numString = numString + convertedNum + "."; } else{ numString = numString + convertedNum; } } //System.out.println(numString); finalNum = Double.parseDouble(numString); char nChar = bNum.charAt(0); if(nChar == '-'){ finalNum = finalNum * -1; } return finalNum; } public double getPercent(){ return percent; } public void setPercent(double newPercent){ this.percent = newPercent; } public int getScore(){ return score; } public String getWeights(int first, int second){ return weights[first][second]; } public void setWeights(int first, int second, String num){ weights[first][second] = num; } public String getNeuronFunction(int num){ return neuronFunction[num]; } public void setNeuronFunction(int num, String s){ neuronFunction[num] = s; } public int getX(){ return this.x; } public int getY(){ return this.y; } public int getFoodCount(){ return foodCount; } public void setAddedScore(int newScore){ this.score += newScore; } public void setFoodCount(int newFoodCount){ this.foodCount = newFoodCount; } public String getPower(int num){ return neuronPower[num]; } public void setPower(int num, String set){ neuronPower[num] = set; } public void calculateWeights(double[] inputArray){ double[] inputs = new double[inputArray.length]; for(int i = 0; i < rowNum * colunmNum; i++){ neurons[i] = 0; } for(int i = 0; i < outNum; i++){ outPuts[i] = 0; } System.arraycopy(inputArray, 0, inputs, 0, inputArray.length); int cNum = 0; for(int i = 0; i < rowNum; i++){ for(int u = 0; u < colunmNum * colunmNum; u++){ if(i == 0){ if(u >= NeuralNetwork.inNum * colunmNum){ break; } cNum = (int)Math.round((float)(u / NeuralNetwork.inNum)); neurons[cNum] += toDecimal(weights[i][u]) / divNeuron * inputs[u % NeuralNetwork.inNum]; } else{ cNum = (int)Math.floor((float)(u / colunmNum)); neurons[cNum + i * colunmNum] = neurons[cNum + i * colunmNum] + toDecimal(weights[i][u]) / divNeuron * neurons[u % colunmNum + (i - 1) * colunmNum]; } } for(int u = 0; u < colunmNum; u++){ //neurons[u + i * colunmNum] = 1 / (1 + Math.pow(2.7, -neurons[u + i * colunmNum])); if(neurons[u + i * colunmNum] > toDecimal(neuronFunction[u + i * colunmNum]) / divFunction){ neurons[u + i * colunmNum] = 1 / (1 + Math.pow(2.7, -neurons[u + i * colunmNum])); } else{ neurons[u + i * colunmNum] = 0; } /* if(neurons[u + i * colunmNum] > toDecimal(neuronFunction[u + i * colunmNum]) / divFunction){ neurons[u + i * colunmNum] = toDecimal(neuronPower[u + i * colunmNum]) / divPower; } else{ neurons[u + i * colunmNum] = 0; } */ //System.out.println("Neuron " + (u + i * colunmNum) + ": " + neurons[u + i * colunmNum]); } } for(int u = 0; u < colunmNum * outNum; u++){ cNum = (int)Math.floor((float)(u / colunmNum)); this.outPuts[cNum] += (toDecimal(weights[rowNum - 1][u])) / 8 * neurons[u % colunmNum + colunmNum * (rowNum - 1)]; //this.outPuts[cNum] = this.outPuts[cNum] / colunmNum; } for(int u = 0; u < outNum; u++){ //this.outPuts[u] = 1 / (1 + Math.pow(2.7, -this.outPuts[u])); if(this.outPuts[u] > toDecimal(neuronFunction[neurons.length + u]) / divFunction){ this.outPuts[u] = 1 / (1 + Math.pow(2.7, -this.outPuts[u])); } else{ this.outPuts[u] = 0; } /* if(this.outPuts[u] > toDecimal(neuronFunction[neurons.length + u]) / divFunction){ this.outPuts[u] = toDecimal(neuronPower[neurons.length + u]) / divPower; } else{ this.outPuts[u] = 0; } */ } //System.out.println("Output 1: " + outPuts[1]); //System.out.println("Output 2: " + outPuts[2]); } public void calculateMovement(){ rotation += (this.outPuts[1] - this.outPuts[2]) / 2; if(rotation > 2 * Math.PI){ rotation = 0; } if(rotation < 0){ rotation = 2 * Math.PI; } double xVec = Math.cos(rotation) * this.outPuts[0]; double yVec = -Math.sin(rotation) * this.outPuts[0]; dx = dx + (double)(xVec / speedReduce); dy = dy + (double)(yVec / speedReduce); if(dx > 640){ dx = (double)4; } if(dy > 480){ dy = (double)4; } if(dx < 0){ dx = (double)636; } if(dy < 0){ dy = (double)476; } x = (int)dx; y = (int)dy; } public int canSee(Shapes object, int fov){ double shapeX, shapeY; if(object.getType().equals("Circle")){ Circle objectC = (Circle)object; shapeX = (object.getX() + Math.round(objectC.getRadius())) - (this.x - this.radius); shapeY = (this.y + this.radius) - (object.getY() + Math.round(objectC.getRadius())); double newAngle = Math.atan(shapeY / shapeX); if(shapeX < 0){ if(shapeY < 0){ newAngle = newAngle - Math.PI; } else{ newAngle = newAngle + Math.PI; } } if(newAngle < 0){ newAngle = newAngle + (2 * Math.PI); } if(Math.abs(newAngle - rotation) < (fov * Math.PI / 360)){ return this.getDistance(object); } else if(Math.abs(newAngle - rotation) > (2 * Math.PI) - (fov * Math.PI / 360)){ return this.getDistance(object); } } else if(object.getType().equals("Square")){ Square objectS = (Square)object; shapeX = (object.getX() - Math.round(objectS.getWidth() / 2)) - this.x; shapeY = this.y - (object.getY() - Math.round(objectS.getHeight() / 2)); double newAngle = Math.atan(shapeY / shapeX); if(shapeX < 0){ if(shapeY < 0){ newAngle = newAngle + Math.PI / 2; } else{ newAngle = newAngle - Math.PI / 2; } } } return 800; } public int getDistance(Shapes object){ int distance = 0; if(object.getType().equals("Circle")){ distance = Math.round((float)Math.sqrt(Math.pow((double)(object.getX() + ((Circle)object).getRadius()) - (this.x + this.radius), 2.0) + Math.pow((double)(object.getY() + ((Circle)object).getRadius()) - (this.y + this.radius), 2.0))); } else if(object.getType().equals("Square")){ distance = (int) Math.round(Math.sqrt(Math.pow((double)(object.getX() + ((Square) object).getWidth() / 2) - this.x, 2.0) + Math.pow((double)(object.getY() + ((Square) object).getHeight() / 2) - this.y, 2.0))); } return distance; } } class Shapes{ int x; int y; String type; public Shapes(){ } public String getType(){ return type; } public int getX(){ return x; } public int getY(){ return y; } } class Circle extends Shapes{ int radius; public Circle(int radius, int x, int y){ this.x = x; this.y = y; this.radius = radius; this.type = "Circle"; } public int getRadius(){ return this.radius; } } class Square extends Shapes{ int height; int width; public Square(int height, int width, int x, int y){ this.x = x; this.y = y; this.height = height; this.width = width; this.type = "Square"; } public String getType(){ return type; } public int getHeight(){ return this.height; } public int getWidth(){ return this.width; } }
To learn more HTML/CSS, check out these tutorials!