Решения дилера и недилера не совпадают. все симметрично в том, что сброс недилера формирует вторую руку дилера, так что дилер хочет сбросить там хорошие карты, а недилер хочет сбросить плохие карты. Кроме того, структура игры с привязкой такова, что разные карты будут хороши для дилера и не дилера. дилеру соответственно, чтобы вернуть лучшую комбинацию из четырех карт, которую следует держать, при заданном счете (игра до 121, поэтому игра будет отличаться при разных счетах, учитывая, что это пошаговая игра, и первая, набравшая 121, забирает все ). Затем из каждой руки с четырьмя картами для трех известных мертвых карт (вырезка и наши сбросы), лучший ход, вторая [согласно данному ответу], третья, четвертая и т.д. Я не совершенно уверен, как структурировать эту проблему, но мы видим, что с 52C6 * 46C6 * 40 = 7,63 * 10 ^ 15 возможных начальных позиций, а затем 6C4 * 6C4 * 4! 4! = 129 600 возможных путей решения для каждого, которые могут стать слишком большой проблемой для решения.
Для упрощения я рассматриваю случай, когда масти не имеют значения. Здесь их не больше 18! / 12! / 6! = 18 564 руки для первого игрока, а не 52! / 6! / 46! = 20 358 520, если подходят масти, или меньшее число, если мы попытаемся сравнить эквивалентные руки, похожие на Генерация всех 5-карточных покерных рук .
Алгоритм, который я придумал, заключается в повторении ряды карт, с повторением, для шестикарточной, шестикарточной и затем сокращенной карты. Это генерирует уникальные паттерны, однако не исключает таких последовательностей 10,10,10,10,10,10,10,10, которые невозможны, поскольку в колоде всего четыре карты каждого ранга.
Это не так сложно исправить, поскольку обычно существует 4C4 = 1 способа выбрать все четыре карты данного ранга, 4C3 = 4, 4C2 = 6 и 4C1 = 4 способа выбрать 3, 2, 1, карты разной масти. Это означает, что, например, 13 карт 0,1,2,3,4,5,6,7,8,9,10,11,12 могут быть сформированы 4 ^ 13 способами. В любом случае, когда имеется более 4 игроков данного ранга, то есть 0 способов добиться этого, и нам не нужно оценивать выбор игрока для этого набора карт.
Я написал несколько код, который доходит до подсчета способов достижения каждого набора, но он очень медленный и тратит более 90% своего времени на расчет весов.
Сначала я попробовал Linq groupBy, который был ужасно медленным, поэтому Я переключился на словарь, а теперь на массив.
Однако что-то все же кажется неправильным в том, что на подсчет уходит очень много времени.
readonly byte[] ranks = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// main method, pass any parameter as they're not implemented yet
bool PlayCribbage(byte poneScore, byte dealerScore)
{
Stopwatch sw = new Stopwatch();
sw.Start();
var dealerHands = GetCombinations<byte>(ranks, 6);
long totalCombos = 0;
long validCombos = 0;
foreach (var dealerHand in dealerHands)
{
var playerHands = GetCombinations<byte>(ranks, 6);
foreach (var playerHand in playerHands)
{
foreach (var cut in ranks)
{
totalCombos++;
long weight = this.CountWeight(dealerHand, playerHand, cut);
if (weight > 0) validCombos++;
}
}
}
sw.Stop();
MessageBox.Show(sw.Elapsed.TotalSeconds.ToString());
return true;
}
// this is where the program is spending all its time
long CountWeight(IEnumerable<byte> dealer, IEnumerable<byte> player, byte cut)
{
long weight = 1;
byte[] counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// the program spends most of its time iterating over these next two lines of code
foreach (byte card in dealer) counts[card]++;
foreach (byte card in player) counts[card]++;
counts[cut]++;
foreach (byte count in counts)
{
switch (count)
{
case 1:
weight *= 4;
break;
case 2:
weight *= 6;
break;
case 3:
weight *= 4;
break;
case 4:
case 0:
break;
default:
return 0;
}
}
return weight;
}
IEnumerable<IEnumerable<T>> GetCombinations<T>(IEnumerable<T> items, int count)
{
int i = 0;
foreach (var item in items)
{
if (count == 1)
yield return new T[] { item };
else
{
foreach (var result in GetCombinations(items.Skip(i), count - 1))
yield return new T[] { item }.Concat(result);
}
++i;
}
}
Возможно, я как-то лает не на то дерево. Или, может быть, я просто ожидаю, что что-то произойдет быстрее, чем на однопоточном i5-7300U, учитывая 18 564 внешних l oop элементов, 18 564 средних l oop элементов, 13 внутренних l oop элементов, а затем повторение через IEnumerable.
Что-то не так с моим подходом или с использованием IEnumerable здесь?
Я могу продолжить как есть, но я еще не сделал какую-либо полезную работу и это уже занимает слишком много времени (час!).
Изменить: кажется, что-то не так с IEnumerable или, по крайней мере, с тем, как это разрешено в al oop здесь. Я добавил . ToArray () вызывает внутри метода dellierHand / playerHand foreach в файле diverHands / playerHands, и это делает его намного быстрее. Кажется, что из-за того, что перечислитель происходит много раз, и итерация через IEnumerable медленнее, чем byte [] или даже List