/* !ideas! add an inherited trait to splitting ?connect processes like moving speed and resting to comsumption of mass? add abilty to quickly run canvas off screen without drawing spectate focus jumps around on low raidus cells also can crash on low raidus cells maybe give every cell a unique id for tracking and sorting think of other traits for cells to pass on add in a way to control how many 1/dist there are as a cell trait. add in regions to the game space so that cells only have to think about objects nearby make an array that lists the cells in order of radius for drawing order give each cell a team ID cells will not dividing into or chase the same team ID let that ID evolve so that 5% of the time the ID will shift to a new ID use cell name as the team ID AI visualization for focused cell? cell division trait: reuse for determining how often a cell will divide range, lower size limit? have food pellet grow until hitting a cap of 5 like in the game */ var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; document.body.appendChild(canvas); window.onresize = function(event) { ctx.canvas.width = window.innerWidth; ctx.canvas.height = window.innerHeight; }; //game bounds var container = new function() { this.foodID = 0; this.cycle = 0; //cycle keeps track of the interations of the game loop this.pause = false; this.spectate = true; this.visualize = true; this.info = false; this.cellFocus = 0; this.nextFocus = function() { container.cellFocus++; if (container.cellFocus > cell.length - 1) container.cellFocus = 0; }; this.CameraX = 0; this.CameraY = 0; this.width = 10000; // size of the map //10000 this.height = 8000; // size of the map //6000 this.friction = 0.97; //closer to 1 means higher speeds and less turning this.speed = 0.1; //controls thrust per cycle from cells this.divideSpeed = 20; //speed that cell mopve at after a division at 30 it lets them move about 1000 befor ethey slow down (at speed = 0.09 and friciton =0.97) this.scale = 0.01; //the zoom this.scaleGoal = 0.2; //what the scale will smoothly move towards this.totalFood = (this.width * this.height) * 0.0000100; //0.0000150 this.totalVirus = (this.width * this.height) * 0.0000002; //0.0000003 this.totalCells = (this.width * this.height) * 0.0000005; //0.0000010 this.nameMutationRate = 0.1; }; //container.scaleGraphPaper(); //small scale testing setup comment this out for full scale /* container.width = 3600; container.height = 2600; container.scaleGoal = 0.34; container.spectate = false; container.totalFood = 500; container.totalVirus = 6; container.totalCells = 8 */ /* //huge scale testing setup comment this out for full scale container.width = 40000; container.height = 20000; container.scaleGoal = 0.1; container.spectate = false; container.totalFood = 2000; container.totalVirus = 20; container.totalCells = 20; */ var gui = new dat.GUI(); //gui.close(); gui.add(container, 'info'); gui.add(container, 'pause'); scontroller = gui.add(container, 'spectate'); scontroller.onFinishChange(function(value) { container.scaleGoal = 0.14; }); gui.add(container, 'scaleGoal', 0.1, 1); //gui.add(container, 'nextFocus'); var guiSettings = gui.addFolder('environment'); guiSettings.add(container, 'speed', 0.01, 1).step(0.01); guiSettings.add(container, 'friction', 0.9, .999).step(0.001); guiSettings.add(container, 'width', 100, 20000).step(100); guiSettings.add(container, 'height', 100, 20000).step(100); var fcontroller = guiSettings.add(container, 'totalFood', 0, 3000).step(1); fcontroller.onFinishChange(function(value) { if (food.length > container.totalFood) food.splice(value); }); var vcontroller = guiSettings.add(container, 'totalVirus', 0, 100).step(1); vcontroller.onFinishChange(function(value) { if (virus.length > container.totalVirus) virus.splice(value); }); var ccontroller = guiSettings.add(container, 'totalCells', 1, 200).step(1); ccontroller.onFinishChange(function(value) { //if (cell.length>container.totalCells) cell.length = value; if (cell.length > container.totalCells) cell.splice(value); }); //repeating check to spawn food up the max value function spawner() { if (food.length < container.totalFood) { pushFood(Math.random() * container.width, Math.random() * container.height); } if (cell.length < container.totalCells) { newCellRandom(); } if (virus.length < container.totalVirus) { pushVirus(); } } function randomName() { var text = ""; var possible = "abcdefghijklmnopqrstuvwxyz"; //capitalized first letter text += possible.charAt(Math.floor(Math.random() * possible.length)); text = text.toUpperCase(); //add a random number of other letters for (var k = 0; k < 3 + Math.round(Math.random() * 10); k++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } text[0] = text.charAt(0).toUpperCase() return text; } //replace a char in a string function also a chance to just add a letter or remove a letter at the replacement site String.prototype.mutateName = function(index) { var possible = "abcdefghijklmnopqrstuvwxyz"; return this.substr(0, index + Math.round(2 * (Math.random() - 0.5))) + possible.charAt(Math.floor(Math.random() * possible.length)) + this.substr(index + 1); } var cell = []; function pushCell(x, y, Vx, Vy, r, name, color, division, inertia, seekFood, seekCell, fleeCell, fleeVirus) { cell.push({ alive: true, Vx: Vx, //Velocity in x direction on spawn Vy: Vy, //Velocity in Y direction on spawn zone: { x: 0, y: 0 }, dir: 0, //direction cell is moving rDraw: 0, //makes cell radius changes look smooth stroke: "", //cell outline color is always a bit darker then fill color //traits that evolve / are passed on with division x: x, //Math.random() * container.width, y: y, //Math.random() * container.height, r: r, //30, //30 name: name, color: color, ///rainbow in is color library file textColor: "", division: division, //100 if cell raidus goes above this division occurs inertia: inertia, //0.000002 higher means less turning, lower is more jerky seekFood: seekFood, //1 1/dist^3 seekCell: seekCell, //30 1/dist^3 fleeCell: fleeCell, //8000 1/dist^4 fleeVirus: fleeVirus, //5 1/dist^4 }); //set cell stroke to be a bit darker than fill cell[cell.length - 1].stroke = ColorLuminance(cell[cell.length - 1].color, -0.1); //set text color to be a color that readable compared to the body color cell[cell.length - 1].textColor = invertCssColor(cell[cell.length - 1].color); } function newCellRandom() { pushCell(Math.random() * container.width, //x Math.random() * container.height, //y 0, 0, 30, //r randomNameAustin(), //name rainbow(32, Math.floor(Math.random() * 32)), //color Math.random() * 500, //division 200 Math.random() * 100000, //inertia 100000/10000000 = 0.1 //50-50 chance of having negative traits (0.5 - Math.random()) * 20000, //seekFood (0.5 - Math.random()) * 20000, //seekCell (0.5 - Math.random()) * 20000, //fleeCell (0.5 - Math.random()) * 20000 //fleeVirus /* //all traits have the "best" number sign Math.random() * 10000, //seekFood Math.random() * 10000, //seekCell Math.random() * -10000, //fleeCell Math.random() * -10000 //fleeVirus */ ); } for (var i = 0; i < container.totalCells; i++) { newCellRandom() } //this.speed = 0.08 * (1 + 150 / this.r + 4000 / this.r / this.r) //if (this.r > 50) this.r = 0.99995 * this.r - 0.00000005 * this.r * this.r function cellDivision(i, dir) { //original cell knock back //cell[i].Vx = -Math.cos(dir); //cell[i].Vy = -Math.sin(dir); //pushCell(x,y,Vx,Vy,r,name,color,division,inertia,seekFood,seekCell,fleeCell,fleeVirus) pushCell(cell[i].x + cell[i].r * Math.cos(dir), cell[i].y + cell[i].r * Math.sin(dir), container.divideSpeed * Math.cos(dir), container.divideSpeed * Math.sin(dir), cell[i].r, cell[i].name, colorMutate(cell[i].color), cell[i].division, cell[i].inertia, cell[i].seekFood, cell[i].seekCell, cell[i].fleeCell, cell[i].fleeVirus ); //mutations values shift by up to +-10% cell[i].division += cell[i].division * (0.5 - Math.random()) * 0.20; cell[i].inertia += cell[i].inertia * (0.5 - Math.random()) * 0.20; cell[i].seekFood += cell[i].seekFood * (0.5 - Math.random()) * 0.20; cell[i].seekCell += cell[i].seekCell * (0.5 - Math.random()) * 0.20; cell[i].fleeCell += cell[i].fleeCell * (0.5 - Math.random()) * 0.20; cell[i].fleeVirus += cell[i].fleeVirus * (0.5 - Math.random()) * 0.20; //10% chance to mutate name if (Math.random() < container.nameMutationRate) { cell[i].name = cell[i].name.mutateName(Math.floor(Math.random() * cell[i].name.length)); } //cell[i].color = cell[i].color.mutateColor(); //cell[i].stroke = ColorLuminance(cell[i].color, -0.1) } function cellMove() { var cellLength = cell.length; for (var i = 0; i < cellLength; i++) { //determine zone cell[i].zone.x = Math.floor(cell[i].x / 1000); cell[i].zone.y = Math.floor(cell[i].y / 1000); //AI //inertia range: good around (0.0001) no negatives (evolving range 1 to 0.00001) var inertia = cell[i].inertia / 10000000; var vector = { x: inertia * Math.cos(cell[i].dir), y: inertia * Math.sin(cell[i].dir) }; var length = food.length; //move towards food for (var j = 0; j < length; j++) { var dx = food[j].x - (cell[i].x); var dy = food[j].y - (cell[i].y); var dist = Math.sqrt(dx * dx + dy * dy) - cell[i].r; dx = cell[i].seekFood * dx / dist / (dist * dist * cell[i].r); dy = cell[i].seekFood * dy / dist / (dist * dist * cell[i].r); vector.x += dx; vector.y += dy; } length = virus.length; //avoid viruses for (j = 0; j < length; j++) { if (virus[j].r < cell[i].r) { var dy = virus[j].y - (cell[i].y); var dx = virus[j].x - (cell[i].x); var dist = Math.sqrt(dx * dx + dy * dy) - cell[i].r; dx = cell[i].fleeVirus * dx / dist / (dist * dist * dist * dist) * 1000; dy = cell[i].fleeVirus * dy / dist / (dist * dist * dist * dist) * 1000; vector.x += dx; vector.y += dy; } } length = cell.length; //chase and run from cells for (j = 0; j < length; j++) { //if (i != j) { //START AI if (cell[i].name != cell[j].name) { var dx = cell[j].x - cell[i].x; var dy = cell[j].y - cell[i].y; var dist = Math.sqrt(dx * dx + dy * dy) - cell[i].r; //seekCell if (cell[i].r > cell[j].r * 1.12) { //chase smaller cells dx = dx / dist / (dist * dist * dist); dy = dy / dist / (dist * dist * dist); sizeWeight = cell[j].r * cell[j].r; //larger cells are more valuable and eaiser to catch vector.x += cell[i].seekCell * dx * sizeWeight; vector.y += cell[i].seekCell * dy * sizeWeight; //check for division target if (cell[i].seekCell > 0 && cell[j].fleeCell < 0 && //if you chase and they run cell[i].r * 0.65 > cell[j].r * 1.12 && // if the cell is big enough to eat cell[j].r * 1.5 > cell[i].r * 0.65 && //if the cell is large enough to be worth eatting dist < 250 && dist > 150 && // if the cell isn't too close or too far cell[i].r * 0.65 > 40) { //if you are big enough to split and not starve //There is a cost to division so less than 0.707 cell[i].r *= 0.65; //radius*1/2^(0.5) because of volume. var dirX = (cell[j].x + 10 * cell[j].Vx) - cell[i].x; var dirY = (cell[j].y + 10 * cell[j].Vy) - cell[i].y; cellDivision(i, Math.atan2(dirY, dirX)); } //fleeCell } else if (cell[i].r < cell[j].r) { //run from bigger cells dx = dx / dist / (dist * dist * dist * dist) * 1000; dy = dy / dist / (dist * dist * dist * dist) * 1000; vector.x += cell[i].fleeCell * dx; vector.y += cell[i].fleeCell * dy; } //END AI } //eat smaller cells if (cell[i].r > cell[j].r * 1.1) { var x = cell[i].x - cell[j].x; var y = cell[i].y - cell[j].y; var d = Math.sqrt(x * x + y * y); if (d < cell[i].r) { //Volume+volume = volume PIr^2+PIr^2 =PIr^r root(r^2+r^2)=new r cell[i].r = Math.sqrt(cell[i].r * cell[i].r + cell[j].r * cell[j].r) cell[j].alive = false; } } } //direction cell[i].dir = Math.atan2(vector.y, vector.x); //move var speed = container.speed * (2 + 50 / cell[i].r) // + 2000 / cell[i].r / cell[i].r) cell[i].Vx = container.friction * cell[i].Vx + speed * Math.cos(cell[i].dir); cell[i].Vy = container.friction * cell[i].Vy + speed * Math.sin(cell[i].dir); cell[i].x += cell[i].Vx; cell[i].y += cell[i].Vy; //walls if (cell[i].x < 0) { cell[i].x = 0; cell[i].Vx = 0; } else if (cell[i].x > container.width) { cell[i].x = container.width; cell[i].Vx = 0; } if (cell[i].y < 0) { cell[i].y = 0; cell[i].Vy = 0; } else if (cell[i].y > container.height) { cell[i].y = container.height; cell[i].Vy = 0; } //mass loss cell[i].r *= 0.99999; //starve? if (cell[i].r < 20) { //dont' switch r to r draw here or script will make too many new cells cell[i].alive = false; for (var k = 0; k < 3; k++) { //spawn some food where cell dies pushFood(cell[i].x + 25 - Math.random() * 50, cell[i].y + 25 - Math.random() * 50); }; } /* //divide? else if (cell[i].seekCell < 0 && cell[i].r > cell[i].division) { //There is a cost to division so less than 0.707 cell[i].r *= 0.65; //radius*1/2^(0.5) because of volume. cellDivision(i, -cell[i].dir); } */ //touching virus j = virus.length while (j--) { if (cell[i].r > virus[j].r) { var x = virus[j].x - cell[i].x var y = virus[j].y - cell[i].y var d = Math.sqrt(x * x + y * y); if (d < cell[i].r) { var spawnCells = 0; //for big cells divide into 10 cells with larger raidus if (cell[i].r > 95) { // 94 = root(10 x 30^2) = root(spawnCells x (new cellRadius)^2) if (cell[i].r > 300) { if (cell[i].r > 400) cell[i].r = 400; //cell radius can't be too big //relocate virus on hitting large cells virus[j].x = Math.random() * container.width; virus[j].y = Math.random() * container.height; virus[j].r = 80 + Math.random() * 25; } spawnCells = 10; cell[i].r /= Math.sqrt(spawnCells); //relocate virus for large cell collisions } else { // for smaller cells dividing into up to 10 cells spawnCells = Math.round(Math.sqrt(cell[i].r * cell[i].r / (30 * 30))); cell[i].r = 30; } for (var k = 0; k < spawnCells; k++) { //spawn mutated versions of mother cell cellDivision(i, Math.random() * 6.28); } } } } } var k = cell.length; while (k--) { //die (needs to come at the end of the while loop) if (!cell[k].alive) { cell.splice(k, 1); } } } var virus = []; function pushVirus() { virus.push({ x: Math.random() * container.width, y: Math.random() * container.height, r: 80 + Math.random() * 25, }); } //spawn viruses at start for (var i = 0; i < container.totalVirus; i++) { pushVirus(); } var food = []; //build a 10x10 array to track zones /* var zone = []; for (var i = 0; i < 10; i++) { zone.push([]); } for (var j = 0; j < 10; j++) { for (var i = 0; i < 10; i++) { zone[i].push([]); } } */ //new food cells are pushed into a zone //find food by each food's id and remove them from the array when eaten //https://www.w3schools.com/jsref/jsref_indexof_array.asp //use index of to find the index needed to delete function pushFood(x, y) { food.push({ id: container.foodID, //IDs are used to that food can be removed from the zone tracker after it is eaten x: x, y: y, color: rainbow(32, Math.floor(Math.random() * 32)), r: 12, rShift: Math.random() * 2 * Math.PI, angle: Math.random() * 2 * Math.PI }); //add food to food zone tracker /* var i = food.length - 1; var zX = Math.floor(food[i].x / 1000); var zY = Math.floor(food[i].y / 1000); //add 3 array eleemnt at a time zone[zX][zY].push(food[i].id,food[i].x,food[i].y); container.foodID += 3; */ } //spawn food at start for (var z = 0; z < container.totalFood; z++) { pushFood(Math.random() * container.width, Math.random() * container.height); } //check if player is near food function touchingFood() { var length = cell.length; for (var j = 0; j < length; j++) { var i = food.length; while (i--) { var dX = food[i].x - cell[j].x; var dY = food[i].y - cell[j].y; var dist = Math.sqrt(dX * dX + dY * dY); if (dist < cell[j].r - 2 * food[i].r) { cell[j].r = Math.sqrt(cell[j].r * cell[j].r + food[i].r * food[i].r); //remove food from zone array /* var zoneX = Math.floor(food[i].x / 1000); var zoneY = Math.floor(food[i].y / 1000); var index = zone[zoneX][zoneY].indexOf(food[i].id) zone[zoneX][zoneY].splice(index,3); */ //remove from food array food.splice(i, 1); //player.waves += ((Math.random() > 0.5) ? 1 : -1); } else if (dist < cell[j].r + food[i].r * 2) { var y = cell[j].y - food[i].y; var x = cell[j].x - food[i].x; var angle = Math.atan2(y, x); var speed = 1 + Math.sqrt(cell[j].Vx * cell[j].Vx + cell[j].Vy * cell[j].Vy); food[i].x += speed * Math.cos(angle); // + cell[j].Vx; food[i].y += speed * Math.sin(angle); // + cell[j].Vy; } } } } function draw() { //clear screen ctx.clearRect(0, 0, canvas.width, canvas.height); //scale and translate //exponential function that controls how scale depends on cell raidus if (container.spectate) { container.scaleGoal = 0.83 * Math.exp(-0.001941 * cell[container.cellFocus].r); } if (container.scaleGoal < 0.1) container.scaleGoal = 0.1; //max zoom container.scale += (container.scaleGoal - container.scale) * 0.01; //smooth zooming container.CameraX += (cell[container.cellFocus].x - container.CameraX) * 0.03; //smooth translation X container.CameraY += (cell[container.cellFocus].y - container.CameraY) * 0.03; //smooth translation Y //transforms and rotates the canvas camera ctx.save(); ctx.scale(container.scale, container.scale); if (container.spectate) { ctx.translate(window.innerWidth * 0.5 / container.scale - container.CameraX, window.innerHeight * 0.5 / container.scale - container.CameraY); } else { ctx.translate(50, 50); } //draw graph paper background var size = 50; var length = Math.ceil(container.width / size) + 1; ctx.lineWidth = 0.5; ctx.strokeStyle = '#707070'; ctx.beginPath(); for (var i = 0; i < length; i++) { ctx.moveTo(i * size, 0); ctx.lineTo(i * size, container.height); } var length = Math.ceil(container.height / size) + 1; for (var i = 0; i < length; i++) { ctx.moveTo(0, i * size); ctx.lineTo(container.width, i * size); } ctx.stroke(); //draw food length = food.length; var root3p2 = Math.sqrt(3) / 2 for (var i = 0; i < length; i++) { ctx.fillStyle = food[i].color; if (container.scale > 0.4) { //if zoomed in draw hexagons ctx.save(); food[i].angle += 0.007; //food rotation speed food[i].r = 12 + 1 * Math.sin(container.cycle * 0.08 + food[i].rShift); ctx.translate(food[i].x, food[i].y); ctx.rotate(food[i].angle); ctx.translate(-food[i].x, -food[i].y); ctx.beginPath(); ctx.lineTo(food[i].x + 0.5 * food[i].r, food[i].y + root3p2 * food[i].r); ctx.lineTo(food[i].x + food[i].r, food[i].y); ctx.lineTo(food[i].x + 0.5 * food[i].r, food[i].y - root3p2 * food[i].r); ctx.lineTo(food[i].x - 0.5 * food[i].r, food[i].y - root3p2 * food[i].r); ctx.lineTo(food[i].x - food[i].r, food[i].y); ctx.lineTo(food[i].x - 0.5 * food[i].r, food[i].y + root3p2 * food[i].r); ctx.closePath(); ctx.fill(); ctx.restore(); } else { // if zoomed out just draw circles ctx.beginPath(); ctx.arc(food[i].x, food[i].y, food[i].r, 0, 2 * Math.PI); ctx.fill(); } } //draw cells ctx.globalAlpha = 0.9 ctx.textAlign = "center"; length = cell.length; for (var i = length - 1; i > -1; i--) { cell[i].rDraw += (cell[i].r - cell[i].rDraw) * 0.07; //controls cell raidus zoom ctx.lineWidth = 12; ctx.fillStyle = cell[i].color; ctx.beginPath(); ctx.arc(cell[i].x, cell[i].y, cell[i].rDraw, 0, 2 * Math.PI); ctx.fill(); ctx.strokeStyle = cell[i].stroke; ctx.stroke(); //cell names ctx.lineWidth = 2; //ctx.font = "bold " + Math.floor(24 + cell[i].rDraw / 8) + "px Arial"; ctx.font = "bold " + Math.floor(6 + cell[i].rDraw / 5) + "px Arial"; //ctx.fillStyle = 'white'; //ctx.fillStyle = invertCssColor(cell[i].color); ctx.fillStyle = cell[i].textColor; ctx.fillText(cell[i].name, cell[i].x, cell[i].y + 5); } //draw viruses length = virus.length; for (var i = 0; i < length; i++) { ctx.fillStyle = 'lime'; ctx.lineWidth = 7; ctx.strokeStyle = '#00e600'; ctx.beginPath(); if (container.scale > 0.4) { //if zoomed in draw spikes var amp = 2.5; var sineCount = 40; //for loop is sinecount*4 //angle = 360/(sineCount*4) * Math.PI / 180 * j; for (var j = 0; j < 160; j++) { var angle = 0.03927 * j; //0.0448798951 * j; var x = Math.round(virus[i].x + (virus[i].r + amp * Math.sin(sineCount * angle)) * Math.cos(angle)); var y = Math.round(virus[i].y + (virus[i].r + amp * Math.sin(sineCount * angle)) * Math.sin(angle)); ctx.lineTo(x, y); } } else { ctx.arc(virus[i].x, virus[i].y, virus[i].r, 0, 2 * Math.PI); } ctx.closePath(); ctx.fill(); ctx.stroke(); } ctx.globalAlpha = 1; //info for all on screen if (container.info) { var length = cell.length; ctx.textAlign = "left"; ctx.font = "15px Arial"; for (var i = 0; i < length; i++) { ctx.save(); ctx.translate(cell[i].x, cell[i].y - cell[i].r - 3); ctx.scale(1 / container.scale, 1 / container.scale); var x = -60 ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; ctx.fillRect(x - 5, -5, 130, -110); ctx.fillStyle = 'white'; ctx.fillText("seekFood = " + Math.round(cell[i].seekFood), x, -100); ctx.fillText("seekCell = " + Math.round(cell[i].seekCell), x, -85); ctx.fillText("fleeCell = " + Math.round(cell[i].fleeCell), x, -70); ctx.fillText("fleeVirus = " + Math.round(cell[i].fleeVirus), x, -55); ctx.fillText("inertia = " + Math.round(cell[i].inertia), x, -40); ctx.fillText("division = " + Math.round(cell[i].division), x, -25); ctx.fillText("radius = " + Math.round(cell[i].r), x, -10); ctx.restore(); } } ctx.restore(); //undo translate and scale effects //player info /* if (container.spectate) { ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; //ctx.fillRect(8, window.innerHeight - 5, 120, -60); ctx.fillRect(8, window.innerHeight - 5, 120, -110); ctx.fillStyle = 'white'; ctx.textAlign = "left"; ctx.font = "15px Arial"; ctx.fillText("seekFood = " + Math.round(cell[container.cellFocus].seekFood), 11, window.innerHeight - 100); ctx.fillText("seekCell = " + Math.round(cell[container.cellFocus].seekCell), 11, window.innerHeight - 85); ctx.fillText("fleeCell = " + Math.round(cell[container.cellFocus].fleeCell), 11, window.innerHeight - 70); ctx.fillText("fleeVirus = " + Math.round(cell[container.cellFocus].fleeVirus), 11, window.innerHeight - 55); ctx.fillText("inertia = " + Math.round(cell[container.cellFocus].inertia), 11, window.innerHeight - 40); ctx.fillText("division = " + Math.round(cell[container.cellFocus].division), 11, window.innerHeight - 25); ctx.fillText("radius = " + Math.round(cell[container.cellFocus].r), 11, window.innerHeight - 10); //leader board ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; ctx.fillRect(window.innerWidth - 5, 25, -200, 320); ctx.fillStyle = 'white'; ctx.textAlign = "center"; ctx.font = "bold 26px Arial"; ctx.fillText("Leaderboard", window.innerWidth - 105, 55); ctx.font = "bold 19px Arial"; var leaderLength = 10; if (cell.length < 10) leaderLength = cell.length; for (var i = 0; i < leaderLength; i++) { ctx.fillText(i + 1 + ". " + cell[i].name, window.innerWidth - 105, 88 + i * 27); } } */ } function cycle() { container.cycle++; draw(); if (!container.pause) { touchingFood(); cellMove(); spawner(); } requestAnimationFrame(cycle); } requestAnimationFrame(cycle); var myVar = setInterval(function() { sortCells(); }, 6000); function sortCells() { // sort by value cell.sort(function(a, b) { if (a.r > b.r) return -1; //flip the signs on the 1s to reverse order if (a.r < b.r) return 1; return 0; }); } function colorMutate(color) { var index = Math.ceil(Math.random() * 6) var char = color.charAt(index); if (Math.random() > 0.5) { char = shiftCharUp(char); char = shiftCharUp(char); } else if (Math.random() > 0.5) { char = shiftCharDown(char); char = shiftCharDown(char); } return color.substr(0, index) + char + color.substr(index + 1); } function shiftCharUp(char) { switch (char) { case "0": char = '1' break; case "1": char = '2' break; case "2": char = '3' break; case "3": char = '4' break; case "4": char = '5' break; case "5": char = '6' break; case "6": char = '7' break; case "7": char = '8' break; case "8": char = '9' break; case "9": char = 'a' break; case "a": char = 'b' break; case "b": char = 'c' break; case "c": char = 'd' break; case "d": char = 'e' break; default: char = 'f' } return char; } function shiftCharDown(char) { switch (char) { case "2": char = '1' break; case "3": char = '2' break; case "4": char = '3' break; case "5": char = '4' break; case "6": char = '5' break; case "7": char = '6' break; case "8": char = '7' break; case "9": char = '8' break; case "a": char = '9' break; case "b": char = 'a' break; case "c": char = 'b' break; case "d": char = 'c' break; case "e": char = 'd' break; case "f": char = 'e' break; default: char = '0' } return char; } function randomNameAustin(){ var name= ['Arron','Adam','Adrian','Alan','Alex','Albert','Alec','Alvin','Armando','Anderson','Andres','Andrew','Anthony','Anton','Antonio','Arnold','Alejandro','Ben','Boe','Blake','Byron','Bjorn','Bart','Bin','Boof','Bartholemeu','Birtha','Alexa','Erin','Charles','Charlie','Chalk','Chalupa','Candice','Chatauqua','Channing','Cody','Colonel','Drew','Dendrite','Dawn','Dorothy','Dibble','Dolby','Drake','Effigy','Ephemeral','Endive','Eloquent','Fat','Full','Fun','Fandrew','Guy','Gory','Gland','Gargantuant','Glop','Gope','Hilt','Hunter','Huntington','Igloo','Ink','Interior','Jack','Jalopy','Jaunt','Jubilance','Kool','Kwan','Knox','Krook','Lewis','Lawn','Lorpe','Link','Latch','Man','Mourn','Map','Maude','Max','Nando','Null','Nix','Ophelia','Operation','Prawn','Paul','Patricia']; var adj= ['Arcadian', 'Baleful', 'Bellicose', 'Bilious', 'Boorish', 'Calamitous', 'Caustic', 'Cerulean', 'Comely', 'Concomitant', 'Contumacious', 'Corpulent', 'Crapulous', 'Defamatory', 'Didactic', 'Dilatory', 'Dowdy', 'Efficacious', 'Effulgent', 'Egregious', 'Endemic', 'Equanimous', 'Execrable', 'Fastidious', 'Feckless', 'Fecund', 'Friable', 'Fulsome', 'Garrulous', 'Guileless', 'Gustatory', 'Heuristic', 'Histrionic', 'Hubristic', 'Incendiary', 'Insidious', 'Insolent', 'Intransigent', 'Inveterate', 'Invidious', 'Irksome', 'Jejune', 'Jocular', 'Judicious', 'Lachrymose', 'Limpid', 'Loquacious', 'Luminous', 'Mannered', 'Mendacious', 'Meretricious', 'Minatory', 'Mordant', 'Munificent', 'Nefarious', 'Noxious', 'Obtuse', 'Parsimonious', 'Pendulous', 'Pernicious', 'Pervasive', 'Petulant', 'Platitudinous', 'Precipitate', 'Propitious', 'Puckish', 'Querulous', 'Quiescent', 'Rebarbative', 'Recalcitrant', 'Redolent', 'Rhadamanthine', 'Risible', 'Ruminative', 'Sagacious', 'Salubrious', 'Sartorial', 'Sclerotic', 'Serpentine', 'Spasmodic', 'Strident', 'Taciturn', 'Tenacious', 'Tremulous', 'Trenchant', 'Turbulent', 'Turgid', 'Ubiquitous', 'Uxorious', 'Verdant', 'Voluble', 'Voracious', 'Wheedling', 'Withering', 'Zealous', ]; var adjselec = adj[Math.floor(Math.random() * adj.length)]; var nameselec = name[Math.floor(Math.random() * name.length)]; return adjselec +' ' + nameselec; } /* function AI() { //moves towards the sum of all distance wieghted food pointing vectors var inertia = 0.000002 // a larger coefficient makes the bot less likely to change direction var vector = { x: inertia * Math.cos(player.dir), y: inertia * Math.sin(player.dir) }; if (container.visual) { ctx.strokeStyle = 'rgba(0, 255, 0, 0.3)';//'green'; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(0.5 * window.innerWidth, 0.5 * window.innerHeight); ctx.lineTo(300000000 * vector.x + 0.5 * window.innerWidth, 300000000 * vector.y + 0.5 * window.innerHeight); ctx.stroke(); ctx.lineWidth = 1; ctx.strokeStyle = 'black'; } var length = food.length; for (var i = 0; i < length; i++) { var dy = food[i].y - (player.y); var dx = food[i].x - (player.x); var dist = Math.sqrt(dx * dx + dy * dy) - player.r;; dx = dx / dist / (dist * dist * dist) // x/dist is a directionless unit vector dy = dy / dist / (dist * dist * dist) // 1/(dist*dist*dist) weights the closer vectors higher vector.x += dx; vector.y += dy; //vector visualization if (dist < 800 && container.visual) { ctx.beginPath(); ctx.moveTo(0.5 * window.innerWidth, 0.5 * window.innerHeight); ctx.lineTo(300000000 * dx + 0.5 * window.innerWidth, 300000000 * dy + 0.5 * window.innerHeight); ctx.stroke(); } } length = virus.length; for (var i = 0; i < length; i++) { if (virus[i].r < player.r) { var dy = virus[i].y - (player.y); var dx = virus[i].x - (player.x); var dist = Math.sqrt(dx * dx + dy * dy) - player.r; dx = 10 * dx / dist / (dist * dist * dist * dist) // x/dist is a directionless unit vector dy = 10 * dy / dist / (dist * dist * dist * dist) // 1/(dist*dist*dist*dist) wieghts the closer vectors vector.x -= dx; vector.y -= dy; if (dist < 800 && container.visual) { ctx.strokeStyle = 'red'; ctx.beginPath(); ctx.moveTo(0.5 * window.innerWidth, 0.5 * window.innerHeight); ctx.lineTo(-300000000 * dx + 0.5 * window.innerWidth, -300000000 * dy + 0.5 * window.innerHeight); ctx.stroke(); } } } //find the angle of the vector player.dir = Math.atan2(vector.y, vector.x); player.move(); }*/

!