My objective: learn from the model.
My current role: sanitize/restructure so the code won't overwhelm students
http://ArtStkMkt.sourceforge.net
has release information/patches/CVS archive.
Original Objective-C version written on Next computer by SFI team
Swarm version of code originally by Brandon Weber (version 2.0)
ArtStkMkt site presents patches and justifications for revisions
Agents keep track of the world by strings of bits (actually, "trits"), as in [01 10 00 10 01 10 01 10].
To save memory, this is packed into a 16 bit integer, and the 0's and 1's are manipulated with bit math.Usage example:
myworld[WORD(i)] |= realworld[n] << SHIFT[i];
This accesses the i'th "trit" in a larger sequence,
and adjust it, using a SHIFT macro which is defined elsewhere.
Within the plain C approach, this was necessary because there was no
object to whom one could delegate the bit math.
(void) setConditionsbit: (int) bit To: (int) x
-(int) getConditionsbit: (int)bit
-(void) switchConditionsbit: (int) bit
The "trits" are translated into and from integer values according
to a table like this:
binary value | integer equivalent | meaning |
00 | 0 | # or "don't care" |
01 | 1 | NO |
10 | 2 | YES |
11 | 3 | not in use, a place holder value |
All of the bit math is confined to the BitVector class, all other operations to get or set the bit values are used in a standard object oriented way. Create a conditions object from the BFcast class, and tell that one to set the third bit to integer value 1.
[conditions setConditionsbit: 3 To: 1];
Original Objective-C ASM model used no collections
concepts. It used "raw" C memory allocation
(from sfsm/src/bfagent.m):
1. Declare instance variables for pointers to arrays of structs
struct BF_rule *rule; // array of size numrules2. Allocate a big chunk of memory for the array of structs (p->numrules=integer size)
struct BF_rule *rptrtop; // top of rule array (rule + p->numrules)
3. Step through it with pointer math.
rule = (Rule) getmem(sizeof(RuleStruct)*p->numrules); //gets memory for structs
rptrtop = rule + p->numrules; //pointer math finds the "last" pointer
1. Rewrite "structs" to objects.
2. Use container to keep objects. In this case, a Swarm Array
is used:
fcastList=[Array create: [self getZone] setCount: numfcasts];
3. Use standard Swarm approach to "step through" the collection.
Here are a couple of "before" and "after" examples for comparison:
Before:
struct BF_fcast *fptr, *topfptr; topfptr = fcast + p->numfcasts; for (fptr = fcast; fptr < topfptr; fptr++) { if (fptr->conditions[0] & real0) continue; *nextptr = fptr; nextptr = &fptr->next; } |
After:
id <Index> index=[ fcastList begin: [self getZone]]; for ( aForecast=[index next]; [index getLoc]==Member;
aForecast=[index next] )
|
//Before
//This is an example of a "homemade" list traversal for (fptr=activelist; fptr!=NULL; fptr=fptr->next) { fptr->lastactive = currentTime; if (++fptr->count >= mincount) { ++nactive; if (fptr->strength > maxstrength) { maxstrength = fptr->strength; bestfptr = fptr; } } } |
//After introduction of Swarm collections
index=[activeList begin: [self getZone]]; for( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) { [aForecast setLastactive: currentTime]; if([aForecast incrCount] >= mincount) { double strength=[aForecast getStrength]; ++nactive; if (strength > maxstrength) { maxstrength = strength; bestForecast= aForecast; } } } [index drop]; |
Observe in main.m, agents are created and then the system repeats until a pre-designated time.
// Perform any events scheduled for startup
performEvents();
// Main loop on periods
while (t < lasttime) {
// Either perform a fake warmup period or a normal
one (increments t)
if (t < 0)
warmup();
else
period();
// Perform any scheduled events
performEvents();
}
This is calling functions warmup() and period() that exist in control.m, which in turn loop over agents.
As far as I can see, this scheduling approach is not wrong, but it certainly
isn't very right.
All Swarms that have been created in the program can "throw actions"
onto the belt when desired. When Swarms are created, their "time
scales" mesh because the actions they mandate fit "onto" this chain at
the
appropriate time.
Swarm has standard terminology/structure to assure that:
a repeated action does in fact repeat
several actions thrown onto the belt at the
same time are handled "appropriately".
"Appropriately" means there are ways to control the
order in which actions at the same time are executed. One may design
simulations that randomize actions that are in the same link of the the
conveyor chain, or not.
Swarm provides some standardized graphical displays that make it easier
to monitor and interact with an on-going simulation.
http://ArtStkMkt.sourceforge.net/screenshots/asm-20000530.gif
As the original ASM authors discovered, it is difficult and risky to
write a GUI from scratch!