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 //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; 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