1

我正在用 PHP 开发一个非常小的二十一点游戏。

我目前正在编写计算牌的函数,但是 A 正在踢我的屁股。

我的卡片都是这样排列的:

$card_array = array( "ca", "c10", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9",
    "cj", "ck", "cq", "da", "d10", "d2", "d3", "d4", "d5", "d6", "d7", "d8",
    "d9", "dj", "dk", "dq", "ha", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "h9",
    "hj", "hk", "hq", "sa", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9",
    "s10", "sj", "sk", "sq");`

其中第一个字符是花色,之后的所有字符都是卡片(j 代表杰克等)

到目前为止,这是我对值进行计数的内容:

function bj_calculate_values( $cards ) {
    $card_values = 0;
    foreach ($cards as $card) {
        if (substr($card, 1) == "k" || substr($card, 1) == "q" || substr($card, 1) == "j") {
            $card_values += 10;
        }
        else if (substr($card, 1) != "a") {
            $card_values += substr($card, 1);
        }
    }
}

最初,我也有一张价值 11 的王牌,但显然这会导致问题。我觉得我需要继续循环以确保 A 不会让我们超过 21,但我不完全确定最好的方法来做到这一点。

伙计们,我将不胜感激。 编辑

我能想到的最好的就是将它添加到函数中

foreach ($cards as $card) {
    if (substr($card, 1) == "a") {
        if ($card_values + 11 <= 21) {
            $card_values += 11;
        }
        else {
            $card_values += 1;
        }
    }
}

其实我觉得这可能行得通。给我几分钟测试一下。

编辑:

不,没有工作。4, ace, ace, 6 就这样出来了 22。

4

1 回答 1

3

我对二十一点了解不多,但这是我的尝试。

    function bj_calculate_values( $cards ) {
        $card_values = 0;
        $ace = 0;
        foreach ($cards as $card) {
            $val = substr($card, 1);
            if (!is_numeric($val))
            {
                    if ('a' == $val)
                    {
                            $val = 0;
                            $ace ++;
                    }
                    else
                            $val = 10;
            }
            $card_values    += $val;
        }
        while ($ace)
            $card_values += ($card_values + 11*$ace-- <= 21) ? 11: 1;


        return $card_values;
    }

更新

如果我理解您的其他评论,您希望 6 4 AAA 出现 13、6 4 AA 出现 12 和 6 4 A 出现 21。所以剩余的 A数很重要。相应地修改了源。

解释

首先我们计算非王牌的价值。这些是不变的,所以我们结束了。而这个总数是(初步的)$card_values

那么我们可能有(或没有)一些A。有 a 是有意义的,while($aces)这样如果我们没有 A,我们什么也不做。

现在的问题是,我们如何处理这些 A?这就是你的要求:总和必须适合 21。这意味着我不能只说“我在 10,我有一张 A,所以它适合 21”,因为在那之后可能还有另一个A . 所以我必须计算如果我将所有剩余的 A 都加起来,最坏的情况是什么?这当然是 ace 的数量乘以 11。虽然最坏的情况仍然很好(即$card_values + 11*$ace小于 21),但我可以加 11。虽然不是,我必须加 1。

这是“贪婪”算法的一种形式:我尽可能满足所有 1,同时确保留下足够的 11 以达到不超过 21 的最接近方法。

我似乎记得(我可能错了,呵呵)这个实现中有一个讨厌的错误,只要 NumberOfItems * LowestValue >= HighestValue 就会出现(虽然我不太确定“等于”)。但在这种情况下,它需要 NumberOfItems 高于 11/1 = 11 个 ace,而且牌组中没有那么多 ace,所以我们很酷。可以肯定的是,可能的情况有五种——从一手牌中的“无”到“四”A——并且很容易测试所有其他牌的总和:

    // J Q K are worth 10, so we can use 10 instead.
    // And a fake card with value of 0 stands for "nothing".
    // We use the suit of Klingon, with up to four identical aces :-D

    for ($first = 0; $first <= 21; $first++)
    {
        $hand = array("k$first");
        for ($aces = 1; $aces < 5; $aces++)
        {
            $hand[] = "ka";
            $value  = bj_calculate_values($hand);

            // Let us ponder the possible values of $value.

            if ($value <= 11)
            {
                    // This is an error: we could have gotten closer to 21
                    // by upvaluing an ace. A correct implementation will
                    // never enter here except in the case of a LONE ACE.
                    if (($first != 0) || ($aces != 1))
                        print "ERROR: " . implode(" ", $hand) . " = $value\n";
            }
            // We have a value from 12 to 21.
            // Could we have done better? What cards do we have?
            // If no aces, of course no. All card values are then fixed.

            // If at least one ace, interpreted as 11, again not:
            // cannot interpret it above 11, and interpret as 1 would LESSEN the score.

            // If at least one ace, interpreted as 1, were we to interpret it
            // as 11, we would be summing 10. And 12 would become 22, and blow.

            // So, from 12 to 21, the result MUST be correct.

            // Finally, $value more than 21.
            if ($value > 21)
            {
                   // This is admissible ONLY if we could have done no better, which
                   // means $value is given by the fixed cards' value, $first, plus 
                   // ALL ACES COUNTED AS ONES.
                   if ($value != ($first + $aces))
                        print "ERROR: " . implode(" ", $hand) . " = $value\n";
             }
        }
    }

输出(版本 1)

VERIFY: k0 ka = 11              Correct, we had a lone ace and gave it 11.
VERIFY: k18 ka ka ka ka = 22    Correct, we had 18 in other cards and all A's to 1's.
VERIFY: k19 ka ka ka = 22       Ditto, 19 in cards.
VERIFY: k19 ka ka ka ka = 23    Ditto, 19 in cards.
...                             ...

当前输出(添加代码仅打印错误):

-- nothing :-)
于 2013-02-07T00:15:28.447 回答