Swarm Space Library
The Swarm Space library is the beginnings of a library to assist in
building environments for interacting agents. In general, environments
can be just as varied as the agents themselves (in one view, the
environment itself is simply another agent). However, many simulations
have similar types of environments that can be helpfully supported by
generic code.
The current space library only addresses simple kinds of discretized
2d space. Improvement is planned in the future: see the todo list for ideas. Briefly, coordinates need
to be elevated to the status of objects, which should hopefully allow
spaces of different scales and boundary conditions to interact through
a common reference system. In addition, other types of spaces are
desired: continuous coordinates, other dimensions, arbitrary graphs,
etc.
Discrete2d
Root class of all 2d discrete spaces. A Discrete2d is basically a 2d
array of ids. Subclasses add particular space semantics onto this.
Currently Discrete2d grids are accessed by integer pairs of X and Y
coordinates.
Creation
-setSizeX: (int) x Y: (int) y
- Set the world size.
-createEnd
- Create the lattice, precompute the offsets based on Y coordinate.
Querying Discrete2d state
-(int) getSizeX and -(int) getSizeY
- Get the size of the lattice.
Accessing elements
The Discrete2d is essentially a 2d array of ids. However, we also
allow users to access the array as if it were an array of integers:
the integers are cast into ids for storage. The Object methods
retrieve values as objects, the Value methods cast things to integers
before returning. We believe this is safe on all architectures.
-getObjectAtX: (int) x Y: (int) y
- Return the pointer stored at (x,y).
-getValueAtX: (int) x Y: (int) y
- Return the integer stored at (x,y).
-putObject: anObject atX: (int) x Y: (int) y
- Put the given pointer to (x,y) overwriting whatever was there.
-putValue: (int) v atX: (int) x Y: (int) y
- Put the given integer to (x,y) overwriting whatever was there.
Low level access
For speed, sometimes you want to go below the method call layer and
access cells quickly. To allow this Discrete2d defines some macros
that give you internal access. Use these at your own peril! In
particular, subclasses have no way to redefine the macros.
-(id *) getLattice
- Returns the lattice pointer - use this for fast access.
discrete2dSiteAt(lattice, offsets, x, y)
- Macro to return a pointer to the value at site x, y. lattice is
the return value from getLattice, offsets is the precomputed
offsets (stored in self->offsets).
Miscellaneous
These methods are used in -createEnd
to actually allocate the
array. Subclasses might need to use these.
-makeOffsets
- Given an array size, compute the offsets array that caches the
multiplication by ysize. See the
discrete2dSiteAt
macro.
-(id *)allocLattice
- Allocate memory for the lattice.
Grid2d
Grid2d is a generic container class to represent agent position on a
2d lattice. It gets most of its behaviour from Discrete2d, adding
extra code to check that you don't overwrite things by accident.
Grid2d is pretty primitive: only one object can be stored at a site,
no boundary conditions are implied, etc. A fancier Grid2d is in the
works.
New Methods
-setOverwriteWarnings: (BOOL) b
- If set to true, then if you try to store something at a site
that doesn't have 0x0 there, a warning will be generated.
Overridden methods
-putObject: anObject atX: (int) x Y: (int) y
- Replaces the Discrete2d method. First check to see if it
should do overwrite warnings, and if so if you're going to
overwrite: if both conditions are true, print out a warning
message. Regardless of the check, it writes the new object in.
+createBegin: (id) aZone
- Make overwrite warnings be on by default.
DblBuffer2d
DblBuffer2d augments Discrete2d to provide a form of double buffered
space. Two lattices are maintained: lattice (the current state), and
newLattice (the future state). All reads take place from lattice, all
writes take place to newLattice. newLattice is copied to lattice when
-updateLattice
is called. DblBuffer2d can be used to implement
one model of concurrent action, like in Ca2ds. NOTE: be very
careful if you're using low-level macro access to the world, in
particular be sure that you preserve the write semantics on the newLattice.
New Methods
-updateLattice
- Copy newLattice to lattice, in effect updating the lattice.
-(id *)getNewLattice
- Like
-(id *)getLattice
: return a pointer to the
newLattice buffer.
Overridden Methods
-createEnd
- Rewrites the method from Discrete2d. Allocate two lattices,
makes the offsets.
-putObject: anObject atX: (int) x Y: (int) y
and
-putValue: (int) value atX: (int) x Y: (int) y
and
- Overridden so writes happen to newLattice.
Ca2d
Inherits from DblBuffer2d, defines abstract protocol for cellular automata.
New Methods
-setNumStates: (int) d
- Record the number of states the CA understands.
-initializeLattice
- Use this to set up your CA to a default initial state.
Unimplemented in Ca2d.
-stepRule
- One iteration of the CA rule. Unimplemented in Ca2d.
Overridden Methods
-createEnd
- Check that numStates has been set.
ConwayLife2d
Classic 2d Conway's Life CA.
Overridden Methods
+createBegin: (id) aZone
- Set number of states to 2.
-initializeLattice
- Initialize lattice to random 1/3 in state 1.
-stepRule
- Run Conway's Life rule (simpleminded version).
Diffuse2d
Discrete 2nd order approximation to 2d diffusion with evaporation.
Math is done in integers on the range [0,0x7fff].
New Methods
-setDiffusionConstant: (double) d
- Set the diffusion constant. Values over 1.0 might not be valid.
-setEvaporationRate: (double) d
- Set the evaporation rate. Values over 1.0 don't make much sense.
Overridden Methods
+createBegin: (id) aZone
- Set diffusion constant and evaporation rate to 1.0, numStates
to 0x7fff.
-initializeLattice
- Initialize world to 0.
-stepRule
- Run discrete approximation to diffusion. Roughly, it's
newHeat = evapRate * (self + diffuseConstant*(nbdavg - self))
where nbdavg is the weighted average of the 8 neighbours.
Object2dDisplay
Object2dDisplay helps display 2d arrays of objects. Create a
Object2dDisplay, give it a Raster widget to draw on, a Discrete2d, a
message to call on each object, and (optionally) a collection of
objects and it will dispatch the message to all objects with the
Raster widget as an argument. In addition, Object2dDisplay can help
you make probees.
Creation Phase
-setDisplayWidget: (Raster *) r
- Set the display widget to use for drawing.
-setDiscrete2dToDisplay: (Discrete2d *) c
- Set the 2d array to draw
-setDisplayMessage: (SEL) s
- Set the message to be sent to each object in the grid to make it
draw itself.
-setObjectCollection: objects
- (optional) set a collection of objects to be displayed. If this
is not given, then Object2dDisplay loops through the 2d grid
sending draw messages to all objects it finds there. Giving an
explicit collection of objects to draw is more efficient if your
grid is sparsely populated.
Use
-display
- Draw all objects in the array (or optionally, the collection)
on the raster widget. All that happens here is the display
message is sent to each object - it is the object's
responsibility to render itself.
-makeProbeAtX: (int) x Y: (int) y
- Find an object at the given (x,y) coordinate and build a probe
display for it. This is a good method to make a button client
for a raster widget like
[aRaster setButtonClient: aObject2dDisplay Message: M(makeProbeAtX:Y:)]
Value2dDisplay
Value2dDisplay helps display 2d arrays of values. Value2dDisplay goes
through a given Discrete2d array, turn states into colours, and draws
them into a Raster widget.
Creation Phase
-setDisplayWidget: (Raster *) r Colormap: (XColormap *) c
- Set the display widget and the colourmap to use to draw the
value array.
-setDiscrete2dToDisplay: (Discrete2d *) c
- Set which array to draw.
-setDisplayMappingM: (int) m C: (int) c
- Linear transform of states to colours for drawing.
color = state / m + c
If not set, assume m == 1 and c == 0.
Use
-display
- Draw the array on the given widget. Note that you still have to
tell the widget to draw itself afterwards. The code for display
uses the fast macro access in Discrete2d on the cached return
value from getLattice. It also caches the
drawPointX:Y:
method lookup on the display widget - this is a nice trick that
you might want to look at.
Nelson Minar <nelson@santafe.edu>
Last modified: Mon May 13 02:51:33 MDT 1996