%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