Reputation: 1751
I am solving the " rock, paper, scissor" game. Input an array and recursively output the winner. Here is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError; end
def self.winner(player1, player2)
strategy = player1[1]+player2[1]
raise NoSuchStrategyError.new("Strategy must be one of R,P,S") if strategy !~ /(R|P|S){2}/
strategy =~ /rs|sp|pr|rr|ss|pp/i ? player1 : player2
end
def self.tournament_winner(tournament)
if tournament.length==2 && tournament.first[0].is_a?(String)
winner(tournament[0],tournament[1])
else
#keep slice the array in half
***winner(tournament_winner(tournament[0,tournament.length/2]), tournament_winner(tournament[tournament.length/2]))***
end
end
end
I got stack level too deep because that code in bold. Is it because the tournament.length is changing so I shouldn't put it inside the recursion? Could someone gives a detailed explanation about how that happened?
I searched the answer and someone used the code below and worked. I wonder why that reference to tournament won't cause the same recursion issue.
winner(tournament_winner(tournament[0]), tournament_winner(tournament[1]))
Thank you for any help!
sample input:
[
[
[ ["Armando", "P"], ["Dave", "S"] ],
[ ["Richard", "R"], ["Michael", "S"] ],
],
[
[ ["Allen", "S"], ["Omer", "P"] ],
[ ["David E.", "R"], ["Richard X.", "P"] ]
]
]
Upvotes: 0
Views: 227
Reputation: 80051
There are so many better ways of handling this than messy regexs, confusing abbreviations, and strings all over the place.
For example, you could go with a more object oriented approach:
module RockPaperScissors
class NoSuchStrategyError < StandardError; end
class Move
include Comparable
attr_reader :strategy
def initialize(strategy)
raise NoSuchStrategyError unless strategies.include?(strategy)
@strategy = strategy
end
def <=>(opposing_move)
if strengths[strategy] == opposing_move.strategy
1
elsif strengths[opposing_move.strategy] == strategy
-1
else
0
end
end
protected
def strengths
{
rock: :scissors,
scissors: :paper,
paper: :rock
}
end
def strategies
strengths.keys
end
end
class Player
attr_reader :name, :move
def initialize(name, move)
@name, @move = name, move
end
end
class Tournament
def initialize(*players)
@player_1, @player_2, _ = *players
end
def results
p1move = @player_1.move
p2move = @player_2.move
if p1move > p2move
"#{@player_1.name} wins."
elsif p2move > p1move
"#{@player_2.name} wins."
else
"Tie."
end
end
end
end
Example usage:
rock = RockPaperScissors::Move.new(:rock)
paper = RockPaperScissors::Move.new(:paper)
player_1 = RockPaperScissors::Player.new('John Smith', rock)
player_2 = RockPaperScissors::Player.new('Corey', paper)
tournament = RockPaperScissors::Tournament.new(player_1, player_2)
tournament.results #=> "Corey wins."
Upvotes: 0
Reputation: 37409
Your code does not slice the tournament in half - tournament[tournament.length/2]
does not return the second half of the array - it only returns the item in position tournament.length/2
, instead you should do:
winner(tournament_winner(tournament[0...tournament.length/2]), tournament_winner(tournament[tournament.length/2..-1]))
Also, you do not consider an array of length 1
, which results in an endless recursion.
Here is a working version of your code:
def tournament_winner(tournament)
if tournament.length == 1
tournament_winner(tournament[0])
elsif tournament.length==2 && tournament.first[0].is_a?(String)
winner(tournament[0],tournament[1])
else
winner(tournament_winner(tournament[0...tournament.length/2]),
tournament_winner(tournament[tournament.length/2..-1]))
end
end
Upvotes: 1