优化Poker-Monte-Carlo-Simulation的手动评估算法

我为Hold’em Poker写了一个平衡器,作为一个爱好项目。 它工作正常,但仍然有一件我不满意的事情:在模拟手的整个过程中,评估手的过程大约需要35%的时间。 对于我而言,与其他必须完成的工作相似,如迭代和克隆大型数组和东西。

任何关于如何让这个更高效的想法都会很棒。

这是代码:

private static int getHandvalue(List sCards) { // --- Auf Straight / Straight Flush prüfen --- if ((sCards[0].Value - 1 == sCards[1].Value) && (sCards[1].Value - 1 == sCards[2].Value) && (sCards[2].Value - 1 == sCards[3].Value) && (sCards[3].Value - 1 == sCards[4].Value)) { if ((sCards[0].Color == sCards[1].Color) && (sCards[1].Color == sCards[2].Color) && (sCards[2].Color == sCards[3].Color) && (sCards[3].Color == sCards[4].Color)) return (8 << 20) + (byte)sCards[0].Value; // Höchste Karte Kicker else return (4 << 20) + (byte)sCards[0].Value; // Höchste Karte Kicker (Straight) } // --- Auf Wheel / Wheel Flush prüfen --- if ((sCards[4].Value == Card.CardValue.Two) && (sCards[3].Value == Card.CardValue.Three) && (sCards[2].Value == Card.CardValue.Four) && (sCards[1].Value == Card.CardValue.Five) && (sCards[0].Value == Card.CardValue.Ace)) { if ((sCards[0].Color == sCards[1].Color) && (sCards[1].Color == sCards[2].Color) && (sCards[2].Color == sCards[3].Color) && (sCards[3].Color == sCards[4].Color)) return(8 << 20) + (byte)sCards[1].Value; // Zweithöchste Karte Kicker else return(4 << 20) + (byte)sCards[1].Value; // Zweithöchste Karte Kicker (Straight) } // --- Auf Flush prüfen --- if ((sCards[0].Color == sCards[1].Color) && (sCards[1].Color == sCards[2].Color) && (sCards[2].Color == sCards[3].Color) && (sCards[3].Color == sCards[4].Color)) return (5 << 20) + ((byte)sCards[0].Value << 16) + ((byte)sCards[1].Value << 12) + ((byte)sCards[2].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[4].Value; // --- Auf Vierling prüfen --- if (((sCards[0].Value == sCards[1].Value) && (sCards[1].Value == sCards[2].Value) && (sCards[2].Value == sCards[3].Value)) || ((sCards[1].Value == sCards[2].Value) && (sCards[2].Value == sCards[3].Value) && (sCards[3].Value == sCards[4].Value))) return (7 << 20) + (byte)sCards[1].Value; // Wert des Vierlings (keine Kicker, da nicht mehrere Spieler den selben Vierling haben können) // --- Auf Full-House / Drilling prüfen --- // Drilling vorne if ((sCards[0].Value == sCards[1].Value) && (sCards[1].Value == sCards[2].Value)) { // Full House if (sCards[3].Value == sCards[4].Value) return (6 << 20) + ((byte)sCards[0].Value << 4) + (byte)sCards[3].Value; // Drilling (höher bewerten) // Drilling return (3 << 20) + ((byte)sCards[0].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[4].Value; // Drilling + Kicker 1 + Kicker 2 } // Drilling hinten if ((sCards[2].Value == sCards[3].Value) && (sCards[3].Value == sCards[4].Value)) { // Full House if (sCards[0].Value == sCards[1].Value) return (6 << 20) + ((byte)sCards[2].Value << 4) + (byte)sCards[0].Value; // Drilling (höher bewerten) // Drilling return (3 << 20) + ((byte)sCards[2].Value << 8) + ((byte)sCards[0].Value << 4) + (byte)sCards[1].Value; // Drilling + Kicker 1 + Kicker 2 } // Drilling mitte if ((sCards[1].Value == sCards[2].Value) && (sCards[2].Value == sCards[3].Value)) return (3 << 20) + ((byte)sCards[1].Value << 8) + ((byte)sCards[0].Value << 4) + (byte)sCards[4].Value; // Drilling + Kicker 1 + Kicker 2 // --- Auf Zwei Paare prüfen --- // Erstes Paar vorne, zweites Paar mitte if ((sCards[0].Value == sCards[1].Value) && (sCards[2].Value == sCards[3].Value)) return (2 << 20) + ((byte)sCards[0].Value << 8) + ((byte)sCards[2].Value << 4) + (byte)sCards[4].Value; // Erstes Paar + Zweites Paar + Kicker // Erstes Paar vorne, zweites Paar hinten if ((sCards[0].Value == sCards[1].Value) && (sCards[3].Value == sCards[4].Value)) return (2 << 20) + ((byte)sCards[0].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[2].Value; // Erstes Paar + Zweites Paar + Kicker // Erstes Paar mitte, zweites Paar hinten if ((sCards[1].Value == sCards[2].Value) && (sCards[3].Value == sCards[4].Value)) return (2 << 20) + ((byte)sCards[1].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[0].Value; // Erstes Paar + Zweites Paar + Kicker // --- Auf Paar prüfen --- // Paar vorne if (sCards[0].Value == sCards[1].Value) return (1 << 20) + ((byte)sCards[0].Value << 12) + ((byte)sCards[2].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[4].Value; // Paar + Kicker 1 + Kicker 2 + Kicker 3 // Paar mitte-vorne if (sCards[1].Value == sCards[2].Value) return (1 << 20) + ((byte)sCards[1].Value << 12) + ((byte)sCards[0].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[4].Value; // Paar + Kicker 1 + Kicker 2 + Kicker 3 // Paar mitte-hinten if (sCards[2].Value == sCards[3].Value) return (1 << 20) + ((byte)sCards[2].Value << 12) + ((byte)sCards[0].Value << 8) + ((byte)sCards[1].Value << 4) + (byte)sCards[4].Value; // Paar + Kicker 1 + Kicker 2 + Kicker 3 // Paar hinten if (sCards[3].Value == sCards[4].Value) return (1 << 20) + ((byte)sCards[3].Value << 12) + ((byte)sCards[0].Value << 8) + ((byte)sCards[1].Value << 4) + (byte)sCards[2].Value; // Paar + Kicker 1 + Kicker 2 + Kicker 3 // --- High Card bleibt übrig --- return ((byte)sCards[0].Value << 16) + ((byte)sCards[1].Value << 12) + ((byte)sCards[2].Value << 8) + ((byte)sCards[3].Value << 4) + (byte)sCards[4].Value; // High Card + Kicker 1 + Kicker 2 + Kicker 3 + Kicker 4 } 

此方法返回扑克中每个已排序的5卡组合的精确值。 它被另一个方法调用:

  private static int getHandvalueList(List sCards) { int count = sCards.Count; if (count == 5) return getHandvalue(sCards); int HighestValue = 0; Card missingOne; int tempValue; for (int i = 0; i  HighestValue) HighestValue = tempValue; sCards.Insert(i, missingOne); } missingOne = sCards[count - 1]; sCards.RemoveAt(count - 1); tempValue = getHandvalueList(sCards); if (tempValue > HighestValue) HighestValue = tempValue; sCards.Add(missingOne); return HighestValue; } 

此递归方法返回所有可能的5张卡组合的最高值。 而这个被最终的公共方法调用:

  public static int GetHandvalue(List sCards) { if (sCards.Count < 5) return 0; sCards.Sort(new ICardComparer()); return getHandvalueList(sCards); } 

它最多可以发送7张卡。

更新

到目前为止:每次使用7张牌调用公共function(大多数情况下就是这种情况),它必须调用21次手动评估方法(每次可能的5张卡片组合一次)。

我考虑过在哈希表中为每个可能的5到7张卡片缓存值,然后查看它。 但如果我没有错,它将需要存储超过133.784.560 32位整数值,大约500MB。

什么可能是好的哈希函数将每个可能的组合分配给一个arrayindex?

创建了一个新问题: Hashfunction映射5到7张牌的组合

更新:为了进一步改进已接受的答案,请参考: 随机选择设置位的有效方法

我过去写过一个相当快速的扑克手评估员。 我使用了与你不同的代表,我认为这是表现的关键。

我用64位整数表示最多7张牌的牌; 第4位用于两颗心,第2位用于两颗钻石,6颗用于两个黑桃,7分用于两个球杆,8个用于三颗心,等等。

你先检查直冲; formshh = h & (h>>4) & (h>>8) & (h>>12) & (h>>16) 。 如果hh非零,则从高位开始有一个直接齐平。

然后你检查四种类型; formshh = h & (h>>1) & (h>>2) & (h>>3) & 0x1111...1 。 如果hh非零,那么你自己就是四个人。

在这一点上,你想知道哪些排名有三种,哪些排名有对。 类似于四种情况,形成位掩码,告诉你哪些排名至少有三张牌,哪些排名至少有两张牌,哪些排名至少有一张牌。 (考虑对手的每个半字节进行排序。)如果popcount(threes) + popcount(twos) >= 2 ,你有一个完整的房子可以找到。

冲洗和直线条件很容易检查。 而且,在这一点上,三种,两对和成对条件也是如此。

这种方法的一个很好的特点是它可以直接返回一个表示手的等级的整数 ,减少手比较到一堆比特摆动以预处理手然后进行单个整数比较。 (正如你正在做的那样,现在我再次查看你的post。)如果写得正确,它也可以直接在7张牌上操作,无需迭代手中5张牌的所有子集。