R
Ruby Quiz
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 aper, :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
aper
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 aper
case hand2
when aper
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 aper
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 aper
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/
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 aper, :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
aper
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 aper
case hand2
when aper
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 aper
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 aper
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/