Java StarLogo 1.2 `turtle` turtles-own [hasfood scentstrength foodpoints] ; workers have "hasfood" and "scentstrength" ; food has "foodpoints" ; foodscent and nestscent also have scentstrength to workerturn ; if a worker is on the nest then if they have food ; drop it and count it and set scentstrength to 100 ; then set color to red, and turn around 180 if count-nest-here = 1 [if hasfood = 1 [set hasfood 0 set foodgathered (foodgathered + 1) seth (heading + 180)] set scentstrength 100 setc red] ; if a worker is on food then pick some up ; unless already carrying food. Then ; set scentstrength to 100, set color to blue, ; and turn around 180 if count-food-here >= 1 [if hasfood = 0 [set hasfood 1 ask-turtle one-of-food-here [foodtaken] seth (heading + 180)] set scentstrength 100 setc blue] ; now we're done dropping and/or picking up food for this turn, make a move. ; when a worker makes a move, it depends on some factors: ; am I carrying food, is there nest scent in front of me, ; is there food scent in front of me, reaction factors ; if I have food, then look for nestscent let [:followingscent 0] ifelse hasfood = 1 [ ; I need to see if there's a nest scent around here and if I ; want to follow it set :followingscent (searchscent "n") ] [ ; else I'm looking for food set :followingscent (searchscent "f") ] ; if :followingscent = 1 then I am already following that scent, take a step and stop if :followingscent = 1 [ workerstepfd stop ] ; if no scent, or ignoring scent, then maybe want to randomly turn if (random 100) > HeadingConsistency [lt random HeadingDegreeRandomizer rt random HeadingDegreeRandomizer] workerstepfd end ; when taking a step, if the worker has enough scentstrength left then ; they should leave some scent on the ground to workerstepfd if scentstrength >= 1 [ ; OK, have some scent to leave, which type? ifelse hasfood = 1 [ ; leaving food scent ifelse count-foodscent-here = 1 [ ; there is a foodscent turtle here already let [:plusscent scentstrength] ask-turtle one-of-foodscent-here [set scentstrength (scentstrength + :plusscent)] ][ ; need to make a foodscent turtle here hatch [set breed foodscent] ] ] [ ; else leaving nest scent ifelse count-nestscent-here = 1 [ ; there is a nestscent turtle here already let [:plusscent scentstrength] ask-turtle one-of-nestscent-here [set scentstrength (scentstrength + :plusscent)] ][ ; need to make a nestscent turtle here hatch [set breed nestscent] ] ] ] set scentstrength (scentstrength * ScentStrengthDecay / 1000) fd 1 end to searchscent :scenttype ; declare some locals first let [:bestdir -1000 :beststrength 0 :curdist 1 :curangle 0] ; depending on the type of scent we're looking for we need to do slightly different things ifelse :scenttype = "n"[ ; interested in nest scent ; search for SearchDistance slider squares away if debug = 1 [ print to-string ["nestsearch, heading is " heading] ] ; loop through each progressive distance repeat SearchDistance [ set :curdist (:curdist + 1) set :curangle -45 ; loop through the three 45 degree angles at this distance repeat 3 [ ; first check if I can see the nest, if so, do not pass go, instead turn directly toward the nest ; always if (count-nest-towards (:curangle) :curdist) = 1 [ seth (towards (xcor-of one-of-nest-towards (:curangle) :curdist) (ycor-of one-of-nest-towards (:curangle) :curdist)) if debug = 1 [ print to-string ["found nest, new heading is " heading] ] output 1 ] ; no nest there, so check out the nestscent if (scentstrength-of one-of-nestscent-towards :curangle :curdist) > :beststrength [ ; found a new maximum, update variables if debug = 1 [ print to-string ["found max before update heading is " heading " and :curangle is " :curangle] ] set :bestdir (towards (xcor-of one-of-nestscent-towards :curangle :curdist) (ycor-of one-of-nestscent-towards :curangle :curdist)) if debug = 1 [ print to-string ["found new nestscent maximum at dir " :bestdir " while my heading is " heading " and :curangle is " :curangle " and :curdist is " :curdist " for turtle " one-of-nestscent-towards :curangle :curdist] ] set :beststrength (scentstrength-of one-of-nestscent-towards :curangle :curdist) ] ; increment angle set :curangle (:curangle + 45) ] ] ] [ ; interested in food scent ; search for SearchDistance slider squares away if debug = 1 [ print to-string ["foodsearch, heading is " heading] ] ; loop through each progressive distance repeat SearchDistance [ set :curdist (:curdist + 1) set :curangle -45 ; loop through the three 45 degree angles at this distance repeat 3 [ ; first check if I can see some food, if so, do not pass go, instead turn directly toward the food ; always if (count-food-towards (:curangle) :curdist) = 1 [ seth (towards (xcor-of one-of-food-towards (:curangle) :curdist) (ycor-of one-of-food-towards (:curangle) :curdist)) if debug = 1 [ print to-string ["found food, new heading is " heading] ] output 1 ] ; no food there, so check out the foodscent if (scentstrength-of one-of-foodscent-towards :curangle :curdist) > :beststrength [ ; found a new maximum, update variables set :bestdir (towards (xcor-of one-of-foodscent-towards :curangle :curdist) (ycor-of one-of-foodscent-towards :curangle :curdist)) set :beststrength (scentstrength-of one-of-foodscent-towards :curangle :curdist) if debug = 1 [ print to-string ["found new foodscent maximum at dir " :bestdir] ] ] ; increment angle set :curangle (:curangle + 45) ] ] ] ; now we've either found a max or nothing, if :bestdir = -1000 then nothing if :bestdir = -1000 [output 0] if debug = 1 [ print to-string ["best scent heading is " :bestdir] ] ; OK, so we found a max, take the following probability then compare to a random number. ; this gives us an answer of whether the ant will follow the scent or not ifelse (FollowProbability / 10) >= (random 100) [ ; yes, following scent seth :bestdir if debug = 1 [ print to-string ["taking best scent heading, now heading " heading] ] output 1 ] [ ; nope, not going to follow the scent output 0 ] end to foodtaken set foodpoints (foodpoints - 1) ifelse foodpoints <= 0 [die] [scale-color green foodpoints 0 (FoodPointsPerItem + 5)] end to scentturn ; first, make sure the scent is not greater than 100 if scentstrength > 100 [set scentstrength 100] ; then let the scent decay by its set rate set scentstrength (scentstrength * GroundScentDecay / 1000) ; scents less than 1 die if scentstrength < 1 [die] ; for foodscent, scale them as blue and for nestscent scale them as red ifelse breed = foodscent [ scale-color blue scentstrength 0 120 ] [ scale-color red scentstrength 0 120 ] end `observer` breeds [worker nest food nestscent foodscent barrier] globals [foodgathered debug] to evolve ; declare a variable to hold avg food gathered to eval performance let [:avgFoodGathered 0] ; run the simulation twice at its current settings to determine the foodgathered avg let [:sumFood 0] repeat 2[ startSetupButton startExecuteButton wait 120 stopSetupButton stopExecuteButton print to-string [ "foodgathered is " foodgathered] set :sumFood (:sumFood + foodgathered) ] set :avgFoodGathered (:sumFood / 2) print to-string ["avgFoodGathered for init is " :avgFoodGathered] ; now have a avgFoodGathered for the current settings as a baseline ; declare some local variables for the properties we want to evolve let [:bestFollowProbability FollowProbability] ; now loop repeatedly and evolve the variables loop [ ; randomize the FollowProbability within 3 of the current, and run twice set FollowProbability ( FollowProbability + (random 7) - 3) print to-string [ "trying with FollowProbability set to " FollowProbability] startSetupButton startExecuteButton wait 120 stopSetupButton stopExecuteButton print to-string ["first try for this evolution foodgathered is " foodgathered] set :sumFood foodgathered ; run the second try startSetupButton startExecuteButton wait 120 stopSetupButton stopExecuteButton print to-string ["second try for this evolution foodgathered is " foodgathered] set :sumFood (:sumFood + foodgathered) print to-string [ "avgFoodGathered for this evolution is " (:sumFood / 2) ] ; check if this is the best so far or not if (:sumFood / 2) > :avgFoodGathered [ ; this evolution is beneficial, keep it set :avgFoodGathered (:sumFood / 2) set :bestFollowProbability FollowProbability print "this evolution is the best so far, keeping it" ] ; reset values for next cycle, maybe the same if this cycle was an improvement set FollowProbability :bestFollowProbability ] end to setup ask-turtles [die] ;clear all turtles from previous run set foodgathered 0 set debug 0 create-nest-and-do 1 [setc yellow] create-food-and-do NumFood [setc green setx (random-gaussian 3) + 30 sety (random-gaussian 3) + 30 set foodpoints FoodPointsPerItem scale-color green foodpoints 0 (FoodPointsPerItem + 5)] repeat NumWorkers [ wait 1 create-worker-and-do 1 [set scentstrength 100 setc red seth random 360] ] end to executeturn ask-worker [workerturn] ask-nestscent [scentturn] ask-foodscent [scentturn] end to debugturn clear-output set debug 1 executeturn set debug 0 end to getfoodgathered output foodgathered end `information` `SLPlot` title "Untitled Graph" xlabel "Time" ylabel "" xmin 0 xmax 1000 ymin 0 ymax 1000 pen 1 0 15 pen 2 0 25 pen 3 0 35 pen 4 0 45 pen 5 0 55 pen 6 0 65 pen 7 0 75 pen 8 0 85 pen 9 0 95 pen 10 0 105 `interface` SLButton turtle-or-observer? observer top-left 19 14 width-height 71 35 name "SetupButton" line-to-run "setup" forever? false button-number 2 show-name? false whichip 10 SLButton turtle-or-observer? observer top-left 21 104 width-height 83 31 name "ExecuteButton" line-to-run "executeturn" forever? true button-number 3 show-name? false whichip 11 SLButton turtle-or-observer? observer top-left 430 197 width-height 91 36 name "button4" line-to-run "evolve" forever? false button-number 5 show-name? false whichip 13 SLSlider top-left 243 8 width-height 189 25 name "slider5" variable "HeadingDegreeRandomizer" min-value 10 max-value 180 current-value 78 slider-number 4 show-name? false SLSlider top-left 334 19 width-height 152 25 name "slider7" variable "GroundScentDecay" min-value 800 max-value 999 current-value 983 slider-number 6 show-name? false SLSlider top-left 204 22 width-height 153 25 name "slider4" variable "HeadingConsistency" min-value 50 max-value 100 current-value 89 slider-number 3 show-name? false SLSlider top-left 422 28 width-height 140 25 name "slider9" variable "FollowProbability" min-value 0 max-value 999 current-value 934 slider-number 8 show-name? false SLSlider top-left 388 24 width-height 147 25 name "slider8" variable "SearchDistance" min-value 1 max-value 5 current-value 2 slider-number 7 show-name? false SLSlider top-left 74 20 width-height 93 25 name "slider1" variable "NumWorkers" min-value 5 max-value 99 current-value 45 slider-number 0 show-name? false SLSlider top-left 116 22 width-height 85 25 name "slider2" variable "NumFood" min-value 0 max-value 99 current-value 31 slider-number 1 show-name? false SLSlider top-left 295 15 width-height 154 25 name "slider6" variable "ScentStrengthDecay" min-value 800 max-value 999 current-value 932 slider-number 5 show-name? false SLButton turtle-or-observer? observer top-left 61 128 width-height 58 30 name "button3" line-to-run "debugturn" forever? false button-number 4 show-name? false whichip 12 SLSlider top-left 152 24 width-height 148 25 name "slider3" variable "FoodPointsPerItem" min-value 3 max-value 100 current-value 21 slider-number 2 show-name? false SLMonitor top-left 110 115 width-height 79 36 name "FoodGathered" list-to-run "getfoodgathered" digits 0 delay 1.0 monitor-number 1 show-name? true whichip 14 onewhichip 15 SLCanvas top-left 5 203 `settings` patch-size 3 num-shapes 256 screen-half-width 78 screen-half-height 67 interface-window-xcor 288 interface-window-ycor 19 interface-window-size 692 552 output-window-xcor 49 output-window-ycor 353 output-window-width 715 output-window-height 322 info-window-xcor 0 info-window-ycor 0 info-window-width 500 info-window-height 419 control-center-xcor 0 control-center-ycor 14 control-center-width 977 control-center-height 690 turtle-command-center-height 77 observer-command-center-height 150 plot-window-xcor 0 plot-window-ycor 0 plot-window-width 508 plot-window-height 376