Алгоритм оценки MasterMind в C # с использованием LINQ

Я ищу элегантный способ вычислить оценку догадки в игре MasterMind на C #, предпочтительно используя LINQ.

В MasterMind кодоискатель генерирует секретный код из 4 цифр, используя цифры с 1 по 6. Цифра может использоваться более одного раза. Например, секретный код:

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

Codebreaker пытается разбить секретный код, представив предположение. В этом примере предполагается следующее:

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

(Оба кода и догадки теперь хранятся в массиве, но другие типы коллекций тоже в порядке).

Кодекса затем «оценивает» это предположение, объявляя количество «черных» и «белых». За каждую цифру присваивается черный цвет, который правилен как по значению, так и по положению. Для каждой правильной цифры помещается белый цвет в неправильном положении. В этом примере оценка 1 черная (для «1» в позиции 1) и 2 белых (для «1» и «2» в позициях 2 и 3).

Вернемся к вопросу: я ищу элегантный способ вычислить оценку угадывания в C #, предпочтительно используя LINQ. До сих пор я придумал инструкцию, которая вычисляет количество черных:

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

Я шел по строкам, что количество белых – это общее количество матчей (3) минус количество черных. Поэтому я попробовал:

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

Но, увы, IEnumerable.Intersect () создает {1, 2} вместо {1, 1, 2}, потому что он смотрит только на отдельные цифры. Поэтому он вычисляет белые = 1 вместо 2.

Я не могу придумать другой способ вычисления «белых», кроме как использовать вложенные петли типа «С». Ты можешь? Предпочтительно использовать LINQ – мне нравится способ, которым алгоритм может быть выражен в коде с использованием LINQ. Скорость выполнения не является проблемой.

 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; 

Дано:

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

Затем:

 black == 1 && white == 2 

Вот один из способов (если я правильно понял проблему):

  1. Найти черный счет – это достаточно легко; это просто вопрос засtagsвания последовательностей вверх и подсчет количества соответствующих элементов, которые соответствуют.

  2. Найдите количество «общих элементов» между обеими последовательностями – это должна быть сумма белых и черных баллов.

  3. Найдите белый счет – просто разница между 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: Смешанный черный и белый.

EDIT: (OP не входит в .NET 4). В .NET 3.5 вы можете рассчитать черный цвет с помощью:

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

Ответ Ани хорош. Вот более удобный (более понятный) способ сделать эту группировку и присоединиться.

 ILookup 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;