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