如何在相同的indes和不匹配的索引处查找计数匹配字符

时间:2018-01-30 00:13:45

标签: ruby loops

我构建了一个mastermind版本,用于检查用户的输入,并根据用户对获胜序列的猜测程度提供反馈。如果您不熟悉游戏,则会得到反馈,表明在同一索引中有多少字符被正确猜测,并且序列中猜到了多少个字符,但索引错误。如果猜测中有重复项,那么除非它们对应于密码中相同数量的重复项,否则您不会计算额外值。

示例:如果序列是[" G"," G"," G"," Y"]并且用户猜测[" G"," Y"," G"," G"]然后您想要返回2秘密序列中包含的不同索引的项目使用相同的索引和2。

另一个例子:如果顺序是[" X"," R"," Y"," T"]和用户猜测[" T"," T"," Y"," Y"]然后你返回1为猜测的字符在序列中但索引错误时的索引1相同。

无论如何,对我而言,这不是一个简单的问题需要解决。这是我用来使它工作的代码,但它并不优雅。肯定有更好的办法。我希望有人能告诉我我在这里失踪了什么? Ruby的新手......

def index_checker(input_array, sequence_array)
  count = 0
  leftover_input = []
  leftover_sequence = []

  input.each_with_index do |char, idx|
    if char == sequence[idx]
      count += 1
    else
      leftover_input << char
      leftover_sequence << sequence[idx]
    end
  end

diff_index_checker(leftover_input, leftover_sequence, count)
end

def diff_index_checker(input, sequence, count)
  count2 = 0
  already_counted = []

    input.each do |char|
      if sequence.include?(char) && !already_counted.include?(char)
        count2 += 1
        already_counted << char
      end
    end
[count, count2]
end

4 个答案:

答案 0 :(得分:2)

这是一个干净的Ruby解决方案,用惯用的Ruby面向对象风格编写:

class Mastermind
  def initialize(input_array, sequence_array)
    @input_array = input_array
    @sequence_array = sequence_array
  end

  def matches
    [index_matches, other_matches]
  end

  def results
    [index_matches.size, other_matches.size]
  end

  private

  attr_reader :input_array, :sequence_array

  def index_matches
    input_array.select.with_index { |e, i| e == sequence_array[i] }
  end

  def other_matches
    non_exact_input & non_exact_sequence
  end

  def non_exact_input
    array_difference(input_array, index_matches)
  end

  def non_exact_sequence
    array_difference(sequence_array, index_matches)
  end

  # This method is based on https://stackoverflow.com/a/3852809/5961578
  def array_difference(array_1, array_2)
    counts = array_2.inject(Hash.new(0)) { |h, v| h[v] += 1; h }
    array_1.reject { |e| counts[e] -= 1 unless counts[e].zero? }
  end
end

您可以按如下方式使用此课程:

>> input_array = ["G","G","G","Y"]
>> sequence_array = ["G", "Y","G","G"]
>> guess = Mastermind.new(input_array, sequence_array)
>> guess.results
#> [2, 2]
>> guess.matches
#> [["G", "G"], ["G", "Y"]]

以下是它的工作原理。首先,一切都进入了一个名为Mastermind的课程。我们为类创建一个构造函数(在Ruby中是一个名为initialize的方法),我们接受两个参数:输入数组(用户猜测)和序列数组(答案)。

我们将每个参数设置为一个实例变量,该变量以@开头表示。然后我们使用attr_reader@input_array@sequence_array创建getter方法,这样我们就可以通过从任何实例方法中调用input_arraysequence_array来获取值上课。

然后我们定义了两个公共方法:matches(返回精确匹配的数组和其他匹配的数组(匹配但索引错误的匹配)和results(返回这两个数组的计数。)

现在,在我们班的private部分,我们可以定义逻辑的内容。每个方法都有一个特定的工作,每个方法都被命名为(希望)帮助读者了解它正在做什么。

index_matches返回input_array的一个子集,其元素与sequ​​ence_array完全匹配。

other_matches返回input_array的一个子集,其元素与sequ​​ence_array完全匹配,但在错误的索引处匹配。

other_matches依赖于non_exact_inputnon_exact_sequence,每个都是使用array_difference方法计算的,我从另一个SO答案中复制了该方法。 (没有方便的Ruby方法允许我们从一个数组中减去一个数组而不删除重复项。)

答案 1 :(得分:1)

<强>代码

def matches(hidden, guess)
  indices_wo_match = hidden.each_index.reject { |i| hidden[i] == guess[i] }
  hidden_counts = counting_hash(hidden.values_at *indices_wo_match)
  guess_counts = counting_hash(guess.values_at *indices_wo_match)
  [hidden.size - indices_wo_match.size, guess_counts.reduce(0) { |tot, (k, cnt)|
    tot + [hidden_counts[k], cnt].min }]
end

def counting_hash(arr)
  arr.each_with_object(Hash.new(0)) { |s, h| h[s] += 1 }
end

<强>实施例

matches ["G","G","G","Y"], ["G", "Y","G","G"]
  #=> [2, 2]
matches ["X","R","Y","T"] , ["T","T","Y","Y"]
  #=> [1, 1]

<强>解释

步骤如下。

hidden = ["G","G","G","Y"]
guess = ["G", "Y","G","G"]

保存i的索引hidden[i] != guess[i]

indices_wo_match = hidden.each_index.reject { |i| hidden[i] == guess[i] }
  #=> [1, 3]

请注意,值相等的索引数如下:

hidden.size - indices_wo_match.size
  #=> 2

现在计算guess的剩余元素的数量,这些元素与hidden的其余值之一配对,具有相同的值。首先计算hidden的每个唯一元素的实例数,然后对guess执行相同操作。

hidden_counts = counting_hash(hidden.values_at *indices_wo_match)
  #=> {"G"=>1, "Y"=>1}
guess_counts = counting_hash(guess.values_at *indices_wo_match)
  #=> {"Y"=>1, "G"=>1}

要了解counting_hash的工作原理,请参阅Hash::new,尤其是提供默认值作为new参数的效果说明。简而言之,如果定义了哈希h = Hash.new(3),那么如果h没有密钥k,则h[k]会返回默认值,此处为3(哈希没有改变。)

现在计算guess元素在同一索引处不等于hidden的值的匹配数以及具有相同元素的hidden元素对的匹配数值。

val_matches = guess_counts.reduce(0) do |tot, (k, cnt)|
  tot + [hidden_counts[k], cnt].min
end
  #=> 2

最后,返回感兴趣的值。

[hidden.size - indices_wo_match.size, val_matches]
  #=> [2, 2]

在上面的代码中,我已经替换了变量val_matches

使用Ruby 2.4+,可以使用Enumerable#sum替换

guess_counts.reduce(0) { |tot, (k, cnt)| tot + [hidden_counts[k], cnt].min }

guess_counts.sum { |k, cnt| [hidden_counts[k], cnt].min }  

答案 2 :(得分:1)

def judge(secret, guess)
  full = secret.zip(guess).count { |s, g| s == g }
  semi = secret.uniq.sum { |s| [secret.count(s), guess.count(s)].min } - full
  [full, semi]
end

演示:

> judge(["G","G","G","Y"], ["G","Y","G","G"])
=> [2, 2]
> judge(["X","R","Y","T"], ["T","T","Y","Y"])
=> [1, 1]

一个较短的选择,虽然我发现不太清楚:

  full = secret.zip(guess).count(&:uniq!)

答案 3 :(得分:1)

我更喜欢我的另一个答案,因为它的简单性,但是如果有人想将它用于比Mastermind更大的数组,那么这个会更快。

// Example: Promote PKPaymentAuthorizationViewController to optional so that we can verify
// that our paymentRequest is valid. Otherwise, an invalid paymentRequest would crash our app.

 if let vca = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest) as PKPaymentAuthorizationViewController?
 {
    ...logic...
 } else {
    ->this is what i get when using pounds or anything else<-
 }

演示:

def judge(secret, guess)
  full = secret.zip(guess).count { |s, g| s == g }
  pool = secret.group_by(&:itself)
  [full, guess.count { |g| pool[g]&.pop } - full]
end