[QUIZ] Paper Rock Scissors (#16)

Discussion in 'Ruby' started by Ruby Quiz, Jan 21, 2005.

  1. Ruby Quiz

    Ruby Quiz Guest

    The three rules of Ruby Quiz:

    1. Please do not post any solutions or spoiler discussion for this quiz until
    48 hours have passed from the time on this message.

    2. Support Ruby Quiz by submitting ideas as often as you can:

    http://www.grayproductions.net/ruby_quiz/

    3. Enjoy!

    -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    Alright generals, break out you copies of "The Art of War" and let's get a
    little competition going!

    Your task is to build a strategy for playing the game of Paper Rock Scissors
    against all manner of opponents. The question here is if you can adapt to an
    opponent's strategy and seize the advantage, while he is doing the same to you
    of course.

    If you're not familiar with this childhood game, it's very simple. Both players
    choose one of three items at the same time: Paper, a Rock, or Scissors. A
    "winner" is chosen by the following rules:

    Paper covers a Rock. (Paper beats a Rock.)
    Scissors cut Paper. (Scissors beat Paper.)
    A Rock smashes Scissors. (A Rock beats Scissors.)
    Anything else is a "draw".

    Defining a player for straight forward. I'm providing a class you can just
    inherit from:

    class YourPlayer < Player
    def initialize( opponent )
    # optional
    #
    # called at the start of a match verses opponent
    # opponent = String of opponent's class name
    #
    # Player's constructor sets @opponent
    end

    def choose
    # required
    #
    # return your choice of :paper, :rock or :scissors
    end

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

    We'll need some rules for defining players, to make it easy for all our
    strategies to play against each other:

    * send in one file for each strategy
    * a file should contain exactly one subclass of Player
    * start the name of your subclass with your initials
    * start the name of your files with your initials
    * start any data files you write to disk with your initials

    Those rules should help with testing how different algorithms perform against
    each other.

    Here are two dumb Players to practice with:

    class JEGPaperPlayer < Player
    def choose
    :paper
    end
    end

    class JEGQueuePlayer < Player
    QUEUE = [ :rock,
    :scissors,
    :scissors ]

    def initialize( opponent )
    super

    @index = 0
    end

    def choose
    choice = QUEUE[@index]

    @index += 1
    @index = 0 if @index == QUEUE.size

    choice
    end
    end

    Here's how those two do against each other in a 1,000 game match:

    JEGPaperPlayer vs. JEGQueuePlayer
    JEGPaperPlayer: 334
    JEGQueuePlayer: 666
    JEGQueuePlayer Wins

    Finally, here's the game engine that supports the players:

    #!/usr/bin/env ruby

    class Player
    @@players = [ ]

    def self.inherited( player )
    @@players << player
    end

    def self.each_pair
    (0...(@@players.size - 1)).each do |i|
    ((i + 1)...@@players.size).each do |j|
    yield @@players, @@players[j]
    end
    end
    end

    def initialize( opponent )
    @opponent = opponent
    end

    def choose
    raise NoMethodError, "Player subclasses must override choose()."
    end

    def result( you, them, win_lose_or_draw )
    # do nothing--sublcasses can override as needed
    end
    end

    class Game
    def initialize( player1, player2 )
    @player1 = player1.new(player2.to_s)
    @player2 = player2.new(player1.to_s)
    @score1 = 0
    @score2 = 0
    end

    def play( match )
    match.times do
    hand1 = @player1.choose
    hand2 = @player2.choose
    case hand1
    when :paper
    case hand2
    when :paper
    draw hand1, hand2
    when :rock
    win @player1, hand1, hand2
    when :scissors
    win @player2, hand1, hand2
    else
    raise "Invalid choice by #{@player2.class}."
    end
    when :rock
    case hand2
    when :paper
    win @player2, hand1, hand2
    when :rock
    draw hand1, hand2
    when :scissors
    win @player1, hand1, hand2
    else
    raise "Invalid choice by #{@player2.class}."
    end
    when :scissors
    case hand2
    when :paper
    win @player1, hand1, hand2
    when :rock
    win @player2, hand1, hand2
    when :scissors
    draw hand1, hand2
    else
    raise "Invalid choice by #{@player2.class}."
    end
    else
    raise "Invalid choice by #{@player1.class}."
    end
    end
    end

    def results
    match = "#{@player1.class} vs. #{@player2.class}\n" +
    "\t#{@player1.class}: #{@score1}\n" +
    "\t#{@player2.class}: #{@score2}\n"
    if @score1 == @score2
    match + "\tDraw\n"
    elsif @score1 > @score2
    match + "\t#{@player1.class} Wins\n"
    else
    match + "\t#{@player2.class} Wins\n"
    end
    end

    private

    def draw( hand1, hand2 )
    @score1 += 0.5
    @score2 += 0.5
    @player1.result(hand1, hand2, :draw)
    @player2.result(hand2, hand1, :draw)
    end

    def win( winner, hand1, hand2 )
    if winner == @player1
    @score1 += 1
    @player1.result(hand1, hand2, :win)
    @player2.result(hand2, hand1, :lose)
    else
    @score2 += 1
    @player1.result(hand1, hand2, :lose)
    @player2.result(hand2, hand1, :win)
    end
    end
    end

    match_game_count = 1000
    if ARGV.size > 2 and ARGV[0] == "-m" and ARGV[1] =~ /^[1-9]\d*$/
    ARGV.shift
    match_game_count = ARGV.shift.to_i
    end

    ARGV.each do |p|
    if test(?d, p)
    Dir.foreach(p) do |file|
    next if file =~ /^\./
    next unless file =~ /\.rb$/
    require File.join(p, file)
    end
    else
    require p
    end
    end

    Player.each_pair do |one, two|
    game = Game.new one, two
    game.play match_game_count
    puts game.results
    end

    To use:

    paper_rock_scissors.rb jeg_paper_player.rb jeg_queue_player.rb

    Or you can point it at a directory and it will treat all the ".rb" files in
    there as Players:

    paper_rock_scissors.rb players/

    You can also change the match game count:

    paper_rock_scissors.rb -m 10000 players/
    Ruby Quiz, Jan 21, 2005
    #1
    1. Advertising

  2. Ruby Quiz

    Avi Bryant Guest

    > * a file should contain exactly one subclass of Player
    > * start the name of your subclass with your initials


    Why use prefixes on the class names, rather than a module? Wouldn't it
    be better for my subclass to be AJB::MyPlayer than AJBMyPlayer?

    Avi
    Avi Bryant, Jan 22, 2005
    #2
    1. Advertising

  3. On Jan 22, 2005, at 8:10 AM, Avi Bryant wrote:

    >> * a file should contain exactly one subclass of Player
    >> * start the name of your subclass with your initials

    >
    > Why use prefixes on the class names, rather than a module? Wouldn't it
    > be better for my subclass to be AJB::MyPlayer than AJBMyPlayer?


    It's a fair style issue you raise and you're probably right. But, I
    didn't think of it at the time and I do have reasons for you NOT to do
    it that way. (Score print out, for one.) So, just consider it coding
    to a specification. ;)

    James Edward Gray II
    James Edward Gray II, Jan 22, 2005
    #3
  4. > Here's how those two do against each other in a 1,000 game match:
    >
    > JEGPaperPlayer vs. JEGQueuePlayer
    > JEGPaperPlayer: 334
    > JEGQueuePlayer: 666
    > JEGQueuePlayer Wins


    I would suggest that you need at least 55% to win. (45%-55% is draw)
    Otherwise, a strategy simply selecting a random choice will always have a
    50% chance of winning - The results of a single run will be highly
    unpredictable and change from time to time.
    Benedikt Huber, Jan 23, 2005
    #4
  5. Ruby Quiz

    Avi Bryant Guest

    Well, how is this competition going to be scored? A strategy trying to
    win the most number of matches will be somewhat different from one
    trying to win by the largest margin.
    Avi Bryant, Jan 23, 2005
    #5
  6. Ruby Quiz

    Avi Bryant Guest

    Here's my submission. I take no credit for the basic idea, which is
    shamelessly ripped from Dan Egnor's famous "Iocaine Powder" program;
    mostly what I was trying to do was provide a clean Ruby implementation
    of Dan's insights.

    For reasons that will become clear later on, this submission actually
    contains several subclasses of Player in the same file - if this is a
    problem when running the competition, let me know, it's easy to work
    around. However, the "real" player being submitted here is
    AJBMetaPlayer.

    The basic idea behind AJBMetaPlayer is this: it keeps an array of
    other instances of Player subclasses. When it gets handed the
    result(), it passes the information along to each of these players;
    when it needs to choose(), it picks just one of the players and
    returns its choice. It also keeps a running tally on each move of
    which of the player instances would have won or lost if it had been
    the one chosen for that move, and then uses that when picking a player
    for next time. It will always pick the player that would have had the
    best record so far, had it been chosen every time.

    The interesting twist is that AJBMetaPlayer also makes use of two
    PlayerDecorators, which present a Player interface but are wrapped
    around existing players. One of these is the Inverter, which reverses
    the results that are provided to it: a Player instance wrapped in an
    Inverter will see the world as your opponent sees it. The other is
    the Defeater, which shifts the choices made in choose() so that they
    defeat the original choice (ie, :rock becomes :paper and so on). By
    using all of the possible combinations of these, a single Player class
    can show up 6 times in the AJBMetaPlayer's players array: normally,
    defeated, defeated twice (so :rock becomes :scissors), inverted,
    inverted + defeated, inverted + defeated + defeated. This allows it
    to model all of the potential second- and triple-guessing an opponent
    might be doing. The generic algorithm for picking the best player
    instance will automatically detect and exploit this if it's there.

    Absent any randomness, if you have a player instance identical to your
    opponent in the players array, wrapped with an Inverter (so it gets
    the same results your opponent does) and then a Defeater, you will
    beat it every time. Indeed, if it were considered legal, a very
    effective approach would be to search for all active Player subclasses
    and construct an instance of each, wrapped in all the possible
    variations. Since this seems a little too much like cheating,
    AJBMetaPlayer by default uses only two base strategies that are
    hopefully representative of the deterministic players it will
    encounter. One of these, AJBFrequencyPlayer, just counters whatever
    move its opponent has played most often in the past. The other,
    AJBHistoryPlayer, builds a tree of its opponents previous runs of
    moves (of lengths 1..20), and assumes that if it finds the same run
    again, the player might continue it in the same way. The meta player
    should do quite well against players that are either susceptible to
    such analysis, *or are using a sufficiently similar analysis
    themselves*. For safety, there's also AJBRandomPlayer thrown in by
    default, as a fallback if nothing else seems to work.

    Here's the code:
    # AJBMetaPlayer.rb
    # (c) Avi Bryant 2005
    # heavily inspired by Dan Egnor's "Iocaine Powder":
    # http://dan.egnor.name/iocaine.html

    class Hash
    def max_key
    max{|a,b| a[1]<=>b[1]}[0] unless empty?
    end
    end

    class Symbol
    def defeat
    case self
    when :paper
    :scissors
    when :scissors
    :rock
    when :rock
    :paper
    end
    end
    end

    class AJBRandomPlayer < Player
    def choose
    [:paper, :scissors, :rock][rand(3)]
    end
    end

    class AJBFrequencyPlayer < AJBRandomPlayer
    def initialize(op)
    super
    @frequencies = Hash.new(0)
    end

    def choose
    (@frequencies.max_key || super).defeat
    end

    def result(y, t, win)
    @frequencies[t] += 1
    end
    end

    class AJBHistoryPlayer < AJBRandomPlayer

    class Node
    def initialize
    @children = {}
    @scores = Hash.new(0)
    end

    def add_child(key)
    @scores[key] += 1
    @children[key] ||= Node.new
    end

    def add_scores_to(totals)
    @scores.each{|k,v| totals[k] += v}
    end
    end

    MaxNodes = 20

    def initialize(op)
    super
    @nodes = []
    @root = Node.new
    end

    def choose
    scores = Hash.new(0)
    @nodes.each{|n| n.add_scores_to(scores)}
    (scores.max_key || super).defeat
    end

    def result(y, t, win)
    (@nodes << @root).collect!{|n| n.add_child(t)}
    @nodes.shift until @nodes.size <= MaxNodes
    end
    end

    class AJBMetaPlayer < Player
    class PlayerDecorator
    def initialize(player)
    @player = player
    end
    end

    class Defeater < PlayerDecorator
    def choose
    @player.choose.defeat
    end

    def result(y, t, win)
    end
    end

    class Inverter < PlayerDecorator
    def choose
    @player.choose
    end

    def result(y, t, win)
    @player.result(t, y, !win)
    end
    end

    def initialize(op)
    super
    @players = [AJBRandomPlayer.new(op)] +
    variations(AJBHistoryPlayer) +
    variations(AJBFrequencyPlayer)
    @scores = {}
    @players.each{|p| @scores[p] = 0}
    end

    def result(y, t, win)
    @players.each{|p| score(p, t)}
    @players.each{|p| p.result(y, t, win)}
    end

    def choose
    @scores.max_key.choose
    end

    :private

    def variations(klass)
    straight = klass.new(@opponent)
    inverted = Inverter.new(klass.new(@opponent))
    [straight,
    inverted,
    Defeater.new(straight),
    Defeater.new(inverted),
    Defeater.new(Defeater.new(straight)),
    Defeater.new(Defeater.new(inverted))]
    end

    def score(player, move)
    @scores[player] += ScoreTable[[player.choose, move]]
    end

    ScoreTable =
    {[:scissors, :rock] => -1,
    [:scissors, :scissors] => 0,
    [:scissors, :paper] => 1,
    [:paper, :rock] => 1,
    [:paper, :scissors] => -1,
    [:paper, :paper] => 0,
    [:rock, :rock] => 0,
    [:rock, :scissors] => 1,
    [:rock, :paper] => -1}
    end
    Avi Bryant, Jan 23, 2005
    #6
  7. --------------000402080705020908050800
    Content-Type: text/plain; charset=ISO-8859-15; format=flowed
    Content-Transfer-Encoding: 7bit

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    This is my first player (Markov chain based)
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.2.6 (Darwin)
    Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

    iD8DBQFB87jh5YRWfc27RzQRAoqpAJ4uEzEGtiKqZpgl7wRJopUofihhywCcDK+k
    eG/3jZfl12/u8KFhfHqxuwE=
    =RXxb
    -----END PGP SIGNATURE-----


    --------------000402080705020908050800
    Content-Type: text/plain; x-mac-type="2A2A2A2A"; x-mac-creator="48647261";
    name="jix_player_m.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline;
    filename="jix_player_m.rb"

    class JIXPlayerM < Player
    WIN = {:paper => :scissors, :rock => :paper , :scissors => :rock}
    def initialize( opponent )
    @mk=Hash.new
    @last=[nil]*3 # try other values
    end
    def choose
    if !@last[0].nil?
    nodekey = @last.map do |i|
    i[0].to_s+"-"+i[1].to_s
    end.join(",")
    @mk[nodekey]= MKNode.new if !@mk[nodekey]
    @mk[nodekey].choose
    else
    [:paper,:rock,:scissors][rand(3)]
    end
    end

    def result( you, them, win_lose_or_draw )

    if !@last[0].nil?
    nodekey = @last.map do |i|
    i[0].to_s+"-"+i[1].to_s
    end.join(",")
    @mk[nodekey]= MKNode.new if !@mk[nodekey]
    @mk[nodekey]<< WIN[them]
    end
    @last[0,1]=[]
    @last<< [you,them]

    end
    private
    class MKNode
    def initialize(paper=0,rock=0,scissors=0)
    @paper=paper
    @rock=rock
    @scissors=scissors
    end
    def choose
    if @paper+@rock+@scissors == 0
    [:paper,:rock,:scissors][rand(3)]
    else
    rnd = rand(@paper+@rock+@scissors)
    if rnd < @paper
    :paper
    elsif rnd < @paper+@rock
    :rock
    else
    :scissors
    end
    end
    end
    def <<(x)
    case x
    when :paper
    @paper+=1
    when :rock
    @rock+=1
    when :scissors
    @scissors+=1
    end
    end
    def inspect
    max = @paper+@_f
    if max == 0
    "#<JIXPlayerM::MKNode --- >"
    else
    "#<JIXPlayerM::MKNode p{#{@paper/max}} r{#{@rock/max}} s{#{@scissors/max}} >"
    end
    end
    end
    end
    --------------000402080705020908050800--
    Jannis Harder, Jan 23, 2005
    #7
  8. Ruby Quiz

    Bill Atkins Guest

    My 12-line solution has so far won 100% of the time against every
    player I've been able to come up with, even players whose moves are
    completely random. Here it is:

    class Cheater < Player
    def initialize opponent
    Object.const_get(opponent).send :define_method, :choose do
    :paper
    end
    end

    def choose
    :scissors
    end
    end


    # :D

    --
    $stdout.sync = true
    "Just another Ruby hacker.".each_byte do |b|
    ('a'..'z').step do|c|print c+"\b";sleep 0.007 end;print b.chr
    end; print "\n"
    Bill Atkins, Jan 23, 2005
    #8
  9. Bill Atkins schrieb:

    > My 12-line solution has so far won 100% of the time against every
    > player I've been able to come up with, even players whose moves are
    > completely random.



    # Start
    class HyperCheater < Player
    def initialize(opponent)
    @opponent=opponent
    Object.const_get(opponent).send :define_method, :choose do
    :scissors
    end
    end
    def choose
    :rock
    end
    def result( you, them, win_lose_or_draw )
    Object.const_get(@opponent).send :define_method, :choose do
    :scissors
    end
    Object.const_get(self.class.to_s).send :define_method, :choose
    do # SelfRepair
    :rock
    end
    end
    end
    END
    # :p
    Jannis Harder, Jan 23, 2005
    #9
  10. On Jan 23, 2005, at 5:40 AM, Benedikt Huber wrote:

    > Otherwise, a strategy simply selecting a random choice will always
    > have a
    > 50% chance of winning


    Looks like we are seeing some strategies that do better than 50%
    against a random player. ;)

    James Edward Gray II
    James Edward Gray II, Jan 23, 2005
    #10
  11. On Jan 23, 2005, at 9:41 AM, Bill Atkins wrote:

    > My 12-line solution has so far won 100% of the time against every
    > player I've been able to come up with, even players whose moves are
    > completely random. Here it is:


    My own was pretty much identical. ;)

    James Edward Gray II

    #!/usr/biin/env ruby

    class JEGCheater < Player
    def initialize( opponent )
    Object.const_get(opponent).class_eval do
    alias_method :eek:ld_choose, :choose
    def choose
    :paper
    end
    end
    end

    def choose
    :scissors
    end
    end
    James Edward Gray II, Jan 23, 2005
    #11
  12. On Jan 23, 2005, at 10:43 AM, Jannis Harder wrote:

    > Bill Atkins schrieb:
    >
    >> My 12-line solution has so far won 100% of the time against every
    >> player I've been able to come up with, even players whose moves are
    >> completely random.

    >
    >
    > # Start
    > class HyperCheater < Player
    > def initialize(opponent)
    > @opponent=opponent
    > Object.const_get(opponent).send :define_method, :choose do
    > :scissors
    > end
    > end
    > def choose
    > :rock
    > end
    > def result( you, them, win_lose_or_draw )
    > Object.const_get(@opponent).send :define_method, :choose do
    > :scissors
    > end
    > Object.const_get(self.class.to_s).send :define_method, :choose
    > do # SelfRepair
    > :rock
    > end
    > end
    > end
    > END


    Protecting yourself, eh? That looks like a challenge. Anyone want to
    out cheat this cheater? It can be done!

    James Edward Gray II
    James Edward Gray II, Jan 23, 2005
    #12
  13. On Jan 23, 2005, at 7:31 AM, Christian Neukirchen wrote:

    > Ruby Quiz <> writes:
    >
    >> Your task is to build a strategy for playing the game of Paper Rock
    >> Scissors
    >> against all manner of opponents. The question here is if you can
    >> adapt to an
    >> opponent's strategy and seize the advantage, while he is doing the
    >> same to you
    >> of course.

    >
    > First, congratulations to this great quiz. It was a great fun for me
    > and other people on #ruby-lang.


    Any interesting discussion occur on the channel you would like to share
    with the rest of the group?

    James Edward Gray II
    James Edward Gray II, Jan 23, 2005
    #13
  14. Ruby Quiz

    Dennis Ranke Guest

    Here is my history based solution. It tries to guess the most probable
    choice based on the last few rounds and then counters that.

    class DRPatternPlayer < Player
    MAX_PATTERN_LENGTH = 6

    def initialize(opponent)
    super
    @stat = {}
    @history = []
    end

    def choose
    paper = rock = scissors = 0
    (1..MAX_PATTERN_LENGTH).each do |i|
    break if i > @history.size*2
    stat = @stat[@history[0, i*2]]
    next unless stat
    p = stat[:paper]
    r = stat[:rock]
    s = stat[:scissors]
    count = (p + r + s).to_f
    sig = [p, r, s].max / count - 1.0 / 3
    f = sig * (1 - 1/count)
    p /= count
    r /= count
    s /= count
    if p > 0.4 && r > 0.4
    r += p
    p = s
    s = 0
    end
    if r > 0.4 && s > 0.4
    s += r
    r = p
    p = 0
    end
    if s > 0.4 && p > 0.4
    p += s
    s = r
    r = 0
    end
    paper += p * f
    rock += r * f
    scissors += s * f
    end
    case rand(3)
    when 0: paper += 0.2
    when 1: rock += 0.2
    when 2: scissors += 0.2
    end
    paper *= rand()
    rock *= rand()
    scissors *= rand()
    return :scissors if paper > rock && paper > scissors
    return :paper if rock > scissors
    return :rock
    end

    def result(you, them, result)
    (1..MAX_PATTERN_LENGTH).each do |i|
    break if i > @history.size*2
    key = @history[0, i*2]
    @stat[key] ||= {:paper => 0, :rock => 0, :scissors => 0}
    @stat[key][them] += 1
    end
    @history = ([you, them] + @history)[0, MAX_PATTERN_LENGTH*2]
    end
    end
    Dennis Ranke, Jan 23, 2005
    #14
  15. James Edward Gray II wrote:

    > Protecting yourself, eh? That looks like a challenge. Anyone want to
    > out cheat this cheater? It can be done!


    Use .freeze or the method_added etc. hooks. :)
    Florian Gross, Jan 23, 2005
    #15
  16. On Jan 23, 2005, at 12:01 PM, Christian Neukirchen wrote:

    > James Edward Gray II <> writes:
    >
    >> On Jan 23, 2005, at 7:31 AM, Christian Neukirchen wrote:
    >>
    >>> First, congratulations to this great quiz. It was a great fun for me
    >>> and other people on #ruby-lang.

    >>
    >> Any interesting discussion occur on the channel you would like to
    >> share with the rest of the group?
    >>
    >> James Edward Gray II
    >>
    >>

    >
    > Check:
    >
    > http://meme.b9.com/cview.html?channel=ruby-lang&date=050122
    >
    > 11 o'clock and later.


    Thanks for the link. Here's a good quote from there:

    lypanov notes that the quiz is a bit silly as the reason many good
    players win in real life
    is because they react fast to the hand formations so unless you have a
    real person playing
    against a bot guessing the players move by recognition, its kind of
    boring :p

    Actually, this is one of the reasons I chose to use Paper Rock
    Scissors. I could have easily picked something with a little more
    strategy. ;)

    James Edward Gray II
    James Edward Gray II, Jan 23, 2005
    #16
  17. --------------010706050109070401000700
    Content-Type: text/plain; charset=ISO-8859-15; format=flowed
    Content-Transfer-Encoding: 7bit

    Test results (-m 5000)(Generated using my edited paper_rock_scissors.rb):

    # Name Score
    1 AJBMetaPlayer 53116.0
    2 JIXPlayerD 50965.0
    3 DRPatternPlayer 49527.0
    4 JIXPlayerM 48782.5
    5 AJBHistoryPlayer 45518.0
    6 CNBiasInverter 35336.0
    7 AJBRandomPlayer 35101.5
    8 CNIrrflug 33951.5
    9 JIXPlayerT 31794.5
    10 JIXPlayerC 26901.0
    11 CNBiasFlipper 23821.5
    12 CNBiasBreaker 23498.5
    13 CNStepAhead 23043.0
    14 AJBFrequencyPlayer 22988.5
    15 CNMeanPlayer 20655.5

    JIXPlayer D,T and C:
    D: Dynamic Length Markov Chains
    T: Word lengths of a text ;)
    C: p r r s p s,p r r s ...



    --------------010706050109070401000700
    Content-Type: text/plain; x-mac-type="2A2A2A2A"; x-mac-creator="48647261";
    name="jix_player_c.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline;
    filename="jix_player_c.rb"

    class JIXPlayerC < Player
    def initialize( opponent )
    @que=%w{paper rock rock scissors paper scissors}.map{|z|z.to_sym}
    @i=0
    end
    def choose
    @i+=1
    @i%=@que.size
    @que[@i]
    end
    end
    --------------010706050109070401000700
    Content-Type: text/plain; x-mac-type="2A2A2A2A"; x-mac-creator="48647261";
    name="jix_player_d.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline;
    filename="jix_player_d.rb"

    class JIXPlayerD < Player
    WIN = {:paper => :scissors, :rock => :paper , :scissors => :rock}
    def initialize( opponent )
    @mk=Hash.new
    @last=[nil]*10
    end

    def choose
    out = []
    0.upto(@last.size-2) do |z|

    if !@last[z].nil?
    nodekey = @last[z..-1].map do |i|
    i[0].to_s+"-"+i[1].to_s
    #i[1].to_s
    end.join(",")
    out << @mk[nodekey] if @mk[nodekey]

    end
    end

    return [:paper,:rock,:scissors][rand(3)] if out == []

    out.sort_by{|z| - z.dfm}
    out[0].choose
    end

    def result( you, them, win_lose_or_draw )


    0.upto(@last.size-2) do |z|
    if !@last[z].nil?
    nodekey = @last[z..-1].map do |i|
    i[0].to_s+"-"+i[1].to_s
    #i[1].to_s
    end.join(",")
    @mk[nodekey]= MKNode.new if !@mk[nodekey]
    @mk[nodekey]<< WIN[them]
    end
    end


    @last[0,1]=[]
    @last<< [you,them]

    end
    private



    class MKNode
    def initialize(paper=0,rock=0,scissors=0)
    @paper=paper
    @rock=rock
    @scissors=scissors
    end
    def choose
    if @paper+@rock+@scissors == 0
    [:paper,:rock,:scissors][rand(3)]
    else
    rnd = rand(@paper+@rock+@scissors)
    if rnd < @paper
    :paper
    elsif rnd < @paper+@rock
    :rock
    else
    :scissors
    end
    end
    end
    def dfm
    mid = (@paper+@rock+@scissors)/3.0
    (mid-@paper).abs+(mid-@rock).abs+(mid-@scissors).abs+mid
    end
    def <<(x)
    case x
    when :paper
    @paper+=1
    when :rock
    @rock+=1
    when :scissors
    @scissors+=1
    end
    end
    def inspect
    max = @paper+@_f
    if max == 0
    "#<JIXPlayerDM::MKNode --- >"
    else
    "#<JIXPlayerDM::MKNode p{#{@paper/max}} r{#{@rock/max}} s{#{@scissors/max}} dfm{#{dfm}}>"
    end
    end
    end
    end
    --------------010706050109070401000700
    Content-Type: text/plain; x-mac-type="2A2A2A2A"; x-mac-creator="48647261";
    name="jix_player_t.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline;
    filename="jix_player_t.rb"

    class JIXPlayerT < Player
    def initialize( opponent )

    text = <<'EOT'
    Man is driven to create; I know I really love to create things. And while I'm not good at painting, drawing, or music, I can write software.

    Shortly after I was introduced to computers, I became interested in programming languages. I believed that an ideal programming language must be attainable, and I wanted to be the designer of it. Later, after gaining some experience, I realized that this kind of ideal, all-purpose language might be more difficult than I had thought. But I was still hoping to design a language that would work for most of the jobs I did everyday. That was my dream as a student.

    Years later I talked with colleagues about scripting languages, about their power and possibility. As an object-oriented fan for more than fifteen years, it seemed to me that OO programming was very suitable for scripting too. I did some research on the 'net for a while, but the candidates I found, Perl and Python, were not exactly what I was looking for. I wanted a language more powerful than Perl, and more object-oriented than Python.

    Then, I remembered my old dream, and decided to design my own language. At first I was just toying around with it at work. But gradually it grew to be a tool good enough to replace Perl. I named it Ruby---after the precious red stone---and released it to the public in 1995.

    Since then a lot of people have become interested in Ruby. Believe it or not, Ruby is actually more popular than Python in Japan right now. I hope that eventually it will be just as well received all over the world.

    I believe that the purpose of life is, at least in part, to be happy. Based on this belief, Ruby is designed to make programming not only easy, but also fun. It allows you to concentrate on the creative side of programming, with less stress. If you don't believe me, read this book and try Ruby. I'm sure you'll find out for yourself.

    I'm very thankful to the people who have joined the Ruby community; they have helped me a lot. I almost feel like Ruby is one of my children, but in fact, it is the result of the combined efforts of many people. Without their help, Ruby could never have become what it is.

    I am especially thankful to the authors of this book, Dave Thomas and Andy Hunt. Ruby has never been a well-documented language. Because I have always preferred writing programs over writing documents, the Ruby manuals tend to be less thorough than they should be. You had to read the source to know the exact behavior of the language. But now Dave and Andy have done the work for you.

    They became interested in a lesser-known language from the Far East. They researched it, read thousands of lines of source code, wrote uncountable test scripts and e-mails, clarified the ambiguous behavior of the language, found bugs (and even fixed some of them), and finally compiled this great book. Ruby is certainly well documented now!

    Their work on this book has not been trivial. While they were writing it, I was modifying the language itself. But we worked together on the updates, and this book is as accurate as possible.

    It is my hope that both Ruby and this book will serve to make your programming easy and enjoyable. Have fun!

    Yukihiro Matsumoto, a.k.a. ``Matz''
    EOT
    @sizes=text.gsub(/[^A-Za-z\s]/,"").split(/\s+/).map{|z|z.size%3}
    @count=rand(@sizes.size)
    end
    def choose
    @count+=1
    @count%=@sizes.size
    [:paper,:rock,:scissors][@sizes[@count]]
    end
    end
    --------------010706050109070401000700
    Content-Type: text/plain; x-mac-type="2A2A2A2A"; x-mac-creator="48647261";
    name="paper_rock_scissors.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline;
    filename="paper_rock_scissors.rb"

    #!/usr/bin/env ruby
    class Player
    @@players = [ ]

    def self.inherited( player )
    @@players << player
    end

    def self.each_pair
    (0...(@@players.size - 1)).each do |i|
    ((i + 1)...@@players.size).each do |j|
    yield @@players, @@players[j]
    end
    end
    end

    def initialize( opponent )
    @opponent = opponent
    end

    def choose
    raise NoMethodError, "Player subclasses must override choose()."
    end

    def result( you, them, win_lose_or_draw )
    # do nothing--sublcasses can override as needed
    end
    end

    class Game
    attr_reader :score1,:score2
    def initialize( player1, player2 )
    @player1 = player1.new(player2.to_s)
    @player2 = player2.new(player1.to_s)
    @score1 = 0
    @score2 = 0
    end

    def play( match )
    match.times do
    hand1 = @player1.choose
    hand2 = @player2.choose
    case hand1
    when :paper
    case hand2
    when :paper
    draw hand1, hand2
    when :rock
    win @player1, hand1, hand2
    when :scissors
    win @player2, hand1, hand2
    else
    raise "Invalid choice by #{@player2.class}."
    end
    when :rock
    case hand2
    when :paper
    win @player2, hand1, hand2
    when :rock
    draw hand1, hand2
    when :scissors
    win @player1, hand1, hand2
    else
    raise "Invalid choice by #{@player2.class}."
    end
    when :scissors
    case hand2
    when :paper
    win @player1, hand1, hand2
    when :rock
    win @player2, hand1, hand2
    when :scissors
    draw hand1, hand2
    else
    raise "Invalid choice by #{@player2.class}."
    end
    else
    raise "Invalid choice by #{@player1.class}."
    end
    end
    end

    def results
    match = "#{@player1.class} vs. #{@player2.class}\n" +
    "\t#{@player1.class}: #{@score1}\n" +
    "\t#{@player2.class}: #{@score2}\n"
    if @score1 == @score2
    match + "\tDraw\n"
    elsif @score1 > @score2
    match + "\t#{@player1.class} Wins\n"
    else
    match + "\t#{@player2.class} Wins\n"
    end
    end

    private

    def draw( hand1, hand2 )
    @score1 += 0.5
    @score2 += 0.5
    @player1.result(hand1, hand2, :draw)
    @player2.result(hand2, hand1, :draw)
    end

    def win( winner, hand1, hand2 )
    if winner == @player1
    @score1 += 1
    @player1.result(hand1, hand2, :win)
    @player2.result(hand2, hand1, :lose)
    else
    @score2 += 1
    @player1.result(hand1, hand2, :lose)
    @player2.result(hand2, hand1, :win)
    end
    end
    end


    match_game_count = 1000
    if ARGV.size > 2 and ARGV[0] == "-m" and ARGV[1] =~ /^[1-9]\d*$/
    ARGV.shift
    match_game_count = ARGV.shift.to_i
    end

    ARGV.each do |p|
    if test(?d, p)
    Dir.foreach(p) do |file|
    next if file =~ /^\./
    next unless file =~ /\.rb$/
    require File.join(p, file)
    end
    else
    require p
    end
    end
    overallscore=Hash.new(0)

    Player.each_pair do |one, two|
    game = Game.new one, two
    game.play match_game_count
    puts game.results
    overallscore[one]+=game.score1
    overallscore[two]+=game.score2
    end
    puts("%2s %20s %10s" % ["#","Name","Score"])
    z=0

    overallscore.to_a.sort_by{|i|-i[1]}.each do |scorepair|
    puts("%2i %20s %10.1f" % [z+=1,scorepair[0],scorepair[1]])
    end
    --------------010706050109070401000700--
    Jannis Harder, Jan 23, 2005
    #17
  18. On Mon, 24 Jan 2005 01:46:58 +0900, James Edward Gray II wrote:

    > On Jan 23, 2005, at 5:40 AM, Benedikt Huber wrote:
    >
    >> Otherwise, a strategy simply selecting a random choice will always
    >> have a
    >> 50% chance of winning

    >
    > Looks like we are seeing some strategies that do better than 50%
    > against a random player. ;)


    This is impossible for players which do not cheat.
    You can't give any predictions on the next move of a random player.
    Therefore you have a 1/3 prop. to choose a winning,losing or drawing move.

    Here is another 'cheater' (which does not redefine a method):

    SYMBOLS = [ :rock,
    :paper,
    :scissors ]
    KILLER = { :rock => :paper, :paper => :scissors, :scissors => :rock }

    class BHCheatPlayer < Player

    def initialize( opponent )
    super
    @opp = Object.const_get(opponent).new(self)
    end

    def choose
    KILLER[@opp.choose]
    end

    def result(you,them,result)
    @opp.result(them,you,result)
    end

    end
    Benedikt Huber, Jan 23, 2005
    #18
  19. I have a similar solution:

    SYMBOLS = [ :rock,
    :paper,
    :scissors ]
    KILLER = { :rock => :paper, :paper => :scissors, :scissors => :rock }
    MAXSIZE = 5

    class Symbol
    def +(o)
    return (self.to_s+o.to_s).to_sym
    end
    end

    class BHAdaptPlayer4 < Player

    def initialize( opponent )
    @stats = Hash.new()
    @lastmoves = []
    end

    def get_keys
    keys = [ ]
    @lastmoves.each do |pair|
    keys.unshift( keys.empty? ? pair : (keys[0]+pair))
    end
    keys
    end

    def choose
    info = nil
    get_keys.each do |key|
    info = @stats[key]
    break if info
    end
    if ! info
    SYMBOLS[rand(3)]
    else
    max = -1
    msym = nil
    info.keys.each do |sym|
    msym,max = sym,info[sym] if(info[sym] > max)
    end
    KILLER[msym]
    end
    end

    # called after each choice you make to give feedback
    # you = your choice
    # them = opponent's choice
    # win_lose_or_draw = :win, :lose or :draw, your result
    def result( you, them, win_lose_or_draw )
    get_keys.each do |key|
    @stats[key] = create_stat if ! @stats[key]
    @stats[key][them] += 1
    end
    @lastmoves.unshift(@lastchoice)
    @lastmoves.pop if @lastmoves.size > MAXSIZE

    @lastchoice = them+you
    end

    def create_stat
    stat = {}
    SYMBOLS.each {|sym| stat[sym] = 0 }
    stat
    end

    end
    Benedikt Huber, Jan 23, 2005
    #19
  20. Benedikt Huber wrote:

    > This is impossible for players which do not cheat.
    > You can't give any predictions on the next move of a random player.
    > Therefore you have a 1/3 prop. to choose a winning,losing or drawing move.
    >
    > Here is another 'cheater' (which does not redefine a method):


    This is similar to my solution, but mine does also copy the state of its
    enemy.
    Florian Gross, Jan 23, 2005
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. TooNaive

    Building Paper, Rock, Scissors

    TooNaive, Jun 14, 2004, in forum: C Programming
    Replies:
    7
    Views:
    545
    T.M. Sommers
    Jun 14, 2004
  2. Replies:
    2
    Views:
    622
  3. Ruby Quiz
    Replies:
    1
    Views:
    136
    martinus
    Jan 27, 2005
  4. Alex Norton
    Replies:
    1
    Views:
    123
    Denis McMahon
    May 13, 2013
  5. Benjamin Kaplan
    Replies:
    0
    Views:
    124
    Benjamin Kaplan
    May 12, 2013
Loading...

Share This Page