UNPKG

agentscript

Version:

AgentScript Model in Model/View architecture

189 lines (174 loc) 5.89 kB
import * as util from './utils.js' import AgentList from './AgentList.js' // Class Patch instances represent a rectangle on a grid. They hold variables // that are in the patches the turtles live on. The set of all patches // is the world on which the turtles live and the model runs. // Flyweight object creation: // Objects within AgentSets use "prototypal inheritance" via Object.create(). // Here, the Patch class is given to Patches for use creating Proto objects // (new Patch(agentSet)), but only once per model/breed. // The flyweight Patch objects are created via Object.create(protoObject), // This lets the new Patch(agentset) object be "defaults". // https://medium.com/dailyjs/two-headed-es6-classes-fe369c50b24 /** * Class Patch instances represent a square on the {@link Patches} grid. * They hold variables * that are in the patches the turtles live on. The set of all patches * is the world on which the turtles live and the model runs. * * You do not use `new Patch`, rather Class {@link Model} creates the patches * for you, using the {@link World} data passed to the Model. * * You *never* do this: * */ class Patch { static defaults = { turtles: null, z: 0, // Set by AgentSet agentSet: null, model: null, name: null, } static variables = { // none } constructor() { Object.assign(this, Patch.defaults) } newInstance(agentProto) { const insstance = Object.create(agentProto) Object.assign(insstance, Patch.variables) return insstance } get x() { return (this.id % this.model.world.width) + this.model.world.minX } get y() { return ( this.model.world.maxY - Math.floor(this.id / this.model.world.width) ) } // get z() { // return 0 // } // set z(z) {} /** * Return whether or not this patch is on the edge of the atches. * * @returns {boolean} */ isOnEdge() { return this.patches.isOnEdge(this) } // Getter for neighbors of this patch. // Uses lazy evaluation to promote neighbors to instance variables. // To avoid promotion, use `patches.neighbors(this)`. // Promotion makes getters accessed only once. // defineProperty required: can't set this.neighbors when getter defined. /** * Return an array of this patch's 8 * [Moore neighbors](https://en.wikipedia.org/wiki/Moore_neighborhood). * * @returns {AgentArray} */ get neighbors() { // lazy promote neighbors from getter to instance prop. const n = this.patches.neighbors(this) Object.defineProperty(this, 'neighbors', { value: n, enumerable: true }) return n } /** * Return an array of this patch's 4 * [Von Neumann neighbors](https://en.wikipedia.org/wiki/Von_Neumann_neighborhood) * (north, south, east, west). * * @returns {AgentArray} */ get neighbors4() { const n = this.patches.neighbors4(this) Object.defineProperty(this, 'neighbors4', { value: n, enumerable: true, }) // console.log('n4', n, this.neighbors4) return n } // Promote this.turtles on first call to turtlesHere. /** * Return an Array of the turtles on this patch. * * @returns {AgentArray} */ get turtlesHere() { if (this.turtles == null) { this.patches.ask(p => { p.turtles = new AgentList(this.model) }) this.model.turtles.ask(t => { t.patch.turtles.push(t) }) } return this.turtles } // Returns above but returning only turtles of this breed. /** * Returns an Array of the particular breed on this patch. * * @param {String} breed * @returns {AgentArray} */ breedsHere(breed) { const turtles = this.turtlesHere return turtles.withBreed(breed) } // 6 methods in both Patch & Turtle modules // Distance from me to x, y. // 2.5D: use z too if both z & this.z exist. // REMIND: No off-world test done distanceXY(x, y, z = null) { const useZ = z != null && this.z != null return useZ ? util.distance3(this.x, this.y, this.z, x, y, z) : util.distance(this.x, this.y, x, y) } // Return distance from me to object having an x,y pair (turtle, patch, ...) // 2.5D: use z too if both agent.z and this.z exist // distance (agent) { this.distanceXY(agent.x, agent.y) } distance(agent) { const { x, y, z } = agent return this.distanceXY(x, y, z) } // distanceXY(x, y) { // return util.distance(this.x, this.y, x, y) // } // // Return distance from me to object having an x,y pair (turtle, patch, ...) // distance(agent) { // return this.distanceXY(agent.x, agent.y) // } // Return heading towards agent/x,y using current geometry towards(agent) { return this.towardsXY(agent.x, agent.y) } towardsXY(x, y) { // return util.radiansTowardXY(this.x, this.y, x, y) let rads = util.radiansTowardXY(this.x, this.y, x, y) return this.model.fromRads(rads) } // Return patch w/ given parameters. Return undefined if off-world. // Return patch dx, dy from my position. patchAt(dx, dy) { return this.patches.patch(this.x + dx, this.y + dy) } patchAtHeadingAndDistance(heading, distance) { return this.patches.patchAtHeadingAndDistance(this, heading, distance) } sprout(num = 1, breed = this.model.turtles, initFcn = turtle => {}) { return breed.create(num, turtle => { turtle.setxy(this.x, this.y) initFcn(turtle) }) } } export default Patch