Code viewer for World: CELLS WITH MIND

// Cloned by SinfulSalad on 25 Jun 2018 from World "Cells" by SinfulSalad 
// Please leave this clone trail here.
 

// ==== Starter World ===============================================================================================
// (c) Ancient Brain Ltd. All rights reserved.
// This code is only for use on the Ancient Brain site.
// This code may be freely copied and edited by anyone on the Ancient Brain site.
// This code may not be copied, re-published or used on any other website.
// To include a run of this code on another website, see the "Embed code" links provided on the Ancient Brain site.
// ==================================================================================================================
	
	

// ===================================================================================================================
// === 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 

const 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 = new Audio ( 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;
    else if  (r < 0) r = 0;
 
    var b = ((num >> 8) & 0x00FF) + amt;
 
    if (b > 255) b = 255;
    else if  (b < 0) b = 0;
 
    var g = (num & 0x0000FF) + amt;
 
    if (g > 255) g = 255;
    else if (g < 0) g = 0;
 
    return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
  
}


//calculate the unsigned value of the angle between two 2D vector
function 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 array
function 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[ range
function randomintAtoB ( A, B )			 
{
 return  ( Math.floor ( randomfloatAtoB ( A, B ) ) );
}


//---------------end useful functions-----------------------------------------------


//----------------------------- MINDS ------------------------------------------


/*
//Here is the template to create your own mind
//(An example of a basic mind is coded just below)

//There is only one order the mind can give to the world :
//which cell should try to infect which other cell, and when.
//To give this order, the mind have to define the attribute team.ordersArray
//and the function team.ACTION() (more on that below).

//Any kind of function or attribute can be defined here
//When you are done, don't forget to push your mind into the mindArray
//(you can do it right below the basic mind example)

var newMind = function(team) {
    
    //this attribute is necessary for the mind to work properly
    //each order is a tuple containing two elements :
    //1. the cell the mind wants to split
    //2. the cell the mind is trying to infect
    //ex : team.ordersArray.push([team.cells[2], team.possibleTargets[12]]);
    //note : each mind can technically acces any cell on the battlefield because
    //there are no private attributes, but it is only fair that it should only
    //control the cells that belong to it's own team.
    team.ordersArray = [];
    
    team.newAttribute;
    
    team.newFunction = function(){
        
    };
    
    
    //This function is necessary for the mind to work properly
    //This is the function called by the World when it is waiting for
    //an input from the mind.
    //It should contain any kind of code using the functions and attributes created
    //above, in order to generate orders. The orders will then be executed by the World.
    //ACTION() should not modify any of the variables contained in the World,
    //just read them to generate orders accordingly.
    team.ACTION = function() {
        
        //code
        
        return team.ordersArray;
    };
    
};

*/

//TODO : BUILD YOUR OWN MIND HERE

var mind1 = function(team) {
    
    team.ordersArray = [];
    
    //there is no strategy here, each cell acts on its own, and choses
    //its target randomly among enemies and neutral cell
    team.individualMind = function(){
        var rand;
        
        for (var i = 0 ; i<team.cells.length ; i++)
        {
            
            //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 cells
            if (team.cells[i].type == "huge")
            {
                //console.log("I want to split huge things");
 
                rand = (team.cells[i].maxVal - team.cells[i].value);
                if (randomintAtoB(0, rand) === 0 && team.cells[i].team.possibleTargets.length !== 0)
                {
                    //console.log("IMSPLITTINGTHIS")
                    rand = randomintAtoB(0, team.cells[i].team.possibleTargets.length);
                    team.ordersArray.push([team.cells[i], team.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 cells
            else if ((team.cells[i].type == "small" || team.cells[i].type == "avg") && team.cells[i].value == team.cells[i].maxVal)
            {
                if (team.cells[i].team.possibleTargets.length !== 0)
                {
                    rand = randomintAtoB(0, team.cells[i].team.possibleTargets.length);
                    team.ordersArray.push([team.cells[i], team.possibleTargets[rand]]);
                }
            }
        }
    
    };
    
    team.ACTION = function(){
        team.ordersArray = [];
        team.individualMind();
        //console.log(team.ordersArray.length);
        return team.ordersArray;  
    };
    
};



//TODO : ADD YOUR MIND HERE
var mindArray = [];
mindArray.push(mind1);











function World() 
{
    
    //Speed of the simulation when it starts
    var SPEED = 1;
    
    var timer = 0;
    
    var canvas;
    var context;
    
    //define the middle of the drawing area
    var centerX;
    var centerY;
    
    //contains all the cells from the simulation
    var allCells = [];
    
    //contains all the "ships" that travels from one cell to an other 
    var allShips = [];
    
    //array containing all the teams
    var allTeams = [];
    
    
    
     //constructor of Team objects
    function Team(name, color, startingCell)
    {
        var team = Object.create(teamPrototype);
        team.name = name;
        team.score = 0;
        
        //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;
        startingCell.team = team;
        team.cells.push(startingCell);
        
        //create an array that contains only the cells that need to be conquered
        team.possibleTargets = allCells.slice();
        team.possibleTargets.splice(getIndexOfItem(startingCell, team.possibleTargets), 1);
        
        allTeams.push(team);
        
        return team;
    }
    
    
    var teamPrototype = {
        //no functions to be defined at the moment
    };
    
    
    
    
    //TODO???
    function getTeamByColor(color)
    {
        for (var i = 0 ; i<allTeams.length ; i++)
        {
            if (allTeams[i].color == color) return allTeams[i];
            else 
            {
                console.log("geTeamByColor failed");
                return undefined;
            }
        }
    }
    
    
    
    
    
    
    //constructor of cells
    function Cell(x,y, type, color = '#555555', destination = allCells, team = undefined)
    {
        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.production = cell.maxVal/60 ;
        
        //the default color of each cell is '#555555'
        cell.color = color;
        cell.team = team;
        
        //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 newShip = Cell(this.x, this.y, "small", this.color, allShips, this.team);
            newShip.value = Math.floor(this.value/2);
            newShip.dest = dest;
            newShip.direction;
            newShip.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 enough
            if (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
                allShips.splice(getIndexOfItem(this, allShips), 1);
                
                //calculate the resulting value of the destination
                if (this.dest.color != this.color) this.dest.value -= this.value;
                else this.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 accordingly
                if (this.dest.value < 0)
                {
                    this.dest.value = -this.dest.value;
                    
                    if (this.dest.color !== "#555555")
                    {
                        //delete cell from original team.cell
                        this.dest.team.cells.splice(getIndexOfItem(this.dest,this.dest.team.cells), 1);
                        //add cell to original team.target
                        this.dest.team.possibleTargets.push(this.dest);
                    }

                    this.dest.color = this.color;
                    this.dest.team = this.team;
                    //delete cell from new team.target
                    this.team.possibleTargets.splice(getIndexOfItem(this.dest, this.team.possibleTargets), 1);
                    //add cell to new team.cell
                    this.team.cells.push(this.dest);
                }
                
                if (this.dest.value > this.dest.maxVal) this.dest.value = this.dest.maxVal;
                
            }
        },
        
        
        grow : function(){

            if (this.color !== "#555555" && this.value < this.maxVal)
            {
                this.value += this.production;
                if (this.value > this.maxVal) this.value = this.maxVal;

            }
            
        }
        
    };
    
    
    
    //------------------------------- INIT FUNCTION ----------------------------
    
     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 = allCells.length;
        for (var i=0 ; i<length ; i++)
        {
            allCells[i].draw();
            allCells[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 teams
    function initTeams()
    {
        Team("pink", "#FE2E9A", allCells[0]);
        Team("orange", "#FE642E", allCells[14]);
        Team("blue", "#58FAF4", allCells[15]);
        Team("green", "#80FF00", allCells[16]);
        Team("unknown", "#104FFF", allCells[56]);
        
        for (var i = 0 ; i<allTeams.length ; i++) mind1(allTeams[i]);
    }
    
    
    
     //-------------------------- UPDATE FUNCTIONS ------------------------------
    
    
    
    function updateChart()
    {

        for (var i = 0 ; i<allTeams.length ; i++) allTeams[i].score = 0;
        
        
        for (i = 0 ; i < allCells.length ; i++)
        {
           if (allCells[i].team !== undefined)
           {
               allCells[i].team.score += allCells[i].value;
           }
        }
        
        for (i = 0 ; i < allShips.length ; i++) allShips[i].team.score += allShips[i].value;
        }
    
    
    //use the data contained in the variable 'score', and use it to draw a
    //cute little chart on the left side of the screen
    function drawChart()
    {
        context.font = "12px Arial";
        
        //this position is NOT relative to centerX and centerY
        //these are the absolute coordinates of the canvas
        var posX = 25;
        var 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, 150, canvas.height); 
        
        var widthEach = Math.floor(120/allTeams.length);
        
        for (var i =0 ; i<allTeams.length ; i++)
        {
            context.fillStyle = allTeams[i].color;
            context.fillRect(posX, posY-allTeams[i].score/6, widthEach, allTeams[i].score/6);
            context.fillStyle = "#FFFFFF";
            context.fillText(Math.floor(allTeams[i].score), posX+8, posY-5-allTeams[i].score/6);
            
            posX += widthEach;
        }
        
        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;
    }
    
    
    //changes the position of all the ships, one step closer to their destination
    function move()
    {
        for (var i=0 ; i<allShips.length ; i++)
        {
            allShips[i].x += SPEED*Math.cos(allShips[i].direction);
            allShips[i].y += SPEED*Math.sin(allShips[i].direction);
            allShips[i].infect();
        }
    }
    
    //this function redraws everything at each new step
    function update()
    {
        updateBackground();
        updateChart();
        drawChart();
        
        for (var i=0 ; i<allCells.length ; i++)
        {
            if (timer%Math.floor(1000/(33*2*SPEED)) === 0 && timer !== 0)
            {
                allCells[i].grow();
            }
            
            allCells[i].draw();
        }
        
        if (timer%Math.floor(1000/(33*2*SPEED)) === 0 && timer !== 0)
        {
            TAKE_ACTION();
        }
        
        for (i=0 ; i<allShips.length ; i++)
        {
            allShips[i].draw();
        }
        
        
    }
    
    
    
    //------------------------- RUN FUNCTIONS --------------------------------------


    function TAKE_ACTION()
    {
        var ordersArray = [];
        
        for (var i = 0 ; i<allTeams.length ; i++)
        {
            ordersArray = ordersArray.concat(allTeams[i].ACTION());
            //console.log(ordersArray.length);
        }
        
        for (i = 0 ; i<ordersArray.length ; i++)
        {
            ordersArray[i][0].split(ordersArray[i][1]);
        }
        
    }



    this.newRun = function() 
    {
        threeworld.init (  SKYCOLOR  );
        initButtons();
        initMusic();
        
        canvas = threeworld.canvas;
        initContext();
        centerX = 30+canvas.width / 2;
        centerY = canvas.height / 2;
        
        initStage();
        initTeams();
        
        
        
    };
    
     
    this.nextStep = function()
    {
        centerX = 30+canvas.width / 2;
        centerY = canvas.height / 2;
        
        move();
        update();
        
        timer++;

        console.log(allShips.length);
        
    };
    
    
    this.endRun = function()
    {
        
    };
    
    
       //------------------------- USER INTERFACE ---------------------------------
    //this part is there to create the 3 buttons that change the speed of the simulation
    
    function 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;}    
        
    function fastSpeed() {SPEED = 2;}
    
    function fasterSpeed() {SPEED = 4;}

}