Skip to content

Instantly share code, notes, and snippets.

@wybo
Last active May 1, 2017 23:04
Show Gist options
  • Save wybo/95eddda521dfaf11c015 to your computer and use it in GitHub Desktop.
Save wybo/95eddda521dfaf11c015 to your computer and use it in GitHub Desktop.
Advanced Template
# 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