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 <simtools.h> // InFile
6-011 #import <misc.h> // 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 <Grid2d>)getAgentGrid
6-215 {
6-216   return agentGrid;
6-217 }
6-218 
6-219 - (id <Discrete2d>)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
