简体   繁体   English

使用LINQ在C#中使用MasterMind评分算法

[英]MasterMind scoring algorithm in C# using LINQ

I'm looking for an elegant way to compute the score of a guess in the MasterMind game in C#, preferably using LINQ. 我正在寻找一种优雅的方法来计算C#中MasterMind游戏的猜测得分,最好是使用LINQ。

In MasterMind, the codemaker generates a secret code of 4 digits using the digits 1 through 6. A digit may be used more than once. 在MasterMind中,代码生成器使用数字1到6生成4位数的密码。可以多次使用数字。 As an example, the secret code is: 例如,密码是:

int[] secret = { 1, 2, 3, 1 };

The codebreaker tries to break the secret code by presenting a guess. 代码破解者试图通过猜测来破解密码。 In this example, the guess is: 在这个例子中,猜测是:

int[] guess  = { 1, 1, 2, 2 };

(Both code and guess are now stored in an array, but other collection types are okay too). (代码和猜测现在都存储在一个数组中,但其他集合类型也可以)。

The codemaker then "scores" this guess by announcing the number of "blacks" and "whites". 然后,代码制作者通过宣布“黑人”和“白人”的数量来“评分”这个猜测。 A black is awarded for each digit from the guess which is correct in both value and position. 从猜测的每个数字中奖励黑色,其在值和位置都是正确的。 A white is awarded for each correct digit placed in the wrong position. 对于放置在错误位置的每个正确数字,将获得白色。 In this example, the score is 1 black (for the "1" in position 1) and 2 whites (for the "1" and "2" in positions 2 and 3). 在该示例中,得分为1黑色(对于位置1中的“1”)和2个白色(对于位置2和3中的“1”和“2”)。

Back to the question: I'm looking for an elegant way to compute the score of a guess in C#, preferably using LINQ. 回到问题:我正在寻找一种优雅的方法来计算C#中的猜测分数,最好使用LINQ。 So far, I've come up with a statement that computes the number of blacks: 到目前为止,我已经提出了一个计算黑人数量的声明:

int blacks = new int[] { 0, 1, 2, 3 }.Count(i => (guess[i] == secret[i]));

I was going to proceed along the lines that the number of whites is the total number of matches (3) minus the number of blacks. 我打算按照白色数量是匹配总数(3)减去黑色数量的方式进行。 So I tried: 所以我尝试过:

int whites = guess.Intersect(secret).Count() - blacks;

But, alas, IEnumerable.Intersect() produces { 1, 2 } instead of { 1, 1, 2 }, because it looks at distinct digits only. 但是,唉,IEnumerable.Intersect()产生{1,2}而不是{1,1,2},因为它只查看不同的数字。 So it computes whites = 1 instead of 2. 所以它计算白色= 1而不是2。

I cannot come up with another way of computing "whites", except from using "C" style nested loops. 除了使用“C”样式的嵌套循环之外,我无法想出另一种计算“白人”的方法。 Can you? 你能? Preferably using LINQ - I like the way an algorithm can be expressed in code using LINQ. 最好使用LINQ - 我喜欢使用LINQ在代码中表示算法的方式。 Execution speed is not really an issue. 执行速度不是真正的问题。

var black = guess
        .Zip(secret, (g, s) => g == s)
        .Count(z => z);

var white = guess
        .Intersect(secret)
        .Sum(c =>
            System.Math.Min(
                secret.Count(x => x == c),
                guess.Count(x => x == c))) - black;

Given: 鉴于:

int[] secret = { 1, 2, 3, 1 };
int[] guess  = { 1, 1, 2, 2 };

Then: 然后:

black == 1 && white == 2

Here's one way (assuming I've understood the problem correctly): 这是一种方式(假设我已正确理解问题):

  1. Find the black score - This is easy enough; 找到黑色分数 - 这很容易; it's simply a matter of zipping the sequences up and counting the number of corresponding elements that match. 它只是压缩序列并计算匹配的相应元素的数量。

  2. Find the number of "common elements" between both sequences - This must be the sum of the white and black scores. 找出两个序列之间“共同元素”的数量 - 这必须是白色和黑色分数的总和。

  3. Find the white score - Simply the difference between 2. and 1. 找到白色分数 - 只需2.和1之间的差异。


// There must be a nicer way of doing this bit
int blackPlusWhite = secret.GroupBy(sNum => sNum)
                           .Join(guess.GroupBy(gNum => gNum),
                                 g => g.Key,
                                 g => g.Key,
                                (g1, g2) => Math.Min(g1.Count(), g2.Count()))
                           .Sum();   

int black = guess.Zip(secret, (gNum, sNum) => gNum == sNum)
                 .Count(correct => correct); 

int white = blackPlusWhite - black;

EDIT: Mixed up black and white. 编辑:混合黑色和白色。

EDIT: (The OP is not on .NET 4) In .NET 3.5, you can calculate black with: 编辑:( OP不在.NET 4上)在.NET 3.5中,您可以使用以下方法计算黑色:

int black = Enumerable.Range(0, secret.Count)
                      .Count(i => secret[i] == guess[i]); 

Ani's answer is good. Ani的答案很好。 Here's a nicer (clearer) way to do that grouping and joining. 这是一种更好(更清晰)的方式来进行分组和加入。

ILookup<int, int> guessLookup = guess.ToLookup(i => i);

int blackPlusWhite
(
  from secretNumber in secret.GroupBy(i => i)
  let secretCount = secretNumber.Count()
  let guessCount = guessLookup[secretNumber.Key].Count()
  select Math.Min(secretCount, guessCount)
).Sum()

int black = Enumerable.Range(0, secret.Count).Count(i => guess[i] == secret[i]);

int white = blackPlusWhite - black;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM