agentscript
Version:
AgentScript Model in Model/View architecture
96 lines (84 loc) • 3.32 kB
JavaScript
import World from 'https://code.agentscript.org/src/World.js'
import Model from 'https://code.agentscript.org/src/Model.js'
import * as util from 'https://code.agentscript.org/src/utils.js'
// The NetLogo models library wall followers example
// http://ccl.northwestern.edu/netlogo/models/WallFollowingExample
// The key ideas are, for a right hand wall follower:
//
// * Turn right if there is no wall to the right, but there is one
// to the right and behind.
// * Keep turning until there is no wall head
// * Take a step
// * Make sure you are alwas on a patch center
//
// Similarly for left-hand wall followers w/ obvious changes, managed
// by a +/-1 direction turtle variable.
export default class WallFollowerModel extends Model {
population = 40
wallPercent = 0.04
// ======================
constructor(worldOptions = World.defaultOptions(35)) {
super(worldOptions)
}
setup() {
this.patchBreeds('walls')
this.turtleBreeds('lefty righty')
// righty turtles follow the right hand rule, lefty the left.
// righty turn right, a negative angle. lefty positive.
this.righty.setDefault('turn', -1) // -90 degrees
this.lefty.setDefault('turn', +1) // 90 degrees
// Have random disks become walls
this.patches.ask(p => {
if (util.randomFloat(1) < this.wallPercent) {
const neighbors = this.patches.inRadius(p, util.randomFloat(3))
neighbors.ask(n => {
n.setBreed(this.walls)
})
}
})
// Make sure single patch gaps are filled.
this.patches.edgePatches().ask(p => {
p.setBreed(this.patches)
})
this.patches
.filter(p => !p.isBreed(this.walls))
.filter(p => p.neighbors4.every(n => n.isBreed(this.walls)))
.ask(p => p.setBreed(this.walls))
// Sprout this.population agents on the non-wall patches.
this.patches
.filter(p => !p.isBreed(this.walls))
.nOf(this.population)
.ask(p => {
const breed = util.randomInt(2) ? this.lefty : this.righty
p.sprout(1, breed, t => {
t.face(p.neighbors4.oneOf())
})
})
}
step() {
this.turtles.ask(t => {
this.walk(t)
})
}
walk(t) {
// Calculate a right turn angle for lefty/righty
const rtAngle = 90 // Math.PI / 2
const turnAngle = rtAngle * t.turn
// Keeps me on the wall when drifting off: If a wall to my west/east and
// a space to my south west/east, rotate towards wall
if (!this.wallAt(t, turnAngle) && this.wallAt(t, 1.5 * turnAngle)) {
t.rotate(turnAngle)
}
// Turn Turn left/right if wall is ahead.
while (this.wallAt(t, 0)) t.rotate(-turnAngle)
// After all that, move forward, still on patch centers.
t.forward(1)
}
wallAt(t, angle) {
// Note that angle may be positive or negative. if angle is
// positive, the turtle looks right, if negative, left.
// Return true if wall is ahead.
const p = t.patchLeftAndAhead(angle, 1)
return p && p.isBreed(this.walls)
}
}