class System{
constructor(props){
this.ctx = props.ctx
this.width = props.width
this.height = props.height
this.numTrees = props.numTrees
this.frames = props.frames
this.numTicks = props.numTicks
this.mutRate = props.mutRate
this.mutSize = props.mutSize
this.marks = d3.cumsum(this.frames)
this.stand = this.makeStand(this.numTrees)
this.frame = 0
this.tick = 0
this.means = []
}
growthTick(){
this.stand.map(tree => tree.grow(tree.adultHeight/this.frames[0]))
this.frame = this.frame + 1
}
existTick(){
this.stand.map(tree => tree.exist())
this.frame = this.frame + 1
}
decayTick(){
this.stand.map(tree => tree.decay(tree.adultHeight/this.frames[2]))
this.frame = this.frame + 1
}
loop(){
this.ctx.clearRect(0,0,this.width,this.height)
this.drawLine(this.height/2,"steelblue")
this.drawLine(this.means[this.means.length-1],"red")
if(this.frame < this.marks[0]){
this.growthTick()
window.requestAnimationFrame(this.loop.bind(this))
}else if(this.marks[0] <= this.frame && this.frame < this.marks[1]){
this.existTick()
window.requestAnimationFrame(this.loop.bind(this))
}else if(this.marks[1] <= this.frame && this.frame < this.marks[2]){
this.decayTick()
window.requestAnimationFrame(this.loop.bind(this))
}else if(this.tick < this.numTicks){
mutable data = this.means
const mean = d3.mean(this.stand,tree => tree.adultHeight)
this.means.push(mean)
this.tick = this.tick + 1
this.frame = 0
this.stand = this.genStand(this.stand)
window.requestAnimationFrame(this.loop.bind(this))
}
}
drawLine(level,color){
this.ctx.save()
this.ctx.strokeStyle = color
this.ctx.beginPath()
this.ctx.moveTo(0,level)
this.ctx.lineTo(this.width,level)
this.ctx.stroke()
this.ctx.closePath()
this.ctx.restore()
}
makeStand(n){
const trees = []
for(let i = 0; i < n; i = i + 1){
const props = {
ctx:this.ctx,
root:d3.randomUniform(0,this.width)(),
adultHeight:d3.max([d3.randomNormal(this.height/2,20)(),.001])
}
trees.push(new Tree(props))
}
return trees
}
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)
}
mutate(tree){
const r = d3.randomUniform(0,1)()
if(r < this.mutRate){
return d3.randomNormal(tree.adultHeight,this.mutSize)()
}else{
return tree.adultHeight
}
}
reproduce(tree){
const props = {
ctx:this.ctx,
root:d3.randomUniform(0,this.width)(),
adultHeight:this.mutate(tree)
}
return new Tree(props)
}
pickParent(stand){
const fitness = stand.map(tree => tree.adultHeight)
const wheel = this.makeWheel(fitness)
return this.spinWheel(wheel)
}
genStand(stand){
const newStand = []
for(let i = 0; i < this.numTrees; i = i + 1){
const index = this.pickParent(stand)
const tree = this.reproduce(stand[index])
newStand.push(tree)
}
return newStand
}
}