Java StarLogo 1.1 `turtle` ;; ===================================================================== to realrandom :limit output ((random (:limit * 100000)) / 100000) end ;; ===================================================================== to setxy-of :someturtle :some-x :some-y setxcor-of :someturtle :some-x setycor-of :someturtle :some-y end ;; ===================================================================== to positive-modulus :intvalue :intmodulus if (tracking = true) [ print [tracking positive modulus] setoutputline-count (outputline-count + 1) ] let [:local-intvalue :intvalue] let [:local-intmodulus :intmodulus] loop [ if (tracking = true) [ print (se [looping in positive modulus] :local-intvalue) setoutputline-count (outputline-count + 1) ] ifelse (:local-intvalue < 0) [ let [:local-intvalue (:local-intvalue + :local-intmodulus)] ] [ ifelse (:local-intvalue >= :local-intmodulus) [ let [:local-intvalue (:local-intvalue - :local-intmodulus)] ] [ output :local-intvalue ] ] ] end ;; ===================================================================== to eat-output :anything end ;; ===================================================================== to rotate-list :somelist if (tracking = true) [ print [tracking rotate list] setoutputline-count (outputline-count + 1) ] output (lput (first :somelist) (butfirst :somelist)) end ;; ===================================================================== to empty-list output (butfirst (butfirst (list 0 0 ) ) ) end ;; ===================================================================== to display-turtle :someturtle if (tracking = true) [ print [tracking display turtle] setoutputline-count (outputline-count + 1) ] let [:someturtledata turtledata-of :someturtle] if breed = artists [ print [is an artist] setoutputline-count (outputline-count + 1) ] if breed = travelers [ print [is a traveler] let [:somegenome first :someturtledata] print (se [with genome] :somegenome) let [:somecolor first last :someturtledata] print (se [with color] :somecolor) let [:somefitness last last :someturtledata] print (se [with fitness] :somefitness) setoutputline-count (outputline-count + 4) ] if breed = towns [ print [is a town] let [:sometown-x first :someturtledata] print (se [with town-x] :sometown-x) let [:sometown-y last :someturtledata] print (se [with town-y] :sometown-y) setoutputline-count (outputline-count + 3) ] if breed = erasers [ print [is an eraser] setoutputline-count (outputline-count + 1) ] if ((breed = travelers) or (breed = erasers)) [ let [:sometarget-x target-x-of :someturtle] print (se [with sometarget-x] :sometarget-x) let [:sometarget-y target-y-of :someturtle] print (se [with sometarget-y] :sometarget-y) let [:sometarget-dist target-dist-of :someturtle] print (se [with sometarget-dist] :sometarget-dist) setoutputline-count (outputline-count + 3) ] if breed = travelers [ let [:someannealing-enabled annealing-enabled-of :someturtle] print (se [with annealing-enabled] :someannealing-enabled) let [:someannealing-overshoot-limit annealing-overshoot-limit-of :someturtle] print (se [with annealing-overshoot-limit] :someannealing-overshoot-limit) setoutputline-count (outputline-count + 2) ] end ;; ===================================================================== to setup-screen if (breed != artists) [print [ERROR! breed bad in setup-screen] die] pu setc white ifelse ( screen-height < screen-width ) [ setxy 0 screen-half-height seth 180 pd fd screen-height ] [ setxy screen-half-width 0 seth 270 pd fd screen-width ] die end ;; ===================================================================== to setup-towns let [:sometown who] if (breed != towns) [print [ERROR! breed bad in setup-towns] die] setturtledata-of :sometown (list set-town-x set-town-y) pu setc 9 setxy ((get-town-x :sometown) + city-offset-x) ((get-town-y :sometown) + city-offset-y) end ;; ===================================================================== to set-town-x output ( ( 0.9 * realrandom box-size-x ) - ( box-size-x * 0.45 ) ) end ;; ===================================================================== to get-town-x :sometown if (tracking = true) [ print [tracking get town x] setoutputline-count (outputline-count + 1) ] if (breed != towns) [print [ERROR! breed bad in get-town-x] die] output first turtledata-of :sometown end ;; ===================================================================== to set-town-y output ( ( 0.9 * realrandom box-size-y ) - ( box-size-y * 0.45 ) ) end ;; ===================================================================== to get-town-y :sometown if (tracking = true) [ print [tracking get town y] setoutputline-count (outputline-count + 1) ] if (breed != towns) [print [ERROR! breed bad in get-town-y] die] output last turtledata-of :sometown end ;; ===================================================================== to setup-travelers let [:sometraveler who] if (tracking = true) [ print (se [tracking setup travelers] :sometraveler) setoutputline-count (outputline-count + 1) ] if (breed-of :sometraveler != travelers) [print [ERROR! breed bad in setup-travelers] die] setturtledata-of :sometraveler (list ( set-permuted-genome list-of-towns ) ( list set-color :sometraveler huge-fitness-initializer ) ) put-fitness :sometraveler set-fitness :sometraveler print ( list [genome] ( first turtledata-of :sometraveler ) ) print ( list [color fitness] ( last turtledata-of :sometraveler ) ) ifelse annealing-prepass-feature-enabled [ setannealing-enabled-of :sometraveler true setannealing-overshoot-limit-of :sometraveler ( annealing-overshoot-fraction * ( get-fitness :sometraveler ) ) ] [ setannealing-enabled-of :sometraveler false setannealing-overshoot-limit-of :sometraveler 0 ;; just for neatness ] print ( se [annealing enabled] ( se ( annealing-enabled-of :sometraveler ) ( se [overshoot-limit] ( annealing-overshoot-limit-of :sometraveler ) ) ) ) pu setc get-color :sometraveler eat-output spin-home :sometraveler go-hither-and-aim-yon :sometraveler print ( se [curtownx] ( se ( target-x-of :sometraveler ) ( se [curtowny] ( se ( target-y-of :sometraveler ) ( se [curdist] ( target-dist-of :sometraveler ) ) ) ) ) ) pd end ;; ===================================================================== to get-random-traveler output item (1 + random the-travelers-count) list-of-travelers end ;; ===================================================================== to select-best-and-worst-with-replacement :how-many let [ :local-how-many :how-many ] let [ :local-best-fitness ( 0 + huge-fitness-initializer ) ] let [ :local-most-fit ( 0 - 1 ) ] let [ :local-worst-fitness ( 0 - huge-fitness-initializer ) ] let [ :local-least-fit ( 0 - 1 ) ] repeat :local-how-many [ let [ :current-traveler get-random-traveler ] let [ :current-fitness get-fitness :current-traveler ] if ( :current-fitness < :local-best-fitness ) [ let [ :local-most-fit :current-traveler ] let [ :local-best-fitness :current-fitness ] ] if ( :current-fitness > :local-worst-fitness ) [ let [ :local-least-fit :current-traveler ] let [ :local-worst-fitness :current-fitness ] ] ] output ( list :local-most-fit :local-least-fit ) end ;; ===================================================================== to go-hither-and-aim-yon :sometraveler setxy-of :sometraveler ( ( get-current-town-x :sometraveler ) + plot-offset-x ) ( ( get-current-town-y :sometraveler ) + plot-offset-y ) if (tracking = true) [ print ( se [this town] ( se ( get-current-town :sometraveler ) ( se ( xcor-of :sometraveler ) ( ycor-of :sometraveler ) ) ) ) setoutputline-count (outputline-count + 1) ] rotate-genome :sometraveler settarget-x-of :sometraveler ( ( get-current-town-x :sometraveler ) + plot-offset-x ) settarget-y-of :sometraveler ( ( get-current-town-y :sometraveler ) + plot-offset-y ) settarget-dist-of :sometraveler ( distance-nowrap ( target-x-of :sometraveler ) ( target-y-of :sometraveler ) ) seth-of :sometraveler ( towards-nowrap ( target-x-of :sometraveler ) ( target-y-of :sometraveler ) ) if (tracking = true) [ print ( se [next town] ( se ( get-current-town :sometraveler ) ( se ( target-x-of :sometraveler ) ( se ( target-y-of :sometraveler ) ( se ( target-dist-of :sometraveler ) ( heading-of :sometraveler ) ) ) ) ) ) setoutputline-count (outputline-count + 1) ] end ;; ===================================================================== to get-random-town output pick list-of-towns end ;; ===================================================================== to setup-eraser let [:some-eraser who] if (tracking = true) [ print ( se [tracking setup eraser] :some-eraser ) setoutputline-count (outputline-count + 1) ] pu setc black let [:currenttown get-random-town] let [:mycurrent turtledata-of :currenttown] setxy-of :some-eraser ((first :mycurrent) + plot-offset-x) ((last :mycurrent) + plot-offset-y) let [:nexttown get-random-town] let [:mynext turtledata-of :nexttown] setturtledata-of :some-eraser :nexttown settarget-dist-of :some-eraser ( town-distance :currenttown :nexttown ) settarget-x-of :some-eraser ((first :mynext) + plot-offset-x) settarget-y-of :some-eraser ((last :mynext) + plot-offset-y) seth-of :some-eraser ( towards-nowrap ( target-x-of :some-eraser ) ( target-y-of :some-eraser ) ) pd end ;; ===================================================================== to erase let [:some-eraser who] if (tracking = true) [ print ( se [tracking erase] :some-eraser ) setoutputline-count (outputline-count + 1) ] if (wobble = true) [ let [:nexttown get-random-town] let [:mynext turtledata-of :nexttown] settarget-dist-of :some-eraser ( town-distance ( turtledata-of :some-eraser ) :nexttown ) setturtledata-of :some-eraser :nexttown settarget-x-of :some-eraser ((first :mynext) + plot-offset-x) settarget-y-of :some-eraser ((last :mynext) + plot-offset-y) seth-of :some-eraser ( towards-nowrap ( target-x-of :some-eraser ) ( target-y-of :some-eraser ) ) ] stop end ;; ===================================================================== to get-color :sometraveler if (tracking = true) [ print ( se [tracking get color] :sometraveler ) setoutputline-count (outputline-count + 1) ] if (breed != travelers) [print [ERROR! breed bad in get-color] die] output ( first ( last ( turtledata-of :sometraveler ) ) ) end ;; ===================================================================== to set-permuted-genome :inlist if (tracking = true) [ print [tracking set permuted genome] setoutputline-count (outputline-count + 1) ] if ((length :inlist) < 1) [print [ERROR! null inlist in set-permuted-genome] die] let [:myinlist :inlist] let [:myoutlist empty-list] loop [ ifelse ((length :myinlist) = 1) [ let [:myoutlist lput first :myinlist :myoutlist] output :myoutlist ] [ let [:itemshifts ( 1 + random (length :myinlist))] repeat :itemshifts [ let [:myinlist ( lput ( first :myinlist ) ( butfirst :myinlist ) )] ] let [:myoutlist lput first :myinlist :myoutlist] let [:myinlist butfirst :myinlist] ] ] end ;; ===================================================================== to get-current-town :sometraveler if ((breed-of :sometraveler) != travelers) [ print ( se [bad traveler in get current town] :sometraveler ) die ] if (tracking = true) [ print ( se [tracking get current town] :sometraveler ) setoutputline-count (outputline-count + 1) ] output ( first ( first turtledata-of :sometraveler ) ) end ;; ===================================================================== to get-current-town-x :sometraveler if (tracking = true) [ print ( se [tracking get current town x] :sometraveler ) setoutputline-count (outputline-count + 1) ] output ( first turtledata-of ( get-current-town :sometraveler ) ) end ;; ===================================================================== to get-current-town-y :sometraveler output ( last turtledata-of ( get-current-town :sometraveler ) ) end ;; ===================================================================== to set-color :sometraveler output ( 15 + ( color-step * ( positive-modulus :sometraveler the-travelers-count ) ) ) end ;; ===================================================================== to rotate-genome :sometraveler if (tracking = true) [ print ( se [tracking rotate genome] :sometraveler ) setoutputline-count (outputline-count + 1) ] setturtledata-of :sometraveler ( list ( se ( butfirst ( first turtledata-of :sometraveler ) ) ( first ( first turtledata-of :sometraveler ) ) ) ( last turtledata-of :sometraveler ) ) end ;; ===================================================================== to put-fitness :sometraveler :some-fitness if (tracking = true) [print [tracking put fitness] setoutputline-count (outputline-count + 1)] setturtledata-of :sometraveler (list (first turtledata-of :sometraveler) (list (first (last turtledata-of :sometraveler) ) :some-fitness) ) end ;; ===================================================================== to set-fitness :sometraveler if (tracking = true) [print [tracking set fitness] setoutputline-count (outputline-count + 1)] let [:fitness 0] repeat the-towns-count [ let [:fitness (:fitness + town-distance (item 1 first turtledata-of :sometraveler ) (item 2 first turtledata-of :sometraveler ) )] rotate-genome :sometraveler ] if ( fitness-best-ever > :fitness ) [ setfitness-best-ever :fitness ] if ( fitness-worst-ever < :fitness ) [ setfitness-worst-ever :fitness ] output :fitness end ;; ===================================================================== to town-distance :town-a :town-b if (tracking = true) [print [tracking town distance] setoutputline-count (outputline-count + 1)] let [:ax first turtledata-of :town-a] let [:ay last turtledata-of :town-a] let [:bx first turtledata-of :town-b] let [:by last turtledata-of :town-b] let [:dx (:ax - :bx)] let [:dy (:ay - :by)] output sqrt( ( :dx * :dx ) + ( :dy * :dy ) ) end ;; ===================================================================== to attempt-improvement print [trying a non-annealing improvement for this traveler] setoutputline-count (outputline-count + 1) ;; We want to recompute this every time so that the user can modify the ;; weights while the program is running and make it have effect. let [ :random-high ( do-nothing-selection-weight + ordered-crossover-selection-weight + partial-match-crossover-selection-weight + cyclic-crossover-selection-weight + inversion-selection-weight + mutation-selection-weight + permutation-selection-weight ) ] let [ :this-random random :random-high ] let [ :cumulative-weight 0 ] let [ :cumulative-weight (:cumulative-weight + do-nothing-selection-weight) ] if ( :this-random < :cumulative-weight ) [ output do-nothing ] let [ :cumulative-weight (:cumulative-weight + ordered-crossover-selection-weight) ] if ( :this-random < :cumulative-weight ) [ output do-ordered-crossover ] let [ :cumulative-weight (:cumulative-weight + partial-match-crossover-selection-weight) ] if ( :this-random < :cumulative-weight ) [ output do-partial-match-crossover ] let [ :cumulative-weight (:cumulative-weight + cyclic-crossover-selection-weight) ] if ( :this-random < :cumulative-weight ) [ output do-cyclic-crossover ] let [ :cumulative-weight (:cumulative-weight + inversion-selection-weight) ] if ( :this-random < :cumulative-weight ) [ output do-inversion ] let [ :cumulative-weight ( :cumulative-weight + mutation-selection-weight ) ] if ( :this-random < :cumulative-weight ) [ output do-mutation ] let [ :cumulative-weight (:cumulative-weight + permutation-selection-weight) ] if ( :this-random < :cumulative-weight ) [ output do-permutation ] end ;; ===================================================================== to get-genome if (tracking = true) [print [tracking get genome] setoutputline-count (outputline-count + 1)] output first turtledata end ;; ===================================================================== to put-genome :some-genome setturtledata (list :some-genome (last turtledata)) end ;; ===================================================================== to spin-to :some-genome :some-town if (tracking = true) [print [tracking spin to] setoutputline-count (outputline-count + 1)] repeat the-towns-count [ ifelse ( ( first :some-genome ) = :some-town ) [ output :some-genome ] [ let [:some-genome ( lput ( first :some-genome ) ( butfirst :some-genome ) ) ] ] ] print [ERROR! spin-to did not find town in genome] end ;; ===================================================================== to split-to :some-genome :some-town if (tracking = true) [print [tracking split to] setoutputline-count (outputline-count + 1)] let [:inversion-part ([])] repeat the-towns-count [ ifelse ((first :some-genome) = :some-town) [ let [:inversion-part (fput (first :some-genome) :inversion-part)] let [:some-genome (butfirst :some-genome)] output (list :inversion-part :some-genome) ] [ let [:inversion-part (fput (first :some-genome) :inversion-part)] let [:some-genome (butfirst :some-genome)] ] ] print [ERROR! split-to did not find town in genome] end ;; ===================================================================== to draw-new-best end ;; ===================================================================== to augment-annealing-overshoot-limit ifelse ( ( breed = travelers ) and ( annealing-enabled = true ) ) [ output annealing-overshoot-limit ] [ output 0 ] end ;; ===================================================================== to augment-fitness ifelse ( breed = travelers ) [ output get-fitness who ] [ output 0 ] end ;; ===================================================================== to minimize-fitness-best if ( breed = travelers ) [ let [:local-fitness last last turtledata] if ( :local-fitness < fitness-best ) [ setfitness-best :local-fitness ] ] end ;; ===================================================================== to maximize-fitness-worst if (breed = travelers ) [ let [:local-fitness last last turtledata] if ( :local-fitness > fitness-worst ) [ setfitness-worst :local-fitness ] ] end ;; ===================================================================== to minimize-annealing-limit-smallest if ((breed = travelers) and (annealing-enabled = true)) [ setannealing-limit-smallest ( min annealing-limit-smallest annealing-overshoot-limit ) ] end ;; ===================================================================== to maximize-annealing-limit-largest if ((breed = travelers) and (annealing-enabled = true)) [ setannealing-limit-largest ( max annealing-limit-largest annealing-overshoot-limit ) ] end ;; ===================================================================== to pick-inversion-ends :genome let [:local-genome :genome] loop [ let [:start-at pick :local-genome] let [:end-at pick :local-genome] let [:local-genome (spin-to :local-genome :start-at)] if ( not ( ( :start-at = :end-at ) or ( ( :end-at = ( last :local-genome ) ) or ( :end-at = ( last ( butlast :local-genome ) ) ) ) ) ) [ output (list :start-at :end-at) ] ] end ;; ===================================================================== to anneal if (tracking = true) [print [tracking do annealing] setoutputline-count (outputline-count + 1)] let [:tries 0] ;; loop is your friend, helps prevent the heartbreak of recursion. ;; just use output as your break statement loop [ let [:tries ( :tries + 1 ) ] setcurrent-tries :tries let [:genome get-genome] let [:ends pick-inversion-ends :genome] let [:start-at first :ends] let [:end-at last :ends] let [:genome (spin-to :genome :start-at )] let [:genome (split-to :genome :end-at)] let [:after-end-at (first (last :genome))] let [:before-start-at (last (last :genome))] let [:delta (((town-distance :end-at :before-start-at) - (town-distance :end-at :after-end-at)) + ((town-distance :start-at :after-end-at) - (town-distance :before-start-at :start-at))) ] let [:genome (se (first :genome) (last :genome))] if ( :delta < 0 ) [ output annealing-improved :delta :tries :genome ] if ( ( :delta > 0 ) and ( ( :delta < annealing-overshoot-limit ) and ( annealing-slider-success > ( random 1000 ) ) ) ) [ output annealing-worstened :delta :tries :genome ] if ( :delta = 0 ) [ output annealing-misbehaved :delta :tries :genome ] ifelse (0 = (random annealing-no-surrender-effort)) [ output annealing-surrenders :tries ] [ adjust-annealing-overshoot-limit ;; must do this even here or infinite loop may result if ( annealing-enabled = false ) ;; stop if we must [ output false ] ] ] end ;; ===================================================================== to annealing-surrenders :tries print ( se [surrendered after ------------>] ( se :tries ( se [tries and change "0.0000000000000000"] ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) adjust-annealing-overshoot-limit ;; must do this even here or infinite loop may result setoutputline-count ( outputline-count + 1 ) output true end ;; ===================================================================== to adjust-annealing-overshoot-limit setannealing-overshoot-limit ( annealing-overshoot-limit * annealing-stepdown-fraction ) if ( annealing-overshoot-limit < annealing-computed-cutoff ) [ setannealing-enabled false print ( se [has disabled annealing at] ( se annealing-overshoot-limit ( se [with fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) setoutputline-count ( outputline-count + 1 ) if ( 0 = count-turtles-with [ ( breed = travelers ) and ( annealing-enabled = true ) ] ) [ setannealing-prepass-feature-enabled false print ( se [is the last turtle to complete the annealing pass with cutoff] ( annealing-computed-cutoff )) setoutputline-count ( outputline-count + 1 ) ] ] end ;; ===================================================================== to report-living-annealer ifelse ( ( breed = travelers ) and ( annealing-enabled = true ) ) [ output true ] [ output false ] end ;; ===================================================================== to annealing-improved :delta :tries :genome put-fitness who ( :delta + get-fitness who ) put-genome :genome ifelse ( ( get-fitness who ) < fitness-best-ever ) [ print ( se [new best ever after ---------->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) ) setfitness-best-ever get-fitness who draw-new-best ] [ ifelse ( ( get-fitness who ) < fitness-best ) [ print ( se [new best recent after -------->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) ) ] [ print ( se [improved after --------------->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) ) ] ] setoutputline-count ( outputline-count + 1 ) adjust-annealing-overshoot-limit output true end ;; ===================================================================== to annealing-worstened :delta :tries :genome put-fitness who ( :delta + get-fitness who ) put-genome :genome ifelse ( ( get-fitness who ) > fitness-worst-ever ) [ print ( se [new worst ever after --------->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) ) setfitness-worst-ever get-fitness who ] [ ifelse ( ( get-fitness who ) > fitness-worst ) [ print ( se [new worst recent after ------->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) ) ] [ print ( se [worstened after -------------->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( annealing-overshoot-limit ) ) ) ) ) ) ) ) ] ] setoutputline-count ( outputline-count + 1 ) adjust-annealing-overshoot-limit output true end ;; ===================================================================== to annealing-misbehaved :delta :tries :genome print ( se [remarkably unmodified after -->] ( se :tries ( se [tries and change] ( se :delta ( se [giving fitness] ( se get-fitness who ( se [at annealing limit] ( se annealing-overshoot-limit ( [DEBUG] ))))))))) print (se [old genome] get-genome) print (se [new genome] :genome) put-fitness who ( :delta + get-fitness who ) put-genome :genome setoutputline-count ( outputline-count + 3 ) adjust-annealing-overshoot-limit output true end ;; ===================================================================== to victimize :somevictim :somegenome print [pre-genome-change] display-turtle :somevictim setturtledata-of :somevictim (list :somegenome (last turtledata-of :somevictim)) print [post-genome-change] display-turtle :somevictim put-fitness :somevictim set-fitness :somevictim settracking true print [post-fitness-calculation] display-turtle :somevictim eat-output spin-home :somevictim settracking false print [post-spin-home] display-turtle :somevictim end ;; ===================================================================== to do-ordered-crossover if (tracking = true) [print [tracking do ordered crossover] setoutputline-count (outputline-count + 1)] let [:local-genome get-genome] let [:mate1 first select-best-and-worst-with-replacement sexual-breeder1-sample-size] let [:mate2 first select-best-and-worst-with-replacement sexual-breeder2-sample-size] let [:victim1 last select-best-and-worst-with-replacement replace-sample-size] let [:victim2 last select-best-and-worst-with-replacement replace-sample-size] let [:genome1 first turtledata-of :mate1] let [:genome2 first turtledata-of :mate2] print ( se [mates] ( se :mate1 ( se :mate2 ( se [victims] ( se :victim1 ( se :victim2 ( se [genomes] ( se :genome1 ( se [and] ( :genome2 ) ) ) ) ) ) ) ) ) ) repeat 1 + random the-towns-count [ let [:genome1 rotate-list :genome1] ] repeat 1 + random the-towns-count [ let [:genome2 rotate-list :genome2] ] print ( se [rotated genomes] ( se :genome1 ( se [and] ( :genome2 ) ) ) ) let [:ordered-prefix-length (2 + random (the-towns-count - 4))] let [:split-at item :ordered-prefix-length :genome1] let [:split-list split-to :genome1 :split-at] let [:ordered-prefix1 first :split-list] let [:unordered-suffix1 last :split-list] print ( se [split list genome after] ( se :ordered-prefix-length ( se [gives] ( list :ordered-prefix1 :unordered-suffix1 ) ) ) ) let [:ordered-prefix2 empty-list] let [:unordered-suffix2 empty-list] let [:repcount 0] repeat the-towns-count [ let [:repcount (:repcount + 1)] let [:current-item item :repcount :genome2] ifelse (member? (item :repcount :genome2) :ordered-prefix1) [ let [:ordered-prefix2 (lput :current-item :ordered-prefix2)] ] [ let [:unordered-suffix2 (lput :current-item :unordered-suffix2)] ] ] print ( se [split genomes] ( se ( list :ordered-prefix1 :unordered-suffix2 ) ( se [and] ( list :ordered-prefix2 :unordered-suffix1 ) ) ) ) victimize :victim1 (se :ordered-prefix1 :unordered-suffix2) victimize :victim2 (se :ordered-prefix2 :unordered-suffix1) output true end ;; ===================================================================== to do-nothing output true end ;; ===================================================================== to do-partial-match-crossover output true end ;; ===================================================================== to do-cyclic-crossover output true end ;; ===================================================================== to do-inversion output true end ;; ===================================================================== to do-mutation output true end ;; ===================================================================== to do-permutation output true end ;; ===================================================================== to get-fitness :some-traveler output last last turtledata-of :some-traveler end ;; ===================================================================== to spin-home :sometraveler if (tracking = true) [print (se [tracking spin home] :sometraveler) setoutputline-count (outputline-count + 1)] let [:local-count the-towns-count] let [:local-traveler :sometraveler] repeat :local-count [ if (tracking = true) [print (se [tracking spin home in repeat loop] :local-traveler) setoutputline-count (outputline-count + 1)] let [:local-town (get-current-town :local-traveler)] if (tracking = true) [print (se [tracking spin home begin repeat loop] :local-town) setoutputline-count (outputline-count + 1)] if (tracking = true) [print (se [tracking spin home begin repeat loop] (get-current-town :local-traveler)) setoutputline-count (outputline-count + 1)] if (tracking = true) [print (se [tracking spin home begin repeat loop] :local-town) setoutputline-count (outputline-count + 1)] if (tracking = true) [print (se [tracking spin home begin repeat loop] (positive-modulus :local-town :local-count)) setoutputline-count (outputline-count + 1)] let [:local-modulus (positive-modulus :local-town :local-count)] if (tracking = true) [print (se [tracking spin home begin repeat loop] :local-modulus) setoutputline-count (outputline-count + 1)] ifelse ( :local-modulus = 0 ) [ if (tracking = true) [print [tracking spin home ifelse true case] setoutputline-count (outputline-count + 1)] output true ] [ if (tracking = true) [print [tracking spin home ifelse false case] setoutputline-count (outputline-count + 1)] rotate-genome :sometraveler ] if (tracking = true) [print [tracking spin home end repeat loop] setoutputline-count (outputline-count + 1)] ] print [ERROR! spin-home did not find home town in genome] end ;; ===================================================================== to travel if (breed != travelers) [print [ERROR! breed bad in travel] die] if ( wobble = true ) [ if (0 = positive-modulus (get-current-town who) the-towns-count) [ setcurrent-traveler who ifelse (annealing-prepass-feature-enabled) [ ifelse annealing-enabled [ ;; anneal is using output as a loop exit, the output isn't otherwise valuable eat-output anneal eat-output spin-home who ;; we were or are sitting at home, fix genome so our next move is from home ] [ print [not annealing this traveler] setoutputline-count (outputline-count + 1) ] ] [ eat-output grab-semaphore eat-output attempt-improvement free-semaphore eat-output spin-home who ;; we were or are sitting at home, fix genome so our next move is from home ] ] go-hither-and-aim-yon who ] ifelse ((0 = (random travelers-die-feature-lifespan)) and travelers-die-feature-enabled) [ die ] [ stop ] end ;; ===================================================================== to grab-semaphore loop [ ifelse mutex-semaphore-available [ setmutex-semaphore-available false output true ] [ wait 1 ] ] end ;; ===================================================================== to free-semaphore setmutex-semaphore-available true end ;; ===================================================================== to wobble ;; outputs true when next target city is reached ifelse (target-dist < speed-control) [ fd target-dist settarget-dist 0 output true ] [ fd speed-control settarget-dist (target-dist - speed-control) output false ] end `observer` ;; This is the data that belongs to each turtle breed, though not all use all types, and not all ;; uses of one type are the same. This could have been done much better with accessors and ;; enumeration constants and a single turtle data variable. luckily this code isn't chiseled in ;; stone, either. turtles-own [ turtledata target-x target-y target-dist annealing-enabled annealing-overshoot-limit] globals [ ;; How many turtles of various breeds to create, where the value is variable. The "erasers" are ;; actually the artist breed of turtles reused, since the first artist dies quickly. ;; Do not laugh at my erasers, they are the trick that makes or breaks the visual part of this ;; application, by cleaning up edges of the complete graph no longer in use, or rarely in use, ;; among candidate BTSP solution graphs. the-towns-count the-erasers-count the-travelers-count ;; Stuff used for drawing the graphics part in different orientations plot-offset-x plot-offset-y city-offset-x city-offset-y box-size-x box-size-y smaller-box-edge-length ;; The "travelers die" feature is just for test use, to make something change so the displayed ;; monitor values will change, when nothing special else is affecting them. By killing off the ;; travelers slowly, the best, worst, and average values of the population change noticeably. travelers-die-feature-enabled travelers-die-feature-lifespan ;; Variable set to effective "infinity" for fitness, to use when initializing many variables ;; that are used for minimum or maximum calculations. huge-fitness-initializer ;; Compute once, use many times floating point value, done to save a speck of CPU effort. screen-quarter-width screen-quarter-height ;; Computational variable used to spread the travelers colors across the StarLogo spectrum, ;; based on the number of travelers read from the slider at setup time. color-step ;; Display variables for the StarLogo window monitors, showing various interesting things about ;; population genome fitness. fitness-best fitness-random fitness-average fitness-worst fitness-best-ever fitness-worst-ever ;; The output window stops when its buffer gets full; this count is used to control clearing that ;; window and emptying its buffer. outputline-count ;; how many samples to take from the population to find the most or least fit member for some use sexual-breeder1-sample-size sexual-breeder2-sample-size asexual-budder-sample-size mutate-sample-size replace-sample-size ;; selection weights for which proportion of the time what kind of GA genome modification is used; ;; their sum forms the denominator, and their individual values the numerators, for the fraction ;; of the time they are chosen. The do nothing selection weight is set to one to avoid ;; passing random an input of zero. ordered-crossover-selection-weight partial-match-crossover-selection-weight cyclic-crossover-selection-weight inversion-selection-weight mutation-selection-weight permutation-selection-weight do-nothing-selection-weight ;; A "permutation" involves taking two to many towns numbers from the genome, shuffling them, and putting ;; them back in the same slots. This is a more powerful operation the larget the upper limit is, but it ;; has rapidly diminishing chance of successfully improving things too, and must be used with caution. permutation-count-limit ;; A simulated annealing prepass seeds the population with highly fit genomes. This toggled variable ;; is the top level control for whether this feature is used or not. annealing-prepass-feature-enabled ;; Annealing stops when the annealing overshoot variable for each travelers-breed turtle reaches a ;; user controlled lower limit. This variable tells how many travelers are still executing annealing ;; steps. Annealing must finish before other kinds of GA genome modifications can begin. annealing-count-of-travelers ;; Fun display variables used in monitors to tell the user how the annealing is progressing; these show ;; the remaining overshoot limits available for travelers to use to shake up their chosen paths to avoid ;; getting stuck in local optima. annealing-limit-average annealing-limit-smallest annealing-limit-largest annealing-limit-random annealing-computed-cutoff ;; slider value converted to fitness units, a constant annealing-slider-cutoff ;; * 1000 as fraction of screen-half-height 0.001 to 0.999 annealing-slider-overshoot ;; * 1000 overshoot allowed as fraction of starting fitness annealing-overshoot-fraction ;; fraction of starting path allowed as starting overshoot annealing-slider-stepdown ;; * 1000 as fractional down-multiplier from 0.999 to 0.001 annealing-stepdown-fraction ;; per step down-multiplier as a real number fraction annealing-slider-success ;; * 1000 as probability of acceptance from 0.001 to 0.999 -- used in integer form annealing-no-surrender-effort ;; chance against one of trying again when anneling changes nothing current-traveler current-tries mutex-semaphore-available ;; we cannot abide genetically manipulating more than one set of travelers at a time! tracking speed-control ;; Interactive control from eye glazingly slow to blindingly fast ] breeds [ artists erasers towns travelers auditors ] ;; ===================================================================== to setup ca setup-constants setup-toggles setup-annealing setup-fitness-display setup-annealing-display create-artists 1 ask-artists [ setup-screen ] create-towns the-towns-count ask-towns [ setup-towns ] create-travelers the-travelers-count ask-travelers [ setup-travelers ] create-erasers the-erasers-count ask-erasers [ setup-eraser ] setup-output setup-plot end ;; ===================================================================== to setup-constants settracking false setscreen-quarter-width ( screen-half-width / 2 ) setscreen-quarter-height ( screen-half-height / 2 ) setcolor-step ( 124 / the-travelers-count ) sethuge-fitness-initializer ( the-towns-count * ( screen-height + screen-width ) ) setdo-nothing-selection-weight 1 ifelse (screen-height < screen-width) [ setplot-offset-x (0 - screen-quarter-width) setplot-offset-y 0 setcity-offset-x (screen-quarter-width) setcity-offset-y 0 setbox-size-x screen-height setbox-size-y screen-half-width ] [ setplot-offset-x 0 setplot-offset-y (0 - screen-quarter-height) setcity-offset-x 0 setcity-offset-y (screen-quarter-height) setbox-size-x screen-half-height setbox-size-y screen-width ] setsmaller-box-edge-length min box-size-x box-size-y setmutex-semaphore-available true end ;; ===================================================================== to setup-toggles settravelers-die-feature-enabled false setannealing-prepass-feature-enabled true end ;; ===================================================================== to setup-output setoutputline-count 200 ;; We want to clear the setup output before starting the go loop, but after the user has seen it. end ;; ===================================================================== to setup-plot end ;; ===================================================================== to setup-annealing setannealing-computed-cutoff ( ( smaller-box-edge-length * annealing-slider-cutoff ) / 10000 ) setannealing-overshoot-fraction ( annealing-slider-overshoot / 1000 ) setannealing-stepdown-fraction ( annealing-slider-stepdown / 1000 ) end ;; ===================================================================== to setup-annealing-display setannealing-limit-smallest huge-fitness-initializer setannealing-limit-largest (0 - huge-fitness-initializer) setannealing-limit-average -1 setannealing-limit-random -1 ifelse annealing-prepass-feature-enabled [ setannealing-count-of-travelers the-travelers-count ] [ setannealing-count-of-travelers 0 ] end ;; ===================================================================== to setup-fitness-display setfitness-best huge-fitness-initializer setfitness-worst ( 0 - huge-fitness-initializer ) setfitness-random -1 setfitness-average -1 setfitness-best-ever huge-fitness-initializer setfitness-worst-ever ( 0 - huge-fitness-initializer ) end ;; ===================================================================== to audit-fitness-all if ( display-survivors > 0 ) [ setfitness-best huge-fitness-initializer setfitness-worst 0 let [:local-fitness-average 0] ask-turtles [ minimize-fitness-best maximize-fitness-worst ] let [:local-fitness-average sum-of-turtles [ augment-fitness ]] setfitness-average ( :local-fitness-average / display-survivors ) ] end ;; ===================================================================== to audit-annealing-all if (annealing-prepass-feature-enabled = true) [ ifelse ( display-annealing-count-of-travelers > 0 ) [ let [:local-annealing-limit-average sum-of-turtles [ augment-annealing-overshoot-limit ]] setannealing-limit-average ( :local-annealing-limit-average / display-annealing-count-of-travelers ) setannealing-limit-smallest huge-fitness-initializer setannealing-limit-largest (0 - huge-fitness-initializer) ask-travelers [ minimize-annealing-limit-smallest maximize-annealing-limit-largest ] ] [ setannealing-prepass-feature-enabled false ] ] end ;; ===================================================================== to audit-fitness-random if ( display-survivors > 0 ) [ let [:hobo pick list-of-travelers] ask-travelers [ if ( who = :hobo ) [ setfitness-random get-fitness :hobo ] ] ] end ;; ===================================================================== to audit-annealing-random setannealing-count-of-travelers ( count-turtles-with [ report-living-annealer ] ) if ( annealing-count-of-travelers > 0 ) [ let [:annealers list-of-turtles-with [ report-living-annealer ] ] let [:annealer pick :annealers] ask-turtles [ if ( who = :annealer ) [setannealing-limit-random annealing-overshoot-limit] ] ] end ;; ===================================================================== to toggle-travelers-die ifelse travelers-die-feature-enabled [settravelers-die-feature-enabled false] [settravelers-die-feature-enabled true] end ;; ===================================================================== to display-travelers-die ifelse travelers-die-feature-enabled [output 1] [output 0] end ;; ===================================================================== to toggle-annealing-prepass ifelse annealing-prepass-feature-enabled [setannealing-prepass-feature-enabled false] [setannealing-prepass-feature-enabled true] end ;; ===================================================================== to display-annealing-prepass ifelse annealing-prepass-feature-enabled [output 1] [output 0] end ;; ===================================================================== to display-fitness-best output fitness-best end ;; ===================================================================== to display-fitness-random output fitness-random end ;; ===================================================================== to display-fitness-average output fitness-average end ;; ===================================================================== to display-fitness-worst output fitness-worst end ;; ===================================================================== to display-fitness-best-ever output fitness-best-ever end ;; ===================================================================== to display-fitness-worst-ever output fitness-worst-ever end ;; ===================================================================== to display-survivors output count-travelers end ;; ===================================================================== to display-annealing-count-of-travelers output annealing-count-of-travelers end ;; ===================================================================== to display-annealing-limit-average output annealing-limit-average end ;; ===================================================================== to display-annealing-limit-smallest output annealing-limit-smallest end ;; ===================================================================== to display-annealing-limit-largest output annealing-limit-largest end ;; ===================================================================== to display-annealing-limit-random output annealing-limit-random end ;; ===================================================================== to display-annealing-computed-cutoff output annealing-computed-cutoff end ;; ===================================================================== to display-current-traveler output current-traveler end ;; ===================================================================== to display-current-tries output current-tries end ;; ===================================================================== to go if (outputline-count > 50) [ co setoutputline-count 0 audit-fitness-all audit-annealing-all ] ask-travelers [ travel ] ask-erasers [ erase ] if (0 = (random 60)) [ audit-fitness-all audit-annealing-all ] if (0 = (random 10)) [ audit-fitness-random audit-annealing-random ] end `information` Blind Traveling Salesman Problem Demo in StarLogo 1.00 The latest version of StarLogo is available from MIT via URL http://www.media.mit.edu/starlogo/ "StarLogo for Java" Copyright status of this demo: public domain Development status of this demo: work in progress; so far the simulated annealing prepass is working and lots of scaffolding is in place for futher efforts. Original Author: Kent Paul Dolan, xanthian@well.com Maintained at this personal website URL: http://www.well.com/user/xanthian/public/code/StarLogo/BTSP/ Development environment: Windows NT 4.0 server. Purpose: to give enough control and visual feedback so that the user gains an intellectual understanding of how the problem is being solved in an abstract sense. Inspiration: Scott Robert Ladd's Blind Traveling Salesman Problem demo, written as a Java applet, accessible from links at his URL: http://www.coyotegulch.com/ Scott's demo is a wonderful way to waste hours and hours changing parameters and watching how they affected the running of the demo. Not having the source code to his demo yet, but itching to "tweak" it, made me want so much to use another set of methods to start with a better population and grow the fitness of the population, rather than just the fitness of the most fit member of the population, that I started this effort. Thanks, Scott! Features: Gobs of slider controls for changing parameters. Many monitors for watching fitness and other values change. Split screen, cities shown on one half, travelers tracing their paths on another. This actually turns out to be a feature, not a bug. Travelers, displayed in different colors, execute all at once in a single coordinate frame, so that the user can watch the solution set converge toward a best solution when all goes well. Invisible "eraser" travelers in the same coordinate frame clean up unused graph edges, by each tracing a random route of the complete graph connecting the cites, each drawing in the screen background color. This also "cleans up" some edges still in use, but that also turns out to be a feature instead of a bug. Achievements: The original alpha release of this program was reported also to have run without difficulty under FreeBSD. Under simulated annealing only, a population of 64 travelers converged to the same solution for eight cities. [This is Logo, remember, even if apparently complied to java byte code before it runs, so don't expect huge performance capabilities.] The demo will at least setup and try to run with hundreds of cities and hundreds of travelers. Bless StarLogo! There is a lot of informative stuff going to the output window, so keep that open and visible. Bugs: The demo is too big for small screens. Inside its window frame, it is 1210 by 750 pixels. Oops. Under some systems, this isn't a big issue, because windows larger than the screen are supported, under others, it is a show stopper. Typical of Java and NT, my development platform, stressed hard enough, the demo will lock up. DO NOT TRY TO SAVE A LOCKED UP StarLogo PROGRAM. Your most likely result is an empty source code file. To be fair, StarLogo 1.00 was pretty much a beta release itself, so I may be blaming the wrong parties. There is also the little factor that StarLogo is an intellectually challenging language to grasp, with two cooperating (or conflicting) source code sets, the observer code and the turtle code. Putting functionality on the wrong side of the divider between them can cause amazing and amusing chaos, and rafts of incomprehensible error messages. There are still too many "magic numbers" in the code. The design of the per-turtle data needs rework, it should have been done in a more OO way, as is it causes lots of coding headaches. Because communicating between interface and code is done in part via global variables, there are a _lot_ of global variables. Again, these need encapsulation. To do: Add the rest of the genetic algorithm code. User Manual: Install StarLogo. Open this program under StarLogo -- untested yet under the newer release. Set sliders to interesting values. Press the SetUp BTSP button. Wait a bit. Press the Stop/Go Toggle button. The rest is pretty much intuitive. `interface` SLSlider top-left 260 10 width-height 370 25 name "Integer Proportion of Asexual Mutation" variable "mutation-selection-weight" min-value 0 max-value 20 current-value 0 slider-number 0 show-name? true SLSlider top-left 290 10 width-height 370 25 name "Integer Proportion of Partial Match Crossovers" variable "partial-match-crossover-selection-weight" min-value 0 max-value 20 current-value 0 slider-number 1 show-name? true SLSlider top-left 320 10 width-height 370 25 name "Integer Proportion of Cyclic Crossovers" variable "cyclic-crossover-selection-weight" min-value 0 max-value 20 current-value 0 slider-number 2 show-name? true SLSlider top-left 350 10 width-height 370 25 name "Integer Proportion of Asexual Inversion" variable "inversion-selection-weight" min-value 0 max-value 20 current-value 0 slider-number 3 show-name? true SLSlider top-left 380 10 width-height 370 25 name "Integer Proportion of Asexual Permutation" variable "permutation-selection-weight" min-value 0 max-value 20 current-value 0 slider-number 4 show-name? true SLSlider top-left 410 10 width-height 370 25 name "Integer Proportion of Ordered Crossovers" variable "ordered-crossover-selection-weight" min-value 0 max-value 20 current-value 20 slider-number 5 show-name? true SLButton turtle-or-observer? observer top-left 130 10 width-height 240 36 name "Toggle Simulated Annealing Prepass Feature" line-to-run "toggle-annealing-prepass" forever? false button-number 4 show-name? true SLSlider top-left 450 10 width-height 370 25 name "Upper Limit for Number of Towns Permuted " variable "permutation-count-limit" min-value 2 max-value 6 current-value 3 slider-number 6 show-name? true SLSlider top-left 490 10 width-height 370 25 name "Samples Used to Select a Best First Breeder" variable "sexual-breeder1-sample-size" min-value 1 max-value 20 current-value 3 slider-number 7 show-name? true SLSlider top-left 520 10 width-height 370 25 name "Samples Used to Select a Best Second Breeder" variable "sexual-breeder2-sample-size" min-value 1 max-value 20 current-value 3 slider-number 8 show-name? true SLSlider top-left 550 10 width-height 370 25 name "Samples Used to Select a Best Asexual Budder" variable "asexual-budder-sample-size" min-value 1 max-value 20 current-value 3 slider-number 9 show-name? true SLSlider top-left 580 10 width-height 370 25 name "Samples Used to Select a Best Mutant-to-be" variable "mutate-sample-size" min-value 1 max-value 20 current-value 3 slider-number 10 show-name? true SLSlider top-left 610 10 width-height 370 25 name "Samples Used to Select a Genome to Discard" variable "replace-sample-size" min-value 1 max-value 20 current-value 10 slider-number 11 show-name? true SLMonitor top-left 90 140 width-height 110 36 name "Random Genome" list-to-run "display-fitness-random" digits 3 delay 0.5 monitor-number 1 show-name? true SLMonitor top-left 50 10 width-height 110 36 name "Best Genome" list-to-run "display-fitness-best" digits 3 delay 0.5 monitor-number 2 show-name? true SLMonitor top-left 641 304 width-height 81 36 name "current tries" list-to-run "display-current-tries" digits 0 delay 0.2 monitor-number 3 show-name? true SLMonitor top-left 642 10 width-height 84 36 name "current traveler" list-to-run "display-current-traveler" digits 0 delay 0.2 monitor-number 4 show-name? true SLMonitor top-left 50 140 width-height 110 36 name "Average Genome" list-to-run "display-fitness-average" digits 3 delay 0.5 monitor-number 5 show-name? true SLMonitor top-left 50 270 width-height 110 36 name "Worst Genome" list-to-run "display-fitness-worst" digits 3 delay 0.5 monitor-number 6 show-name? true SLMonitor top-left 130 270 width-height 110 36 name "off = 0 on = 1" list-to-run "display-annealing-prepass" digits 0 delay 0.5 monitor-number 7 show-name? true SLMonitor top-left 170 10 width-height 136 36 name "Smallest Annealing Limit" list-to-run "display-annealing-limit-smallest" digits 3 delay 0.5 monitor-number 8 show-name? true SLMonitor top-left 210 10 width-height 136 36 name "Largest Annealing Limit" list-to-run "display-annealing-limit-largest" digits 3 delay 0.5 monitor-number 9 show-name? true SLMonitor top-left 210 150 width-height 136 36 name "Random Annealing Limit" list-to-run "display-annealing-limit-random" digits 3 delay 0.5 monitor-number 10 show-name? true SLMonitor top-left 170 150 width-height 136 36 name "Average Annealing Limit" list-to-run "display-annealing-limit-average" digits 3 delay 0.5 monitor-number 11 show-name? true SLMonitor top-left 170 290 width-height 90 36 name "Cutoff Limit" list-to-run "display-annealing-computed-cutoff" digits 3 delay 0.5 monitor-number 12 show-name? true SLMonitor top-left 210 290 width-height 90 36 name "Annealer Count" list-to-run "display-annealing-count-of-travelers" digits 0 delay 0.5 monitor-number 13 show-name? true SLMonitor top-left 90 10 width-height 110 36 name "Best Genome Ever" list-to-run "display-fitness-best-ever" digits 3 delay 0.5 monitor-number 14 show-name? true SLMonitor top-left 90 270 width-height 110 36 name "Worst Genome Ever" list-to-run "display-fitness-worst-ever" digits 3 delay 0.5 monitor-number 15 show-name? true SLSlider top-left 411 396 width-height 614 25 name "Number of Erasers" variable "the-erasers-count" min-value 0 max-value 20 current-value 5 slider-number 14 show-name? true SLSlider top-left 450 394 width-height 616 25 name "Speed Control in Traveler Motion per 'Go' Cycle; Low for Long may Crash Java" variable "speed-control" min-value 1 max-value 100 current-value 10 slider-number 20 show-name? true SLSlider top-left 351 396 width-height 613 25 name "Number of Towns" variable "the-towns-count" min-value 4 max-value 128 current-value 20 slider-number 12 show-name? true SLSlider top-left 381 396 width-height 613 25 name "Size of Traveler Population" variable "the-travelers-count" min-value 1 max-value 160 current-value 60 slider-number 13 show-name? true SLSlider top-left 490 393 width-height 619 25 name "Annealing Overshoot Starting Limit as Fraction per Thousand of Total Path Length" variable "annealing-slider-overshoot" min-value 1 max-value 1000 current-value 150 slider-number 15 show-name? true SLSlider top-left 520 395 width-height 619 25 name "Multiplier per Thousand to Decrement Annealing Overshoot each Round" variable "annealing-slider-stepdown" min-value 800 max-value 999 current-value 990 slider-number 16 show-name? true SLSlider top-left 550 393 width-height 621 25 name "Annealing Overshoot Stopping Limit as Fraction per Ten-Thousand of Screen Edge" variable "annealing-slider-cutoff" min-value 1 max-value 1000 current-value 100 slider-number 17 show-name? true SLSlider top-left 580 393 width-height 621 25 name "Fraction per Thousand of In-Limits Annealing Overshoots to Accept" variable "annealing-slider-success" min-value 1 max-value 1000 current-value 100 slider-number 18 show-name? true SLSlider top-left 610 393 width-height 622 25 name "How Hard to Try Annealing before Surrendering an Overshoot Decrement with No Change" variable "annealing-no-surrender-effort" min-value 1 max-value 500 current-value 250 slider-number 19 show-name? true SLButton turtle-or-observer? observer top-left 10 200 width-height 180 36 name "Stop/Go Toggle" line-to-run "go" forever? true button-number 3 show-name? true SLButton turtle-or-observer? observer top-left 10 10 width-height 180 36 name "Set Up BTSP" line-to-run "setup" forever? false button-number 2 show-name? true SLCanvas top-left 8 396 `settings` patch-size 8 num-shapes 256 screen-half-width 38 screen-half-height 19 interface-window-xcor 533 interface-window-ycor 0 interface-window-size 1024 686 output-window-xcor 533 output-window-ycor 421 output-window-width 1068 output-window-height 750 info-window-xcor 0 info-window-ycor 0 info-window-width 500 info-window-height 419 control-center-xcor 0 control-center-ycor 538 control-center-width 533 control-center-height 419 turtle-command-center-height 138 observer-command-center-height 138 plot-window-xcor 0 plot-window-ycor 0 plot-window-width 508 plot-window-height 372 `string table` H4sIAAAAAAAAAGNgYGAAABzfRCEEAAAAAAAABA== `symbol table` H4sIAAAAAAAAAGNgYGAAABzfRCEEAAAAAAAABA== `double table` H4sIAAAAAAAAAGNgYGAAABzfRCEEAAAAAAAABA== `list table` H4sIAAAAAAAAAGNgYGAAABzfRCEEAAAAAAAABA== `observer world` H4sIAAAAAAAAAN1U6VLbMBBeegAFAoEAAcLdix5qC73+9lFQ5HWsqSy5kpyEPm0f pSuBHWMoD9DMeCb6vm8P7a4Wupdm6NCO0R47zz1e/oD69xbmFeqRz+j/H1golPEF 6kgtR3HNPqHvO6z5DJk3E+2YMKX2UbkeQLScgszgOehFreVjctIgHkEnhGEmTR16 NiXo8W3oKobrCOmvmqqnt6GgmoeloZkyJ39jlCw0zoFfhL7LuaL4LOCYjJDVN3oG B7PsEoksRe5LS1fRfKgwIckSHN4vUTJFV/BQqWXYyUrym0qv0TkmtfSSK0rBErsC m05YRM1+ldx6SmQikxi+A1stJkM5ykKJVmFJGGUscx4LOq/BSuV9iC4ourBaIZbr xOShDbBWYZSy5SMkcAM6FTgxNtr2YL3pjeE4ZroJG7eUFb4FXVP6ovRKaqy7uA0D h9OSKzakOyRoz5njeaEwFp8E/bbgoiXYgT1eKcqEBC1+FzbyMoxgC9+DnsVCcdEm BnBqLPnBhAlrnDPj4JN6J7w0mk2q6u7DWUEFpx6xnHuRPaw+gBNxJZQUD8sOYU9q 4lxA7+GPYDfe5h/0MewXaB9SnMAgMUwbn0k9uk9wCv2mi9gomtNcBvI5nHCtkcaS jAuqH6c23533F7A/k117MOnsDZPiJfRniui9MWyvYKdNXj+/OHev75oqbkfX5Bls t8l6sN/AbjOrnGYx9Lj0tAniDmv4dUqGUarJdzRlbTL00GXGhLjvmzeuCZZaHstL CtaMfuMhvMuEtiDRH2DQoG/wpv3HZlEq+1IIemjEfoKDGasNMdaiDhpMU2NDiufQ FRH1dScIvaBtWKMSg6/PccaQ9h/mvMgMtZaPuVShu0R/gUWyFz8pEp2+QscVGOpo tLdGEfQtrO25uLv/z+8voZXBlAsHAAAAAAcL `patch world` H4sIAAAAAAAAAH3Wy2qDe5rdYSU0iSAEBPFAAw2EMcIYYWRZtiVZtlTn82HvuoFu iiI9aLqLpAbpmS7Nl9SXkEp31reLqvU9Bh8fr/87/U3++9//8R/+9Pt/XP7vP/3D n/7w95fJ8PHjyd/9n9//y//695//8x9//+evs8l/+ac//PP//NM//vnnxeS//vGf /uVPf/zDP//5l/nk7/71P/51Ovlv//7QX/zfZLL68+f/+H/PnP/tP57O97/4+E+5 XG0Km8FuYHPYAraE3cLuYCvYPewBtoY9wjawJ9gW9gzbwV5gr7A32B52gB1h77AT 7AP2CTvDLrLruH0Pu+9h933svo/dD7D7AXY/xO6H2P0Iux9h92PsfozdT7D7CXY/ xe6n2P0Mu59h93Psfo7dL7D7BXa/xO6X2P0Ku19h92vsfo3db7D7DXa/xe63F5je /Bq3b3DvG9z7Bve+wb1vce9b3PsW977Fvd/h3u/G7+Vvl7+h71qj2hQ2g93A5rAF bAm7hd3BVrB72ANsDXuEbWBPsC3sGbaDvcBeYW+wPewAO8LeYSfYB+wTdoZdZNdx +x5238Pu+9h9H7sfYPcD7H6I3Q+x+xF2P8Lux9j9GLufYPcT7H6K3U+x+xl2P8Pu 59j9HLtfYPcL7H6J3S+x+xV2v8Lu19j9GrvfYPcb7H6L3f9vjW5682vcvsG9b3Dv G9z7Bve+xb1vce9b3PsW936He78bvze0xrTsJrApbAa7gc1hC9gSdgu7g61g97AH 2Br2CNvAnmBb2DNsB3uBvcLeYHvYAXaEvcNOsA/YJ+wMu8iu45bWqIZdWqMadmmN atilNaphl9aohl1aoxp2aY1q2KU1qmGX1qiGXVqjGnZpjWrYpTWqYZfWqIZdWqMa dmmNatilNapdYHrza9zSGtVwL61RDffSGtVwL61RDffSGtXG7w2tMSu7CWwKm8Fu YHPYAraE3cLuYCvYPewBtoY9wjawJ9gW9gzbwV5gr7A32B52gB1h77AT7AP2CTvD LrLruKU1qmGX1qiGXVqjGnZpjWrYpTWqYZfWqIZdWqMadmmNatilNaphl9aohl1a oxp2aY1q2KU1qmGX1qiGXVqjGnZpjWoXmN78Gre0RjXcS2tUw720RjXcS2tUw720 RrXxe0Nr3JTdBDaFzWA3sDlsAVvCbmF3sBXsHvYAW8MeYRvYE2wLe4btYC+wV9gb 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 SAEAAAFI4Q== `turtle world` H4sIAAAAAAAAAEVPy04DMQw0NOkuFCEhcUHi0ENPSMsB+AA+gI+o21ibSCFZJe7r K/hk8LrVYinSjDOeseF+zbvCkZaVkWn9CVP9wDxS6tkL/gK7KURO4AqaIWYeKAl5 BbvNMReBz2BOW0V3sFCzafoDWpG7fEiDsDew1eNAAl/AHM8zBmYHnzV3Xv1F+Q6N J3Qh9UIexy+4PW/rkFHFLWPpibuj4KuJnYRdw+LCXKgsjRk8YEqEUfw6SriJeo+B p/923lOR+MxdDN9hnLL6GiwsLlVTGipYRachlmXZqvY3XHAvV+uPgRZ3LnBWZn+l xn3/AF918DVwAQAAAAABcA==