%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.mainloop