4-001 // Sugarscape in Swarm. Copyright © 1997 Nelson Minar
4-002 // This program is distributed without any warranty; without even the
4-003 // implied warranty of merchantability or fitness for a particular purpose.
4-004 // See file LICENSE for details and terms of copying.
4-005 
4-006 #import "SugarAgent.h"
4-007 #import <simtools.h>
4-008 #import "ModelSwarm.h"
4-009 #import <gui.h>
4-010 
4-011 @implementation SugarAgent
4-012 
4-013 // One "step" for an agent. Depending on the rules in effect, this step
4-014 //   could have a lot of different meanings.
4-015 - step
4-016 {
4-017   // The agent moves to a nearby spot.
4-018   [self moveToBestOpenSpot];
4-019 
4-020   // Aspects of an agents lifecycle - eating, metabolism, death.
4-021   // Eat the sugar at the current spot
4-022   currentSugar += [sugarSpace takeSugarAtX: x Y: y];
4-023 
4-024   // Spend the sugar we need to stay alive
4-025   currentSugar -= metabolism;
4-026 
4-027   // I'm now one year older
4-028   age++;
4-029 
4-030   // Check if I'm dying
4-031   if (currentSugar <= 0 || age >= deathAge)
4-032     {
4-033       [sugarSpace removeAgent: self];
4-034       [modelSwarm agentDeath: self];
4-035     }
4-036   
4-037   return self;
4-038 }
4-039 
4-040 // This is rule "M" described in the Sugarscape book.
4-041 //   Briefly: look around for the closest, empty spot with the most food
4-042 //     that is within our vision range and move there.
4-043 //   The algorithm for this behaviour is complicated because we want
4-044 //     to make sure that if there are several spots that are equally good,
4-045 //     that we give them all an equal chance.
4-046 
4-047 // A dumb macro to get the absolute value of an integer
4-048 #define intabs(a) ((a) < 0 ? -(a) : (a))
4-049 
4-050 - moveToBestOpenSpot
4-051 {
4-052   int xLook, yLook;
4-053   SugarValue bestSugar;
4-054   int bestDistance;
4-055   int goodSpots;
4-056   int goodX[16], goodY[16];			  // 4 should be adequate
4-057   int chosenSpot, newX, newY;
4-058   
4-059   // prime the algorithm with out of range values
4-060   bestSugar = -1;
4-061   goodSpots = 0;
4-062   bestDistance = 999999;			  // big number
4-063 
4-064   // First, look in the X direction for good spots.
4-065   yLook = y;
4-066   for (xLook = x - vision; xLook <= x + vision; xLook++)
4-067     {
4-068       // Is the spot currently empty?
4-069       if ([sugarSpace getAgentAtX: xLook Y: yLook] == nil)
4-070         {
4-071           // is the spot we're looking at the best we've ever seen?
4-072           if ([sugarSpace getSugarAtX: xLook Y: yLook] > bestSugar)
4-073             {
4-074               // yes, best spot ever. Forget everything else, record this
4-075               // as the only good spot
4-076               bestSugar = [sugarSpace getSugarAtX: xLook Y: yLook];
4-077               bestDistance = intabs(x - xLook);
4-078               goodSpots = 0;
4-079               goodX[0] = xLook;
4-080               goodY[0] = yLook;
4-081               goodSpots++;
4-082             }
4-083           else if ([sugarSpace getSugarAtX: xLook Y: yLook] == bestSugar)
4-084             {
4-085               // No, it's only as good as anything else we've seen. Is it closer
4-086               // than any other spot we've seen with this sugar?
4-087               if (intabs(x - xLook) < bestDistance) {
4-088                 // Yes, forget all the rest - this is the only good spot
4-089                 bestDistance = intabs(x - xLook);
4-090                 goodSpots = 0;
4-091                 goodX[0] = xLook;
4-092                 goodY[0] = yLook;
4-093                 goodSpots++;
4-094               }
4-095               else if (intabs (x - xLook) == bestDistance)
4-096                 {
4-097                   // No, this spot is as good as some other one. Just add this
4-098                   // one on as a good spot.
4-099                   goodX[goodSpots] = xLook;
4-100                   goodY[goodSpots] = yLook;
4-101                   goodSpots++;
4-102                 }
4-103             } 
4-104         }
4-105     }
4-106   
4-107   // Now repeat the same choice in the Y axis.
4-108   xLook = x;
4-109   for (yLook = y - vision; yLook <= y + vision; yLook++)
4-110     {
4-111       if ([sugarSpace getAgentAtX: xLook Y: yLook] == nil)
4-112         {
4-113           if ([sugarSpace getSugarAtX: xLook Y: yLook] > bestSugar)
4-114             {
4-115               bestSugar = [sugarSpace getSugarAtX: xLook Y: yLook];
4-116               bestDistance = intabs(y - yLook);
4-117               goodSpots = 0;
4-118               goodX[0] = xLook;
4-119               goodY[0] = yLook;
4-120               goodSpots++;
4-121             }
4-122           else if ([sugarSpace getSugarAtX: xLook Y: yLook] == bestSugar)
4-123             {
4-124               if (intabs(y - yLook) < bestDistance)
4-125                 {
4-126                   bestDistance = intabs(y - yLook);
4-127                   goodSpots = 0;
4-128                   goodX[0] = xLook;
4-129                   goodY[0] = yLook;
4-130                   goodSpots++;
4-131                 }
4-132               else if (intabs(y - yLook) == bestDistance)
4-133                 {
4-134                   goodX[goodSpots] = xLook;
4-135                   goodY[goodSpots] = yLook;
4-136                   goodSpots++;
4-137                 }
4-138             } 
4-139         }
4-140     }
4-141   
4-142   // A bit of debug printing to make sure the agents are behaving sensibly.
4-143   // (turned off normally)
4-144 #ifdef DEBUG
4-145   {
4-146     int i;
4-147     printf("Found %d good spots\n", goodSpots);
4-148     for (i = 0; i < goodSpots; i++)
4-149       printf("  (%d,%d) = %d\n", goodX[i], goodY[i],
4-150 	     [sugarSpace getSugarAtX: goodX[i] Y: goodY[i]]);
4-151   }
4-152 #endif
4-153   
4-154   // Alright, goodX[] and goodY[] record the best spots we've found.
4-155   // Let's figure out where to move.
4-156 
4-157   if (goodSpots == 0)				  // No spots are good
4-158     ;						  // don't even move
4-159   else
4-160     {
4-161       if (goodSpots == 1)				  // only one good spot
4-162         chosenSpot = 0;
4-163       else					  // pick a random spot
4-164         chosenSpot = [uniformIntRand getIntegerWithMin: 0 withMax: goodSpots-1];
4-165       newX = goodX[chosenSpot];			  // get the coordinate
4-166       newY = goodY[chosenSpot];			  // and move there!
4-167       [sugarSpace moveAgent: self toX: newX Y: newY];
4-168     }
4-169   return self;
4-170 }
4-171 
4-172 //// The code below here is not very interesting - it has to do with
4-173 //// the technical details of managing the data in the model, not the
4-174 //// modelling itself. You can safely ignore it until you want to know
4-175 //// the details of how the program works.
4-176 
4-177 // set methods for various values
4-178 - setModelSwarm: s
4-179 {
4-180   modelSwarm = s;
4-181   sugarSpace = [s getSugarSpace];
4-182   return self;
4-183 }
4-184 
4-185 - (SugarValue)getCurrentSugar
4-186 {
4-187   return currentSugar;
4-188 }
4-189 
4-190 - setCurrentSugar: (SugarValue)cs
4-191 {
4-192   currentSugar = cs;
4-193   return self;
4-194 }
4-195 
4-196 - (int)getMetabolism
4-197 {
4-198   return metabolism;
4-199 }
4-200 
4-201 - setMetabolism: (int)m
4-202 {
4-203   metabolism = m;
4-204   return self;
4-205 }
4-206 
4-207 - (int)getVision
4-208 {
4-209   return vision;
4-210 }
4-211 
4-212 - setVision: (int)v
4-213 {
4-214   vision = v;
4-215   return self;
4-216 }
4-217 
4-218 - (int)getAge
4-219 {
4-220   return age;
4-221 }
4-222 
4-223 - setDeathAge: (int)s
4-224 {
4-225   deathAge = s;
4-226   return self;
4-227 }
4-228 
4-229 // Graphics code - how to draw oneself (hardcoded colour value here. If
4-230 // agents have different properties, these colour should be different.)
4-231 - drawSelfOn: (id <Raster>)r
4-232 {
4-233   [r drawPointX: x Y: y Color: 100];
4-234   return self;
4-235 }
4-236 
4-237 @end
