10-001 // Sugarscape in Swarm. Copyright © 1997-1998 Nelson Minar
10-002 // This program is distributed without any warranty; without even the
10-003 // implied warranty of merchantability or fitness for a particular purpose.
10-004 // See file LICENSE for details and terms of copying.
10-005 
10-006 #import "ObserverSwarm.h"
10-007 #import <gui.h> //Pixmap
10-008 
10-009 
10-010 @implementation ObserverSwarm
10-011 
10-012 // Set up configurable display parameters
10-013 //   how often to update the display,
10-014 + createBegin: aZone
10-015 {
10-016   ObserverSwarm *obj;
10-017   id <ProbeMap> probeMap;
10-018   
10-019   obj = [super createBegin: aZone];
10-020 
10-021   obj->displayFrequency = 1;
10-022   obj->drawPopulationGraph = 1;
10-023   obj->drawWealthHistogram = 1;
10-024   obj->parameterFile = NULL;
10-025 
10-026   probeMap = [EmptyProbeMap createBegin: aZone];
10-027   [probeMap setProbedClass: [self class]];
10-028   probeMap = [probeMap createEnd];
10-029 
10-030   [probeMap addProbe: [probeLibrary getProbeForMessage: "setParameterFile:"
10-031  				    inClass: [self class]]];
10-032   [probeMap addProbe: [probeLibrary getProbeForMessage: "saveParameters:"
10-033 				    inClass: [self class]]];
10-034   [probeMap addProbe: [probeLibrary getProbeForVariable: "drawPopulationGraph"
10-035 				    inClass: [self class]]];
10-036   [probeMap addProbe: [probeLibrary getProbeForVariable: "drawWealthHistogram"
10-037 				    inClass: [self class]]];
10-038   [probeMap addProbe: [probeLibrary getProbeForVariable: "displayFrequency"
10-039 				    inClass: [self class]]];
10-040 
10-041   [probeLibrary setProbeMap: probeMap For: [self class]];
10-042 
10-043   return obj;
10-044 }
10-045 
10-046 - _wealthHistogramDeath_: caller
10-047 {
10-048   [wealthHistogram drop];
10-049   wealthHistogram = nil;
10-050   return self;
10-051 }
10-052 
10-053 - _worldRasterDeath_: caller
10-054 {
10-055   [worldRaster drop];
10-056   worldRaster = nil;
10-057   return self;
10-058 }
10-059 
10-060 // Create all the objects.
10-061 - buildObjects
10-062 {
10-063   int i;
10-064   int maxSugarValue;
10-065   SugarSpace *sugarSpace;
10-066   id agentList;
10-067 
10-068   [super buildObjects];
10-069   
10-070   // Show a probe map so that users can type in a parameter
10-071   // file name if they want. It is ARCHIVED, meaning it will
10-072   // save the window positions if you hit the "save" button.
10-073   CREATE_ARCHIVED_PROBE_DISPLAY (self);
10-074 
10-075  // Set our state to stopped - this code also waits until the user
10-076   // clicks "go" so he has a chance to change parameters.
10-077   [controlPanel setStateStopped];
10-078 
10-079 
10-080   // If a parameter file was entered, use it. 
10-081   if (parameterFile != NULL)
10-082     {
10-083       id archiver = [LispArchiver create: self setPath: parameterFile];
10-084       
10-085 
10-086       if ((modelSwarm = [archiver getWithZone: self
10-087                                             key: "model"]) == nil)
10-088 	raiseEvent(InvalidOperation,
10-089 		   "Can't find the parameters to create modelSwarm");
10-090       [archiver drop];
10-091     }
10-092   else
10-093     {
10-094       modelSwarm = [ModelSwarm create: self];
10-095     }
10-096   // Now create probes for the Swarms so the user can retool settings
10-097   CREATE_ARCHIVED_PROBE_DISPLAY (modelSwarm);
10-098   
10-099   // Stop the control panel again in case users want to revise
10-100   // the settings.
10-101   [controlPanel setStateStopped];
10-102 
10-103   
10-104   // Now tell the model swarm to build its objects.
10-105   [modelSwarm buildObjects];
10-106 
10-107  
10-108   // Read some handy objects out of the model swarm
10-109   sugarSpace = [modelSwarm getSugarSpace];
10-110   agentList = [modelSwarm getAgentList];
10-111 
10-112   // Compute the colourmap
10-113   maxSugarValue = [[modelSwarm getSugarSpace] getGlobalMaxSugar];
10-114 
10-115   fprintf(stderr,"MaxSugarValue %d \n," , maxSugarValue);
10-116 
10-117   colormap = [Colormap create: self];
10-118   // Colours - shades of yellow for sugar values
10-119   for (i = 0; i < maxSugarValue; i++)
10-120     [colormap setColor: i
10-121   	      ToRed: (double) i / (maxSugarValue - 1.0)
10-122   	      Green: (double) i / (maxSugarValue - 1.0)
10-123   	      Blue: 0];
10-124  
10-125 
10-126   // Red for agents. Assigned to the number 100 - the agent needs to
10-127   // know this magic number.
10-128   [colormap setColor: 100 ToName: "red"];	  // agent colour
10-129 
10-130   // Next, create a 2d window for displaying the world.
10-131   worldRaster = [ZoomRaster createBegin: self];
10-132   SET_WINDOW_GEOMETRY_RECORD_NAME (worldRaster);
10-133   worldRaster = [worldRaster createEnd];
10-134   [worldRaster enableDestroyNotification: self
10-135                notificationMethod: @selector (_worldRasterDeath_:)];
10-136   [worldRaster setColormap: colormap];
10-137   [worldRaster setZoomFactor: 6];
10-138   [worldRaster setWidth: [[sugarSpace getAgentGrid] getSizeX]
10-139                Height: [[sugarSpace getAgentGrid] getSizeY]];
10-140   [worldRaster setWindowTitle: "SugarScape"];
10-141   [worldRaster pack];                             // draw the window.
10-142 
10-143   // Create an object to display the sugar values
10-144   sugarDisplay = [Value2dDisplay createBegin: self];
10-145   [sugarDisplay setDisplayWidget: worldRaster colormap: colormap];
10-146   [sugarDisplay setDiscrete2dToDisplay: [[modelSwarm getSugarSpace] getSugarValues]];
10-147   // [sugarDisplay setDisplayMappingM: 1 C: 0];	  // map to colourmap
10-148   sugarDisplay = [sugarDisplay createEnd];
10-149 
10-150   // And an object to display the agents
10-151   agentDisplay = [Object2dDisplay createBegin: self];
10-152   [agentDisplay setDisplayWidget: worldRaster];
10-153   [agentDisplay setDiscrete2dToDisplay: [sugarSpace getAgentGrid]];
10-154   [agentDisplay setObjectCollection: [modelSwarm getAgentList]];
10-155   [agentDisplay setDisplayMessage: M(drawSelfOn:)];   // draw method
10-156   agentDisplay = [agentDisplay createEnd];
10-157 
10-158   // Enable probes on the world.
10-159   [worldRaster setButton: ButtonRight
10-160                Client: agentDisplay
10-161                Message: M(makeProbeAtX:Y:)];
10-162 
10-163   if (drawPopulationGraph)
10-164     {
10-165       // And create a graph of population in the world
10-166       populationGraph = [EZGraph createBegin: self];
10-167       SET_WINDOW_GEOMETRY_RECORD_NAME (populationGraph);
10-168       [populationGraph setTitle: "Population over time"];
10-169       [populationGraph setAxisLabelsX: "time" Y: "population"];
10-170       populationGraph = [populationGraph createEnd];
10-171       
10-172       // One data sequence in the graph - total population
10-173       [populationGraph createSequence: "population"
10-174                        withFeedFrom: agentList
10-175                        andSelector: M(getCount)];
10-176     }
10-177   
10-178   // Create a graph for various agent attributes
10-179   attributeGraph = [EZGraph createBegin: self];
10-180   SET_WINDOW_GEOMETRY_RECORD_NAME (attributeGraph);
10-181   [attributeGraph setTitle: "Agent attributes over time"];
10-182   [attributeGraph setAxisLabelsX: "time" Y: "attribute"];
10-183   attributeGraph = [attributeGraph createEnd];
10-184   
10-185   // Two data sequences here. Average vision for all the bugs
10-186   [attributeGraph createAverageSequence: "vision"
10-187 		  withFeedFrom: agentList
10-188 		  andSelector: M(getVision)];
10-189   // And average metabolism
10-190   [attributeGraph createAverageSequence: "metabolism"
10-191 		  withFeedFrom: agentList
10-192 		  andSelector: M(getMetabolism)];
10-193 
10-194   if (drawWealthHistogram)
10-195     {
10-196       // Create a histogram of agent wealth distribution
10-197       wealthHistogram = [EZBin createBegin: self];
10-198       SET_WINDOW_GEOMETRY_RECORD_NAME (wealthHistogram);
10-199       [wealthHistogram setTitle: "Agent wealth distribution"];
10-200       [wealthHistogram setAxisLabelsX: "wealth" Y: "number of agents"];
10-201       [wealthHistogram setBinCount: 9];
10-202       [wealthHistogram setLowerBound: 0];
10-203       [wealthHistogram setUpperBound: 300];
10-204       [wealthHistogram setCollection: agentList];
10-205       [wealthHistogram setProbedSelector: M(getCurrentSugar)];
10-206       wealthHistogram = [wealthHistogram createEnd];
10-207       [wealthHistogram enableDestroyNotification: self
10-208                        notificationMethod: @selector(_wealthHistogramDeath_:)];
10-209     }
10-210   
10-211   return self;
10-212 }  
10-213 
10-214 - _updateHistogram_
10-215 {
10-216   if (wealthHistogram)
10-217     {
10-218       [wealthHistogram reset];
10-219       [wealthHistogram update];
10-220       [wealthHistogram output];
10-221     }
10-222   return self;
10-223 }
10-224 
10-225 - _updateDisplay_
10-226 {
10-227   if (worldRaster)
10-228     {
10-229       [sugarDisplay display];
10-230       [agentDisplay display];
10-231       [worldRaster drawSelf];
10-232     }
10-233   return self;
10-234 }
10-235 
10-236 - (char*)setParameterFile: (char*)aString
10-237 {
10-238   char temp[100];
10-239   sprintf (temp,"parameters/%s.scm",aString); 
10-240   parameterFile = strdup (temp);
10-241   return parameterFile;
10-242 }
10-243 
10-244       
10-245 - saveParameters: (char*)aString
10-246  {
10-247    char dataArchiveName[100];
10-248    snprintf(dataArchiveName,100,"parameters/%s.scm",aString);
10-249    id dataArchiver = [LispArchiver create: self setPath: dataArchiveName];
10-250    
10-251    [dataArchiver putShallow: "model" object: modelSwarm];
10-252    [dataArchiver sync];
10-253    [dataArchiver drop];
10-254    return self;
10-255  }
10-256 
10-257 
10-258 
10-259 // The display schedule
10-260 - buildActions 
10-261 {
10-262   [super buildActions];
10-263   
10-264   [modelSwarm buildActions];
10-265 
10-266   // The display schedule is just a list of actions in a row
10-267   //   display the sugar
10-268   //   display the agents
10-269   //   update the display itself
10-270   //   update the graphs
10-271   //   update probes
10-272   //   update control panel events
10-273   displayActions = [ActionGroup create: self];
10-274   [displayActions createActionTo: self message: M(_updateDisplay_)];
10-275 
10-276   [displayActions createActionTo: attributeGraph   message: M(step)];
10-277   if (drawPopulationGraph)
10-278     [displayActions createActionTo: populationGraph message: M(step)];
10-279   if (drawWealthHistogram)
10-280     [displayActions createActionTo: self message: M(_updateHistogram_)];
10-281 
10-282   [displayActions createActionTo: probeDisplayManager  message: M(update)];
10-283   [displayActions createActionTo: actionCache          message: M(doTkEvents)];
10-284 #ifdef MAKEMOVIE
10-285   [displayActions createActionTo: self                 message: M(writeFrame)];
10-286 #endif
10-287   
10-288   displaySchedule = [Schedule createBegin: self];
10-289   [displaySchedule setRepeatInterval: displayFrequency];
10-290   displaySchedule = [displaySchedule createEnd];
10-291   [displaySchedule at: 0 createAction: displayActions];
10-292 
10-293   return self;
10-294 }  
10-295 
10-296 // scheduling details. The model swarm is activated here.
10-297 - activateIn: swarmContext
10-298 {
10-299   [super activateIn: swarmContext];
10-300   [modelSwarm activateIn: self];
10-301   [displaySchedule activateIn: self];
10-302   return [self getActivity];
10-303 }
10-304 
10-305 #ifdef MAKEMOVIE
10-306 - writeFrame
10-307 {
10-308   char filename[256];
10-309   id aPixmap;
10-310   sprintf(filename, "%04ld.ppm", getCurrentTime());
10-311   aPixmap = [Pixmap createBegin: self];
10-312   [aPixmap setWidget: worldRaster];
10-313   [aPixmap setDecorationsFlag: NO];
10-314   aPixmap = [aPixmap createEnd];
10-315   [aPixmap save: filename];
10-316   [aPixmap drop];
10-317   return self;
10-318 }
10-319 #endif
10-320 
10-321 @end
