Published
Edited
Nov 18, 2021
2 stars
Insert cell
Insert cell
Insert cell
class Ant{
constructor(props){
this.ctx = props.ctx
this.x = props.x
this.y = props.y
this.dna = props.dna
this.draw()
}

draw(){
this.ctx.save()
this.ctx.translate(this.x,this.y)
this.ctx.beginPath()
this.ctx.arc(0,0,2,0,2*Math.PI)
this.ctx.fill()
this.ctx.closePath()
this.ctx.restore()
}

step(direction,size){
this.x = this.x + size*Math.cos(direction)
this.y = this.y + size*Math.sin(direction)
this.draw()
}
}
Insert cell
class System{
constructor(props){
this.ctx = props.ctx
this.width = props.width
this.height = props.height
this.numFrames = props.numFrames
this.numTicks = props.numTicks
this.stepSize = props.stepSize
this.numAnts = props.numAnts
this.numGen = props.numGen
this.basket = props.basket
this.mutRate = props.mutRate
this.mutSize = props.mutSize

this.colony = this.makeColony()
this.frame = 0
this.tick = 0
this.generation = 0
}

loop(){
this.ctx.clearRect(0,0,this.width,this.height)
this.drawBasket()
this.colony.map(ant => {
if(this.within(ant)){
ant.draw()
}else{
ant.step(ant.dna[this.tick],this.stepSize)
}
})
const text = d3.sum(this.colony,ant => this.within(ant))
this.ctx.fillText(text,400,50)
if(this.frame < this.numFrames){
this.frame = this.frame + 1
window.requestAnimationFrame(this.loop.bind(this))
}else if(this.tick < this.numTicks){
this.frame = 0
this.tick = this.tick + 1
window.requestAnimationFrame(this.loop.bind(this))
}else if(this.generation < this.numGen){
this.frame = 0
this.tick = 0
this.generation = this.generation + 1
this.colony = this.genColony()
window.requestAnimationFrame(this.loop.bind(this))
}
}

makeAnt(){
const dna = []
for(let i = 0; i <= this.numTicks; i = i + 1){
const direction = d3.randomUniform(0,2*Math.PI)()
dna.push(direction)
}
const props = {
ctx:this.ctx,
x:this.width/2,
y:this.height/2,
dna:dna
}
return new Ant(props)
}

makeColony(){
const colony = []
for(let i = 0; i < this.numAnts; i = i + 1){
const ant = this.makeAnt()
colony.push(ant)
}
return colony
}

drawBasket(){
this.ctx.save()
this.ctx.fillStyle = "steelblue"
this.ctx.beginPath()
this.ctx.arc(this.basket[0],this.basket[1],this.basket[2],0,2*Math.PI)
this.ctx.fill()
this.ctx.closePath()
this.ctx.restore()
}

distance(ant){
return Math.sqrt((ant.x - this.basket[0])**2 + (ant.y - this.basket[1])**2)
}

within(ant){
if(this.distance(ant) < this.basket[2]){
return true
}else{
return false
}
}

makeWheel(a){
const sum = d3.sum(a)
return a.map(d => {
return {value:d, prob:d/sum}
})
}

spinWheel(wheel){
const probs = wheel.map(slice => slice.prob)
const cprobs = d3.cumsum(probs)
const r = d3.randomUniform(0,1)()
return [...cprobs,r].sort((a,b) => a - b).indexOf(r)
}

crossover(x,y){
const n = x.length
const xPart = x.slice(0,n/2)
const yPart = y.slice(n/2)
return [...xPart,...yPart]
}

mutate(dna){
const r = d3.randomUniform(0,1)()
if(r < this.mutRate){
const i = d3.randomInt(0,dna.length)()
dna[i] = dna[i] + d3.randomNormal(0,this.mutSize)()
}
return dna
}

reproduce(ant1,ant2){
let dna = this.crossover(ant1.dna,ant2.dna)
dna = this.mutate(dna)
const props = {
ctx:this.ctx,
x:this.width/2,
y:this.height/2,
dna:dna
}
return new Ant(props)
}

pickParents(){
let fitness = this.colony.map(ant => 1/this.distance(ant))
let wheel = this.makeWheel(fitness)
const p1 = this.spinWheel(wheel)
const colony2 = [...this.colony.slice(0,p1), ...this.colony.slice(p1+1)]
fitness = colony2.map(ant => 1/this.distance(ant))
wheel = this.makeWheel(fitness)
const p2 = this.spinWheel(wheel)
return [this.colony[p1],colony2[p2]]
}

genColony(){
const newColony = []
for(let i = 0; i < this.numAnts; i = i + 1){
const parents = this.pickParents()
const child = this.reproduce(parents[0],parents[1])
newColony.push(child)
}
return newColony
}
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more