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 4-008 #import "ModelSwarm.h" 4-009 #import 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 )r 4-232 { 4-233 [r drawPointX: x Y: y Color: 100]; 4-234 return self; 4-235 } 4-236 4-237 @end