Как сравнить целые индексы в массивах при наличии повторяющихся значений?

Во-первых, некоторый необходимый фон. Я пытаюсь сделать числовую версию игры Mastermind как способ обучения коду в Ruby. Мой код в основном работает следующим образом:

  • Компьютер генерирует массив (@computer_sequence) из 4 случайных чисел из 1-5
  • Пользователь вводит 4-значную последовательность, которая завершается в массиве с именем @user_array.
  • Метод, называемый compare, выполняет итерацию через @user_array, сравнивая значение и индекс каждого числа с параметрами в @computer_sequence.. Затем программа сообщает пользователю, сколько их номеров имеет правильное значение и правильное положение, или сколько чисел имеет только правильное значение.

Проблема: Если в массиве имеется несколько экземпляров числа, они получают одинаковый индекс, правильно? Например, если у меня есть массив [1, 3, 3, 4], номер три имеет индекс 1, хотя есть два 3s. Тем не менее, для работы этой программы каждый номер должен иметь уникальную позицию (это индекс, даже слово, которое я хочу здесь?) В массиве, даже если число происходит несколько раз. Это имеет смысл?

Кроме того, здесь код для метода compare:

def compare
    value_only = 0
    value_and_place = 0
    puts "The computer values are: #{@computer_sequence}"
    puts "The user 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

Ответ 1

предполагать

n = 4
computer = Array.new(n) { [1,2,3,4,5].sample }
  #=> [3, 2, 3, 3]
user_digits = [2, 4, 2, 3]

Сначала вычислите пары элементов в одном и том же индексе computer и user_digits.

pairs = computer.zip(user_digits)
  #=> [[3, 2], [2, 4], [3, 2], [3, 3]]

Вычислить количество значений, совпадающих в одном и том же положении

pairs.count { |c,u| c==u }
  #=> 1

Вычислить количество значений, совпадающих на разных позициях

Сначала удалите совпадения в тех же положениях computer и user_digits.

comp, users = pairs.reject { |c,u| c==u }.transpose
  #=> [[3, 2, 3], [2, 4, 2]] 

имея в виду

comp  #=> [3, 2, 3] 
users #=> [2, 4, 2] 

Теперь пройдите через users удалив первый соответствующий элемент в comp (если он есть).

users.each do |n|
  i = comp.index(n)
  comp.delete_at(i) if i
end

А сейчас:

comp #=> [3,3] 

что количество элементов, которые соответствуют различным позициям:

users.size-comp.size
  #=> 1 

Обратите внимание, что мы могли бы альтернативно вычислить количество значений, которые совпадают в том же положении, что и

n - users.size

Для n равного 4 это не дает сколько-нибудь существенной экономии времени, но если бы у нас была проблема с той же структурой и n были большими.

Альтернативный расчет

После вычисления

comp, users = pairs.reject { |c,u| c==u }.transpose

мы могли бы написать

users.size - comp.difference(users).size
  #=> 1

где Array#difference, как я определил ее в моем ответе здесь.

Вот

comp.difference(users)
  #=> [3,3]

Ответ 2

Нет, равные элементы в массиве не имеют одинакового индекса. Возможно, вы думаете, что, поскольку Array#index возвращает только индекс первого элемента, равный его аргументу. Но есть много способов увидеть, что другие равные элементы имеют свои собственные индексы. Например,

a = [1, 3, 3, 4]
a[1] == 3 # true
a[2] == 3 # also true

Помимо этой проблемы, ваш алгоритм не совсем соответствует правилам Mastermind. Если в компьютерной последовательности есть три, и игрок угадывает две тройки, как в разных позициях, чем три в компьютерной последовательности, игроку следует сказать, что только один элемент их последовательности соответствует компьютерной последовательности по значению, но не положению.

Учитывая вышеизложенное, плюс, что я думаю, что было бы яснее рассчитать два числа отдельно, я бы сделал это следующим образом:

value_and_place = 4.times { |i| @user_array[i] == @computer_sequence[i] }
value_only = (@user_array & @computer_sequence).length - value_and_place

Это менее эффективно, чем подход, который вы используете, но эффективность процессора не важна для 4-элементных массивов.

Ответ 3

Вы можете передать значение index в ваш цикл для каждого candidate с помощью метода each_with_index. Итак, когда первые 3 пройдены, index будет 1, а когда второе 3 будет передано, index будет 2.

Проблема с использованием .index(candidate) заключается в том, что он возвращает первый индекс.

Попробуйте следующее:

@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

Ответ 4

Почему бы не сохранить значения компьютера как хэш с цифрами в виде ключей и индексы как значения?

#example hash equivalent to [1, 3, 3, 4]
@computer_sequence = {1=>[0], 3 => [1,2], 4 => [3]}

@user_hash.each_with_index do |v, i|
  if @computer_sequence.values.flatten.include?(v)
    if @computer_sequence[v].include?(i)
      value_and_place += 1
    else
      value_only += 1
    end
  end
end