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

时间:2014-03-14 18:10:21

标签: c# performance

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

任何关于如何提高性能的想法都会很棒。

这是代码:

    private static int getHandvalue(List<Card> 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<Card> sCards)
    {

        int count = sCards.Count;
        if (count == 5) return getHandvalue(sCards);

        int HighestValue = 0;
        Card missingOne;
        int tempValue;

        for (int i = 0; i < count - 1; i++)
        {

            missingOne = sCards[i];
            sCards.RemoveAt(i);

            tempValue = getHandvalueList(sCards);
            if (tempValue > 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<Card> sCards)
    {

        if (sCards.Count < 5) return 0;
        sCards.Sort(new ICardComparer());
        return getHandvalueList(sCards);

    }

最多可以发送7张卡片。

更新

到目前为止:每次使用7张牌调用公共功能(大部分时间都是这种情况),它必须调用手动评估方法21次(每次可能的5张卡组合一次) )。

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

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

就此创建了一个新问题:Hashfunction to map combinations of 5 to 7 cards

更新: 有关接受的答案的进一步改进,请参考: Efficient way to randomly select set bit

1 个答案:

答案 0 :(得分:7)

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

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

首先检查直接冲洗;表格hh = h & (h>>4) & (h>>8) & (h>>12) & (h>>16)。如果hh非零,则从高位开始直接刷新。

然后你检查四种类型;表格hh = h & (h>>1) & (h>>2) & (h>>3) & 0x1111...1。如果hh非零,那么您已经拥有了四种类型。

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

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

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

相关问题