Last active
May 1, 2017 23:04
-
-
Save wybo/95eddda521dfaf11c015 to your computer and use it in GitHub Desktop.
Advanced Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# AgentBase is Free Software, available under GPL v3 or any later version. | |
# Original AgentScript code @ 2013, 2014 Owen Densmore and RedfishGroup LLC. | |
# AgentBase (c) 2014, Wybo Wiersma. | |
# This is a slightly more complicated general template for | |
# creating models. | |
# | |
# To build your own model, extend class ABM.Model supplying the | |
# two built in methods `setup` and `step`. | |
# | |
# `@foo` signifies an instance variable or method. | |
# See [CoffeeScript](http://jashkenas.github.com/coffee-script/#classes) | |
# for explanation of context of a class and its constructor. | |
# | |
# We do not provide a constructor of our own. | |
# | |
# AgentBase automatically calls `Model.constructor <args>` | |
# and `setup` will be called by `Model.constructor`. See: | |
# | |
# model = new AdvancedTemplateModel | |
# | |
# below, which passes all its arguments to `Model` | |
u = ABM.util # shortcut for ABM.util, some tools | |
log = (arg) -> console.log arg # log to the browser console | |
class ABM.AdvancedTemplateModel extends ABM.Model | |
# `startup` initializes resources used by `setup` and `step`. | |
# This is called by the constructor which waits for all files | |
# processed by `starup`. Not needed by simple models. | |
# | |
startup: -> # called by constructor | |
u.shapes.add "bowtie", true, (context) -> | |
u.shapes.polygon context, [[-.5, -.5], [.5, .5], | |
[-.5, .5], [.5, -.5]] | |
# The following lines don't work when opened locally in | |
# Chrome (they work fine from a webserver). Uncomment if you'd | |
# like to add custom images. | |
# u.shapes.add "cc", true, u.importImage("images/coffee.png") | |
# Initialize our model via the `setup` method. This model simply | |
# creates a population of agents with arbitrary shapes with `size` | |
# size and `speed` velocity. We also periodically change the patch | |
# colors to random gray values. | |
# | |
setup: -> # called by Model.constructor | |
# First, we initialize our model instance variables. | |
# Most instance variables are parameters we would like | |
# an external UI to setup for us. | |
@population = 100 | |
@size = 1.5 # size in patch coords | |
@speed = .5 # move forward this amount in patch coords | |
@wiggle = u.degreesToRadians(30) # degrees/radians to wiggle | |
@startCircle = true # initialize agents randomly or in circle | |
# Set the default agent size (conserves storage) | |
@agents.setDefault "size", @size | |
# Set the agent to convert shape to bitmap for better performance. | |
@agents.setUseSprites() | |
# Set animation to 30fps, without multiple steps per draw: | |
@animator.setRate 30, false | |
# The patch grid will have been setup for us. Here we initialize | |
# patch variables, either built-in ones or any new patch variables | |
# our model needs. In this case, we set the built-in color to a | |
# random gray value. | |
for patch in @patches.create() | |
patch.color = u.color.random "gray" | |
# Set x & y axes to a different color | |
if patch.position.x is 0 | |
patch.color = u.color.yellow | |
if patch.position.y is 0 | |
patch.color = u.color.red | |
# Our empty @agents Agents will have been created. Here we | |
# add `population` Agents we use in our model. | |
# | |
# We set the build-in Agent variables `size` and `shape` | |
# and layout the agents randomly or in a circle depending | |
# on our modeel's `startCircle` variable. | |
for agent in @agents.create @population | |
agent.shape = u.shapes.names().sample() # random shapes | |
if @startCircle | |
agent.forward @patches.max.x / 2 # start in circle | |
else | |
agent.moveTo @patches.randomPoint() # set random location | |
# Print the number of agents and patches to the console. | |
# Note CoffeeScript | |
# [string interpolation](http://jashkenas.github.com/coffee-script/#strings) | |
log "total agents: #{@agents.length}, patches: #{@patches.length}" | |
# Print number of agents with each shape | |
for shape in u.shapes.names() | |
num = @agents.with((agent) -> agent.shape == shape).length | |
log "#{num} #{shape}" | |
# Update, or run our model for one step, via the second built in | |
# method, `step`. | |
# | |
step: -> # called by Model.animate | |
# First, update our agents via `updateAgents` below | |
for agent in @agents | |
@updateAgents(agent) | |
# Every 100 steps, update our patches, print stats to | |
# the console, and use the Model refresh flag to redraw | |
# the patches. Otherwise don't refresh. | |
if @animator.ticks % 100 is 0 | |
for patch in @patches | |
@updatePatches(patch) | |
@reportInfo() | |
@refreshPatches = true | |
# Add use of our first pull request: | |
@setSpotlight @agents.sample() if @animator.ticks is 300 | |
@setSpotlight null if @animator.ticks is 600 | |
else | |
@refreshPatches = false | |
log @animator.toString() if @animator.ticks % 100 is 0 | |
# Stop the animation at 1000. | |
# Restart by `ABM.model.start()` in console. | |
if @animator.ticks is 1000 | |
log "..stopping, restart by ABM.model.start()" | |
@stop() | |
# Three of our own methods to manage agents & patches | |
# and report model state. | |
# | |
updateAgents: (agent) -> # a is agent | |
# Have our agent "wiggle" by changing | |
# our heading by +/- `wiggle/2` radians | |
agent.rotate u.randomCentered @wiggle | |
# Then move forward by our speed. | |
agent.forward @speed | |
# Update patch colors to be a random gray. | |
# | |
updatePatches: (patch) -> # p is patch | |
if patch.position.x isnt 0 and patch.position.y isnt 0 | |
patch.color = u.color.random "gray" | |
# Report the average heading, in radians and degrees | |
# | |
reportInfo: -> | |
headings = @agents.getProperty "heading" | |
avgHeading = (headings.reduce (a, b) -> a + b) / @agents.length | |
# Note: multiline strings. block strings also available. | |
log """ | |
average heading of agents: | |
#{avgHeading.toFixed(2)} radians, | |
#{u.radiansToDegrees(avgHeading).toFixed(2)} degrees | |
""" | |
# Now that we've build our class, we call it with Model's | |
# constructor arguments: | |
# | |
# div: name of the div | |
# patchSize = 13: size (width & height) of each patch in pixels | |
# mapSize = 32: size (width & height) of the map in patches | |
# isTorus = false: map has torus topology | |
# | |
# Alternatively to mapSize you can set the map-size manually: | |
# min: {x: x-coordinaye, y: y-coordinate} | |
# max: {x: x-coordinaye, y: y-coordinate} | |
# | |
# Defaults 13 for patchSize and 32 for mapSize | |
# | |
window.model = new ABM.AdvancedTemplateModel { | |
div: "world", | |
patchSize: 13, | |
mapSize: 32 | |
isTorus: true | |
} | |
window.model.start() | |
# Uses the AgentBase library (03-02-2017 release) | |
# https://github.com/wybo/agentbase/commit/3501302567c018da82601cd858be2ea6d0b9c74d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment