I took the OO approach and implemented a strategy pattern.
To see the game play out, pass in the -v flag, and -d will show the
actual command given for each move.
The output of the test file contains a frequency distribution to show
how each matchup plays out.
Here is some sample output for the second matchup in the test over 200
games:
_____________________________________
Game 2: Aggressive vs Random
3: ---------------------------------33
4:
--------------------------------------------------------------------68
5: -------------------------------------------43
6: ------------------------24
7: ---------------15
8: -----5
9: -------7
10: ----4
11: -1
Player 1: 166 wins
Player 2: 34 wins
______________________________________
In an Aggressive vs Aggressive game, the first player wins in 3 rounds
every time. Obviously there are better
strategies that I didn't take the time to explore.
Again, I'm happy to receive any comments. I feel like my solutions
are so clunky compared to what you guys submit.
I am here to learn!
Here's my code:
#file: game.rb
#author: Matt Hulse - www.matt-hulse.com
require 'player'
class Game
attr_reader :p1, :p2, :winner, :rounds
def initialize(p1_strategy = "random", p2_strategy = "random")
@p1 = Player.new(1,p1_strategy, self)
@p2 = Player.new(2,p2_strategy, self)
@rounds = 0
end
def get_opponent(player)
return @p1 if @p2 == player
return @p2 if @p1 == player
end
def loop
#keep going until one person has no hands left
#after 100 rounds we're calling a draw!
puts self if $VERBOSE
until(game_over) do
@rounds += 1
p1.move
puts self if $VERBOSE
unless game_over then
p2.move
puts self if $VERBOSE
end
break if @rounds >= 100
end
if(lost?(p1)) then
@winner = p2
elsif(lost?(p2))
@winner = p1
else
puts "Draw"
end
end
def game_over
lost?(p1) || lost?(p2)
end
def lost?(player)
player.left.finger_count <= 0 and player.right.finger_count <= 0
end
def to_s
"#{p1}\n#{p2}\n"
end
end
#file: hand.rb
#author: Matt Hulse - www.matt-hulse.com
class Hand
attr_reader :right_or_left, :finger_count
def initialize(right_or_left, count = 1)
@right_or_left = right_or_left
@finger_count = count
end
def touch(hand)
hand.add_fingers(self.finger_count)
end
def add_fingers(num)
@finger_count += num
if @finger_count >= 5 then
@finger_count = 0
end
end
def clap(hand,num)
hand.add_fingers(num) #num must be <= self.finger_count
@finger_count -= num
end
def to_s
result = ""
#print left to right
i = 0
(1..5).each{
i += 1
if(i <= @finger_count) then
result += "|"
else
result += "-"
end
}
return result if @right_or_left == :right
return result.reverse
end
end
#file: player.rb
#author: Matt Hulse - www.matt-hulse.com
require 'hand'
class Player
attr_reader :player_num, :right, :left, :strategy, :game
def initialize(player_num, strategy, game)
@player_num = player_num
@strategy = strategy
@game = game
@right = Hand.new(:right,1)
@left = Hand.new(:left,1)
end
def move
begin
eval(@strategy)
rescue NameError => e
random #default to random
end
end
def get_opponent
@game.get_opponent(self)
end
def get_larger_hand
return @right if @right.finger_count > @left.finger_count
return @left
end
def min(a,b)
return a if a <= b
return b
end
def random
#all possible moves, choose randomly among them
opponent = get_opponent
valid_moves = Array.new
opp_rt_count = opponent.right.finger_count
opp_lt_count = opponent.left.finger_count
if(@right.finger_count > 0) then
valid_moves << "@right.touch(opponent.right)" if opp_rt_count >
0
valid_moves << "@right.touch(opponent.left)" if opp_lt_count > 0
#total on hand transferred to cannot be more than 5
#random number is between 1 and the minimum of what left can
receive and right can give
rand_max = min(5- / left.finger_count,@right.finger_count-1)
valid_moves << "@right.clap(@left, #{rand(rand_max) + 1})" if
rand_max > 0
end
if(@left.finger_count > 0) then
valid_moves << "@left.touch(opponent.right)" if opp_rt_count > 0
valid_moves << "@left.touch(opponent.left)" if opp_lt_count > 0
#total on hand transferred to cannot be more than 5
#random number is between 1 and the minimum of what right can
receive and left can give
rand_max = min(5- / right.finger_count,@left.finger_count-1)
valid_moves << "@left.clap(@right, #{rand(rand_max) + 1})" if
rand_max > 0
end
move = valid_moves[rand(valid_moves.size)]
eval(move)
puts "Player #{player_num}: #{move}" if $DEBUG
end
def aggressive
opponent = get_opponent
#every move is to touch the opponents largest hand with the local
largest hand
move = "get_larger_hand.touch(opponent.get_larger_hand)"
eval(move)
puts "Player #{player_num}: #{move}" if $DEBUG
end
def to_s
"#{player_num}: #{@left} #{@right}"
end
end
#file: test.rb
#author: Matt Hulse - www.matt-hulse.com
require 'game'
puts "Game 1: Random vs Random"
num_rounds = Hash.new(0)
wins = Hash.new(0)
(1..200).each{
game = Game.new("random","random")
game.loop
if(game.winner) then
puts "Player #{game.winner.player_num} won in #{game.rounds}
rounds using #{game.winner.strategy} strategy." if $VERBOSE
num_rounds[game.rounds] += 1
wins[game.winner.player_num] +=1
end
}
num_rounds.keys.sort{|a,b| a<=>b}.each{|i|
puts "#{i}: #{'-'*num_rounds[i]}#{num_rounds[i]}"
}
wins.each_pair{|key,value|
puts "Player #{key}: #{value} wins"
}
puts
puts "Game 2: Aggressive vs Random"
num_rounds = Hash.new(0)
wins = Hash.new(0)
(1..200).each{
game = Game.new("aggressive", "random")
game.loop
if(game.winner) then
puts "Player #{game.winner.player_num} won in #{game.rounds}
rounds using #{game.winner.strategy} strategy." if $VERBOSE
num_rounds[game.rounds] += 1
wins[game.winner.player_num] +=1
end
}
num_rounds.keys.sort{|a,b| a<=>b}.each{|i|
puts "#{i}: #{'-'*num_rounds[i]}#{num_rounds[i]}"
}
wins.each_pair{|key,value|
puts "Player #{key}: #{value} wins"
}
puts
puts "Game 3: Aggressive vs Aggressive"
num_rounds = Hash.new(0)
wins = Hash.new(0)
(1..200).each{
game = Game.new("aggressive","aggressive")
game.loop
if(game.winner) then
puts "Player #{game.winner.player_num} won in #{game.rounds}
rounds using #{game.winner.strategy} strategy." if $VERBOSE
num_rounds[game.rounds] += 1
wins[game.winner.player_num] +=1
end
}
num_rounds.keys.sort{|a,b| a<=>b}.each{|i|
puts "#{i}: #{'-'*num_rounds[i]}#{num_rounds[i]}"
}
wins.each_pair{|key,value|
puts "Player #{key}: #{value} wins"
}
Thanks,
__________
Matt Hulse
matt.hulse / gmail.com
http://www.matt-hulse.com
On Apr 13, 5:48 am, Ruby Quiz <j... / grayproductions.net> wrote:
> 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.rubyquiz.com/
>
> 3. Enjoy!
>
> Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
> on Ruby Talk follow the discussion. Please reply to the original quiz message,
> if you can.
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> We have a foreign exchange student from Korea staying with us. The best part of
> that is the intended exchange of cultures. For example, to kill time on a
> recent plane trip, the student taught us a common finger game children play in
> Korea.
>
> The rules are very easy:
>
> 1. Both players start by holding up one finger on each hand.
> 2. On each turn a player must do one of the following:
> a. Touch one of their hands to an opponent's hand, adding the finger
> count on their hand to the touched hand. The player keeps the same
> number of fingers, but the opponent must add the player's finger
> count in addition to the fingers already on that hand.
> b. Clap their hands together to "transfer" one or more fingers from
> one hand to the other. You cannot transfer all the fingers off of
> a hand.
> 3. A hand with five or more fingers is eliminated, which is shown by
> making a fist. An opponent can not add fingers to an eliminated
> hand and an it cannot be used in touches, but players may transfer
> fingers to it, "reviving" it. The first player with two eliminated
> hands loses the game.
>
> For example, here is how a quick game might play out:
>
> 1: ----| |---- Starting fingers.
> 2: ----| |----
>
> 1: ----| |---- Player 1's left hand touches player 2's right hand.
> 2: ----| ||---
>
> 1: ----| |||-- 2's right touches 1's right hand.
> 2: ----| ||---
>
> 1: ----| |||-- 1's right touches 2's right hand.
> 2: ----| -----
>
> 1: ----| ||||- 2's left touches 1's right hand.
> 2: ----| -----
>
> 1: ----| |||-- 1's right touches 2's left hand.
> 2: ----- -----
>
> Of course, as a programmer, I immediately tried to solve this game. I suck the
> fun right out of everything, but at least it gave us another quiz topic.
>
> This week's Ruby Quiz is to programmatically solve Magic Fingers. Is it a win
> for the first or second player with perfect play, or can you always force a draw
> with repeating counts? Have your program print some output that would convince
> anyone beyond the shadow of a doubt what the game's outcome will be.