6-001 // Sugarscape in Swarm. Copyright © 1997 Nelson Minar 6-002 // This program is distributed without any warranty; without even the 6-003 // implied warranty of merchantability or fitness for a particular purpose. 6-004 // See file LICENSE for details and terms of copying. 6-005 6-006 // Here we implement the class SugarSpace. 6-007 #import "SugarSpace.h" 6-008 #import "SugarAgent.h" 6-009 6-010 #import // InFile 6-011 #import // strdup 6-012 6-013 @implementation SugarSpace 6-014 6-015 //// The creation of the SugarSpace world. 6-016 //// Here we create a lot of objects to represent the world space. 6-017 - createEnd 6-018 { 6-019 [super createEnd]; 6-020 6-021 // Check that we have a reasonable size. 6-022 if (xsize <= 0 || ysize <= 0) 6-023 [InvalidCombination raiseEvent: 6-024 "SugarSpace was created with an invalid size\n"]; 6-025 // and a datafile 6-026 if (maxSugarDataFile == NULL) 6-027 [InvalidCombination 6-028 raiseEvent: 6-029 "SugarSpace was created without a data file for max sugar.\n"]; 6-030 6-031 // Create the array to represent the sugar itself. 6-032 sugar = [Discrete2d createBegin: [self getZone]]; 6-033 [sugar setSizeX: xsize Y: ysize]; 6-034 sugar = [sugar createEnd]; 6-035 6-036 [self setSugarGrowRate: 1]; 6-037 6-038 #ifndef USE_HDF5 6-039 // Now create an object to represent the maximum sugar values 6-040 maxSugar = [Discrete2d createBegin: [self getZone]]; 6-041 [maxSugar setSizeX: xsize Y: ysize]; 6-042 maxSugar = [maxSugar createEnd]; 6-043 6-044 //the space library's setDiscrete2d:toFile: method returns the max value 6-045 globalMaxSugar = [maxSugar setDiscrete2d: maxSugar toFile: maxSugarDataFile]; 6-046 6-047 6-048 #else 6-049 { 6-050 int findGlobalMaxSugar (id discrete2d) 6-051 { 6-052 int maxval; 6-053 unsigned x, y; 6-054 6-055 maxval = [discrete2d getValueAtX: 0 Y: 0]; 6-056 for (y = 0; y < ysize; y++) 6-057 for (x = 0; x < xsize; x++) 6-058 { 6-059 int val = [discrete2d getValueAtX: x Y: y]; 6-060 6-061 if (val > maxval) 6-062 maxval = val; 6-063 } 6-064 return maxval + 1; 6-065 } 6-066 6-067 maxSugar = [hdf5AppArchiver getObject: "maxSugarDiscrete2d"]; 6-068 if (!maxSugar) 6-069 { 6-070 maxSugar = [Discrete2d createBegin: [self getZone]]; 6-071 [maxSugar setSizeX: xsize Y: ysize]; 6-072 maxSugar = [maxSugar createEnd]; 6-073 } 6-074 globalMaxSugar = findGlobalMaxSugar (maxSugar); 6-075 } 6-076 #endif 6-077 6-078 // Start the sugar out at maximum 6-079 [maxSugar copyDiscrete2d: maxSugar toDiscrete2d: sugar]; 6-080 6-081 // Finally, set up the grid used to represent agent position 6-082 agentGrid = [Grid2d createBegin: [self getZone]]; 6-083 [agentGrid setSizeX: xsize Y: ysize]; 6-084 agentGrid = [agentGrid createEnd]; 6-085 6-086 return self; 6-087 } 6-088 6-089 6-090 //// Update the sugar - this is the dynamics of the sugarspace world. It 6-091 //// is the rule G_alpha explained on page 26. 6-092 - updateSugar 6-093 { 6-094 unsigned int x, y; 6-095 // loop through the world 6-096 for (x = 0; x < xsize; x++) 6-097 { 6-098 for (y = 0; y < ysize; y++) 6-099 { 6-100 int sugarHere = [sugar getValueAtX: x Y: y]; 6-101 int maxSugarHere = [maxSugar getValueAtX: x Y: y]; 6-102 6-103 if (sugarHere + sugarGrowRate < maxSugarHere) 6-104 sugarHere = sugarHere + sugarGrowRate; 6-105 else 6-106 sugarHere = maxSugarHere; 6-107 [sugar putValue: sugarHere atX: x Y: y]; 6-108 } 6-109 } 6-110 6-111 return self; 6-112 } 6-113 6-114 6-115 //// Handle manipulating the sugar in the world. 6-116 6-117 // Read how much sugar is at a particular spot. 6-118 - (SugarValue)getSugarAtX: (int)x Y: (int)y 6-119 { 6-120 x = [self xnorm: x]; 6-121 y = [self ynorm: y]; 6-122 return (SugarValue) [sugar getValueAtX: x Y: y]; 6-123 } 6-124 6-125 // Take sugar from a spot in the world. 6-126 - (SugarValue)takeSugarAtX: (int)x Y: (int)y 6-127 { 6-128 SugarValue sugarHere; 6-129 6-130 x = [self xnorm: x]; 6-131 y = [self ynorm: y]; 6-132 sugarHere = [sugar getValueAtX: x Y: y]; 6-133 [sugar putValue: 0 atX: x Y: y]; 6-134 6-135 return sugarHere; 6-136 } 6-137 6-138 6-139 //// Code to manage agent positions in the sugar scape. Two technical points: 6-140 //// All coordinates are normalized first. This enforces wraparound world 6-141 //// No effort is made here to make sure two agents don't collide. 6-142 6-143 // Return the agent at a particular spot. 6-144 - (SugarAgent *)getAgentAtX: (int)x Y: (int)y 6-145 { 6-146 return [agentGrid getObjectAtX: [self xnorm: x] Y: [self ynorm: y]]; 6-147 } 6-148 6-149 // Add a new agent to the world. Note, this code updates the agent's 6-150 // own idea of where it is. We're violating OO encapsulation here, 6-151 // but that's ok. 6-152 - addAgent: (SugarAgent *)agent atX: (int)x Y: (int)y 6-153 { 6-154 x = [self xnorm: x]; 6-155 y = [self ynorm: y]; 6-156 agent->x = x; 6-157 agent->y = y; 6-158 [agentGrid putObject: agent atX: x Y: y]; 6-159 6-160 return self; 6-161 } 6-162 6-163 // Remove an agent from the world. 6-164 - removeAgent: (SugarAgent *)agent 6-165 { 6-166 int x, y; 6-167 6-168 x = [self xnorm: agent->x]; 6-169 y = [self ynorm: agent->y]; 6-170 if ([self getAgentAtX: x Y: y] == agent) 6-171 [agentGrid putObject: nil atX: x Y: y]; 6-172 6-173 return self; 6-174 } 6-175 6-176 // Move the agent. 6-177 - moveAgent: (SugarAgent *)agent toX: (int)x Y: (int)y 6-178 { 6-179 [self removeAgent: agent]; 6-180 [self addAgent: agent atX: x Y: y]; 6-181 6-182 return self; 6-183 } 6-184 6-185 //// The code below here is not very interesting - it has to do with 6-186 //// the technical details of managing the data in the model, not the 6-187 //// modelling itself. You can safely ignore it until you want to know 6-188 //// the details of how the program works. 6-189 6-190 // handle the size of the world 6-191 - setSizeX: (int)x Y: (int)y 6-192 { 6-193 xsize = x; 6-194 ysize = y; 6-195 6-196 return self; 6-197 } 6-198 6-199 - (int)getSizeX 6-200 { 6-201 return xsize; 6-202 } 6-203 6-204 - (int)getSizeY 6-205 { 6-206 return ysize; 6-207 } 6-208 6-209 - (SugarValue)getGlobalMaxSugar 6-210 { 6-211 return globalMaxSugar; 6-212 } 6-213 6-214 - (id )getAgentGrid 6-215 { 6-216 return agentGrid; 6-217 } 6-218 6-219 - (id )getSugarValues 6-220 { 6-221 return sugar; 6-222 } 6-223 6-224 // accessor for sugar growth rate 6-225 - setSugarGrowRate: (int)r 6-226 { 6-227 sugarGrowRate = r; 6-228 return self; 6-229 } 6-230 6-231 - (int)getSugarGrowRate 6-232 { 6-233 return sugarGrowRate; 6-234 } 6-235 6-236 // Copy the filename into our object 6-237 - setMaxSugarDataFile: (const char *)s 6-238 { 6-239 maxSugarDataFile = strdup (s); 6-240 return self; 6-241 } 6-242 6-243 // normalize coordinates 6-244 - (int)xnorm: (int)x 6-245 { 6-246 if (x < 0) // negative? 6-247 return (x + xsize * 128) % xsize; // make positive, round 6-248 else if (x >= (int)xsize) // too big? round. 6-249 return x % xsize; 6-250 else 6-251 return x; // just right.. 6-252 } 6-253 6-254 - (int)ynorm: (int)y 6-255 { 6-256 if (y < 0) 6-257 return (y + ysize * 128) % ysize; 6-258 else if (y >= (int)ysize) 6-259 return y % ysize; 6-260 else 6-261 return y; 6-262 } 6-263 6-264 6-265 6-266 @end