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[i], @@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/