UNPKG

cacatoo

Version:

Building, exploring, and sharing spatially structured models

741 lines (629 loc) 28.3 kB
<html> <script src="../scripts/cacatoo.js"></script> <!-- Include cacatoo library (compiled with rollup) --> <script src="../scripts/all.js"></script> <!-- Load other packages --> <link rel="shortcut icon" type="image/jpg" href="../../patterns/cacatoo.png"/> <link rel="stylesheet" href="../../style/cacatoo.css"> <!-- Set style sheet --> <style> body { font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; background-color: rgb(203, 84, 160); margin: 0px; font-size: 13px; overflow-x: hidden; } /* The button itself */ button { background-color: #8438a0; border-radius: 10px 10px 10px 10px; color: white; border: 0px solid rgb(0, 0, 0); padding: 7px; font-size: 16px; margin: 10px; width: 128px; } .gameheader{ font-size: 20px; color:white; width:100%; padding:3px; text-align: center; } .gamecanvas{ padding: 0px; margin: 0px; text-align:center; } .canvas { border: '1px solid red'; display: inline-block; } input[type='checkbox'] { width:30px; height:30px; } input[type='checkbox']:checked { } .skilltree1{ height: 700; padding: 20px; margin-left:70px; width:16%; color: white; font-size:16px; float: left; border-radius: 15px; background-color: #800080AA; } @media (max-width: 1000px) { .skilltree1 { height: 700; padding: 20px; margin-left:70px; margin-top:70px; width:30%; color: white; font-size:16px; float: left; border-radius: 15px; background-color: #800080AA; } } .skilltree2{ height: 700; padding: 20px; margin-right:70px; width:16%; color: black; font-size:16px; float: right; border-radius: 15px; background-color: #e0a709AA; } @media (max-width: 1000px) { .skilltree2 { height: 700 ; padding: 20px; margin-right:70px; margin-top:70px; width:30%; color: white; font-size:16px; float: right; border-radius: 15px; background-color: #e0a709AA; } } .grid-holder { margin: 0px; display: inline-block; } .number-input { display: inline-block; } .number-input button { display: inline-block; background-color: #bb00ff; color: white; border: none; width:100px; padding: 10px 0px; margin:10px; height:70px; font-size: 24px; cursor: pointer; } .number-input button:focus { outline: none; } .number-input input { width: 60px; text-align: center; font-size: 24px; margin: 0 10px; padding: 5px; } </style> <script> /*-----------------------Start user-defined code ---------------------*/ let sim; var pressedKeys = {}; window.onkeyup = function(e) { pressedKeys[e.keyCode] = false; } window.onkeydown = function(e) { pressedKeys[e.keyCode] = true; } var start_totalfood = 50000 var food_purple = 0 var food_gold = 0 var maxpoints = 32 var ant_2_points = maxpoints var ant_1_points = maxpoints // controllable things var ant_1_speed = 1 var ant_2_speed = 1 var ant_1_number = 100 var ant_2_number = 100 var ant_1_pherprod = 1 var ant_2_pherprod = 1 var ant_1_pherprod_food = 1 var ant_2_pherprod_food = 1 var ant_1_strength = 1 var ant_2_strength = 1 var ant_1_sense = 1 var ant_2_sense = 1 var max_foraging_time = 600 var boundary = true var ff = false /** * function cacatoo() contains all the user-defined parts of a cacatoo-model. Configuration, update rules, what is displayed or plotted, etc. It's all here. */ function cacatoo() { let simconfig = { title: "Pheromone trails", // The name of your cacatoo-simulation description: "A center for ants?!", // And a description if you wish maxtime: 1000000, // How many time steps the model continues to run // (note, the onscreen FPS may drop below 60 fps when using fast mode, although many more timesteps may be handled per second) ncol: 100, // Number of columns (width of your grid) nrow: 100, // Number of rows (height of your grid) scale: 7, // Scale of the grid (nxn pixels per grid point) sleep: 0, seed: new Date().getTime(), wrap: [false,false], wrapreflect: 0.6, bgcolour:'#00000011', graph_update: 25, graph_interval: 5 } // FLOCKCONFIG EXAMPLE let flockconfig = { // Flock parameters num_boids: 0, // Starting number of boids (flocking individuals) shape: 'ant', // Shape of the boids drawn (options: bird, arrow, line, rect, dot, ant) max_speed: 1, // Maximum velocity of boids max_force: 0.5, // Maximum steering force applied to boids (separation/cohesion/alignment rules) friction: 0.0, // Mouse parameters mouse_radius: 10, // Radius of boids captured by the mouse overlay draw_mouse_radius: false, // Show a circle where the mouse is click: 'repel', } sim = new Simulation(simconfig) // Initialise the Cacatoo simulation sim.makeGridmodel("pheromones") // Make a grid for the pheromones sim.makeFlockmodel("flock", flockconfig) // Add a flockmodel, which contains invidiuals (boids) in continuous space // sim.flock.placeObstacle({x:sim.ncol/3,y:sim.nrow/3,r:12,fill:'#222222',force:1}) // sim.flock.placeObstacle({x:sim.ncol-sim.ncol/3,y:sim.nrow-sim.nrow/3,r:8,fill:'#222222',force:1}) sim.createFlockDisplay("flock",{label:""}) // Alias for old 'createDisplay' function, makes distinction with new flocks clearer let ant_1_div = document.createElement("div") ant_1_div.className = "skilltree1" ant_1_div.id = "skilltree1" ant_1_div.innerHTML += `<font size='4'><b>Purple ants <p style='margin:10px;font-size:30px' id='ant_1_points_remaining'>` +ant_1_points+`/`+maxpoints+ `</p> attribute points remaining</b></font><br><br>` document.getElementById("canvas_holder").appendChild(ant_1_div) let pb2 = new AntButton({name:"Num ants", var: "ant_1_number", min: 10, max: 200, increment:10, container:ant_1_div, points: 9,color: '#e0a709',digits:1,antpoints: 'ant_1_points', antpointsdiv: 'ant_1_points_remaining'}) let pb1 = new AntButton({name:"Speed", var: "ant_1_speed", min: 0.3, max: 2, increment:0.1, container:ant_1_div, color: '#e0a709',points: 3,digits:2, antpoints: 'ant_1_points', antpointsdiv: 'ant_1_points_remaining'}) let pb3 = new AntButton({name:"Signal home", var: "ant_1_pherprod", min: 0, max: 2, increment:0.1, container:ant_1_div, points: 8,color: '#e0a709',digits:2,antpoints: 'ant_1_points', antpointsdiv: 'ant_1_points_remaining'}) let pb4 = new AntButton({name:"Signal food", var: "ant_1_pherprod_food", min: 0, max: 2, increment:0.1, container:ant_1_div, points: 8,color: '#e0a709',digits:2,antpoints: 'ant_1_points', antpointsdiv: 'ant_1_points_remaining'}) let pb6 = new AntButton({name:"Sensitivity", var: "ant_1_sense", min: 0.0, max: 1.0, increment:0.05, container:ant_1_div, points:2, color: '#e0a709',digits:3,antpoints: 'ant_1_points', antpointsdiv: 'ant_1_points_remaining'}) let pb5 = new AntButton({name:"Strength", var: "ant_1_strength", min: 1, max: 3, increment:0.25, container:ant_1_div, points: 2,color: '#e0a709',digits:3,antpoints: 'ant_1_points', antpointsdiv: 'ant_1_points_remaining'}) let ant_2_div = document.createElement("div") ant_2_div.className = "skilltree2" ant_2_div.id = "skilltree2" ant_2_div.innerHTML += `<font size='4'><b>Golden ants <p style='margin:10px;font-size:30px' id='ant_2_points_remaining'>`+ant_2_points+ `</p> attribute points remaining</b></font><br><br>` document.getElementById("canvas_holder").appendChild(ant_2_div) let gb2 = new AntButton({name:"Num ants", var: "ant_2_number", min: 10, max: 200, increment:10, container:ant_2_div, points: 6, digits:1, antpoints: 'ant_2_points', antpointsdiv: 'ant_2_points_remaining'}) let gb1 = new AntButton({name:"Speed", var: "ant_2_speed", min: 0.1, max: 2, increment:0.1, container:ant_2_div, points: 2, digits:2, antpoints: 'ant_2_points', antpointsdiv: 'ant_2_points_remaining'}) let gb3 = new AntButton({name:"Signal home", var: "ant_2_pherprod", min: 0, max: 2, increment:0.1, container:ant_2_div, points: 8,digits:2,antpoints: 'ant_2_points', antpointsdiv: 'ant_2_points_remaining'}) let gb4 = new AntButton({name:"Signal food", var: "ant_2_pherprod_food", min: 0, max: 2, increment:0.1, container:ant_2_div, points: 8,digits:2,antpoints: 'ant_2_points', antpointsdiv: 'ant_2_points_remaining'}) let gb6 = new AntButton({name:"Sensitivity", var: "ant_2_sense", min: 0, max: 1.0, increment:0.05, container:ant_2_div, points: 8,digits:3,antpoints: 'ant_2_points', antpointsdiv: 'ant_2_points_remaining'}) let gb5 = new AntButton({name:"Strength", var: "ant_2_strength", min: 1, max: 3, increment:0.25, container:ant_2_div, points: 0,digits:3,antpoints: 'ant_2_points', antpointsdiv: 'ant_2_points_remaining'}) sim.spawnAnts = function(){ if(sim.flock.purple_ants < ant_1_number){ sim.flock.purple_ants++ let dir = sim.rng.random()*Math.PI sim.flock.boids.push(boid = {position: {x:3+1*sim.rng.random(),y:3+1*sim.rng.random()}, velocity: {x:Math.sin(dir),y:Math.cos(dir)}, acceleration: {x:0,y:0}, max_speed:ant_1_speed, type: "purple", strength: ant_1_strength, sense: ant_1_sense, pherprod: ant_1_pherprod, pherprod_food: ant_1_pherprod_food, fill: "purple", foraging: true, foraging_time: 0, size: 3 }) } if(sim.flock.golden_ants < ant_2_number){ sim.flock.golden_ants++ let dir = sim.rng.random()*Math.PI sim.flock.boids.push({position: {x:sim.ncol-3-1*sim.rng.random(),y:sim.nrow-3-1*sim.rng.random()}, velocity: {x:-sim.rng.random(),y:-sim.rng.random()}, acceleration: {x:0,y:0}, type: "gold", fill: "#e0a709", max_speed:ant_2_speed, strength: ant_2_strength, sense: ant_2_sense, foraging: true, foraging_time: 0, pherprod: ant_2_pherprod, pherprod_food: ant_2_pherprod_food, size: 3 }) } } sim.reset = function(){ sim.flock.resetPlots() sim.flock.purple_ants = 0 sim.flock.golden_ants = 0 totalfood = start_totalfood food_gold = 0 food_purple = 0 for(let x=0;x<sim.ncol;x++) for(let y=0;y<sim.nrow;y++){ sim.pheromones.grid[x][y].homing_velocity = {x:0.0,y:0.0} sim.pheromones.grid[x][y].food_velocity = {x:0.0,y:0.0} } sim.flock.boids = [] } sim.reset() /* 2. DEFINING THE RULES. Below, the user defines the nextState function. This function will be applied for each grid point when we will update the grid later. */ sim.pheromones.nextState = function(x, y) { this.grid[x][y].homing_velocity = sim.flock.scaleVector(this.grid[x][y].homing_velocity, 0.985) // decay this.grid[x][y].food_velocity = sim.flock.scaleVector(this.grid[x][y].food_velocity, 0.985) // decay for(let i=1; i<=4; i++) { // diffusion let neigh = this.getNeighbour(this,x,y,i) if(neigh==undefined) continue let diffusion_const = 0.0001 neigh.homing_velocity.x += diffusion_const*this.grid[x][y].homing_velocity.x this.grid[x][y].homing_velocity.x -= diffusion_const*this.grid[x][y].homing_velocity.x neigh.homing_velocity.y += diffusion_const*this.grid[x][y].homing_velocity.y this.grid[x][y].homing_velocity.y -= diffusion_const*this.grid[x][y].homing_velocity.y neigh.food_velocity.x += diffusion_const*this.grid[x][y].food_velocity.x this.grid[x][y].food_velocity.x -= diffusion_const*this.grid[x][y].food_velocity.x neigh.food_velocity.y += diffusion_const*this.grid[x][y].food_velocity.y this.grid[x][y].food_velocity.y -= diffusion_const*this.grid[x][y].food_velocity.y } //this.grid[x][y].homing_pheromone = sim.flock.lengthVector(this.grid[x][y].homing_velocity) } sim.pheromones.update = function() { //this.diffuseStateVector("homing_velocity",0.01) //this.diffuseStateVector("food_velocity",0.01) this.asynchronous() } sim.flock.update = function(){ if(sim.time%5==0)sim.spawnAnts() this.plotArray(["Purple","Gold"],[food_purple,food_gold],["#800080","gold"],"Food collected",{width:800}) for(let boid of this.boids) { if(boid.foraging_time>max_foraging_time){ if(boid.type=="purple") { boid.position = {x:3+10*sim.rng.random(),y:3+10*sim.rng.random()} boid.velocity = {x:sim.rng.random(),y:sim.rng.random()} } else { boid.position = {x:sim.ncol-3-10*sim.rng.random(),y:sim.nrow-3-10*sim.rng.random()} boid.velocity = {x:-sim.rng.random(),y:-sim.rng.random()} } boid.acceleration = {x:0,y:0} boid.foraging = true boid.foraging_time = 0 continue } if(boid.foraging){ let dx = boid.position.x - sim.ncol/2 let dy = boid.position.y - sim.nrow/2 let dist = Math.sqrt(dx*dx+dy*dy) let size_food = Math.max(Math.sqrt(totalfood/Math.PI)*0.9,5) //size_food = 10 size_food = 30/this.scale if(dist < size_food && totalfood > 0){ totalfood-= boid.strength boid.foraging = false boid.foraging_time = 0 boid.velocity.x *= -1 boid.food ="white" boid.velocity.y *= -1 if(totalfood <=0) { boid.strength+=totalfood totalfood = 0 if(food_purple>food_gold) document.getElementById('gameheader').innerHTML =`<h2><font color="#800080">Purple wins!!<br><font color="white" size=2>(Hit 'Reset' to start again)</font></h2>` else if (food_purple<food_gold) document.getElementById('gameheader').innerHTML =`<h2><font color="#e0a709">Gold wins!!<br><font color="white" size=2>(Hit 'Reset' to start again)</font></h2>` else document.getElementById('gameheader').innerHTML =`<h2><font color="white">Draw!<br><font color="white" size=2>(Hit 'Reset' to start again)</font></h2>` } } } else if(boid.position.x<10 && boid.position.y<10){ let dx = boid.position.x let dy = boid.position.y let dist = Math.sqrt(dx*dx+dy*dy) if(dist < 6){ boid.foraging=true boid.foraging_time = 0 food_purple += boid.strength boid.food = undefined boid.velocity.x *= -1 boid.velocity.y *= -1 } } else if(boid.position.x>sim.ncol-10 && boid.position.y>sim.nrow-10){ let dx = boid.position.x - sim.ncol let dy = boid.position.y - sim.nrow let dist = Math.sqrt(dx*dx+dy*dy) if(dist<6){ boid.foraging=true food_gold += boid.strength boid.food = undefined boid.velocity.x *= -1 boid.velocity.y *= -1 } } let x = Math.floor(boid.position.x) let y = Math.floor(boid.position.y) let pherx = sim.pheromones.grid[x][y].homing_velocity.x let phery = sim.pheromones.grid[x][y].homing_velocity.y let pheromone_angle = Math.atan2(phery,pherx) // Randomly rotate a bit // let anglerot = sim.rng.randomGaus(0,2) // boid.velocity = this.rotateVector(boid.velocity,anglerot) if(boundary){ if(boid.type=="purple" && boid.position.y > -1-(sim.nrow/sim.ncol)*boid.position.x + sim.nrow) { boid.acceleration.x += -0.5*sim.rng.random()*boid.max_speed // walk back boid.acceleration.y += -0.5*sim.rng.random()*boid.max_speed continue } else if(boid.type=="gold" && boid.position.y < 1-(sim.nrow/sim.ncol)*boid.position.x + sim.nrow) { boid.acceleration.x += 0.5*sim.rng.random()*boid.max_speed boid.acceleration.y += 0.5*sim.rng.random()*boid.max_speed continue } } let angle = sim.rng.randomGaus(0,6) boid.velocity = this.rotateVector(boid.velocity,angle) if(!boid.foraging){ let pheromone_amount = this.lengthVector(sim.pheromones.grid[x][y].homing_velocity) boid.acceleration.x += boid.acceleration.x + Math.cos(pheromone_angle) * pheromone_amount * boid.sense/100 * -1 boid.acceleration.y += boid.acceleration.y + Math.sin(pheromone_angle) * pheromone_amount * boid.sense/100 * -1 } else { let pheromone_amount = this.lengthVector(sim.pheromones.grid[x][y].food_velocity) boid.acceleration.x += boid.acceleration.x + Math.cos(pheromone_angle) * pheromone_amount * boid.sense/100 boid.acceleration.y += boid.acceleration.y + Math.sin(pheromone_angle) * pheromone_amount * boid.sense/100 } if(boid.foraging){ sim.pheromones.grid[x][y].homing_velocity.x += boid.velocity.x*100*boid.pherprod sim.pheromones.grid[x][y].homing_velocity.y += boid.velocity.y*100*boid.pherprod } else { sim.pheromones.grid[x][y].food_velocity.x += boid.velocity.x*100*boid.pherprod_food sim.pheromones.grid[x][y].food_velocity.y += boid.velocity.y*100*boid.pherprod_food } boid.foraging_time++ } sim.normaliseVector = function (x,y,length){ let magnitude = Math.sqrt(x*x + y*y); if (magnitude > length) { // Calculate the scaling factor const scalingFactor = length / magnitude; // Scale the vector components const scaledX = x * scalingFactor; const scaledY = y * scalingFactor; // Return the scaled vector as an object return { x: scaledX, y: scaledY }; } return { x:x, y:y } } sim.canvases[0].underlay = function(){ if(pressedKeys[70] || ff) return this.ctx.strokeStyle = "black" this.ctx.lineWidth = 1 let scale_vector = 1 this.ctx.lineWidth = 1 // for(let x=0;x<sim.ncol;x++){ // for(let y=0;y<sim.nrow;y++){ // red = Math.min(sim.flock.lengthVector(sim.pheromones.grid[x][y].homing_velocity)/30,0.5) // this.ctx.fillStyle = `rgba(0,255,255,${red})`; // this.ctx.fillRect(x*sim.scale,y*sim.scale,sim.scale,sim.scale) // blue = Math.min(sim.flock.lengthVector(sim.pheromones.grid[x][y].food_velocity)/30,0.5) // this.ctx.fillStyle = `rgba(255,0,0,${blue})`; // this.ctx.fillRect(x*sim.scale,y*sim.scale,sim.scale,sim.scale) // } // } for(let x=0;x<sim.ncol;x++){ for(let y=0;y<sim.nrow;y++){ let velox = sim.pheromones.grid[x][y].homing_velocity.x let veloy = sim.pheromones.grid[x][y].homing_velocity.y let length = Math.sqrt(velox*velox + veloy*veloy) let scaled = sim.normaliseVector(velox,veloy,10) this.ctx.beginPath() this.ctx.strokeStyle= `rgba(150,0,0,${length/100})`; this.ctx.lineWidth = 1 this.ctx.moveTo(this.scale*0.5+x*this.scale,this.scale*0.5+y*this.scale) this.ctx.lineTo(this.scale*0.5+x*this.scale+scaled.x*scale_vector,this.scale*0.5+y*this.scale+scaled.y*scale_vector) this.ctx.stroke() this.ctx.closePath() velox = sim.pheromones.grid[x][y].food_velocity.x veloy = sim.pheromones.grid[x][y].food_velocity.y length = Math.sqrt(velox*velox + veloy*veloy) scaled = sim.normaliseVector(velox,veloy,10) this.ctx.beginPath() this.ctx.strokeStyle= `rgba(0,0,150,${length/100})`; this.ctx.lineWidth = 1 this.ctx.moveTo(this.scale*0.5+x*this.scale,this.scale*0.5+y*this.scale) this.ctx.lineTo(this.scale*0.5+x*this.scale+scaled.x*scale_vector,this.scale*0.5+y*this.scale+scaled.y*scale_vector) this.ctx.stroke() this.ctx.closePath() } } } sim.canvases[0].overlay = function(){ if(boundary){ this.ctx.strokeStyle = "black" this.ctx.lineWidth = 5 this.ctx.beginPath() this.ctx.moveTo(0,sim.nrow*this.scale) this.ctx.lineTo(sim.ncol*this.scale,0) this.ctx.stroke() this.ctx.closePath() } // Food source let size = Math.sqrt(totalfood*this.scale) if(size < 10) size =10 size = 40 let x = this.scale*sim.ncol/2 let y = this.scale*sim.nrow/2 this.ctx.fillStyle="#FFFFFFFF" this.ctx.strokeStyle="black" this.ctx.beginPath() this.ctx.arc(x,y,size,0,Math.PI*2) this.ctx.fill() this.ctx.closePath() this.ctx.fillStyle="black" this.ctx.textAlign = "center"; this.ctx.textBaseline = 'middle'; this.ctx.font = "16px sans"; this.ctx.fillText(totalfood,x,y) // Purple nest this.ctx.fillStyle="#80008088" this.ctx.beginPath() this.ctx.arc(20,20,30,0,Math.PI*2) this.ctx.fill() this.ctx.closePath() this.ctx.fillStyle="black" this.ctx.textAlign = "center"; this.ctx.textBaseline = 'middle'; this.ctx.font = "14px sans"; this.ctx.fillText(Math.round(food_purple),20,20) // Golden nest this.ctx.fillStyle="#e0a70988" this.ctx.beginPath() this.ctx.arc(sim.ncol*this.scale-20,sim.nrow*this.scale-20,30,0,Math.PI*2) this.ctx.fill() this.ctx.closePath() this.ctx.fillStyle="black" this.ctx.textAlign = "center"; this.ctx.textBaseline = 'middle'; this.ctx.font = "14px sans"; this.ctx.fillText(Math.round(food_gold),sim.ncol*this.scale-20,sim.nrow*this.scale-20) } } sim.start() // sim.addButton("pause/continue", function () { sim.toggle_play() }) // sim.addButton("step", function () { sim.step(); sim.display() }) sim.addButton("Respec purple", function () { pb6.reset(); pb1.reset();pb2.reset();pb3.reset();pb4.reset();pb5.reset(); }) document.getElementById('Respec purple').style = 'width: 200; margin-left: 7.5em; background:#800080AA; float: left;' sim.addButton("Reset", function () { if(sim.pause) { sim.pause = false; sim.reset(); this.innerHTML = "Reset" return } else { sim.reset(); sim.pause = true; this.innerHTML = "Start!" }}) document.getElementById('Reset').style = '' sim.addButton("Respec gold", function () { gb6.reset(); gb1.reset();gb2.reset();gb3.reset();gb4.reset();gb5.reset(); }) document.getElementById('Respec gold').style = 'width:200; margin-right:7.5em; background:#e0a709AA;float: right; width:200px' sim.addButton("Pause",function () { sim.toggle_play() }) sim.addToggle("boundary", "Boundary") sim.addToggle("ff", "Fast forward") sim.step() sim.pause = false } class AntButton { // Genome constructor constructor(cfg) { this.cfg = cfg this.name = cfg.name this.var = cfg.var this.min = cfg.min this.max = cfg.max this.increment = cfg.increment this.points = cfg.points this.container = cfg.container this.antpoints = cfg.antpoints this.antpointsdiv = cfg.antpointsdiv this.color = cfg.color this.createElement() window[this.antpoints] -= this.points document.getElementById(this.antpointsdiv).innerHTML = window[this.antpoints] + '/' + maxpoints } createElement(){ let div = document.createElement('div') div.classList.add("number-input") this.downbutton = document.createElement("button") this.downbutton.addEventListener('click', (event) => { if(event.shiftKey) for(let i=0;i<5;i++) this.decreaseValue(event) else this.decreaseValue(event) }); this.downbutton.style = "width:50;" + 'background-color:' + this.color + ";" this.downbutton.innerText = "-" this.upbutton = document.createElement("button") this.upbutton.addEventListener('click', (event) => { if(event.shiftKey) for(let i=0;i<5;i++) this.increaseValue(event) else this.increaseValue(event) }); this.upbutton.style = "width:50;" + 'background-color:' + this.color + ";" this.upbutton.innerText = "+" this.varvaluediv = document.createElement("div") this.varvaluediv.style = "display:inline-block; position:relative; top:12px; height:50px; font-size:14px; width:90;" window[this.var] = this.min + this.points*this.increment this.varvaluediv.innerHTML += this.points + '<br>' + this.name + '<br>(' + window[this.var].toFixed(this.cfg.digits-1) + ')' div.appendChild(this.downbutton) div.appendChild(this.varvaluediv) div.appendChild(this.upbutton) this.container.appendChild(div) } increaseValue() { if(window[this.antpoints] > 0 && window[this.var] < this.max){ this.points++ // points returned window[this.var] = this.min + this.points*this.increment this.varvaluediv.innerHTML = this.points + '<br>' + this.name + '<br>(' + window[this.var].toFixed(this.cfg.digits-1) + ')' window[this.antpoints]-- document.getElementById(this.antpointsdiv).innerHTML = window[this.antpoints] + '/' + maxpoints } //sim.reset(); } decreaseValue() { if(window[this.antpoints] < maxpoints && window[this.var] > this.min){ this.points-- // points invested down window[this.var] = this.min + this.points*this.increment this.varvaluediv.innerHTML = this.points + '<br>' + this.name + '<br>(' + window[this.var].toFixed(this.cfg.digits-1) + ')' window[this.antpoints]++ document.getElementById(this.antpointsdiv).innerHTML = window[this.antpoints] + '/' + maxpoints } } reset(){ window[this.antpoints]+= this.points this.points = 0 window[this.var] = this.min + this.points*this.increment this.varvaluediv.innerHTML = this.points + '<br>' + this.name + '<br>(' + window[this.var].toFixed(this.cfg.digits-1) + ')' document.getElementById(this.antpointsdiv).innerHTML = window[this.antpoints] + '/' + maxpoints } } </script> <body onload="cacatoo()"> <div class="gameheader" id="gameheader"> <h2>Epic ant battle!<br><font size=2>(Power in numbers?)</font> </h2> </div> <div class="gamecanvas" id="canvas_holder"></div> <center><div class="" id="form_holder" style="clear: both; width:100%"> </div> <div class="" id="graph_holder" style="display:inline-block; text-align:center"> </div></center> </body> </html>