質問

The problem consists in choosing the best option at every moment of the game following these rules:

  • You can only pick the leftmost or the rightmost card.

  • Your opponent will ALWAYS pick first, and will ALWAYS choose the highest card from either the leftmost or the rightmost card. If it's a tie, it will pick the rightmost. Take into consideration this is not always the best choice.

Sometimes it's impossible to win, but you must anyway display the highest amout you can add by playing against this opponent (or strategy, let's say).

Example:

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

Here, I chosed 1 instead of 4 on the second turn, so I could pick the 8 later. That's why choosing the highest card is not always best.

I've been trying to implement this solution using recursion but I'm not sure it's the best option. Any ideas on how to design this algorithm?

[EDIT] Thanks to @PengOne for it's generous help. This is the code im trying to implement, but unfortunately it's giving me errors. What should I fix in it? I'm editing this as I progress.

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; }
    }
}
役に立ちましたか?

解決

Build the solution out from the simplest cases using recursion.

Let D be the array of cards. Let A be the total of your cards and B be the total of your opponent's cards. Set S = A-B to be the value of the game. You win if S>0, lose if S<0 and tie if S==0.

The easiest is to make two moves at once, your move followed by the opponent's determined move. There are two base cases to consider:

  • If length(D) == 0, return S. The game has ended.

  • If length(D) == 1, return S + D[0]. You choose the remaining card, and the game ends.

For the recursive case, when length(D) > 1, evaluate the two possibilities

  • Let L be the result of the game if you choose the left card followed by the opponent doing his deterministic move, i.e.

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

  • Let R be the result of the game if you choose the right card followed by the opponent doing his deterministic move, i.e.

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

Choose the play corresponding to the larger number, i.e. take D[0] if L>=R, otherwise take D[N-1]. Here N = length(D).

他のヒント

You should look at the Min-Max Algorithm, possibly with Alpha-Beta pruning

Min-Max is the idea that your opponent will always choose the best choice for themselves, thus you can run each possible scenario to discover the best choice that will result in you beating your opponent. "ie, given I make move x, my opponent will take move y, then I'll take..." etc, all the way to the end of the game. Thus, you can determine who will win.

Alpha-Beta pruning is similar that it looks at possible scenarios, but it determines if a list of possible scenarios will ever produce a winning outcome. If you know that if you make "move x" then you will always loose no matter what, you don't need to spend more time looking at "move x then move y". You can "prune" off the whole branch of choices from "move x" because you know it will never be helpful.

This is the code that actually worked in the end. Thanks to everyone for your support.

    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;
            }
        }
    }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top