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 14-009 #import "ModelSwarm.h" 14-010 #import 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 )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