[QUIZ] Rock Paper Scissors

Hello all, I'm doing Rock Paper Scissors from Ruby Quiz book.
the answer (without cheating) gives example of CNBiasInverter,

CNBiasInverter vs. JEGQueuePlayer
CNBiasInverter: 653.5
JEGQueuePlayer: 346.5
CNBiasInverter Wins

while I agree that if oponent has some weight bias on giving out position,
but if he has repeatable cycle, you can do better than that.

so I do following
#file cycle.rb
class CycleDetecter
  CYCLE_MAX_LENGTH = 6
  def self.detect_cycle(arr)
   1.upto(CYCLE_MAX_LENGTH) do |i|
     if isCyclic arr,i
       return Cycle.new(i)
     end
   end
   return Cycle.new(-1)
  end
  def self.isCyclic(arr, cycle)
    #puts arr.inspect
    cycle.times do |i|
       n = 1
       while i+n*cycle < arr.size
         if arr[i] != arr[i + n* cycle]
           return false
         end
         n += 1
      end

    end
    return true
  end
end

class Cycle
  attr_accessor :cycle
  def initialize(cycle)
    @cycle = cycle
  end
  def to_s
    cycle.to_s
  end
  def == (other)
    cycle == other.cycle
  end

end
#file weight.rb
class WeightedDetecter
  WEIGHT_MAX_LENGTH = 6
  def self.detect_weight(arr)
    rockSize=paperSize=scissorsSize=0
    arr.each do |x|
      if x == :rock
        rockSize+=1
      elsif x==:paper
        paperSize +=1
      elsif x== :scissors
        scissorsSize += 1
      end
    end
    Weight.new([rockSize*1.0/arr.size, paperSize*1.0/arr.size,
scissorsSize*1.0/arr.size])
  end

end

class Weight
  attr_accessor :weight
  def initialize(weight)
    @weight = weight
  end
  def == (other)
    weight == other.weight
  end

end
arr = [:rock,:rock,:paper,:scissors]
puts WeightedDetecter.detect_weight(arr).inspect

#file femto_stragety_player.rb
require 'player/stragety/cycle'
require 'player/stragety/weight'
class FemtoStragetyPlayer < Player
  QUEUE = [ :rock, :paper, :scissors ]
  CYCLE = "CYCLE"
  QUEUE_TYPE = "QUEUE_HAND"
  def initialize( opponent_name )
    super
    @myIndex = 0
    @oponent_choices = []
  end

  def choose
      oponentStragety = judgeOponent
      #puts oponentStragety.class
      if oponentStragety.class == Cycle
        result = competeCycle(oponentStragety)
      elsif oponentStragety.class == Weight

        r = rand
        weight = oponentStragety.weight
        if r<weight[0]
          result = QUEUE[1]
        elsif r<weight[0] + weight[1]
          result = QUEUE[2]
        else # r<weight[0] + weight[1] + weight[2] = 1
          result = QUEUE[0]
        end
     else
        #puts "fallback to random choice"
        #fallback to random choice
        result = QUEUE[Integer(rand*3)]
      end
    @myIndex += 1 #add myIndex for next run turn
    #puts "#{oponentStragety},#{@oponent_choices.inspect}, #{result.inspect}"
    result
  end
  def judgeOponent

    if @oponent_choices.size > 0
      startIndex = @myIndex - CycleDetecter::CYCLE_MAX_LENGTH * 2
      startIndex = 0 if startIndex < 0
      arr = @oponent_choices[startIndex, CycleDetecter::CYCLE_MAX_LENGTH * 2]

      result = CycleDetecter.detect_cycle(arr)
      #puts "#{@oponent_choices.inspect}, #{startIndex},
#{arr.inspect}, #{result.inspect} \n"
      if result == Cycle.new(-1)
        result = WeightedDetecter.detect_weight(arr)
      end
      return result
    end
  end

  def competeCycle(cycle)
   findWinHand(@oponent_choices[@myIndex-cycle.cycle])
  end
  def findWinHand(hand)
    if hand == :rock then
       return :paper
    end
    if hand == :paper then
       return :scissors
    end
    if hand == :scissors then
       return :rock
    end
  end

  def result( your_choice, opponents_choice, win_lose_or_draw )
    @oponent_choices << opponents_choice
    # (optional) called after each choice you make to give feedback
    # your_choice = your choice
    # oppenents_choice = opponent's choice
    # win_lose_or_draw = :win, :lose or :draw, your result
  end
end

···

------------------------------------------------------------------
and get the following result, better than CNBiasInverter

FemtoStragetyPlayer vs. JEGQueuePlayer
  FemtoStragetyPlayer: 998
  JEGQueuePlayer: 2
  FemtoStragetyPlayer Wins

(file femto_stragety_player.rb is under subdir 'player'
cycle.rb and weight.rb is under 'player/stragety'
because the main file dynamic requires in femto_stragety_player,
so the working dir is dir up 1 level than 'player' , so in file
femto_stragety_player.rb , I write require 'player/stragety/cycle.rb'
rather than 'stragety/cycle.rb' , do anyone knows how to handle this?)

--------------------------------------------------------
compare to random player:
several runs: can see from the number, that sometimes
FemtoStragetyPlayer beats FemtoRandomPlayer, sometime
be beaten by FemtoRandomPlayer (score up or down around 20)
cause when compared to RandomPlayer, the computing of weight
also make FemtoStragetyPlayer back to Random.

FemtoStragetyPlayer vs. FemtoRandomPlayer
  FemtoStragetyPlayer: 482.0
  FemtoRandomPlayer: 518.0
  FemtoRandomPlayer Wins

FemtoStragetyPlayer vs. FemtoRandomPlayer
  FemtoStragetyPlayer: 510.0
  FemtoRandomPlayer: 490.0
  FemtoStragetyPlayer Wins

FemtoStragetyPlayer vs. FemtoRandomPlayer
  FemtoStragetyPlayer: 482.5
  FemtoRandomPlayer: 517.5
  FemtoRandomPlayer Wins

FemtoStragetyPlayer vs. FemtoRandomPlayer
  FemtoStragetyPlayer: 483.0
  FemtoRandomPlayer: 517.0
  FemtoRandomPlayer Wins

FemtoStragetyPlayer vs. FemtoRandomPlayer
  FemtoStragetyPlayer: 513.0
  FemtoRandomPlayer: 487.0
  FemtoStragetyPlayer Wins

Regards
femto