patternrubyMinor
Small guessing game in Ruby
Viewed 0 times
gamesmallrubyguessing
Problem
I'm writing a small guessing game. I'm writing a points calculation algorithm.
I wrote the following, and it works. But I feel like I'm bringing over procedural background into Ruby, or not leveraging Ruby properly.
How would an experienced Ruby programmer approach the problem? You can test the code on TryRuby.com (copy and paste code in the browser interpreter).
I wrote the following, and it works. But I feel like I'm bringing over procedural background into Ruby, or not leveraging Ruby properly.
How would an experienced Ruby programmer approach the problem? You can test the code on TryRuby.com (copy and paste code in the browser interpreter).
# g = guesses
g = [{ id: 1, elmer: 5, roger: 7, outcome: "Roger Win" },{ id: 2, elmer: 5, roger: 1, outcome: "Elmer Win" },{ id: 3, elmer: 4, roger: 8, outcome: "Roger Win" }]
# r = actual results
r = [{ id: 1, elmer: 3, roger: 9, outcome: "Roger Win" },{ id: 2, elmer: 7, roger: 9, outcome: "Roger Win" },{ id: 3, elmer: 4, roger: 8, outcome: "Roger Win" }]
# points table
p = []
# rules: correct outcome = 1 point, perfect match = 5 points.
# Loop over results.
r.each do |result|
# Loop over guesses.
g.each do |guess|
# Make sure we compare corresponding ids.
# So, compare result 1 to guess 1, r2 to g2, etc....
if result[:id] == guess[:id]
# Init a hash to store score
score = {}
# Did they guess the correct outcome?
if result[:outcome] == guess[:outcome]
# Correct outcome guessed! Make a score hash, give'em a point.
score[:id] = result[:id] # game id
score[:points] = 1 # point
# Was it a perfect match?
if result[:elmer] == guess[:elmer] && result[:roger] == guess[:roger]
# Perfect match! Give them 4 points.
# They already got 1 point for guessing the correct outcome.
score[:points] += 4
end
end
# Add the score to the points table
p.push(score) unless score.empty?
end
end
endSolution
I first propose how to improve your algorithm without changing the data structures.
Your algorithm is quadratic (two nested loops over all the entries). This can be easily avoided because you don't need to loop over all the
If they are not, sort the arrays by the
Then you can use each_with_index:
or, if speed is not an issue (and this does not seem to be the case), you can also zip the arrays together
If you can modify the data structure I propose some alternatives.
Often when you have a set of objects with an id you use a hash.
If the id is a simple incremental integer and you add the entries in
You can create a class to represent each entry of the array (let's call each entry a Match) and then implement the scoring algorithm as a method:
Example usage:
If the string argument just says who is the winner based on the numbers, you can compute it:
Example usage:
Your algorithm is quadratic (two nested loops over all the entries). This can be easily avoided because you don't need to loop over all the
g array to find the item you need. If they are not, sort the arrays by the
id key, using sort_by:r.sort_by! { |e| e[:id] }
g.sort_by! { |e| e[:id] }Then you can use each_with_index:
r.each_with_index { |result, i|
guess = g[i]
# compute the score here
}or, if speed is not an issue (and this does not seem to be the case), you can also zip the arrays together
r.zip(g) and iterate on that:r.zip(g).each { |result, guess|
# compute the score here
}If you can modify the data structure I propose some alternatives.
Often when you have a set of objects with an id you use a hash.
# g = guesses
g = {1 => { elmer: 5, roger: 7, outcome: "Roger Win" }, 2 => {elmer: 5, roger: 1, outcome: "Elmer Win" }, 3 => { elmer: 4, roger: 8, outcome: "Roger Win" }}
# r = actual results
r = {1 => { elmer: 3, roger: 9, outcome: "Roger Win" }, 2 => {elmer: 7, roger: 9, outcome: "Roger Win" }, 3 => { elmer: 4, roger: 8, outcome: "Roger Win" }}
# points table
p = {}
# Loop over results.
r.each { |id, result|
guess = g[id]
# Init a hash to store score
score = {}
# compute the score here
# ...
p[id] = score unless score.empty?
}If the id is a simple incremental integer and you add the entries in
r and g in the same order, you can avoid the id at all and use the array indices instead:# g = guesses
g = [{ elmer: 5, roger: 7, outcome: "Roger Win" }, {elmer: 5, roger: 1, outcome: "Elmer Win" }, { elmer: 4, roger: 8, outcome: "Roger Win" }]
# r = actual results
r = [{ elmer: 3, roger: 9, outcome: "Roger Win" }, {elmer: 7, roger: 9, outcome: "Roger Win" }, { elmer: 4, roger: 8, outcome: "Roger Win" }]
# points table
p = {}
# Loop over results.
r.each_with_index { |result, id|
guess = g[id]
# Init a hash to store score
score = {}
# compute the score here
# ...
p[id] = score unless score.empty?
}You can create a class to represent each entry of the array (let's call each entry a Match) and then implement the scoring algorithm as a method:
class Match
attr_accessor :elmer, :roger, :outcome
def initialize(elmer, roger, outcome)
@elmer = elmer
@roger = roger
@outcome = outcome
end
def compare(m)
score = 0
score = score + 1 if @outcome == m.outcome
score = score + 4 if @elmer == m.elmer and @roger == m.roger
score
end
endExample usage:
guess = Match.new(5, 7, "Roger Win")
result = Match.new(3, 9, "Roger Win")
puts guess.compare(result) # => 1
guess = Match.new(5, 1, "Elmer Win")
result = Match.new(7, 9, "Roger Win")
puts guess.compare(result) # => 0
guess = Match.new(5, 7, "Roger Win")
result = Match.new(5, 7, "Roger Win")
puts guess.compare(result) # => 5
puts guess.compare(guess) # => 5If the string argument just says who is the winner based on the numbers, you can compute it:
class Match
attr_accessor :elmer, :roger
def initialize(elmer, roger)
@elmer = elmer
@roger = roger
end
def outcome
if @elmer > @roger
:elmer
elsif @roger > @elmer
:roger
else
:tie
end
end
def compare(m)
if @elmer == m.elmer and @roger == m.roger
5
elsif outcome == m.outcome
1
else
0
end
end
endExample usage:
guess = Match.new(5, 7)
result = Match.new(3, 9)
puts guess.compare(result) # => 1
guess = Match.new(5, 1)
result = Match.new(7, 9)
puts guess.compare(result) # => 0
guess = Match.new(5, 7)
result = Match.new(5, 7)
puts guess.compare(result) # => 5
puts guess.compare(guess) # => 5
guess = Match.new(2, 2)
result = Match.new(3, 3)
puts guess.compare(result) # => 1
guess = Match.new(2, 2)
result = Match.new(2, 2)
puts guess.compare(result) # => 5Code Snippets
r.sort_by! { |e| e[:id] }
g.sort_by! { |e| e[:id] }r.each_with_index { |result, i|
guess = g[i]
# compute the score here
}r.zip(g).each { |result, guess|
# compute the score here
}# g = guesses
g = {1 => { elmer: 5, roger: 7, outcome: "Roger Win" }, 2 => {elmer: 5, roger: 1, outcome: "Elmer Win" }, 3 => { elmer: 4, roger: 8, outcome: "Roger Win" }}
# r = actual results
r = {1 => { elmer: 3, roger: 9, outcome: "Roger Win" }, 2 => {elmer: 7, roger: 9, outcome: "Roger Win" }, 3 => { elmer: 4, roger: 8, outcome: "Roger Win" }}
# points table
p = {}
# Loop over results.
r.each { |id, result|
guess = g[id]
# Init a hash to store score
score = {}
# compute the score here
# ...
p[id] = score unless score.empty?
}# g = guesses
g = [{ elmer: 5, roger: 7, outcome: "Roger Win" }, {elmer: 5, roger: 1, outcome: "Elmer Win" }, { elmer: 4, roger: 8, outcome: "Roger Win" }]
# r = actual results
r = [{ elmer: 3, roger: 9, outcome: "Roger Win" }, {elmer: 7, roger: 9, outcome: "Roger Win" }, { elmer: 4, roger: 8, outcome: "Roger Win" }]
# points table
p = {}
# Loop over results.
r.each_with_index { |result, id|
guess = g[id]
# Init a hash to store score
score = {}
# compute the score here
# ...
p[id] = score unless score.empty?
}Context
StackExchange Code Review Q#40416, answer score: 5
Revisions (0)
No revisions yet.