// Cloned by Enhanced on 27 Jun 2018 from World "Cloned Cells" by SinfulSalad // Please leave this clone trail here.// Cloned by SinfulSalad on 25 Jun 2018 from World "Cells" by SinfulSalad // Please leave this clone trail here.// ============ Cells ===============================================================================================// 'Cells' is a program that displays 4 team of cells trying to infect each other, until total domination is// accomplished. The rules are simple : once a cell is possessed by a team, it starts generating value, until a maximum// value is reached. Each cell can, at any given time, split and send one of its halves to another cell to infect it.// If the value of this half is higher than the value of the cell it is trying to infect, then the infection will// succeed. The team that wins is the team that controls all cells// ========== Possible Improvements ==================================================================================// The score and the chart is designed to keep track of only 4 teams. Maybe code one that works for any number// of teams.//// With this version, each cell behaves individually, there is no 'teamwork' strategy. Maybe implement a// a 'hivemind' AI that can control all its cells.//// One could code a version of this where the user could actually control one of the team// ===================================================================================================================// === Start of tweaker's box ======================================================================================== // ===================================================================================================================// The easiest things to modify are in this box.// You should be able to change things in this box without being a JavaScript programmer.// Go ahead and change some of these. What's the worst that could happen?// These 3 have default values, so this section is optional:
AB.clockTick =33;// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps =20000;// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep =1000;// Take screenshot on this step. (All resources should have finished loading.) Default 50.const SKYCOLOR ="#000000";// this usage will be of color as a string, not as a number //You can import your own music hereconst MUSIC_BACK ='/uploads/sinfulsalad/Forest.mp3';// ===================================================================================================================// === End of tweaker's box ==========================================================================================// ===================================================================================================================// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.// ------------------------------ MUSIC ----------------------------------------var backmusic =newAudio( MUSIC_BACK );function initMusic(){
backmusic.loop =true;// loop forever
backmusic.play();
$("#w2m_audio1").html(" <img class=audiobutton onclick='backmusic.play();' width=25 src='images/audio.on.1.png' > ");
$("#w2m_audio2").html(" <img class=audiobutton onclick='backmusic.pause();' width=25 src='images/audio.off.1.png' > ");}//---------------------------USEFUL FUNCTIONS-----------------------------------//take the hexadecimal value of a color 'col', and make it brighter or darker//depending on 'amt'function lightenDarkenColor(col, amt){var usePound =false;if(col[0]=="#"){
col = col.slice(1);
usePound =true;}var num = parseInt(col,16);var r =(num >>16)+ amt;if(r >255) r =255;elseif(r <0) r =0;var b =((num >>8)&0x00FF)+ amt;if(b >255) b =255;elseif(b <0) b =0;var g =(num &0x0000FF)+ amt;if(g >255) g =255;elseif(g <0) g =0;return(usePound?"#":"")+(g |(b <<8)|(r <<16)).toString(16);}//calculate the unsigned value of the angle between two 2D vectorfunction angle(vect1, vect2){
rst = vect1.x * vect2.x + vect1.y * vect2.y;
rst /=(Math.sqrt(Math.pow(vect1.x,2)+Math.pow(vect1.y,2)))*(Math.sqrt(Math.pow(vect2.x,2)+Math.pow(vect2.y,2)));
rst =Math.acos(rst);return rst;}//Search for an intem in an array, and returns its index//If the item is present muliple times in the array, it will return the first//occurence of that item//Returns -1 if the item is missing from the arrayfunction getIndexOfItem(item, array){var index =0;while(array[index]!= item && index < array.length) index++;if(index == array.length) index =-1;return index;}//generate a random float between [ A ; B [function randomfloatAtoB ( A, B ){return( A +(Math.random()*(B-A)));}//returns a whole number in from the [A,B[ rangefunction randomintAtoB ( A, B ){return(Math.floor ( randomfloatAtoB ( A, B )));}//---------------end useful functions-----------------------------------------------functionWorld(){//Speed of the simulation when it startsvar SPEED =1;var timer =0;var canvas;var context;//define the middle of the drawing areavar centerX;var centerY;//contains all the cells from the simulationvar cells =[];//contains all the "ships" that travels from one cell to an other var cellShips =[];//4 instances of the object Teamvar teamPink;var teamGreen;var teamBlue;var teamOrange;//will keep track of the score of each teamvar score;//------------------------ CLASSES DECLARATION -----------------------------//constructor of Team objectsfunctionTeam(name, color, startingCell){var team =Object.create(teamPrototype);
team.name = name;//hexadecimal value of the color of its cells
team.color = color;//contains all the cells possessed by the team
team.cells =[];//give a starting sell to the team, and paint it the right color
startingCell.color = team.color;
team.cells.push(startingCell);//create an array that contains only the cells that need to be conquered
team.possibleTargets = cells.slice();
team.possibleTargets.splice(getIndexOfItem(startingCell, team.possibleTargets),1);return team;}var teamPrototype ={//no functions to be defined at the moment}function getTeamByColor(color){switch(color){case"pink":return teamPink;case"green":return teamGreen;case"blue":return teamBlue;case"orange":return teamOrange;case"#FE2E9A":return teamPink;case"#80FF00":return teamGreen;case"#58FAF4":return teamBlue;case"#FE642E":return teamOrange;default:
console.log("it failed");returnundefined;}}//constructor of cellsfunctionCell(x,y, type, color ='#555555', destination = cells){var cell =Object.create(cellPrototype);//the position (x,y) described here are relative to centerX and centerY,//which define the center of the drawing area
cell.x = x;
cell.y = y;//the type of a cell is either 'small', 'average(avg)' or 'huge'//its type changes its radius, its maximum capacity, and its production//rate
cell.type = type;switch(type){case"small":
cell.radius =15;
cell.value =5;break;case"avg":
cell.radius =25;
cell.value =10;break;default:
cell.radius =50;
cell.value =20;break;}
cell.maxVal = cell.value*5;
cell.rate =Math.floor(30/cell.maxVal/AB.clockTick/SPEED*1000);//the default color of each cell is '#555555'
cell.color = color;//when the cell is created, it is saved in an array.//the default array is 'cells[]' (which contains all cells)
destination.push(cell);return cell;}var cellPrototype ={//draw the cell on the canvas ://at the right position//with the color of its team//with its current value written in its center
draw :function(){
context.beginPath();
context.arc(centerX+this.x, centerY+this.y,this.radius,0,2*Math.PI,false);
context.fillStyle =this.color;
context.fill();
context.lineWidth =2;
context.fillStyle ='#000000';
strokeRGB = lightenDarkenColor(this.color,+100);
context.strokeStyle = strokeRGB;
context.fillText(Math.floor(this.value), centerX+this.x-1, centerY+this.y+5);
context.stroke();},//create cells symmetric to this cells (horizontal AND vertical symmetry),//add them to the array 'cells', and draw them on the canvas.
drawSymmetry :function(){var newcell =Cell(-this.x,this.y,this.type,this.color);
newcell.draw();
newcell =Cell(-this.x,-this.y,this.type,this.color);
newcell.draw();
newcell =Cell(this.x,-this.y,this.type,this.color);
newcell.draw();},//any number of cells, located evenly around this cell.//you can change what type of cell you want to produce,//and how far away they should be from this cell
drawSurround :function(nbCells, radius, type){var newCell;for(var i=0; i <2*Math.PI-0.01; i +=2*Math.PI/nbCells){
newCell =Cell(this.x+radius*Math.cos(i),this.y+radius*Math.sin(i), type);
newCell.draw();}},//calculate the direction a 'ship' needs to move towards in order to//reach its destination
setDirection :function(cell){var vect0 ={
x :1,
y :0};var trajectory ={
x : cell.x-this.x,
y : cell.y-this.y
};var ang = angle(vect0, trajectory);if(cell.y <this.y) ang =-ang;this.direction = ang;},//create a 'ship' from this cell, give half the value of the cell to//the 'ship', and give it a destination (a cell)
split :function(dest){var newCellShip =Cell(this.x,this.y,"small",this.color, cellShips);
newCellShip.value =Math.floor(this.value/2);
newCellShip.dest = dest;
newCellShip.direction;
newCellShip.setDirection(dest);this.value =Math.floor(this.value/2);},//when this 'ship' has reached its destination, it transfers its value//to the destination cell. If the value of the ship is superior to the//value of the destination, the infection will work, and the destination//will become a part of the ship's team//the arrays of the concerned teams must be updated
infect :function(){//if close enoughif(Math.abs(this.x-this.dest.x)<2*SPEED &&Math.abs(this.y-this.dest.y)<2*SPEED){//remove the ship from the array of all ships
cellShips.splice(getIndexOfItem(this, cellShips),1);//calculate the resulting value of the destinationif(this.dest.color !=this.color)this.dest.value -=this.value;elsethis.dest.value +=this.value;//if the ship was strong enough to bring the value of the destination//below zero, then change the team of the destination//and update the arrays accordinglyif(this.dest.value <0){this.dest.value =-this.dest.value;if(this.dest.color !=="#555555"){//delete cell from original team.cell
getTeamByColor(this.dest.color).cells.splice(getIndexOfItem(this.dest,getTeamByColor(this.dest.color).cells),1);//add cell to original team.target
getTeamByColor(this.dest.color).possibleTargets.push(this.dest);}this.dest.color =this.color;//delete cell from new team.target
getTeamByColor(this.color).possibleTargets.splice(getIndexOfItem(this.dest,getTeamByColor(this.dest.color).possibleTargets),1);//add cell to new team.cell
getTeamByColor(this.color).cells.push(this.dest);}if(this.dest.value >this.dest.maxVal)this.dest.value =this.dest.maxVal;}},//describe how each cell behave//there is no hivemind in this version of the world
individualMind :function(){var rand;//the more a huge cell is close to its max value, the more it is//likely to split and infect other cells//all targets are chosen randomly between enemies or neutral cellsif(this.type =="huge"){
rand =(this.maxVal -this.value);if(randomintAtoB(0, rand)===0&& getTeamByColor(this.color).possibleTargets.length !==0){
rand = randomintAtoB(0, getTeamByColor(this.color).possibleTargets.length);this.split(getTeamByColor(this.color).possibleTargets[rand]);}}//whenever a small or average cell reaches it's max value, it will split//all targets are chosen randomly between enemies or neutral cellselseif((this.type =="small"||this.type =="avg")&&this.value ==this.maxVal){if(getTeamByColor(this.color).possibleTargets.length !==0){
rand = randomintAtoB(0, getTeamByColor(this.color).possibleTargets.length);this.split(getTeamByColor(this.color).possibleTargets[rand]);}}}};//-------------------------- INIT FUNCTIONS --------------------------------function initContext(){
context = canvas.getContext('2d');
context.font ="bold 15px Arial";
context.textAlign ='center';}//Use the cell constructor to create the 'battlefield'//remember that all cells created are added to the array 'cells'function initStage(){var newCell =Cell(200,200,"huge");
newCell.drawSurround(6,90,"small");
newCell =Cell(500,70,"huge");
newCell =Cell(350,80,"avg");
newCell.drawSurround(3,60,"small");
newCell =Cell(90*Math.cos(Math.PI/8),90*Math.sin(Math.PI/8),"avg");
newCell =Cell(90*Math.cos(3*Math.PI/8),90*Math.sin(3*Math.PI/8),"avg");var length = cells.length;for(var i=0; i<length ; i++){
cells[i].draw();
cells[i].drawSymmetry();}
newCell =Cell(0,0,"huge");
newCell.draw();
newCell =Cell(250,0,"avg");
newCell.draw();
newCell =Cell(-250,0,"avg");
newCell.draw();
newCell =Cell(0,250,"avg");
newCell.draw();
newCell =Cell(0,-250,"avg");
newCell.draw();
newCell =Cell(160,0,"small");
newCell.draw();
newCell =Cell(-160,0,"small");
newCell.draw();
newCell =Cell(0,160,"small");
newCell.draw();
newCell =Cell(0,-160,"small");
newCell.draw();}//give a name, a color, and a starting cell to all teamsfunction initTeams(){
teamPink =Team("pink","#FE2E9A", cells[0]);
teamOrange =Team("orange","#FE642E", cells[14]);
teamBlue =Team("blue","#58FAF4", cells[15]);
teamGreen =Team("green","#80FF00", cells[16]);}//initialise the score variablefunction initChart(){
score ={
pink :20,
orange :20,
blue :20,
green :20}}//-------------------------- UPDATE FUNCTIONS ------------------------------function updateChart(){
score.pink =0;
score.green =0;
score.blue =0;
score.orange =0;//TODO : not optimisedfor(var i =0; i < cells.length ; i++){switch(cells[i].color){case"#FE2E9A":
score.pink += cells[i].value;break;case"#FE642E":
score.orange += cells[i].value;break;case"#58FAF4":
score.blue += cells[i].value;break;case"#80FF00":
score.green += cells[i].value;break;}}for(i =0; i < cellShips.length ; i++){switch(cellShips[i].color){case"#FE2E9A":
score.pink += cellShips[i].value;break;case"#FE642E":
score.orange += cellShips[i].value;break;case"#58FAF4":
score.blue += cellShips[i].value;break;case"#80FF00":
score.green += cellShips[i].value;break;}}}//use the data contained in the variable 'score', and use it to draw a//cute little chart on the left side of the screenfunction drawChart(){
context.font ="12px Arial";//this position is NOT relative to centerX and centerY//these are the absolute coordinates of the canvas
posX =25;
posY =645;//starts by drawing a black rectangle, so that the data is not drawn on//top of the data of the previous step
context.fillStyle ="#000000";
context.fillRect(posX-10,0,130, canvas.height);
context.fillStyle ="#FE2E9A";
context.fillRect(posX, posY-score.pink/6,30, score.pink/6);
context.fillStyle ="#FFFFFF";
context.fillText(Math.floor(score.pink), posX+15, posY-5-score.pink/6);
context.fillStyle ="#FE642E";
context.fillRect(posX+30, posY-score.orange/6,30, score.orange/6);
context.fillStyle ="#FFFFFF";
context.fillText(Math.floor(score.orange), posX+45, posY-5-score.orange/6);
context.fillStyle ="#58FAF4";
context.fillRect(posX+60, posY-score.blue/6,30, score.blue/6);
context.fillStyle ="#FFFFFF";
context.fillText(Math.floor(score.blue), posX+75, posY-5-score.blue/6);
context.fillStyle ="#80FF00";
context.fillRect(posX+90, posY-score.green/6,30, score.green/6);
context.fillStyle ="#FFFFFF";
context.fillText(Math.floor(score.green), posX+105, posY-5-score.green/6);
context.font ="bold 15px Arial";}//at each step, draw a black/transparent rectangle on top of everything,//which creates that fading effect behind the 'ships'function updateBackground(){
context.globalAlpha =0.05;
context.fillRect(0,0, canvas.width, canvas.height);
context.globalAlpha =1;}//increment the value of all cells that are possessed by each team//the incrementation depends on the rate of each cellfunction grow(){for(var i=0; i<cells.length ; i++){if(cells[i].color !=="#555555"&& cells[i].value < cells[i].maxVal){if(timer%Math.floor(cells[i].rate)===0&& timer !==0){
cells[i].value++;
cells[i].individualMind();}}elseif(cells[i].value == cells[i].maxVal) cells[i].individualMind();}}//changes the position of all the ships, one step closer to their destinationfunction move(){for(var i=0; i<cellShips.length ; i++){
cellShips[i].x += SPEED*Math.cos(cellShips[i].direction);
cellShips[i].y += SPEED*Math.sin(cellShips[i].direction);
cellShips[i].infect();}}//this function redraws everything at each new stepfunction update(){
updateBackground();
updateChart();
drawChart();for(var i=0; i<cells.length ; i++){
cells[i].draw();}for(i=0; i<cellShips.length ; i++){
cellShips[i].draw();}}//-------------------------- RUN FUNCTIONS ---------------------------------this.newRun =function(){
threeworld.init ( SKYCOLOR );
initButtons();
initMusic();
canvas = threeworld.canvas;
initContext();
centerX =30+canvas.width /2;
centerY = canvas.height /2;
initStage();
initTeams();
initChart();};this.nextStep =function(){
grow();
move();
update();
timer++;};this.endRun =function(){};//------------------------- USER INTERFACE ---------------------------------//this part is there to create the 3 buttons that change the speed of the simulationfunction initButtons(){
s ="<p><button id='regularButton'>x1</button> <button id='fasterButton'>\> x2 \></button> <button id='f a s t erButton'>\>\> x4 \>\></button></p>";
$("#user_span1").html( s );
document.getElementById("regularButton").addEventListener("click", regularSpeed);
document.getElementById("fasterButton").addEventListener("click", fastSpeed);
document.getElementById("f a s t erButton").addEventListener("click", fasterSpeed);}function regularSpeed(){
SPEED =1;for(var i =0; i < cells.length ; i++){
cells[i].rate =Math.floor(30/cells[i].maxVal/AB.clockTick/SPEED*1000);}}function fastSpeed(){
SPEED =2;for(var i =0; i < cells.length ; i++){
cells[i].rate =Math.floor(30/cells[i].maxVal/AB.clockTick/SPEED*1000);}}function fasterSpeed(){
SPEED =4;for(var i =0; i < cells.length ; i++){
cells[i].rate =Math.floor(30/cells[i].maxVal/AB.clockTick/SPEED*1000);}}}