C#纸牌游戏中的最佳卡片选择

时间:2012-01-12 06:01:15

标签: c# algorithm recursion playing-cards

问题在于遵循以下规则在游戏的每个时刻选择最佳选项:

  • 您只能选择最左边或最右边的卡片。

  • 您的对手将始终先选择,并始终从最左侧或最右侧的牌中选择最高牌。如果它是一个平局,它会选择最右边的。考虑到这并不总是最好的选择。

有时赢得胜利是不可能的,但无论如何你必须通过对抗这个对手(或策略,让我们说)来显示你可以添加的最高分。

示例:

Cards:    1 2 4 2 8 4 3
Opponent: 3 4 2 2 = 11
Me:       1 8 4 = 13

在这里,我在第二回合选择了1而不是4,所以我可以选择8。这就是为什么选择最高卡并不总是最好的。

我一直在尝试使用递归来实现此解决方案,但我不确定它是最好的选择。关于如何设计这种算法的任何想法?

[编辑] 感谢@PengOne的慷慨帮助。这是我试图实现的代码,但不幸的是它给了我错误。我该怎么办呢?随着我的进步,我正在编辑这个。

static int cardGameValue(List<int> D, int myScore, int opponentScore)
{
    if (D.Count == 0) return myScore;
    else
    {
        if (D[0] <= D[D.Count - 1])
        {
            opponentScore += D[D.Count - 1];
            D.RemoveAt(D.Count - 1);
        }
        else
        {
            opponentScore += D[0];
            D.RemoveAt(0);
        }

        int left = cardGameValue(
                new List<int>(D.GetRange(1, D.Count - 1)),
                myScore + D[0],
                opponentScore);

        int right = cardGameValue(
                new List<int>(D.Take(D.Count - 2)),
                myScore + D[D.Count - 1],
                opponentScore);

        if (left >= right)
        { return left; }
        else
        { return right; }
    }
}

3 个答案:

答案 0 :(得分:4)

使用递归从最简单的情况构建解决方案。

D成为卡片阵列。让A成为你牌的总数,B是对手牌的总数。将S = A-B设置为游戏的值。如果S>0,则会获胜,如果S<0则会失败,如果S==0则会失败。

最简单的是一次做两个动作,你的动作接着是对手的决心。有两个基本案例需要考虑:

  • 如果length(D) == 0,请返回S。游戏结束了。

  • 如果length(D) == 1,请返回S + D[0]。您选择剩余的卡片,游戏结束。

对于递归情况,当length(D) > 1时,评估两种可能性

  • 如果您选择左侧牌,然后是对手进行确定性移动,则让L成为游戏的结果,即

    L = D[0] - max(D[1],D[N-1]) + cardGameValue(newD)

  • 如果你选择了正确的牌,然后是对手做出他的确定性移动,那么让R成为游戏的结果,即

    R = D[N-1] - max(D[0],D[N-2]) + cardGameValue(newD)

选择与较大数字相对应的游戏,即D[0] L>=R,然后选择D[N-1]。这里N = length(D)

答案 1 :(得分:3)

您应该查看Min-Max Algorithm,可能会查看Alpha-Beta pruning

Min-Max认为你的对手总是会为自己选择最佳选择,因此你可以运行每个可能的场景来发现最能让你击败对手的选择。 “即,如果我移动x,我的对手将采取行动y,然后我将采取......”等,一直到游戏结束。因此,您可以确定谁将获胜。

Alpha-Beta修剪类似于它查看可能的场景,但它确定可能的场景列表是否会产生获胜结果。如果你知道如果你做“移动x”那么无论如何你总是会松动,你不需要花更多的时间看“移动x然后移动y”。你可以从“移动x”中“修剪”整个选择分支,因为你知道它永远不会有帮助。

答案 2 :(得分:2)

这是最终实际工作的代码。感谢大家的支持。

    static int cardGameValue(List<int> D, int myScore, int opponentScore)
    {
        if (D.Count == 0) return myScore;
        else if (D.Count == 1)
        {
            opponentScore += D[0];
            return myScore;
        }
        else
        {
            if (D[0] <= D[D.Count - 1])
            {
                opponentScore += D[D.Count - 1];
                D.RemoveAt(D.Count - 1);
            }
            else
            {
                opponentScore += D[0];
                D.RemoveAt(0);
            }

            int left = cardGameValue(new List<int>(D.GetRange(1, D.Count - 1)), myScore + D[0], opponentScore);

            int right = cardGameValue(new List<int>(D.GetRange(0, D.Count - 1)), myScore + D[D.Count - 1], opponentScore);

            if (left >= right)
            {
                return left;
            }
            else
            {
                return right;
            }
        }
    }