agentscript
Version:
AgentScript Model in Model/View architecture
91 lines (81 loc) • 3.2 kB
JavaScript
import Model from 'https://code.agentscript.org/src/Model.js'
import * as util from 'https://code.agentscript.org/src/utils.js'
export default class FlockModel extends Model {
population = 100
vision = 3 // radius in patches
speed = 0.25 // distance per step in patches
maxTurn = 3.0 // max turn in degrees
minSeparation = 0.75 // Keep turtles apart by this distance, in patches
// ======================
// We can use Model's constructor, due to using Model's default World.
// If you pass in world options, Model will use them
// constructor() {
// super() // use default world options.
// }
setup() {
this.turtles.setDefault('speed', this.speed)
this.patches.cacheRect(this.vision)
util.repeat(this.population, () => {
this.patches.oneOf().sprout()
})
}
step() {
this.turtles.ask(t => {
this.flock(t)
})
// if (this.ticks % 50 === 49) this.report()
}
report() {
console.log('step', this.ticks + 1, 'cohesion:', this.flockVectorSize())
}
flock(t) {
const flockmates = this.turtles.inRadius(t, this.vision, false)
if (flockmates.length !== 0) {
const nearest = flockmates.minOneOf(f => f.distance(t))
if (t.distance(nearest) < this.minSeparation) {
this.separate(t, nearest)
} else {
this.align(t, flockmates)
this.cohere(t, flockmates)
}
}
t.forward(t.speed)
}
separate(t, nearest) {
// heading is *away* from t towaards nearest, so separates the two
const heading = nearest.towards(t)
this.turnTowards(t, heading)
}
align(t, flockmates) {
this.turnTowards(t, this.averageHeading(flockmates))
}
cohere(t, flockmates) {
this.turnTowards(t, this.averageHeadingTowards(t, flockmates))
}
turnTowards(t, heading) {
// let turn = util.subtractHeadings(heading, t.heading)
let turn = t.subtractHeading(heading)
turn = util.clamp(turn, -this.maxTurn, this.maxTurn)
t.rotate(turn)
}
averageHeading(flockmates) {
const thetas = flockmates.map(f => f.theta)
const dx = thetas.map(t => Math.cos(t)).reduce((x, y) => x + y)
const dy = thetas.map(t => Math.sin(t)).reduce((x, y) => x + y)
return util.radToHeading(Math.atan2(dy, dx))
}
averageHeadingTowards(t, flockmates) {
// const towards = flockmates.map(f => util.headingToRad(f.towards(t)))
const towards = flockmates.map(f => f.towards(t))
const dx = towards.map(t => Math.cos(t)).reduce((x, y) => x + y)
const dy = towards.map(t => Math.sin(t)).reduce((x, y) => x + y)
// return Math.atan2(dy, dx)
return util.radToHeading(Math.atan2(dy, dx))
}
flockVectorSize() {
const thetas = this.turtles.map(t => t.theta)
const dx = thetas.map(theta => Math.cos(theta)).reduce((x, y) => x + y)
const dy = thetas.map(theta => Math.sin(theta)).reduce((x, y) => x + y)
return Math.sqrt(dx * dx + dy * dy) / this.population
}
}