Simulation Code
All of these programs require a Ruby interpreter and the charlie library. The latter can be installed by using the 'gem' tool included with most Ruby distributions (gem install charlie
).
Test tool with graphic user interface. Population is displayed on screen and stats (minimum, maximum, average and standard deviation) are written to stats_history
. Ruby/TK required (included in the windows 'one-click' installer, for linux use the package libtcltk-ruby1.8
).
Downloadable version available here
%w[rubygems charlie tk].each{|x|require x} class traveler < FloatListGenotype(1,2..100) BONUS = 2 def choice genes[0].between(2,100).round end def fight_points(other) c,oc = choice, other.choice c == oc ? [c,c] : c < oc ? [c+BONUS,c-BONUS] : [oc-BONUS,oc+BONUS] end use NullCrossover end BNS,PRB,SZ,GRP,GENS=*0..4 lbl = [["Bonus",2],["Mutation Probability",0.5],["Mutation Size (sigma)",5],["Selection Tournament Size",4],["Generations",1000]] root = TkRoot.new() { title "Traveler's Dilemma Simulation" } mid,bottom = [0,0].map{ TkFrame.new(root).pack('side'=>'top','fill'=>'x','expand'=>true) } popcv = TkCanvas.new(mid) { width 816; height 100; }.pack entries = lbl.map{|l,v| f = TkFrame.new(bottom).pack('side'=>'top','fill'=>'x') TkLabel.new(f) {text l }.pack('side'=>'left') TkEntry.new(f) { set v }.pack('side'=>'right') } $paused = false $thr = nil TkButton.new(bottom) {text "[Run]" ; command{ $thr.kill rescue nil $thr = Thread.new{ history = [] traveler::BONUS = entries[BNS].get.to_f klass = Class.new(traveler){ use ListMutator(:probability[entries[PRB].get.to_f],:gaussian[entries[SZ].get.to_f]), CoTournamentSelection(entries[GRP].get.to_i) def mutate!; super; genes[0] = genes[0].between(traveler::BONUS,100); end } pop = Population.new(klass) entries[GENS].get.to_i.times{|g| sleep 0.3 while $paused gs = pop.evolve_silent(1).map(&:choice).sort history << gs.stats popcv.delete('all') TkcLine.new(popcv,8*2,50,800,50) gs.each{|x| TkcOval.new(popcv,8*x-5,45,8*x+5,55) } TkcText.new(popcv,400,75,:text=>"Generation #{g} : " + gs.map{|x|x.to_s}.join(', ')) } File.open('stats_history','w'){|f| history.transpose.each{|a| f << a.join(' ') << "\n" } } } } }.pack TkButton.new(bottom) {text "[Paused]" ; command{ $paused = !$paused } }.pack Tk.mainloopProgram that generated most of our results. When given the reward as a parameter, runs the algorithm for a variety of mutation and tournament size settings and outputs the average strategy in the population after 1000 generations. Ruby/TK not required.
Downloadable version available here
require 'rubygems' require 'charlie' exit(puts "GIVE ARG!") unless ARGV[0] BONUS = ARGV[0].to_i MIN = BONUS MAX = 100 class traveler < FloatListGenotype(1,MIN..MAX) def choice genes[0].between(MIN,MAX).round end def fight_points(other) c,oc = choice, other.choice c == oc ? [c,c] : c < oc ? [c+BONUS,c-BONUS] : [oc-BONUS,oc+BONUS] end end for group_size in (2..20) $stderr.puts group_size for mut_size in (1..100).map{|x| x / 10.0 } klass = Class.new(traveler){ use ListMutator(:probability[0.5],:gaussian[mut_size]), CoTournamentSelection(group_size) def mutate!; super; genes[0] = genes[0].between(MIN,MAX); end } print Population.new(klass).evolve_silent(1000).map{|x| x.genes[0] }.average, ' ' end puts end