cacatoo
Version:
Building, exploring, and sharing spatially structured models
741 lines (629 loc) • 28.3 kB
HTML
<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;
}
(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;
}
(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>