UNPKG

agentscript

Version:

AgentScript Model in Model/View architecture

141 lines (122 loc) 4.33 kB
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' export default class TSPModel extends Model { nodeCount = 50 travelersCount = 100 growPopulation = true useInversion = true stopTickDifference = 500 // onChange = (length, changes, ticks) => {} // called whenever a tour changes // ====================== constructor(worldOptions = World.defaultOptions(50)) { super(worldOptions) } setup() { this.turtleBreeds('nodes travelers') this.travelers.setDefault('hidden', true) this.nodes.setDefault('theta', 0) // override promotion to random angle // globals this.bestTourNodes = [] this.bestTourLength = 0 this.bestTourTick = 0 this.tourChanges = 0 this.nodes.create(this.nodeCount, node => { this.setupNode(node) }) this.createTourLinks(this.nodes) //() this.bestTourLength = this.links.reduce((sum, l) => sum + l.length(), 0) this.travelers.create(this.travelersCount, t => this.setupTraveler(t)) } setupNode(node) { // REMIND: space the nodes away from each other node.moveTo(this.patches.oneOf()) } setupTraveler(t) { t.tourNodes = this.nodes.clone().shuffle() t.tourLength = this.lengthFromNodes(t.tourNodes) } step() { if (this.done) return this.travelers.ask(t => this.makeTour(t)) this.installBestTour() this.stopIfDone() } createTourLinks(nodeList) { this.links.clear() nodeList.ask((node, i) => { const nextNode = nodeList[(i + 1) % nodeList.length] this.links.create(node, nextNode) }) } lengthFromNodes(nodeList) { let len = 0 nodeList.ask((node, i) => { const nextNode = nodeList[(i + 1) % nodeList.length] len += node.distance(nextNode) }) return len } installBestTour() { while (this.travelers.length > this.travelersCount) { this.travelers.maxOneOf('tourLength').die() } const a = this.travelers.minOneOf('tourLength') if (a.tourLength < this.bestTourLength) { this.bestTourLength = a.tourLength this.bestTourNodes = a.tourNodes this.bestTourTick = this.ticks // this.onChange(a.tourLength, this.tourChanges, this.ticks) this.tourChanges++ this.createTourLinks(this.bestTourNodes) } } makeTour(a) { const nlist = this.useInversion ? this.inversionStrategy(a) : this.randomStrategy(a) const len = this.lengthFromNodes(nlist) if (this.growPopulation) { a.hatch(1, this.travelers, a => { a.tourNodes = nlist a.tourLength = len }) } else if (len < a.tourLength) { a.tourNodes = nlist a.tourLength = len } } randomStrategy(a) { return a.tourNodes.clone().shuffle() } inversionStrategy(a) { return this.newInversion(a.tourNodes) } newInversion(nlist) { let len = nlist.length const i = util.randomInt(len - 1) len = 2 + util.randomInt(len - i - 2) return nlist // result will be agentarray .slice(0, i) .concat(nlist.slice(i, i + len).reverse()) .concat(nlist.slice(i + len)) } stopIfDone() { if (this.ticks - this.bestTourTick === this.stopTickDifference) { console.log( `Stop: no change after ${this.stopTickDifference} ticks`, `Best tour: ${this.bestTourLength} at tick ${this.bestTourTick}` ) this.done = true } } moveNode(node, x, y) { node.setxy(x, y) this.bestTourLength = this.links.reduce((sum, l) => sum + l.length(), 0) // node.tourLength = this.lengthFromNodes(this.nodes) // a.tourLength = @lengthFromNodes a.tourNodes for a in @travelers this.travelers.ask( t => (t.tourLength = this.lengthFromNodes(t.tourNodes)) ) } }