static double displayTime;
int translateX, translateY, delta, b4x, b4y, deltaX, deltaY;
public static int appWidth, appHeight, maintainNum, startNumCreatures, forcedSpawns, superMutations;
public static boolean play, showMenu, maintainPop, drawGenePoolGraph, showCreatureInfo, spawnMode, spawnClicking, saveFPS;
final int brainLength = 12;
public int tileResL = MAP_DIMENSIONS;
public int tileResW = MAP_DIMENSIONS;
public Creature[] creatures = new Creature[9999999];
public Tile[][] tiles = new Tile[tileResW][tileResL];
public static Tile selectedTile;
public static Creature selectedCreature;
public static boolean[][] waterTiles;
size(displayWidth, displayHeight);
appWidth = displayWidth - 2 * APP_WIDTH_SUBTRACTION_FACTOR;
appHeight = displayHeight - 2 * APP_HEIGHT_SUBTRACTION_FACTOR;
surface.setSize(appWidth, appHeight);
textSize(DEFAULT_TEXTSIZE);
timeInterval = GAME_SPEED;
maintainPop = MAINTAIN_DEFAULT;
maintainNum = START_MAINTAIN_NUM;
scaleFactor = DEFAULT_SCALE_FACTOR;
startNumCreatures = START_NUM_CREATURES;
spawnClicking = spawnMode = showMenu = showCreatureInfo = saveFPS = false;
play = drawGenePoolGraph = true;
forcedSpawns = superMutations = 0;
translateX = translateY = 20;
displayTime = b4x = b4y = deltaX = deltaY = 0;
if(MenuPath.path != MenuPath.CREATURE) selectedCreature = null;
if(selectedCreature != null)
translateX = (int) (-scaleFactor * selectedCreature.locationX + p2pw(850));
translateY = (int) (-scaleFactor * selectedCreature.locationY + p2pw(850));
translate(translateX, translateY);
scale((float) scaleFactor);
rect(p2pl(8), 0, p2pl(6), p2pw(10));
displayTime += timeInterval;
displayTime += timeInterval;
translateX = translateY = 20;
scaleFactor = DEFAULT_SCALE_FACTOR;
public void mouseDragged(MouseEvent e)
translateX += mouseX - pmouseX;
translateY += mouseY - pmouseY;
public void mouseClicked()
boolean checkWorldClick = false;
if(mX <= p2pl(1600) && mY >= p2pw(131))
if(DEBUG_PRINTS) System.out.println("check menu");
if(DEBUG_PRINTS) System.out.println("check no menu");
if(menuButton.clicked(mX, mY))
if(start.clicked(mX, mY))
if(selectedCreature != null && creatureInfo.clicked(mX, mY))
showCreatureInfo = !showCreatureInfo;
if(killAll.clicked(mX, mY))
MenuPath.path = MenuPath.GENERAL;
if(spawn.clicked(mX, mY))
if(spawn20.clicked(mX, mY))
if(maintainAt.clicked(mX, mY))
maintainPop = !maintainPop;
if(maintainPopNum.clicked(mX, mY))
if(maintainNum == 1) maintainNum += 4;
if(maintainNum > 50) maintainNum = 1;
if(saveFPSbutton.clicked(mX, mY))
if(DEBUG_PRINTS) System.out.println("checkWorldClick, spawnMode?");
if(spawn.clicked(mX, mY)) spawnMode = !spawnMode;
if(checkWorldClick(mX, mY)) addCreature(mX, mY);
if(DEBUG_PRINTS) System.out.println("not spawnMode");
Creature clickedCreature = checkCreatureClick(mX, mY);
if(clickedCreature != null)
if(DEBUG_PRINTS) System.out.println("creature click");
selectedCreature = clickedCreature;
Tile clickedTile = checkTileClick(mX, mY);
if(DEBUG_PRINTS) System.out.println("tile click");
selectedTile = clickedTile;
MenuPath.path = MenuPath.GENERAL;
public void mouseWheel(MouseEvent e)
float delta = (float) (-e.getCount() > 0 ? 1.05 : -e.getCount() < 0 ? 1.0 / 1.05 : 1.0);
if (!(scaleFactor > 3.0) && !(scaleFactor < .2))
if (scaleFactor > 3.0) scaleFactor = 3.0f;
if (scaleFactor < .2) scaleFactor = .2f;
waterTiles = new boolean[100][100];
for(int i = 0; i < 100; i++)
for(int j = 0; j < 100; j++)
if(6 < i && i < 94 && 6 < j && j < 94) waterTiles[i][j] = false;
else waterTiles[i][j] = true;
if(30 < i && i < 70 && 42 < j && j < 56) waterTiles[i][j] = true;
waterTiles[93][7] = true;
waterTiles[7][93] = true;
waterTiles[93][93] = true;
waterTiles[31][43] = false;
waterTiles[31][55] = false;
waterTiles[69][43] = false;
waterTiles[69][55] = false;
public static double getDisplayTime()
public void managerStart()
public void iterate(double timeInterval)
iterateCreatures(timeInterval);
if(maintainPop) maintain();
static final int GENERAL = 0;
static final int CREATURE = 1;
static final int DATA = 2;
static final int TILE = 3;
public final static int APP_WIDTH_SUBTRACTION_FACTOR = 68;
public final static int APP_HEIGHT_SUBTRACTION_FACTOR = 112;
public final static int DEFAULT_TEXTSIZE = 50;
public final static double DEFAULT_SCALE_FACTOR = 0.26;
public final static boolean DEBUG_PRINTS = false;
public final static int START_MAINTAIN_NUM = 50;
public final static boolean MAINTAIN_DEFAULT = true;
public final static double MUTATE_CHANCE = 0.5;
public final static double SUPER_MUTATE_CHANCE = 0.05;
public final static int START_NUM_CREATURES = 50;
public final static double GAME_SPEED = 0.1;
public final static int FRAMERATE = 60;
public final static boolean KILL_FOR_BIRTH_WITHOUT_MASS = false;
public final static int MAP_DIMENSIONS = 100;
public final static double TILE_REGEN_RATE = 0.025;
public final static int TILE_COOLDOWN_THRESH = 100;
public final static int DISPLAY_GRAPH_SIZE = 150;
public static double sigmoid(double x)
return (2.0 / (1 + pow((float)Math.E, (float)-(x / 1.0)))) - 1.0;
public static double colorSigmoid(double x)
return (2.0 / (1 + pow((float)Math.E, (float)-(x / 1.5)))) - 1.0;
public static double distBtCoords(double x1, double y1, double x2, double y2)
return sqrt((float)((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
public static int p2pl(double frac)
return (int) (frac / 2600.0 * 1920);
public static int p2pw(double frac)
return (int) (frac / 1600.0 * 1080);
double[] populationHistory;
public Button menuButton, creatureInfo;
public Button start, killAll, spawn, spawn20, kill, maintainAt, maintainPopNum, findCreatureByID, saveFPSbutton;
int popHistoryLength = 1;
MenuPath.path = MenuPath.GENERAL;
populationHistory = new double[DISPLAY_GRAPH_SIZE];
populationHistory[0] = (double) START_NUM_CREATURES;
menuButton = new Button(p2pl(2400), 0, p2pl(185), p2pw(120));
start = new Button(p2pl(2400), p2pw(120), p2pl(185), p2pw(120));
killAll = new Button(p2pl(0), 0, p2pl(200), p2pw(120));
spawn = new Button(p2pl(200), 0, p2pl(200), p2pw(120));
spawn20 = new Button(p2pl(400), 0, p2pl(250), p2pw(120));
findCreatureByID = new Button(p2pl(650), 0, p2pl(200), p2pw(120));
maintainAt = new Button(p2pl(850), 0, p2pl(280), p2pw(120));
maintainPopNum = new Button(p2pl(1100), 0, p2pl(140), p2pw(120));
creatureInfo = new Button(p2pl(1650), p2pw(250), p2pl(280), p2pw(120));
saveFPSbutton = new Button(p2pl(1240), 0, p2pl(240), p2pw(120));
colorMode(PConstants.RGB);
generalMenu(displayTime, forcedSpawns, superMutations);
drawButtons(play, showMenu, spawnMode, maintainPop, saveFPS, showCreatureInfo, maintainNum);
drawPopulationGraph(maintainPop, maintainNum);
colorMode(PConstants.RGB);
rect(p2pl(1600), 0, p2pl(1200), p2pw(2000));
drawButtons(play, showMenu, spawnMode, maintainPop, saveFPS, showCreatureInfo, maintainNum);
fill(selectedCreature.colorR, selectedCreature.colorG, selectedCreature.colorB);
drawSingleCreature(selectedCreature);
text("Creature Data", p2pl(1800), p2pw(100));
text("ID: " + selectedCreature.ID, p2pl(1800), p2pw(160));
text("Age: " + (int) Math.rint(selectedCreature.fitness), p2pl(1800), p2pw(210));
text("Generation: " + selectedCreature.generation, p2pl(2100), p2pw(160));
text("Parent: " + selectedCreature.parentID, p2pl(2100), p2pw(210));
text("Total Eaten: " + (int) Math.rint(selectedCreature.totalEaten), p2pl(1620), p2pw(shift + 0 * spacing));
text("Total Decayed: " + (int) Math.rint(selectedCreature.totalDecayed), p2pl(1620), p2pw(shift + 1 * spacing));
text("Location: ( " + (int) Math.rint(selectedCreature.locationX) + ", " + (int) Math.rint(selectedCreature.locationY) + " )", p2pl(1620), p2pw(shift + 2 * spacing));
text("Left XY: ( " + (int) Math.rint(selectedCreature.leftSensorX) + ", " + (int) Math.rint(selectedCreature.leftSensorY) + " )", p2pl(1620), p2pw(shift + 3 * spacing));
text("Mid XP: ( " + (int) Math.rint(selectedCreature.midSensorX) + ", " + (int) Math.rint(selectedCreature.midSensorY) + " )", p2pl(1620), p2pw(shift + 4 * spacing));
text("Right XY: ( " + (int) Math.rint(selectedCreature.rightSensorX) + ", " + (int) Math.rint(selectedCreature.rightSensorY) + " )", p2pl(1620), p2pw(shift + 5 * spacing));
text("Mouth XY: ( " + (int) Math.rint(selectedCreature.mouthSensorX) + ", " + (int) Math.rint(selectedCreature.mouthSensorY) + " )", p2pl(1620), p2pw(shift + 6 * spacing));
text("Heading: " + (int) Math.rint((selectedCreature.rotation * 180 / Math.PI)), p2pl(1620), p2pw(shift + 7 * spacing));
text("Size Decay", p2pl(2200), p2pw(shift2 + 0 * spacing));
text("Age Decay", p2pl(2200), p2pw(shift2 + 1 * spacing));
text("Eat Decay", p2pl(2200), p2pw(shift2 + 2 * spacing));
text("Turn Decay", p2pl(2200), p2pw(shift2 + 3 * spacing));
text("Fwd Decay", p2pl(2200), p2pw(shift2 + 4 * spacing));
text("Att Decay", p2pl(2200), p2pw(shift2 + 5 * spacing));
text("Eat Rate", p2pl(2200), p2pw(shift2 + 6 * spacing));
text("Current Size: " + (int)selectedCreature.size, p2pl(2200), p2pw(shift2 + 10 * spacing));
text(String.format("%.2f", selectedCreature.sizeDecay), p2pl(2400), p2pw(shift2 + 0 * spacing));
text(String.format("%.2f", selectedCreature.fitnessDecay), p2pl(2400), p2pw(shift2 + 1 * spacing));
text(String.format("%.2f", selectedCreature.eatRateDecay), p2pl(2400), p2pw(shift2 + 2 * spacing));
text(String.format("%.2f", selectedCreature.rotationDecay), p2pl(2400), p2pw(shift2 + 3 * spacing));
text(String.format("%.2f", selectedCreature.forwardDecay), p2pl(2400), p2pw(shift2 + 4 * spacing));
text(String.format("%.2f", selectedCreature.attackDecay), p2pl(2400), p2pw(shift2 + 5 * spacing));
text(String.format("%.2f", selectedCreature.eatRate), p2pl(2400), p2pw(shift2 + 6 * spacing));
if(selectedCreature.energyChange < 0) fill(255, 0, 0);
text("Energy Change: " + String.format("%.2f", selectedCreature.energyChange), p2pl(2200), p2pw(shift2 + 8 * spacing));
if(selectedCreature.nearestCreature != null)
text("Nearest Creature ID: " + selectedCreature.nearestCreature.ID, p2pl(1620), p2pw(shift + 8 * spacing));
text("Nearest ID Distance: " + String.format("%.2f", selectedCreature.distToNearest), p2pl(1620), p2pw(shift + 9 * spacing));
text("Nearest ID Color Diff: " + selectedCreature.colorDifferenceToNearest, p2pl(1620), p2pw(shift + 10 * spacing));
text("Angle change to nearest: " + String.format("%.2f", selectedCreature.angleToNearest), p2pl(1620), p2pw(shift + 11 * spacing));
text("Creatures within 10 Tiles: " + selectedCreature.numCreaturesWithin10, p2pl(1620), p2pw(shift + 12 * spacing));
text("Nearest Creature ID: null", p2pl(1620), p2pw(shift + 8 * spacing));
text("Nearest ID Distance: null", p2pl(1620), p2pw(shift + 9 * spacing));
text("Nearest ID Color Diff: null", p2pl(1620), p2pw(shift + 10 * spacing));
text("Angle change to nearest: null", p2pl(1620), p2pw(shift + 11 * spacing));
text("Creatures within 10 Tiles: 0", p2pl(1620), p2pw(shift + 12 * spacing));
else drawCreatureBrain(selectedCreature);
colorMode(PConstants.RGB);
rect(p2pl(1600), 0, p2pl(1200), p2pw(2000));
colorMode(PConstants.HSB, 360, 100, 100);
fill(selectedTile.colorH, selectedTile.colorS, selectedTile.colorV);
rect(p2pl(1620), p2pw(140), p2pl(200), p2pl(200));
colorMode(PConstants.RGB, 255, 255, 255);
drawButtons(play, showMenu, spawnMode, maintainPop, saveFPS, showCreatureInfo, maintainNum);
text("Selected Tile Data", p2pl(1620), p2pw(110));
text("Tile #" + selectedTile.tileNumber, p2pl(1630), p2pw(200));
text("Food: " + String.format("%.2f", selectedTile.food), p2pl(1630), p2pw(460));
text("Row and Column: (" + (selectedTile.xIndex) + ", " + (selectedTile.yIndex) + ")", p2pl(1850), p2pw(200));
text("Regeneration Value: " + Math.round( (selectedTile.regenValue * 1000) ) / 1000.0, p2pl(1850), p2pw(250));
text("HSV: " + selectedTile.colorH + ", " + selectedTile.colorS + ", " + selectedTile.colorV, p2pl(1850), p2pw(300));
text("x Range: " + selectedTile.x + " to " + (selectedTile.x + tileSize), p2pl(1850), p2pw(350));
text("y Range: " + selectedTile.y + " to " + (selectedTile.y + tileSize), p2pl(1850), p2pw(400));
text("Tile Cooldown Status: " + selectedTile.deadCooldown + " / " + TILE_COOLDOWN_THRESH, p2pl(1620), p2pw(600));
public void updateHistoryArrays()
populationHistory[popHistoryLength] = (double) (aliveCreatures);
maxObservedCreatures = (int) Math.max(maxObservedCreatures, populationHistory[popHistoryLength]);
public void generalMenu(double time, int forcedSpawns, int superMutations)
colorMode(PConstants.RGB);
rect(p2pl(1600), 0, p2pl(1200), p2pw(2000));
text("World Time: " + (int) Math.rint(time), p2pl(1620), p2pw(100));
text("Starting Creature Count: " + startNumCreatures, p2pl(1620), p2pw(startLabels + 1 * generalSpacing));
text("Current Creature Count: " + (aliveCreatures), p2pl(1620), p2pw(startLabels + 2 * generalSpacing));
text("Total Creature Births: " + creatureBirths, p2pl(1620), p2pw(startLabels + 5 * generalSpacing));
text("Total Creature Deaths: " + creatureDeaths, p2pl(1620), p2pw(startLabels + 6 * generalSpacing));
if(startNumCreatures > aliveCreatures) sign = "-";
else if(startNumCreatures == aliveCreatures) sign = "";
if(sign.equals("+")) fill(0, 255, 0);
else if(sign.equals("-")) fill(255, 0, 0);
text("Total Change: " + sign + Math.abs(aliveCreatures - startNumCreatures), p2pl(1620), p2pw(startLabels + 3 * generalSpacing));
text("Total Existed: " + creatureCount, p2pl(1620), p2pw(startLabels + 8 * generalSpacing));
text("Forced Spawns: " + forcedSpawns, p2pl(1620), p2pw(startLabels + 9 * generalSpacing));
text("Super Mutations: " + superMutations, p2pl(1620), p2pw(startLabels + 10 * generalSpacing));
public void drawButtons(boolean play, boolean showMenu, boolean spawnMode, boolean maintainPop, boolean saveFPS, boolean showCreatureInfo, int maintainNum)
colorMode(PConstants.RGB);
text("Menu", menuButton.getTextX(), menuButton.getTextY());
if(play) fill(119, 255, 51);
if(play) text("On", start.getTextX(), start.getTextY());
else text("Off", start.getTextX(), start.getTextY());
colorMode(PConstants.HSB);
fill((int)(frameRate / 30.0 * 100), 200, 200);
text("FPS: " + (int) Math.rint(frameRate), start.getX(), start.getY() + 100);
colorMode(PConstants.RGB);
rect(0, 0, p2pl(1600), p2pw(130));
maintainPopNum.drawButton();
findCreatureByID.drawButton();
saveFPSbutton.drawButton();
text("Kill All", killAll.getTextX(), killAll.getTextY());
if(spawnMode) fill(119, 255, 51);
text("Spawn", spawn.getTextX(), spawn.getTextY());
text("Spawn 20", spawn20.getTextX(), spawn20.getTextY());
if(maintainPop) fill(119, 255, 51);
text("Maintain At", maintainAt.getTextX(), maintainAt.getTextY());
text(maintainNum, maintainPopNum.getTextX(), maintainPopNum.getTextY());
text("Find ID", findCreatureByID.getTextX(), findCreatureByID.getTextY());
if(saveFPS) fill(119, 255, 51);
text("FPS Saver", saveFPSbutton.getTextX(), saveFPSbutton.getTextY());
if(MenuPath.path == MenuPath.CREATURE)
creatureInfo.drawButton();
text("Brain Info", creatureInfo.getTextX(), creatureInfo.getTextY());
else text("Debug Info", creatureInfo.getTextX(), creatureInfo.getTextY());
public void drawPopulationGraph(boolean maintainPop, int maintainNum)
int horizontalPosition = 1650;
int verticalPosition = 1350;
colorMode(PConstants.RGB);
text("Relative Population Over Time", p2pl(horizontalPosition), p2pw(verticalPosition - 20));
rect(p2pl(horizontalPosition), p2pw(verticalPosition - 10), p2pl(graphWidth), p2pw(graphHeight + 20));
int width = graphWidth / popHistoryLength;
if(maintainPop) stroke(0, 255, 0);
line(p2pl(horizontalPosition), p2pw(verticalPosition + (graphHeight - (int)(1.0 * maintainNum / maxObservedCreatures * graphHeight))),
p2pl(horizontalPosition + graphWidth), p2pw(verticalPosition + (graphHeight - (int)(1.0 * maintainNum / maxObservedCreatures * graphHeight))));
for(int i = 0; i < popHistoryLength-1; i++)
double ratio = (populationHistory[i] / maxObservedCreatures * graphHeight);
double toRatio = (populationHistory[i+1] / maxObservedCreatures * graphHeight);
line(p2pl(horizontalPosition + (i)*width), p2pw(verticalPosition + (graphHeight - (int)ratio)), p2pl(horizontalPosition + (i+1)*width), p2pw(verticalPosition + (graphHeight - (int)toRatio)));
ellipse(p2pl(horizontalPosition + (i+1)*width), p2pw(verticalPosition + (graphHeight - (int)toRatio)), p2pw(5), p2pw(5));
int cutSize = DISPLAY_GRAPH_SIZE;
boolean cutPopulationHistory = false;
if(popHistoryLength >= cutSize) cutPopulationHistory = true;
for(int i = 0; i < popHistoryLength - 1; i++)
populationHistory[i] = populationHistory[i + 1];
public void drawSingleCreature(Creature c)
colorMode(PConstants.RGB);
fill(c.colorR, c.colorG, c.colorB);
int leftSensorXChange = (int) (c.leftSensorX - c.locationX);
int leftSensorYChange = (int) (c.leftSensorY - c.locationY);
int rightSensorXChange = (int) (c.rightSensorX - c.locationX);
int rightSensorYChange = (int) (c.rightSensorY - c.locationY);
int killerXChange = (int) (c.midSensorX - c.locationX);
int killerYChange = (int) (c.midSensorY - c.locationY);
line(p2pl(1700), p2pw(150), p2pl(1700) + leftSensorXChange, p2pw(150) + leftSensorYChange);
line(p2pl(1700), p2pw(150), p2pl(1700) + rightSensorXChange, p2pw(150) + rightSensorYChange);
line(p2pl(1700), p2pw(150), p2pl(1700) + killerXChange, p2pw(150) + killerYChange);
colorMode(PConstants.HSB);
fill(c.leftSensorColor, 120, 120);
ellipse(p2pl(1700) + leftSensorXChange, p2pw(150) + leftSensorYChange, p2pw(15) * 1, p2pw(15) * 1);
fill(c.rightSensorColor, 120, 120);
ellipse(p2pl(1700) + rightSensorXChange, p2pw(150) + rightSensorYChange, p2pw(15) * 1, p2pw(15) * 1);
colorMode(PConstants.RGB);
fill(c.colorR, c.colorG, c.colorB);
ellipse(p2pl(1700), p2pw(150), (int)c.diameter * 1, (int)c.diameter * 1);
public void drawCreatureBrain(Creature c)
int verticalSpacing = p2pw(640 / brainLength);
text("Inputs", p2pl(1620), p2pw(900));
text("Hidden Layers", p2pl(1940), p2pw(900));
text("Outputs", p2pl(2400), p2pw(900));
colorMode(PConstants.RGB);
text("L Food", p2pl(1610), p2pw(950) + verticalSpacing * 0);
text("C Food", p2pl(1610), p2pw(950) + verticalSpacing * 1);
text("M Food", p2pl(1610), p2pw(950) + verticalSpacing * 2);
text("R Food", p2pl(1610), p2pw(950) + verticalSpacing * 3);
text("Size", p2pl(1610), p2pw(950) + verticalSpacing * 4);
text("Cr Dist", p2pl(1610), p2pw(950) + verticalSpacing * 5);
text("Co Diff", p2pl(1610), p2pw(950) + verticalSpacing * 6);
text("Cr Angl", p2pl(1610), p2pw(950) + verticalSpacing * 7);
text("Cr Bir?", p2pl(1610), p2pw(950) + verticalSpacing * 8);
text("# in 10", p2pl(1610), p2pw(950) + verticalSpacing * 9);
text("-----", p2pl(1610), p2pw(950) + verticalSpacing * 10);
text("-----", p2pl(1610), p2pw(950) + verticalSpacing * 11);
text("For Vel", p2pl(2400), p2pw(950) + verticalSpacing * 0);
text("Rot Vel", p2pl(2400), p2pw(950) + verticalSpacing * 1);
text("Eat Rate", p2pl(2400), p2pw(950) + verticalSpacing * 2);
if(c.outputNeurons[3] >= 0) fill(0, 255, 0);
text("Attack?", p2pl(2400), p2pw(950) + verticalSpacing * 3);
if(c.outputNeurons[4] >= 0) fill(0, 255, 0);
text("Birth?", p2pl(2400), p2pw(950) + verticalSpacing * 4);
text("Att Size", p2pl(2400), p2pw(950) + verticalSpacing * 5);
for(int i = 0; i < c.sensorInput.length; i++)
text(String.format("%.2f", c.sensorInput[i]) + "", p2pl(1710), p2pw(950) + verticalSpacing * i);
for(int i = 0; i < c.hidLayer1.length; i++)
text(String.format("%.2f", c.hidLayer1[i]) + "", p2pl(1935), p2pw(950) + p2pw(40) * i);
for(int i = 0; i < c.hidLayer2.length; i++)
text(String.format("%.2f", c.hidLayer2[i]) + "", p2pl(2155), p2pw(950) + p2pw(40) * i);
for(int i = 0; i < c.outputNeurons.length; i++)
text(String.format("%.2f", c.outputNeurons[i]) + "", p2pl(2510), p2pw(950) + verticalSpacing * i);
for(int i = 0; i < c.inputNeurons.length; i++)
for(int one = 0; one < c.hidLayer1.length; one++)
colore = (int) (colorSigmoid(c.inputToLayer1Axons[i][one].weight + 1.0) * 255);
if(colore < 0) colore = 0;
if(colore > 255) colore = 255;
line(p2pl(1780), p2pw(945) + verticalSpacing * i, p2pl(1920), p2pw(940) + p2pw(40) * one);
for(int i = 0; i < c.hidLayer1.length; i++)
for(int o = 0; o < c.hidLayer2.length; o++)
colore = (int) (colorSigmoid(c.layer1ToLayer2Axons[i][o].weight + 1.0) * 255);
if(colore < 0) colore = 0;
if(colore > 255) colore = 255;
line(p2pl(2020), p2pw(945) + p2pw(40) * i, p2pl(2140), p2pw(940) + p2pw(40) * o);
for(int i = 0; i < c.hidLayer2.length; i++)
for(int o = 0; o < c.outputNeurons.length; o++)
colore = (int) (colorSigmoid(c.layer2ToOutputAxons[i][o].weight + 1.0) * 255);
if(colore < 0) colore = 0;
if(colore > 255) colore = 255;
line(p2pl(2230), p2pw(945) + p2pw(40) * i, p2pl(2380), p2pw(940) + verticalSpacing * o);
public Button(int x, int y, int width, int height)
this.width = width - 2 * buffer;
this.height = height - 2 * buffer;
setTextX(buffer + this.getX() + p2pl(20));
setTextY((int)(buffer + this.getY() + this.height / 2.0));
colorMode(PConstants.RGB);
rect(this.getX(), this.getY(), this.width, this.height);
public boolean clicked(int x, int y)
if(this.getX() < x && x <= this.getX() + this.width)
if(this.getY() < y && y <= this.getY() + this.height)
public void setTextX(int textX)
public void setTextY(int textY)
public int creatureCount;
public double mutateChance = MUTATE_CHANCE;
public int creatureBirths = 0;
public int creatureDeaths = 0;
public int maxObservedCreatures = startNumCreatures;
public int aliveCreatures = 0;
public void iterateCreatures(double timeInterval)
for(int i = 0; creatures[i] != null; i++)
if(creatures[i].alive == true)
creatures[i].iterate(timeInterval);
double eatRequest = creatures[i].requestEat(timeInterval);
int[] foodTile = findTileCoordsAt(creatures[i].mouthSensorX, creatures[i].mouthSensorY);
double allowEatAmt = requestEat(foodTile[0], foodTile[1], eatRequest);
creatures[i].allowEat(allowEatAmt);
if(allowEatAmt == 0.0) tiles[foodTile[0]][foodTile[1]].resetCooldown();
if(creatures[i].requestBirth())
if(creatures[i].size < 400)
if(KILL_FOR_BIRTH_WITHOUT_MASS) creatures[i].size = 10;
creatures[creatureCount] = new Creature(appWidth, appHeight, (int)creatures[i].locationX, (int)creatures[i].locationY,
creatureCount, ( creatures[i].generation+1 ), creatures[i].giveBirth(),
creatures[i].colorR, creatures[i].colorG, creatures[i].colorB, creatures[i].ID);
if(findCreatureID(creatureCount).superMutate) superMutations++;
public void startCreatures()
for(int i = 0; i < startNumCreatures; i++)
int testX = randXInMap();
int testY = randYInMap();
int[] tileTest = findTileCoordsAt(testX, testY);
if(!waterTiles[tileTest[1]][tileTest[0]])
creatures[creatureCount] = new Creature(appWidth, appHeight, testX, testY, creatureCount, 0);
public void addCreature()
int testX = randXInMap();
int testY = randYInMap();
int[] tileTest = findTileCoordsAt(testX, testY);
if(!waterTiles[tileTest[1]][tileTest[0]])
creatures[creatureCount] = new Creature(appWidth, appHeight, testX, testY, creatureCount, 0);
public void addCreature(int x, int y)
creatures[creatureCount] = new Creature(appWidth, appHeight, x, y, creatureCount, 0);
public void spawnNumCreatures(int number)
for(int i = 0; i < number; i++)
public double requestEat(int yIndex, int xIndex, double amount)
if(tiles[yIndex][xIndex].food < amount) return 0.0;
tiles[yIndex][xIndex].food -= amount;
public void checkForDeaths()
for(int i = 0; creatures[i] != null; i++)
if(creatures[i].size < 100 && creatures[i].alive == true)
if(selectedCreature != null && creatures[i].ID == selectedCreature.ID)
MenuPath.path = MenuPath.GENERAL;
creatures[i].alive = false;
public boolean isCreatureAt(double xCoor, double yCoor)
boolean isCreature = false;
int xCoord = (int) xCoor;
int yCoord = (int) yCoor;
for(int i = 0; creatures[i] != null; i++)
if (Math.hypot(xCoord - creatures[i].locationX, yCoord - creatures[i].locationY) < creatures[i].diameter/2)
public Creature checkCreatureClick(int x, int y)
for(int i = 0; creatures[i] != null; i++)
if (Math.hypot(x - creatures[i].locationX, y - creatures[i].locationY) < creatures[i].diameter/2)
MenuPath.path = MenuPath.CREATURE;
public Creature findCreatureID(int ID)
for(int i = 0; creatures[i] != null; i++)
if(creatures[i].alive == true && ID == creatures[i].ID)
public Object[] findClosestCreatureData(Creature from)
Object[] data = new Object[2];
Creature nearestCreature = null;
double closestDist = 999999.9;
for(int i = 0; creatures[i] != null; i++)
if(creatures[i].alive == true)
double dist = distBtCoords(from.locationX, from.locationY,
creatures[i].locationX, creatures[i].locationY);
if(dist < closestDist && dist > 0.01)
nearestCreature = creatures[i];
if(dist / tileSize < 10.0 && dist > 0.01)
data[0] = nearestCreature;
if(aliveCreatures < maintainNum)
for(int i = 0; i < maintainNum - aliveCreatures; i++)
public void drawCreatures()
colorMode(PConstants.RGB);
for(int i = 0; creatures[i] != null; i++)
if(creatures[i].alive == true)
Creature c = creatures[i];
line((int)c.locationX, (int)c.locationY, (int)c.leftSensorX, (int)c.leftSensorY);
if(c.outputNeurons[3] > 0.0) stroke(180, 0, 0);
line((int)c.locationX, (int)c.locationY, (int)c.midSensorX, (int)c.midSensorY);
line((int)c.locationX, (int)c.locationY, (int)c.rightSensorX, (int)c.rightSensorY);
fill(c.colorR, c.colorG, c.colorB);
if(selectedCreature != null && creatures[i].ID == selectedCreature.ID)
ellipse((int)c.locationX, (int)c.locationY, p2pw(c.diameter), p2pw(c.diameter));
colorMode(PConstants.HSB, 360, 100, 100);
fill(c.leftSensorColor, 80, 45);
ellipse((int)c.leftSensorX, (int)c.leftSensorY, p2pw(15), p2pw(15));
fill(c.rightSensorColor, 80, 45);
ellipse((int)c.rightSensorX, (int)c.rightSensorY, p2pw(15), p2pw(15));
fill(c.mouthSensorColor, 80, 45);
colorMode(PConstants.RGB, 255, 255, 255);
for(int i = 0; creatures[i] != null; i++)
if(creatures[i].alive == true)
creatures[i].alive = false;
public double locationX, locationY, size, diameter, birthDate, age,
totalEaten, totalDecayed, fitness, forwardVel, rotation, rotationVel,
leftSensorX, leftSensorY, midSensorX, midSensorY, rightSensorX, rightSensorY, mouthSensorX, mouthSensorY,
attackDecay, sizeDecay, fitnessDecay, eatRateDecay, forwardDecay, rotationDecay,
eatRate, decayRate, energyChange;
public final int brainLength = 12;
public int ID, parentID, generation, births, sensorLength, weaponLength,
leftSensorColor, rightSensorColor, mouthSensorColor,
public int colorR, colorG, colorB;
public double[] defaultSensorValues, sensorInput, inputNeurons, hidLayer1, hidLayer2, outputNeurons;
double mutateFactor = 0.1;
double mutateChance = MUTATE_CHANCE;
double superMutateChance = SUPER_MUTATE_CHANCE;
public boolean superMutate = false;
public Axon[][] inputToLayer1Axons, layer1ToLayer2Axons, layer2ToOutputAxons;
double sensorAngle = Math.PI/6.0;
int startingBirthSize = 250;
public Creature nearestCreature = null;
public double distToNearest, angleToNearest;
public int colorDifferenceToNearest, numCreaturesWithin10;
public Creature(int realWidth, int realHeight, int startX, int startY, int ID, int generation)
this.realWidth = realWidth;
this.realHeight = realHeight;
colorR = (int)(255 * Math.random());
colorG = (int)(255 * Math.random());
colorB = (int)(255 * Math.random());
size = 150 + (Math.random() - .5) * 50;
colorDifferenceToNearest = 200;
angleToNearest = totalEaten = totalDecayed = fitness = rotation = rotationDecay = births = numCreaturesWithin10 = 0;
sensorLength = p2pw((60 + diameter));
weaponLength = p2pw((30 + diameter));
this.generation = generation;
rotation = Math.random() * 2 * Math.PI;
sensorInput = new double[brainLength];
public Creature(int realWidth, int realHeight, int startX, int startY, int ID, int generation,
Axon[][][] brain, int colorR, int colorG, int colorB, int parentID)
this.parentID = parentID;
this.realWidth = realWidth;
this.realHeight = realHeight;
size = startingBirthSize;
colorDifferenceToNearest = 200;
angleToNearest = totalEaten = totalDecayed = fitness = rotation = rotationDecay = births = numCreaturesWithin10 = 0;
sensorLength = p2pw((60 + diameter));
weaponLength = p2pw((30 + diameter));
this.generation = generation;
rotation = Math.random() * 2 * Math.PI;
sensorInput = new double[brainLength];
public void iterate(double timeInterval)
updateSensorInput(timeInterval);
sensorLength = p2pw((60 + diameter));
for(int i = 0; i < sensorInput.length; i++)
sensorInput[i] = Math.copySign(sigmoid(sensorInput[i]), sensorInput[i]);
applyOutputs(timeInterval);
public void updateSensorCoords()
rotation %= (2 * Math.PI);
while (rotation < 0) rotation += (2 * Math.PI);
double rightSensorRotation = rotation + sensorAngle;
double leftSensorRotation = rotation - sensorAngle;
while (rightSensorRotation < 0) rightSensorRotation += 2*Math.PI;
while (leftSensorRotation < 0) leftSensorRotation += 2*Math.PI;
rightSensorRotation %= (2 * Math.PI);
leftSensorRotation %= (2 * Math.PI);
double leftSensorTempAngle = -rightSensorRotation;
double rightSensorTempAngle = -leftSensorRotation;
double midSensorTempAngle = -rotation;
leftSensorX = locationX + (sensorLength * Math.cos(leftSensorTempAngle));
leftSensorY = locationY + (sensorLength * Math.sin(leftSensorTempAngle));
midSensorX = locationX + (weaponLength * Math.cos(midSensorTempAngle));
midSensorY = locationY + (weaponLength * Math.sin(midSensorTempAngle));
rightSensorX = locationX + (sensorLength * Math.cos(rightSensorTempAngle));
rightSensorY = locationY + (sensorLength * Math.sin(rightSensorTempAngle));
mouthSensorX = locationX + (diameter/2.0 * Math.cos(midSensorTempAngle));
mouthSensorY = locationY + (diameter/2.0 * Math.sin(midSensorTempAngle));
defaultSensorValues = new double[brainLength];
inputNeurons = new double[brainLength];
hidLayer1 = new double[brainLength];
hidLayer2 = new double[brainLength];
outputNeurons = new double[brainLength];
brain = new Axon[3][brainLength][brainLength];
inputToLayer1Axons = new Axon[inputNeurons.length][hidLayer1.length];
layer1ToLayer2Axons = new Axon[hidLayer1.length][hidLayer2.length];
layer2ToOutputAxons = new Axon[hidLayer2.length][outputNeurons.length];
for(int lay1 = 0; lay1 < inputNeurons.length; lay1++)
for(int lay2 = 0; lay2 < hidLayer1.length; lay2++)
inputToLayer1Axons[lay1][lay2] = new Axon();
for(int lay1 = 0; lay1 < hidLayer1.length; lay1++)
for(int lay2 = 0; lay2 < hidLayer2.length; lay2++)
layer1ToLayer2Axons[lay1][lay2] = new Axon();
for(int lay1 = 0; lay1 < hidLayer2.length; lay1++)
for(int lay2 = 0; lay2 < outputNeurons.length; lay2++)
layer2ToOutputAxons[lay1][lay2] = new Axon();
inputToLayer1Axons = brain[0];
layer1ToLayer2Axons = brain[1];
layer2ToOutputAxons = brain[2];
if(Math.random() < superMutateChance)
for(int lay1 = 0; lay1 < inputNeurons.length; lay1++)
for(int lay2 = 0; lay2 < hidLayer1.length; lay2++)
if(superMutate) inputToLayer1Axons[lay1][lay2].weight += ( (Math.random()*2 - 1) * 20 * mutateFactor);
else if(Math.random() < mutateChance)
if(Math.random() * 4 - 2 < inputToLayer1Axons[lay1][lay2].weight) inputToLayer1Axons[lay1][lay2].weight -= Math.random() * mutateFactor;
else inputToLayer1Axons[lay1][lay2].weight += Math.random() * mutateFactor;
for(int lay1 = 0; lay1 < hidLayer1.length; lay1++)
for(int lay2 = 0; lay2 < hidLayer2.length; lay2++)
if(superMutate) layer1ToLayer2Axons[lay1][lay2].weight += ( (Math.random()*2 - 1) * 20 * mutateFactor);
else if(Math.random() < mutateChance)
if(Math.random() * 4 - 2 < layer1ToLayer2Axons[lay1][lay2].weight) layer1ToLayer2Axons[lay1][lay2].weight -= Math.random() * mutateFactor;
else layer1ToLayer2Axons[lay1][lay2].weight += Math.random() * mutateFactor;
for(int lay1 = 0; lay1 < hidLayer2.length; lay1++)
for(int lay2 = 0; lay2 < outputNeurons.length; lay2++)
if(superMutate) layer2ToOutputAxons[lay1][lay2].weight += ( (Math.random()*2 - 1) * 20 * mutateFactor);
else if(Math.random() < mutateChance)
if(Math.random() * 4 - 2 < layer2ToOutputAxons[lay1][lay2].weight) layer2ToOutputAxons[lay1][lay2].weight -= Math.random() * mutateFactor;
else layer2ToOutputAxons[lay1][lay2].weight += Math.random() * mutateFactor;
nColorR = Math.min(255, nColorR);
nColorR = Math.max(0, nColorR);
nColorG = Math.min(255, nColorG);
nColorG = Math.max(0, nColorG);
nColorB = Math.min(255, nColorB);
nColorB = Math.max(0, nColorB);
for(int i = 0; i < sensorInput.length; i++)
public void updateBrain()
inputNeurons = sensorInput;
for(int i = 0; i < hidLayer1.length; i++)
for(int j = 0; j < inputNeurons.length; j++)
hidLayer1[i] += inputNeurons[j] * inputToLayer1Axons[j][i].weight;
hidLayer1[i] = sigmoid(hidLayer1[i]);
for(int i = 0; i < hidLayer2.length; i++)
for(int j = 0; j < hidLayer1.length; j++)
hidLayer2[i] += hidLayer1[j] * layer1ToLayer2Axons[j][i].weight;
hidLayer2[i] = sigmoid(hidLayer2[i]);
for(int i = 0; i < outputNeurons.length; i++)
for(int j = 0; j < hidLayer2.length; j++)
outputNeurons[i] += hidLayer2[j] * layer2ToOutputAxons[j][i].weight;
outputNeurons[i] = sigmoid(outputNeurons[i]);
public void updateSensorInput(double timeInterval)
int[] leftTile, midTile, rightTile, mouthTile;
leftTile = findTileCoordsAt(leftSensorX, leftSensorY);
midTile = findTileCoordsAt(midSensorX, midSensorY);
rightTile = findTileCoordsAt(rightSensorX, rightSensorY);
mouthTile = findTileCoordsAt(mouthSensorX, mouthSensorY);
leftSensorColor = tiles[leftTile[0]][leftTile[1]].colorH;
rightSensorColor = tiles[rightTile[0]][rightTile[1]].colorH;
mouthSensorColor = tiles[mouthTile[0]][mouthTile[1]].colorH;
sensorInput[0] = tiles[leftTile[0]][leftTile[1]].food / 10.0 - 5.0;
sensorInput[1] = tiles[midTile[0]][midTile[1]].food / 10.0 - 5.0;
sensorInput[2] = tiles[mouthTile[0]][mouthTile[1]].food / 10.0 - 5.0;
sensorInput[3] = tiles[rightTile[0]][rightTile[1]].food / 10.0 - 5.0;
sensorInput[4] = size / 100.0 - 3.0;
Object[] nearestCreatureData = findClosestCreatureData(this);
nearestCreature = (Creature) nearestCreatureData[0];
numCreaturesWithin10 = (int) nearestCreatureData[1];
if(nearestCreature != null)
distToNearest = distBtCoords(locationX, locationY, nearestCreature.locationX, nearestCreature.locationY);
distToNearest /= tileSize;
sensorInput[5] = distToNearest - 4.0;
colorDifferenceToNearest = 0;
colorDifferenceToNearest += Math.abs(colorR - nearestCreature.colorR);
colorDifferenceToNearest += Math.abs(colorG - nearestCreature.colorG);
colorDifferenceToNearest += Math.abs(colorB - nearestCreature.colorB);
sensorInput[6] = (colorDifferenceToNearest / 50.0) - 4;
tempAngle = findAngleChange(locationX, locationY, nearestCreature.locationX, nearestCreature.locationY);
if(tempAngle < 0) tempAngle += 360;
tempAngle = tempAngle - (rotation / Math.PI * 180.0);
if(tempAngle > 180) tempAngle -= 360;
if(tempAngle < -180) tempAngle += 360;
sensorInput[7] = angleToNearest = tempAngle;
sensorInput[9] = numCreaturesWithin10 - 4;
public void keepCreatureInBounds()
locationX = Math.min(locationX, tiles[tileResL-1][tileResW-1].x + tileSize);
locationY = Math.min(locationY, tiles[tileResL-1][tileResW-1].y + tileSize);
locationX = Math.max(locationX, tiles[0][0].x);
locationY = Math.max(locationY, tiles[0][0].y);
public void applyOutputs(double timeInterval)
forwardVel = outputNeurons[0];
rotationVel = outputNeurons[1];
eatRate = 25 * Math.abs(outputNeurons[2]) / (1 + 2*Math.abs(forwardVel));
if(outputNeurons[3] > 0) attack = true;
weaponLength = (int) ((outputNeurons[5] / 2 + 0.5) * 50 + (diameter));
rotation += rotationVel * timeInterval / 2;
double tempAngle = -(rotation % (2*Math.PI));
double deltaPos = 100 * forwardVel * timeInterval;
locationX += (deltaPos * Math.cos(tempAngle));
locationY += (deltaPos * Math.sin(tempAngle));
double decayModifier = 1.5;
sizeDecay = (size / 200.0) * decayModifier;
fitnessDecay = (fitness / 300.0) * decayModifier;
eatRateDecay = (eatRate / 15.0) * decayModifier;
rotationDecay = (Math.abs(rotationVel) * timeInterval) * decayModifier;
forwardDecay = 40 * (Math.abs(forwardVel) * timeInterval) * decayModifier;
if(attack) attackDecay = 1.0 * decayModifier;
decayRate = (sizeDecay + fitnessDecay + eatRateDecay + rotationDecay + forwardDecay + attackDecay);
size -= (decayRate * timeInterval);
energyChange = eatRate - decayRate;
totalDecayed += (decayRate * timeInterval);
public double requestEat(double timeInterval)
return eatRate * timeInterval;
public void allowEat(double amount)
if(amount == 0.0) energyChange -= eatRate;
public boolean requestBirth()
if(outputNeurons[4] > 0.0) return true;
public Axon[][][] giveBirth()
Axon[][][] giveBrain = new Axon[3][brainLength][brainLength];
giveBrain[0] = inputToLayer1Axons;
giveBrain[1] = layer1ToLayer2Axons;
giveBrain[2] = layer2ToOutputAxons;
private double findStartAngle(double x1, double y1, double x2, double y2) {
return Math.atan2(-yDiff, xDiff) * 180.0 / Math.PI;
private double findAngleChange(double x1, double y1, double x2, double y2)
double target = findStartAngle(x1, y1, x2, y2);
if (Math.abs(a)>Math.abs(b))
if (Math.abs(b) > Math.abs(y))
if(Math.abs(a)>Math.abs(y))
this.weight = Math.random() * 2.0 - 1;
public Axon(double weight)
public int tileSize = 4 * p2pw(1500) / tileResW;
public void iterateTiles()
for(int x = 0; x < tileResL; x++)
for(int y = 0; y < tileResW; y++)
tiles[y][x].regenerateTileFood();
for(int x = 0; x < tileResL; x++)
for(int y = 0; y < tileResW; y++)
tiles[y][x] = new Tile(x * tileSize, y * tileSize, tileSize, count, x, y, waterTiles[x][y]);
for(int x = 0; x < tiles.length; x++)
for(int y = 0; y < tiles.length; y++)
colorMode(PConstants.HSB, 360, 100, 100);
stroke(tiles[y][x].colorH, tiles[y][x].colorS, tiles[y][x].colorV - 10);
fill(tiles[y][x].colorH, tiles[y][x].colorS, tiles[y][x].colorV);
rect(tiles[y][x].x, tiles[y][x].y, tileSize, tileSize);
colorMode(PConstants.RGB, 255, 255, 255);
public int[] findTileCoordsAt(double xCoord, double yCoord)
spot[0] = ((int) yCoord) / tileSize;
spot[1] = ((int) xCoord) / tileSize;
if(spot[0] > tileResW - 1) spot[0] = tileResW - 1;
if(spot[1] > tileResL - 1) spot[1] = tileResL - 1;
if(spot[0] < 0) spot[0] = 0;
if(spot[1] < 0) spot[1] = 0;
public Tile findTileAt(double xCoord, double yCoord)
int spotY = ((int) yCoord) / tileSize;
int spotX = ((int) xCoord) / tileSize;
if(spotY > tileResW - 1) spotY = 0;
if(spotX > tileResL - 1) spotX = 0;
spotY = Math.max(0, spotY);
spotX = Math.max(0, spotX);
return tiles[spotY][spotX];
public Tile checkTileClick(int mX, int mY)
for(int x = 0; x < tiles.length; x++)
for(int y = 0; y < tiles.length; y++)
if (tiles[y][x].x < mX && mX <= tiles[y][x].x + tileSize)
if (tiles[y][x].y < mY && mY <= tiles[y][x].y + tileSize)
MenuPath.path = MenuPath.TILE;
public boolean checkWorldClick(int mX, int mY)
if(tiles[0][0].x <= mX && mX <= tiles[tileResW-1][tileResL-1].x + tileSize &&
tiles[0][0].y <= mY && mY <= tiles[tileResW-1][tileResL-1].y + tileSize)
return tiles[0][0].x + (int)(Math.random() * (tiles[tileResL-1][tileResW-1].x - tiles[0][0].x));
return tiles[0][0].y + (int)(Math.random() * (tiles[tileResL-1][tileResW-1].y - tiles[0][0].y));
public int x, y, xIndex, yIndex, tileSize, maxFood, tileNumber, deadCooldown, cooldownThreshold,
public double food, regenValue;
public Tile(int x, int y, int tileSize, int tileNumber, int xIndex, int yIndex, boolean water)
this.tileNumber = tileNumber;
this.tileSize = tileSize;
if(!water) food = 60 + (int) (Math.random() * 40);
else colorH = (int)(food * 0.7 + 50);
if(water) regenValue = 0;
else regenValue = TILE_REGEN_RATE;
cooldownThreshold = TILE_COOLDOWN_THRESH;
public void regenerateTileFood()
colorH = (int) (food * 0.7 + 50);
if(deadCooldown < cooldownThreshold) return;
else deadCooldown = cooldownThreshold;
public void resetCooldown()