# swarmevolve-2.0.tz # SwarmEvolve, Version 2.0 # Code for evolving swarms of goal-directed agents # c) 2003, Lee Spector (lspector@hampshire.edu, http://hampshire.edu/lspector) # Contributions by Jon Klein (jklein@artificial.com) # # This code implements the "2.0" version of SwarmEvolve described in # "Emergence of Collective Behavior in Evolving Populations of Flying Agents," # by Lee Spector, Jon Klein, Chris Perry, and Mark Feinstein, in the # Proceedings of the Genetic and Evolutionary Computation Conference # (GECCO-2003), to be published by Springer-Verlag. # # This code requires Breve 1.5 or better, which can be obtained from: # http://www.spiderland.org/breve # # To run with current versions of Breve this code also requires the Push # interpreter plugin and associated files. (The Push plugin may be incorporated # into Breve in future releases.) The full package can be obtained from # http://hampshire.edu/lspector/swarmevolve2push.tgz. # # A user interface, provided via SwarmInterface.nib, is available only when # running under MacOS X. @path "classes" @nib_file "SwarmInterface". @include "Object.tz" @include "Stationary.tz" @include "Control.tz" @include "Mobile.tz" @include "Push.tz" # Simulation parameters. Additional parameters defined as controller variables, below. @define MAX_RANDOM_CODE_SIZE 30. @define MAX_CODE_SIZE 50. @define MAX_MUTATION_NEW_CODE_SIZE 15. @define PUSH_EXECUTION_LIMIT 50. @define FEEDERS 20. @define FOOD_EDIBILITY_THRESHOLD 0.05. @define FOOD_BITE_SIZE 0.05. @define BIRD_ENERGY_PER_FOOD_ENERGY 10. @define FOOD_GROWTH_RATE 0.001. @define FEEDER_RANGE 20. @define MIN_BIRDS 10. @define BOOTSTRAP_LIMIT 0. # Helps attain reproductive competence; not used here. @define EPOCH_LENGTH 100. @define HUE_SIMILARITY_THRESHOLD 0.1. @define BIRTH_COST 0.15. @define BIRTH_DISTANCE 1.0. @define MAX_LIFETIME 150. @define COST_OF_LIVING 0.0001. @define COLLISION_COST 0.01. @define SPEED_LIMIT 2. @define CORPSE_LIFETIME 10. @define INITIAL_STABILITY 1100. @define RANDOM_WALK_UNIT 0.1. @define RANDOM_WALK_DEFAULT 0. @define NEIGHBORHOOD_SIZE 1.0. @define TIME_SLIP 0. @define RANDI_LIMIT 100. @define RANDF_LIMIT 1.0. @define RANDV_LIMIT (1.0, 1.0, 1.0). Controller Swarm. Control : Swarm { + variables: # simulation parameters stability (int). # objects in the world feeders (list). # interface-related variables cloudTexture (int). selection (object). autoCameraMode (int). autoCameraMenu (object). manualCameraMenu (object). drawEveryFrameMenu (object). skipFramesIfNecessaryMenu (object). prevCameraAim (vector). prevCameraZoom (float). cameraHasBeenAimed (int). maxZoomDelta (float). maxAimDelta (float). provisionalNewZoom (float). corpseShape (object). unitDriftMode (int). friendExploits (int). otherExploits (int). # world status and data bookkeeping nextID (int). iteration (int). births (int). deaths (int). foodAccum (float). decisions (int). foodDeviations (float). friendFeedings (int). otherFeedings (int). randomBirdsAdded (int). reproductiveDiscounts (int). reproMutations (int). reproMutationRates (float). discrepancies (float). naturalBirths (int). servos (int). runID (string). # generic variables item (object). # debug print-velocities-var (int). shareMode (int). # debug + to print-velocities: return print-velocities-var. + to do-print-velocities: print-velocities-var = 1. + to dont-print-velocities: print-velocities-var = 0. # returns the active share mode. + to get-share-mode: return shareMode. # external access + to increment-friendExploits: friendExploits = friendExploits + 1. + to increment-otherExploits: otherExploits = otherExploits + 1. + to increment-friendFeedings: friendFeedings = friendFeedings + 1. + to increment-otherFeedings: otherFeedings = otherFeedings + 1. + to get-decisions: return decisions. + to set-decisions to d (int): decisions = d. + to get-foodDeviations: return foodDeviations. + to set-foodDeviations to d (float): foodDeviations = d. + to get-feeders: return feeders. + to get-stability: return stability. + to set-stability to value (int): stability = value. + to get-corpseShape: return corpseShape. + to get-nextID: return nextID. + to increment-nextID: nextID += 1. + to increment-births: births += 1. + to increment-deaths: deaths += 1. + to increment-reproductiveDiscounts: reproductiveDiscounts += 1. + to increment-reproMutations: reproMutations += 1. + to increment-reproMutationRates with r (int): reproMutationRates += r. + to increment-naturalBirths: naturalBirths += 1. + to increment-discrepancies by d (float): discrepancies += d. + to random-feeder: return feeders{random[feeders - 1]}. + to get-unitDriftMode: return unitDriftMode. + to set-unitDriftMode to m (int): unitDriftMode = m. + to increment-servos: servos += 1. ########################################################################## # interface stuff # stability interface elements + to catch-interface-id-100 with-string s (string): controller set-stability to s. self set-interface-item with-id 102 to-string s. + to catch-interface-id-102 with-string s (string): controller set-stability to s. self set-interface-item with-id 100 to-string s. # auto camera mode interface element + to catch-interface-id-300 with-string s (string): if s == 1: (self set-autoCameraMode). else if s == 0: (self set-manualCameraMode). + to set-autoCameraMode: autoCameraMode = 1. autoCameraMenu check. manualCameraMenu uncheck. + to set-manualCameraMode: autoCameraMode = 0. autoCameraMenu uncheck. manualCameraMenu check. # unit drift distance (random walk) interface element + to catch-interface-id-600 with-string s (string): controller set-unitDriftMode to s. + to set-drawEveryFrame: drawEveryFrameMenu check. skipFramesIfNecessaryMenu uncheck. self enable-draw-every-frame. + to set-skipFramesIfNecessaryMenu: drawEveryFrameMenu uncheck. skipFramesIfNecessaryMenu check. self disable-draw-every-frame. + to scode: ((controller get-selection) get-pushCode) dump. + to dump-programs: b (object). foreach b in all Birds: (b get-pushCode) dump. + to display-food-supply: total (float). displayTotal (float). feeder (object). total = 0.0. foreach feeder in feeders: { total = total + (feeder get-energy). } total = total / feeders. displayTotal = total * 100. self set-interface-item with-id 101 to-string "$displayTotal". foodAccum = foodAccum + total. + to display-population: popsize (int). popsize = all Birds. self set-interface-item with-id 123 to-string "$popsize". + to display-iteration: self set-interface-item with-id 124 to-string "$iteration". + to click on item (object): #if selection: selection hide-neighbor-lines. #if item: item show-neighbor-lines. selection = item. if item && (item is a "Bird"): (item get-pushCode) dump. super click on item. ########################################################################## + to init: floor (object). i (int). # Parameters. stability = INITIAL_STABILITY. # when run from the command-line, the share mode (a number 0 through 4) # is the SECOND argument. the first argument is a runID string which # doesn't effect the simulation. if (self get-argument-count) > 1: { runID = (self get-argument at-index 1). shareMode = (self get-argument at-index 2). print "running with runID = $runID". print "running with shareMode = $shareMode". } # if this code isn't run from the command-line, you'd want to set the # shareMode variable here manually. # shareMode = ? . # Create the birds and feeders. nextID = 0. births = 0. MIN_BIRDS new Birds. (all Birds) initialize-randomly. feeders = FEEDERS new Feeders. randomBirdsAdded = MIN_BIRDS. # Create the floor. floor = new Stationary. floor register with-shape (new Shape init-with-cube size (100, 2, 100)) at-location (0, -5, 0). floor catch-shadows. # Initialize other variables. deaths = 0. decisions = 0. foodDeviations = 0. friendFeedings = 0. otherFeedings = 0. reproductiveDiscounts = 0. reproMutations = 0. reproMutationRates = 0.0. discrepancies = 0.0. naturalBirths = 0. corpseShape = ((new Shape) init-with-polygon-cone radius .06 sides 5 height .2). servos = 0. #self disable-freed-instance-protection. ################################################## # INITIALIZE INTERFACE # set up the lighting self enable-lighting. self move-light to (0, 20, 20). # camera control menus autoCameraMenu = (self add-menu named "Automatic Camera Control" for-method "set-autoCameraMode"). manualCameraMenu = (self add-menu named "Manual Camera Control" for-method "set-manualCameraMode"). autoCameraMode = 1. autoCameraMenu check. manualCameraMenu uncheck. unitDriftMode = RANDOM_WALK_DEFAULT. i = RANDOM_WALK_DEFAULT. self set-interface-item with-id 666 to-string "$i". # draw every frames menus self add-menu-separator. drawEveryFrameMenu = (self add-menu named "Draw Every Frame" for-method "set-drawEveryFrame"). skipFramesIfNecessaryMenu = (self add-menu named "Skip Frames if Necessary" for-method "set-skipFramesIfNecessaryMenu"). drawEveryFrameMenu check. skipFramesIfNecessaryMenu uncheck. # Set up the background. cloudTexture = (self load-image from "clouds.sgi"). self set-background-texture to cloudTexture. # Set some camera/display options. self offset-camera by (5, 1.5, 6). cameraHasBeenAimed = 0. maxZoomDelta = 0.05. maxAimDelta = 0.05. self enable-shadows. #self enable-reflections. self disable-text. ################################################## + to report: item, item2 (object). avgFood (float). devFood (float). numOthers (float). #declared float to force floating point ops hueDiversity (float). sizeDiversity (float). avProgSize (float). numBirds (int). i, fCount, oCount (int). numBirds = (all Birds). # calculate average food depletion avgFood = foodAccum / EPOCH_LENGTH. # calculate decision deviations from food if decisions == 0: { devFood = 0.0. } else { devFood = foodDeviations / decisions. } # calculate hue diversity hueDiversity = 0.0. foreach item in all Birds: { numOthers = 0. foreach item2 in all Birds: if (item is-other from item2): numOthers += 1. hueDiversity += numOthers / (numBirds - 1). } hueDiversity /= numBirds. # calculate size diversity sizeDiversity = 0.0. foreach item in all Birds: { fCount += (item ask-if-friend-feeder). oCount += (item ask-if-other-feeder). if item: { numOthers = 0. foreach item2 in all Birds: if item2: if ((item get-pushCode) get-size) == ((item2 get-pushCode) get-size) == 0: numOthers += 1. sizeDiversity += numOthers / (numBirds - 1). } } sizeDiversity /= numBirds. # calculate average program size avProgSize = 0.0. foreach item in all Birds: if item: avProgSize += ((item get-pushCode) get-size). avProgSize = avProgSize / numBirds. # calculate diversification if naturalBirths > 0: { discrepancies /= naturalBirths. reproMutationRates /= naturalBirths. # now convert to a more normal mutation rate format reproMutationRates = 1 / (reproMutationRates + 1). } print "iteration: $iteration, population size: $numBirds, hue diversity: $hueDiversity, size diversity: $sizeDiversity, average program size: $avProgSize". print " [ epoch: b: $births, d: $deaths, spnt: $randomBirdsAdded, rmut: $reproMutations, rate: $reproMutationRates, diversification: $discrepancies, food: $avgFood, fdev: $devFood, feed/f: $friendFeedings, /o: $otherFeedings, discounts: $reproductiveDiscounts, servos: $servos, fCount: $fCount, oCount: $oCount ]". print " ". # update epoch stats in GUI i = iteration / EPOCH_LENGTH. self set-interface-item with-id 699 to-string "$i". self set-interface-item with-id 700 to-string "$births". self set-interface-item with-id 698 to-string "$randomBirdsAdded". self set-interface-item with-id 701 to-string "$deaths". self set-interface-item with-id 702 to-string "$reproMutations". self set-interface-item with-id 703 to-string "$reproMutationRates". self set-interface-item with-id 704 to-string "$discrepancies". self set-interface-item with-id 705 to-string "$friendFeedings". self set-interface-item with-id 706 to-string "$otherFeedings". self set-interface-item with-id 707 to-string "$servos". # reset counter variables births = 0. deaths = 0. foodAccum = 0. foodDeviations = 0. decisions = 0. friendFeedings = 0. otherFeedings = 0. friendExploits = 0. otherExploits = 0. randomBirdsAdded = 0. reproductiveDiscounts = 0. reproMutations = 0. reproMutationRates = 0.0. discrepancies = 0.0. naturalBirths = 0. servos = 0. if runID: { # self save-as-xml file "$runID.xml". } + to iterate: item (object). swarmCenter (vector). numBirds (int). numBirds = (all Birds). # Keep track of the iteration number. iteration = iteration + 1. if iteration > 6000: die "$iteration iterations complete". # Compute neighbors for later use. self update-neighbors. # Fly and adjust camera. swarmCenter = (0, 0, 0). foreach item in all Birds: { if item: item fly. if item: swarmCenter += (item get-location). } swarmCenter /= numBirds. if autoCameraMode: self adjust-camera with-location swarmCenter. # Adjust bird sizes foreach item in all Birds: item adjust-size. # Handle deaths foreach item in all Birds: if ((item get-energy) == 0) || ((item get-age) > MAX_LIFETIME): item drop-dead. # Add random birds if necessary self add-random-birds-if-necessary. # Move the feeders occasionally. feeders maybe-teleport. # Report. # do per-iteration interface updates controller display-food-supply. controller display-population. controller display-iteration. # at the end of each epoch do an epoch report if (iteration % EPOCH_LENGTH == 0): self report. # Call the superclass iterate method to step the simulation forward. super iterate. + to add-random-birds-if-necessary: numBirds (int). numBirds = (all Birds). if numBirds < MIN_BIRDS: { (new Bird) initialize-randomly. randomBirdsAdded += 1. self add-random-birds-if-necessary. } + to adjust-camera with-location location (vector): topDiff (float). topDiff = 0.0. foreach item in all Birds: { if topDiff < |location - (item get-location) |: topDiff = | location - (item get-location) |. } # throttled camera motion code provisionalNewZoom = (.5 * topDiff) + 10. if cameraHasBeenAimed == 0: { self aim-camera at location. self zoom-camera to provisionalNewZoom. prevCameraAim = location. prevCameraZoom = provisionalNewZoom. cameraHasBeenAimed = 1. } else { if |prevCameraAim - location| < maxAimDelta: { self aim-camera at location. prevCameraAim = location. } else { self aim-camera at (((location - prevCameraAim) / |location - prevCameraAim|) * maxAimDelta) + prevCameraAim. prevCameraAim = (((location - prevCameraAim) / |location - prevCameraAim|) * maxAimDelta) + prevCameraAim. } if abs(prevCameraZoom - provisionalNewZoom) < maxZoomDelta: { self zoom-camera to provisionalNewZoom. prevCameraZoom = provisionalNewZoom. } else { if prevCameraZoom - provisionalNewZoom < 0: { self zoom-camera to prevCameraZoom + maxZoomDelta. prevCameraZoom = prevCameraZoom + maxZoomDelta. } else { self zoom-camera to prevCameraZoom - maxZoomDelta. prevCameraZoom = prevCameraZoom - maxZoomDelta. } } } } # end of controller methods Mobile : Feeders { + variables: drifting (int). driftLocation (vector). energy (float). lastScale (float). shape (object). + to init: shape = ((new Shape) init-with-sphere radius 0.3). self register with-shape shape. self move to random[(30, 0, 30)] - (15, 0, 15). self set-velocity to (0, 0, 0). energy = 1. self set-color to (1, 1, 1). lastScale = 1. + to maybe-teleport: loc (vector). if random[(controller get-stability)] == 0: { if (controller get-unitDriftMode): { # linear drift loc = (self get-location). self drift to (loc::x + (RANDOM_WALK_UNIT * (random[2] - 1)), 0, loc::z + (RANDOM_WALK_UNIT * (random[2] - 1))). } else { # random walk self drift to random [(FEEDER_RANGE, 0, FEEDER_RANGE)] - (FEEDER_RANGE / 2, 0, FEEDER_RANGE / 2). } } + to iterate: newScale (float). radius (float). if drifting: if (controller get-unitDriftMode): { self move to driftLocation. drifting = 0. } else { self offset by .06 * (driftLocation - (self get-location)). # 0.06 if (|driftLocation - (self get-location)| < .001): { self move to driftLocation. drifting = 0. } } # growth energy = energy + FOOD_GROWTH_RATE. if energy > 1: energy = 1. radius = sqrt(energy). newScale = ((radius * 2) + 0.00001) / lastScale. shape scale by (newScale, newScale, newScale). lastScale = (radius * 2) + 0.00001. + to adjust-energy delta d (float): energy = energy + d. if energy < 0: energy = 0. if energy > 1: energy = 1. + to get-energy: return energy. + to drift to location (vector): drifting = 1. driftLocation = location. } # end of Feeder methods Mobile : Corpse { + variables: age (int). landed (int). + to init: self register with-shape (controller get-corpseShape). self handle-collisions with-type "Stationary" with-method "land". self set-color to (0.2, 0.2, 0.2). age = 0. landed = 0. + to iterate: age = age + 1. if landed == 0: self set-acceleration to (0, -10, 0). if age > CORPSE_LIFETIME: free(self). + to land with ground (object): # if we hit the ground, we stop moving and set the landed flag to 1. self set-acceleration to (0, 0, 0). self set-velocity to (0, 0, 0). #self point vertex (0, 1, 0) at (0, 1, 0). landed = 1. # we don't want to keep colliding with the ground, or else we'll # keep on getting stuck again at every iteration--move up just a # tiny bit. self offset by (0, 0.1, 0). } # end of Corpse methods Mobile : Bird (aka Birds) { + variables: ID (int). landed (int). energy (float). age (int). shape (object). lastScale (float). hue (float). pushCode (object). pushInterpreter (object). servoSetpoint (float). servoGain (float). friendFeeder (int). otherFeeder (int). # access + to get-pushCode: return pushCode. + to get-ID: return ID. + to get-hue: return hue. + to set-hue to h (float): hue = h. + to set-pushCode to c (object): pushCode = c. + to get-pushInterpreter: return pushInterpreter. + to get-energy: return energy. + to set-energy to e (float): energy = e. + to get-age: return age. + to get-fed-by-friend: if !friendFeeder: (controller increment-friendExploits). + to get-fed-by-other: if !otherFeeder: (controller increment-otherExploits). + to ask-if-friend-feeder: return friendFeeder. + to ask-if-other-feeder: return otherFeeder. + to adjust-energy delta d (float): energy = energy + d. if energy < 0: energy = 0. if energy > 1: energy = 1. + to init: # register the object, set the initial location, velocity and color. shape = ((new Shape) init-with-polygon-cone radius .1 sides 5 height .2). self register with-shape shape. self handle-collisions with-type "Stationary" with-method "land". self handle-collisions with-type "Feeders" with-method "eat". self handle-collisions with-type "Birds" with-method "bump". self set-neighborhood-size to NEIGHBORHOOD_SIZE. age = 0. lastScale = 1. controller increment-births. servoSetpoint = 0.5. servoGain = 0.1. ID = (controller get-nextID). controller increment-nextID. pushInterpreter = new PushInterpreter. pushInterpreter add-instruction named "mutate" for-instance self for-method "mutate". pushInterpreter add-instruction named "crossover" for-instance self for-method "crossover". pushInterpreter add-instruction named "spawn" for-instance self for-method "spawn". pushInterpreter add-instruction named "randI" for-instance self for-method "randI". pushInterpreter add-instruction named "randF" for-instance self for-method "randF". pushInterpreter add-instruction named "randV" for-instance self for-method "randV". pushInterpreter add-instruction named "randC" for-instance self for-method "randC". pushInterpreter add-instruction named "setServoSetpoint" for-instance self for-method "setServoSetpoint". pushInterpreter add-instruction named "setServoGain" for-instance self for-method "setServoGain". pushInterpreter add-instruction named "servo" for-instance self for-method "servo". pushInterpreter add-instruction named "toFood" for-instance self for-method "toFood". pushInterpreter add-instruction named "foodIntensity" for-instance self for-method "foodIntensity". pushInterpreter add-instruction named "myAge" for-instance self for-method "myAge". pushInterpreter add-instruction named "myEnergy" for-instance self for-method "myEnergy". pushInterpreter add-instruction named "myHue" for-instance self for-method "myHue". pushInterpreter add-instruction named "myVelocity" for-instance self for-method "myVelocity". pushInterpreter add-instruction named "myLocation" for-instance self for-method "myLocation". pushInterpreter add-instruction named "myProgram" for-instance self for-method "myProgram". pushInterpreter add-instruction named "toFriend" for-instance self for-method "toFriend". pushInterpreter add-instruction named "friendAge" for-instance self for-method "friendAge". pushInterpreter add-instruction named "friendEnergy" for-instance self for-method "friendEnergy". pushInterpreter add-instruction named "friendHue" for-instance self for-method "friendHue". pushInterpreter add-instruction named "friendVelocity" for-instance self for-method "friendVelocity". pushInterpreter add-instruction named "friendLocation" for-instance self for-method "friendLocation". pushInterpreter add-instruction named "friendProgram" for-instance self for-method "friendProgram". pushInterpreter add-instruction named "feedFriend" for-instance self for-method "feedFriend". pushInterpreter add-instruction named "toOther" for-instance self for-method "toOther". pushInterpreter add-instruction named "otherAge" for-instance self for-method "otherAge". pushInterpreter add-instruction named "otherEnergy" for-instance self for-method "otherEnergy". pushInterpreter add-instruction named "otherHue" for-instance self for-method "otherHue". pushInterpreter add-instruction named "otherVelocity" for-instance self for-method "otherVelocity". pushInterpreter add-instruction named "otherLocation" for-instance self for-method "otherLocation". pushInterpreter add-instruction named "otherProgram" for-instance self for-method "otherProgram". pushInterpreter add-instruction named "feedOther" for-instance self for-method "feedOther". pushInterpreter set-evaluation-limit to PUSH_EXECUTION_LIMIT. pushInterpreter set-list-limit to MAX_CODE_SIZE. pushCode = new PushCode. + to initialize-randomly: pushCode make-random-code max-length MAX_RANDOM_CODE_SIZE with-interpreter pushInterpreter. self move to random[(10, 0, 10)] - (5, 3, 5). self set-velocity to (0, 0, 0). #self point vertex (0, 1, 0) at random[(1, 1, 1)] - (0.5, 0.5, 0.5). hue = random[1.0]. self set-color to (controller get-rgb-color for-hsv-color (hue * 360, 1, 1)). energy = 0.8 + random[0.2]. # added push instructions + to toFood: to-closest-food (vector). to-this-food (vector). item (object). highest-strength (float). strength-this-food (float). highest-strength = 0. foreach item in (controller get-feeders): { to-this-food = ((item get-location) - (self get-location)). strength-this-food = (item get-energy) / ((|to-this-food| * |to-this-food|) + 0.001). # to avoid /0 if strength-this-food > highest-strength: { highest-strength = strength-this-food. to-closest-food = to-this-food. } } # push it onto the vector stack pushInterpreter push-vector value to-closest-food. + to foodIntensity: to-closest-food (vector). to-this-food (vector). item (object). highest-strength (float). strength-this-food (float). highest-strength = 0. foreach item in (controller get-feeders): { to-this-food = ((item get-location) - (self get-location)). strength-this-food = (item get-energy) / ((|to-this-food| * |to-this-food|) + 0.001). # to avoid /0 if strength-this-food > highest-strength: { highest-strength = strength-this-food. to-closest-food = to-this-food. } } # push it onto the vector stack pushInterpreter push-float value highest-strength. + to myAge: pushInterpreter push-integer value age. + to myEnergy: pushInterpreter push-float value energy. + to myHue: pushInterpreter push-float value hue. + to myVelocity: pushInterpreter push-vector value (self get-velocity). + to myLocation: pushInterpreter push-vector value (self get-location). + to myProgram: c (object). c = new PushCode. c parse code (pushCode get-string) with-interpreter pushInterpreter. pushInterpreter push-code value c. free(c). + to toFriend: pushInterpreter push-vector value (((self closest-friend) get-location) - (self get-location)). + to friendAge: pushInterpreter push-integer value ((self closest-friend) get-age). + to friendEnergy: pushInterpreter push-integer value ((self closest-friend) get-energy). + to friendHue: pushInterpreter push-integer value ((self closest-friend) get-hue). + to friendVelocity: pushInterpreter push-vector value ((self closest-friend) get-velocity). + to friendLocation: pushInterpreter push-vector value ((self closest-friend) get-location). + to friendProgram: c (object). c = new PushCode. c parse code (((self closest-friend) get-pushCode) get-string) with-interpreter pushInterpreter. pushInterpreter push-code value c. free(c). + to feedFriend: bird (object). mode (int). # we've now executed the feedFriend instruction, so we are a friendFeeder friendFeeder = 1. bird = (self closest-friend). # log the (attempted) feeding event controller increment-friendFeedings. mode = (controller get-share-mode). # mode #4 is the "noop" feeding -- just return if mode == 4: return. # modes #2 (mutual-feeding) and #3 (mutual-feeding + kin) both require # that the recipient is a friend if (mode == 2 || mode == 3) && !(bird ask-if-friend-feeder): return. # mode #3 requires that a certain level of kinship is met if mode == 3 && ((bird get-pushCode) compute-discrepancy from pushCode) > 9: return. if (bird get-energy) < energy: { self adjust-energy delta -0.01. # mode #0 is a throw-away -- we're penalized, but return before the actual feeding if mode == 0: return. bird adjust-energy delta 0.01. } + to toOther: pushInterpreter push-vector value (((self closest-other) get-location) - (self get-location)). + to otherAge: pushInterpreter push-integer value ((self closest-other) get-age). + to otherEnergy: pushInterpreter push-integer value ((self closest-other) get-energy). + to otherHue: pushInterpreter push-integer value ((self closest-other) get-hue). + to otherVelocity: pushInterpreter push-vector value ((self closest-other) get-velocity). + to otherLocation: pushInterpreter push-vector value ((self closest-other) get-location). + to otherProgram: c (object). c = new PushCode. c parse code (((self closest-other) get-pushCode) get-string) with-interpreter pushInterpreter. pushInterpreter push-code value c. free(c). + to feedOther: bird (object). mode (int). # we've now executed the feedOther instruction, so we are an otherFeeder otherFeeder = 1. bird = (self closest-other). # log the (attempted) feeding event controller increment-otherFeedings. mode = (controller get-share-mode). # mode #4 is the "noop" feeding -- just return if mode == 4: return. # modes #2 (mutual-feeding) and #3 (mutual-feeding + kin) both require # that the recipient is a friend if (mode == 2 || mode == 3) && !(bird ask-if-other-feeder): return. # mode #3 requires that a certain level of kinship is met if mode == 3 && ((bird get-pushCode) compute-discrepancy from pushCode) > 9: return. if (bird get-energy) < energy: { self adjust-energy delta -0.01. # mode #0 is a throw-away -- we're penalized, but return before the actual feeding if mode == 0: return. bird adjust-energy delta 0.01. } + to mutate: c (object). size (int). size = (pushInterpreter get-integer-stack-top) % MAX_MUTATION_NEW_CODE_SIZE. if size > 0: { c = new PushCode. pushInterpreter copy-code-stack-top to c. c mutate with-max-new-code-size size with-interpreter pushInterpreter. pushInterpreter pop-integer-stack. pushInterpreter pop-code-stack. pushInterpreter push-code value c. free(c). } + to crossover: c1, c2, c3 (object). c1 = new PushCode. c2 = new PushCode. c3 = new PushCode. pushInterpreter copy-code-stack-top to c1. if (c1 get-size) > 0: { pushInterpreter pop-code-stack. pushInterpreter copy-code-stack-top to c2. if (c2 get-size) > 0: { pushInterpreter pop-code-stack. c3 crossover from-parent1 c1 from-parent2 c2. pushInterpreter push-code value c3. } else { pushInterpreter push-code value c1. } } free(c1). free(c2). free(c3). + to spawn: child (object). temp-code (object). num-birds (int). mut-rate (int). num-birds = all Birds. if energy > (2 * BIRTH_COST): { controller increment-naturalBirths. if num-birds < BOOTSTRAP_LIMIT: { self adjust-energy delta (0 - (BIRTH_COST - ((BOOTSTRAP_LIMIT - num-birds) * BIRTH_COST) / (BOOTSTRAP_LIMIT - MIN_BIRDS))). controller increment-reproductiveDiscounts. } else self adjust-energy delta (0 - (BIRTH_COST)). child = new Bird. child set-energy to BIRTH_COST. child set-hue to (self get-hue). child set-color to (controller get-rgb-color for-hsv-color ((child get-hue) * 360, 1, 1)). child move to (self get-location) + (random[(BIRTH_DISTANCE * 2, BIRTH_DISTANCE * 2, BIRTH_DISTANCE * 2)] - (BIRTH_DISTANCE, BIRTH_DISTANCE, BIRTH_DISTANCE)). #child set-velocity to (self get-velocity). child set-velocity to (0, 0, 0). #child point vertex (0, 1, 0) at (self get-velocity). child point vertex (0, 1, 0) at random[(1, 1, 1)]. temp-code = new PushCode. pushInterpreter copy-code-stack-top to temp-code. mut-rate = (pushInterpreter get-integer-stack-top). pushInterpreter pop-integer-stack. if mut-rate < 0: mut-rate = (- mut-rate). controller increment-reproMutationRates with mut-rate. if random[mut-rate] == 0: { temp-code mutate with-max-new-code-size MAX_MUTATION_NEW_CODE_SIZE with-interpreter pushInterpreter. controller increment-reproMutations. } controller increment-discrepancies by (temp-code compute-discrepancy from pushCode). (child get-pushCode) parse code (temp-code get-string) with-interpreter (child get-pushInterpreter). free(temp-code). } + to randI: pushInterpreter push-integer value (random[2 * RANDI_LIMIT] - RANDI_LIMIT). + to randF: pushInterpreter push-float value (random[2 * RANDF_LIMIT] - RANDF_LIMIT). + to randV: pushInterpreter push-vector value (random[2 * RANDV_LIMIT] - RANDV_LIMIT). + to randC: c (object). c = new PushCode. c make-random-code max-length MAX_RANDOM_CODE_SIZE with-interpreter pushInterpreter. pushInterpreter push-code value c. free(c). + to setServoSetpoint: servoSetpoint = (pushInterpreter get-float-stack-top). pushInterpreter pop-float-stack. + to setServoGain: g (float). g = (pushInterpreter get-float-stack-top). if ((g < -0.01) || (g > 0.01)): servoGain = g. pushInterpreter pop-float-stack. + to servo: val (float). err (float). val = (pushInterpreter get-float-stack-top). err = val - servoSetpoint. pushInterpreter pop-float-stack. pushInterpreter push-float value (val + (err * (- servoGain))). controller increment-servos. # utilities + to closest-other: others (list). item (object). closest-distance (float). this-distance (float). closest-other (object). foreach item in (self get-neighbors): { if (item is a "Bird"): { if ((self is-friend with item) == 0): push item onto others. } } if others < 1: { closest-other = self. } else { closest-distance = 1000000. foreach item in others: { this-distance = |((item get-location) - (self get-location))|. if this-distance < closest-distance: { closest-distance = this-distance. closest-other = item. } } } return closest-other. + to closest-friend: friends (list). item (object). closest-distance (float). this-distance (float). closest-friend (object). foreach item in (self get-neighbors): { if (item is a "Bird"): { if (self is-friend with item): push item onto friends. } } if friends < 1: { closest-friend = self. } else { closest-distance = 1000000. foreach item in friends: { this-distance = |((item get-location) - (self get-location))|. if this-distance < closest-distance: { closest-distance = this-distance. closest-friend = item. } } } return closest-friend. + to hue-diff from bird (object): other-hue (float). other-hue = (bird get-hue). return min( max(hue, other-hue) - min(hue, other-hue), min(hue, other-hue) + (1 - max(hue, other-hue))). + to is-friend with bird (object): if (self hue-diff from bird) <= HUE_SIMILARITY_THRESHOLD: return 1. else return 0. + to is-other from bird (object): if (self hue-diff from bird) > HUE_SIMILARITY_THRESHOLD: return 1. else return 0. + to eat with feeder (object): if (feeder get-energy) > FOOD_EDIBILITY_THRESHOLD: { # adjust bird's energy self adjust-energy delta (FOOD_BITE_SIZE * BIRD_ENERGY_PER_FOOD_ENERGY). # adjust feeder's energy feeder adjust-energy delta (0 - FOOD_BITE_SIZE). } + to bump with bird (object): #num-birds (int). #num-birds = all Birds. #if num-birds >= BOOTSTRAP_LIMIT: { self adjust-energy delta (0 - COLLISION_COST). bird adjust-energy delta (0 - COLLISION_COST). #} + to drop-dead: c (object). controller increment-deaths. # # make corpse c = new Corpse. c move to (self get-location). c point vertex (0, 1, 0) at (self get-velocity). free(pushCode). free(pushInterpreter). free(shape). free(self). + to land with ground (object): # if we hit the ground, we stop moving and set the landed flag to 1. self set-acceleration to (0, 0, 0). self set-velocity to (0, 0, 0). self point vertex (0, 1, 0) at (0, 1, 0). landed = 1. # we don't want to keep colliding with the ground, or else we'll # keep on getting stuck again at every iteration--move up just a # tiny bit. self offset by (0, 0.1, 0). + to check-landed: return landed. + to get-angle to otherMobile (object): tempVector (vector). tempVector = (otherMobile get-location) - (self get-location). return angle((self get-velocity), tempVector). + to fly: bird (object). acceleration (vector). newVelocity (vector). take-off (int). topFloat (float). topVector (vector). savedToFood (vector). hsv (vector). dev (float). age = age + 1. # cost of living for one cycle energy = energy - COST_OF_LIVING. if energy < 0: energy = 0. # if landed don't do anything except possibly take off (in which case act normally) if landed: { take-off = random[40]. if take-off == 1: { # if we decide to take off, pick a random direction, # but not towards the ground. landed = 0. self set-velocity to random[(.1, 1.1, .1)] - (.05, 0, .05). } else { return. } } if (random[TIME_SLIP] == 0) || (age < 2): { pushInterpreter clear-stacks. self toFood. savedToFood = (pushInterpreter get-vector-stack-top). pushInterpreter run code pushCode. #print savedToFood. #pushCode parse code "( toFriend V- )" with-interpreter pushInterpreter. #pushInterpreter run code pushCode. topFloat = (pushInterpreter get-float-stack-top). topVector = (pushInterpreter get-vector-stack-top). if isinf(topFloat) || isnan(topFloat): { #print "infinite or NaN float from Push stack". topFloat = 0.0. } if isinf(topVector::x) || isnan(topVector::x) || isinf(topVector::y) || isnan(topVector::y) || isinf(topVector::z) || isnan(topVector::z): { #print "infinite or NaN component of vector from Push stack". topVector = (0.0, 0.0, 0.0). } hue = (topFloat % 1.0). hsv = (hue * 360, 1, 1). self set-color to (controller get-rgb-color for-hsv-color hsv). acceleration = topVector. #print topVector. #if |acceleration| > 0.0001: # self set-acceleration to (10 * acceleration/|acceleration|). self set-acceleration to acceleration. controller set-decisions to (controller get-decisions) + 1. dev = angle(topVector, savedToFood). if (isnan(dev) || isinf(dev)): dev = 0.0. controller set-foodDeviations to (controller get-foodDeviations) + dev. } newVelocity = (self get-velocity). # speed limit if |newVelocity| > SPEED_LIMIT: { newVelocity = SPEED_LIMIT * newVelocity/|newVelocity|. self set-velocity to newVelocity. } if (controller print-velocities): print "velocity: $newVelocity". # Point the cone vertex in the direction of the new velocity. self point vertex (0, 1, 0) at newVelocity. + to adjust-size: newScale (float). newScale = (energy * 2) + 0.5. shape scale by (newScale / lastScale, 1, newScale / lastScale). lastScale = newScale. + to scale-color color c (float) with-minimum m (float): return (m + (c * (1.0 - m))). #+ to destroy: }