Reputation: 13
First, some necessary background. I'm trying to make a number-based version of the game Mastermind as a way of learning to code in Ruby. My code basically works like this:
@computer_sequence
) of 4 random numbers from 1-5 @user_array
.compare
, iterates through @user_array
, comparing the value and index of each number to those in @computer_sequence.
The program then tells the user how many of their numbers have the correct value and the correct position, or how many numbers have the correct value only.The problem: If there are multiple instances of a number in an array, they get the same index, right? Like if I have the array [1, 3, 3, 4]
, the number three has an index of 1, even though there are two 3s. For this program to work, though, each number has to have a unique position (is index even the word I want here?) in the array, even if the number occurs multiple times. Does that make sense?
Also, here's the code for the compare
method:
def compare
value_only = 0
value_and_place = 0
puts "The computer's values are: #{@computer_sequence}"
puts "The user's values are: #{@user_array}"
@user_array.each do |candidate|
@computer_sequence.each do |computer_number|
if candidate == computer_number && @user_array.index(candidate) == @computer_sequence.index(computer_number)
value_and_place +=1
elsif candidate == computer_number && @user_array.index(candidate) != @computer_sequence.index(computer_number)
value_only +=1
end
end
end
Upvotes: 1
Views: 1878
Reputation: 110685
Suppose
n = 4
computer = Array.new(n) { [1,2,3,4,5].sample }
#=> [3, 2, 3, 3]
user_digits = [2, 4, 2, 3]
First compute pairs of elements at the same index of computer
and user_digits
.
pairs = computer.zip(user_digits)
#=> [[3, 2], [2, 4], [3, 2], [3, 3]]
Compute number of values that match at the same position
pairs.count { |c,u| c==u }
#=> 1
Compute number of values that match at different positions
First remove the matches at the same positions of computer
and user_digits
.
comp, users = pairs.reject { |c,u| c==u }.transpose
#=> [[3, 2, 3], [2, 4, 2]]
meaning
comp #=> [3, 2, 3]
users #=> [2, 4, 2]
Now step through users
removing the first matching element in comp
(if there is one).
users.each do |n|
i = comp.index(n)
comp.delete_at(i) if i
end
So now:
comp #=> [3,3]
meaning that the number of elements that match at different positions is:
users.size-comp.size
#=> 1
Notice that we could alternatively compute the number of values that match at the same position as
n - users.size
For n
equal to 4
this doesn’t offer any significant time saving, but it would if we had a problem with the same structure and n
were large.
Alternative calculation
After computing
comp, users = pairs.reject { |c,u| c==u }.transpose
we could write
users.size - comp.difference(users).size
#=> 1
where Array#difference
is as I defined it in my answer here.
Here
comp.difference(users)
#=> [3,3]
Upvotes: 2
Reputation: 37617
No, equal elements in an array don't have the same index. Maybe you're thinking that because Array#index
only returns the index of the first element equal to its argument. But there are many ways to see that other equal elements have their own indexes. For example,
a = [1, 3, 3, 4]
a[1] == 3 # true
a[2] == 3 # also true
Aside from that issue, your algorithm doesn't quite match the rules of Mastermind. If there is one three in the computer's sequence and the player guesses two threes, both in different positions than the three in the computer's sequence, the player should be told that only one element of their sequence matches the computer's sequence in value but not position.
Given the above, plus that I think it would be clearer to calculate the two numbers separately, I'd do it like this:
value_and_place = 4.times { |i| @user_array[i] == @computer_sequence[i] }
value_only = (@user_array & @computer_sequence).length - value_and_place
That's less efficient than the approach you're taking, but CPU efficiency isn't important for 4-element arrays.
Upvotes: 1
Reputation: 760
You can pass in the index
value to your loop for each candidate
using the each_with_index
method. So when the first 3 is passed in, index
will be 1
and when the second 3 is passed in, index
will be 2
.
The problem with using .index(candidate)
is it returns the first index.
Try this:
@user_array.each_with_index do |candidate, index|
@computer_sequence.each do |computer_number|
if candidate == computer_number && candidate == @computer_sequence[index]
value_and_place +=1
elsif candidate == computer_number && candidate != @computer_sequence[index]
value_only +=1
end
end
end
Upvotes: 0