zensupremacy
zensupremacy

Reputation: 57

How to collect user input and give feedback

I am struggling with my code to write a simple code-breaking game. There is a hidden code:

code = ["a","b","b","c"]

My program asks for user input, then stores it in a variable. I want to compare user input against the secret code variable and give the user feedback: 1 for a good letter in good place, 0 for good letter in wrong place, "-" for wrong letter.

I came up with something like this:

feedback = []
input.each_with_index do |v,i|
  if v == code.fetch(i)
    feedback << "1"
  else
    feedback << "-"
  end
end

It works OK when it compares elements at the same index. I have no idea how I can find elements that are in the code array, but not in the same index and give feedback to the user.

For example:

code = ["a","b","b","c"]
input = ["b","b","a","z"]
feedback = ["0","1","0","-"]

Upvotes: 1

Views: 93

Answers (5)

Eric Duminil
Eric Duminil

Reputation: 54223

This code works with the 3 examples you mentioned.

2 passes are used because the 1s must be returned before the 0s :

def give_feedback(input, code)
  feedback = Array.new(input.size) { '-' }
  code2 = code.dup

  input.each_with_index do |letter, index|
    if letter == code[index]
      feedback[index] = '1'
      code2[index]    = nil
    end
  end

  input.each_with_index do |letter, index|
    next if feedback[index] == '1'
    found = code2.index(letter)
    if found
      feedback[index]  = '0'
      code2[found]     = nil
    end
  end
  feedback
end

p give_feedback(%w(b b a z), %w(a b b c))
# ["0", "1", "0", "-"]
p give_feedback(%w(a a a a), %w(a b b c))
# ["1", "-", "-", "-"]
p give_feedback(%w(c c b a), %w(a b b c))
# ["0", "-", "1", "0"]

Upvotes: 1

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Just out of curiosity:

[code, input].map { |a| (0...a.size).zip(a).to_h }
             .reduce do |e, acc|
  c = acc.values.dup
  acc.merge(e) do |_, v1, v2|
    case (c.delete_at(c.index(v2)) rescue nil)
    when v1 then "1"
    when nil then "-"
    else "0"
    end
  end
end.values

Upvotes: 0

msergeant
msergeant

Reputation: 4801

You can use the include? method to see if that character is in the list at a different index. Something like this:

input.each_with_index do |v,i|
  if v == code.fetch(i)
    feedback << "1"
  elsif code.include?(v)
    # right character, wrong spot
    feedback << "0"
  else
    feedback << "-"
  end
end

Upvotes: 0

kcdragon
kcdragon

Reputation: 1733

You can use zip and map to make it a little more functional. include? will check to see if the input is in code

code = %w(a b b c)
input = %w(b b a z)
result = code.zip(input).map do |c, i|
  if c == i
    '1'
  elsif code.include?(i)
    '0'
  else
    '-'
  end
end
puts result.to_s

Upvotes: 0

Oleksandr Holubenko
Oleksandr Holubenko

Reputation: 4440

One more solution

code = ["a","b","b","c"]
input = ["b","b","a","z"]

feedback = input.map.with_index do |num, ind|
  if code.include? num
    code[ind] == num ? '1' : '0'
  else
    '-'
  end
end

=> ['0', '1', '0', '-']

if you want define feedback before, just edit 1st variant to:

code = ["a","b","b","c"]
input = ["b","b","a","z"]
feedback = []

input.each_with_index do |num, ind|
  if code.include? num
    feedback << (code[ind] == num ? '1' : '0')
  else
    feedback << '-'
  end
end

result would be the same

Upvotes: 0

Related Questions