【问题标题】:Program for number game数字游戏程序
【发布时间】:2015-01-28 17:30:28
【问题描述】:

我尝试为this question 编写 C++ 程序。我假设所有玩家都玩得很完美,并且该程序也在完美地对抗它。我尝试使用递归(通过在每一步将问题减少为 2 个较小的问题)。 “最终状态”给出的值告诉您是赢 (1) 还是输 (0)。我的代码有什么问题,因为手动制定解决方案会产生不同的结果。

#include<iostream>
using namespace std;

bool win(int player,int no_of_players,int counter)
{
    if(no_of_players==1)
        return 1;
    if(counter>=10)
    {
        --no_of_players;
        counter=0;
    }
    if(player>no_of_players)
        player=0;
    if(win(player+1,no_of_players,counter+1)==0)
    {
        cout<<"\nMove 1";
        return 1;
    }
    if(win(player+1,no_of_players,counter+2)==0)
    {
        cout<<"\nMove 2";
        return 1;
    }
    cout<<"\nLosing position";
    return 0;
}

int main()
{
    int a=win(0,2,1);
    cout<<"\nFinal state :"<<a;
    return 0;
}

【问题讨论】:

  • 您在代码中的什么时候第一次注意到有问题?
  • 当您使用调试器并一次执行每个语句时,哪一行会导致问题?
  • @TankorSmash 我只是在简单的情况下尝试该程序,例如 win(1,2,1) 和 win(2,2,1)。在这两种情况下,我的最终答案都是 0,这使得在 2 人游戏中似乎没有一个玩家获胜。
  • @ThomasMatthews 我不知道要使用调试器。该错误是合乎逻辑的,而不是语法。调试器可以提供帮助吗?
  • 调试器通常可以让您查看您所在的给定行的哪些变量具有哪些值(顺便说一句,您可以在程序“暂停”时按照自己的步调逐行执行由调试器)。

标签: c++ algorithm recursion logic


【解决方案1】:

所以正如 Jeffrey 指出的那样,这个游戏不能由两个以上的玩家来解决。所以我的理解是,现在的问题是为什么你的程序不适用于两个玩家。

你对两人游戏逻辑的最大问题是你误解了counter 的价值。 counter 在您的代码中的实际含义是玩家现在需要写入的值(之后也可以选择写入counter + 1)。所以你正确地假设如果counter &gt;= 10,那么一个玩家输了,尽管你处理它的方式可能不正确。既然我们在这里谈论的是两人游戏,我会做的只是return 0,如果counter 大于或等于10

现在是主要问题——调用递归函数的方式。第一次调用就对了

if(win(player+1,no_of_players,counter+1)==0)

你将它传递给另一个玩家(顺便说一句,你没有以任何方式使用 player 变量,所以我会从代码中完全删除它),在板上写 counter,然后通过这个时间我们知道counter 不是10,所以写它是安全的。

但是,您的第二次调用是错误的。

if(win(player+1,no_of_players,counter+2)==0)

在黑板上写下countercounter + 1 之后,您可以有效地将回合传给下一位玩家。但是等等,如果counter + 110 呢?你需要这样称呼它:

if(counter + 1 < 10 && win(player+1,no_of_players,counter+2)==0)

换句话说,如果第二轮会导致打印 10,甚至不要考虑轮流两轮。

最后,您考虑的最后一个问题是,如果您将其称为win(1, 2, 1)win(2, 2, 1),您的程序将返回不同的值。您没有使用变量player,因此它不能返回不同的值。您的win 函数并没有说明序号为player 的玩家是否获胜,它说的是当前 玩家是否获胜。所以为了测试它是否真的有效,改变初始值counter 更有意义,模拟第一个玩家的不同回合,看看返回值如何变化。例如,第一个玩家输了,所以win(1, 2, 1)0,无论第一个玩家转什么回合,第二个玩家都赢了,所以win(2, 2, 2)win(2, 2, 3)1

这是它的外观。我只对您的代码进行了两次更改:如果counter10,则立即将其设置为return 0,并如上所述更改了第二次递归调用的方式。我没有删除 playerno_of_players 变量,但它们并没有真正在代码中使用:

#include<iostream>
using namespace std;

bool win(int player,int no_of_players,int counter)
{
    if(no_of_players==1)
        return 1;
    if(counter>=10)
    {
        return 0;
    }
    if(player>no_of_players)
        player=0;
    if(win(player+1,no_of_players,counter+1)==0)
    {
        cout<<"\nMove 1";
        return 1;
    }
    if(counter + 1 < 10 && win(player+1,no_of_players,counter+2)==0)
    {
        cout<<"\nMove 2";
        return 1;
    }
    cout<<"\nLosing position";
    return 0;
}

int main()
{
    int a=win(0,2,1);
    cout<<"\nFinal state :"<<a<<endl;
    return 0;
}

【讨论】:

    【解决方案2】:

    你的方法有很多问题。

    主要的一点是,由于这不是一个简单的两人最小最大,你不能简单地递归返回一个分数。对于给定玩家的每次游戏,您必须返回每个玩家将获得的分数。比如:

    无效分数(int currentPlayer,int toWrite,int outScores[],int nbOut)

    将返回 outScores 中所有玩家的得分,如果 currentPlayer 要写一个或两个数字,从 toWrite,用 nbOut 玩家已经出局了。

    不过,这并不是最优的。递归地尝试 10 名玩家的所有解决方案意味着要降低 50 多个级别,分支因子为 2。您需要一些动态编程来提高效率。

    我确实编写了代码。它适用于 4 名玩家,最大值为 10。对于更多的玩家来说,它变得非常慢。正如我所说,动态编程是您正在寻找的,在这里。

    另外,我假设一个玩家宁愿只玩一次,如果它仍然会以相同的顺序输掉。

    #include <memory.h>
    #include <vector>
    #include <iostream>
    using namespace std;
    
    const int NB_PLAYERS = 3;
    const int MAX_VALUE = 10;
    
    vector<pair<int, int>> scores(int currentPlayer, int toWrite, int outScores[], int nbOut)
    {
        if (outScores[currentPlayer] == -1) // we're already out, lets just have the next player play
        {
            return scores((currentPlayer + 1) % NB_PLAYERS, toWrite, outScores, nbOut);
        }
    
        if (toWrite == MAX_VALUE) // one player is out
        {
            vector<pair<int, int>> ret;
            if (nbOut + 2 != NB_PLAYERS)
            {
                outScores[currentPlayer] = -1; // so the game can continue without us
                ret = scores((currentPlayer + 1) % NB_PLAYERS, 1, outScores, nbOut + 1);
            }
            else
            {
                for(int i=0; i < NB_PLAYERS; i++)
                {
                    if (outScores[i] == 0)
                    {
                        outScores[i] = NB_PLAYERS; // set the winner
                    }
                }
            }
            outScores[currentPlayer] = nbOut + 1;
            ret.push_back(pair<int, int>(1,currentPlayer));
            return ret;
        }
    
        if (toWrite + 1 == MAX_VALUE) // no point playing to lose, if we can avoid it
        {
            vector<pair<int, int>> ret;
            ret = scores((currentPlayer + 1) % NB_PLAYERS, toWrite + 1, outScores, nbOut);
            ret.push_back(pair<int,int>(1,currentPlayer));
            return ret;
        }
    
        int scoresIfPlay1[NB_PLAYERS];
        int scoresIfPlay2[NB_PLAYERS];
        vector<pair<int,int>> ret1;
        vector<pair<int,int>> ret2;
    
        memcpy(scoresIfPlay1, outScores, sizeof(int) * NB_PLAYERS);
        memcpy(scoresIfPlay2, outScores, sizeof(int) * NB_PLAYERS);
    
        ret1 = scores((currentPlayer + 1) % NB_PLAYERS, toWrite + 1, scoresIfPlay1, nbOut); //recurse with both choices
        ret2 = scores((currentPlayer + 1) % NB_PLAYERS, toWrite + 2, scoresIfPlay2, nbOut);
    
        if (scoresIfPlay2[currentPlayer] > scoresIfPlay1[currentPlayer]) // pick the solution that yields the higher score
        {
            memcpy(outScores, scoresIfPlay2, sizeof(int) * NB_PLAYERS);
            ret2.push_back(pair<int,int>(2,currentPlayer));
            return ret2;
        }
        else
        {
            memcpy(outScores, scoresIfPlay1, sizeof(int) * NB_PLAYERS);
            ret1.push_back(pair<int,int>(1,currentPlayer));
            return ret1;
        }
    }
    
    int main()
    {
        int outScores[NB_PLAYERS] = {0};
    
        vector<pair<int, int>> plays = scores(0, 1, outScores, 0);
    
        for(int i = 0; i < NB_PLAYERS; i++)
        {
            cout << "Player " << i << " has score " << outScores[i] << endl;
        }
        cout << endl;
    
        cout << "Plays were: " << endl;
        for(int i = plays.size() - 1; i >= 0; --i)
        {
            cout << "player " << plays[i].second << " plays " << plays[i].first << " times." << endl;
        }
    
    }
    

    === 旧答案 ===

    如前所述,这个问题无法让您判断自己会赢还是输。为此,您必须以一种或另一种方式更改规则:由于将有 9 个失败者和 1 个获胜者,因此任何未获胜的玩家都可以改变他们的游戏方式,并且在他们仍然失败时影响谁获胜。例如。玩家 x 输了任何一种方式,但可以使玩家 y 赢或输。 x 将如何播放尚未定义。

    你需要选择一个场景:

    • 每个玩家都会尝试尽快消灭其他玩家,只要不让他们输。
    • 或者,稍后被淘汰会产生更多积分。玩家将最大化他们的分数。

    无论如何,解决方案应该比上面的更复杂。你可能想要递归

    int scoreFromPosition(int player,int no_of_players,int counter)

    这将返回当 no_of_player 个玩家离开时玩家 player 可以获得的最大分数,counter 当前是 counter

    【讨论】:

    • 我假设所有玩家都玩得很完美。对于给定的场景,这还不足以确定您是否有可能获胜吗?
    • 在某些情况下绝对不是。考虑一个简单的 3 人游戏,其中 A 无法获胜。他可以玩 x 或 y,但无论哪种方式都会输。如果 A 下 x,B 将获胜。如果 A 下 y,C 将获胜。所以,A 的玩法是不确定的。现在想象另一个游戏,有 4 名玩家,导致上述情况。 D 将无法预测 A 的行为,即使是通过“完美游戏”假设。
    • 好的,但我的算法即使对于 2 人游戏也不起作用。我知道一个事实,第二名的人总是能赢。能否请您找出错误。我可以放一个赏金(50),因为我反正不经常使用该网站(SO)。
    • 编写了一个递归但效率低下的 C++ 解决方案。值得一些赏金吗? :-)
    猜你喜欢
    • 1970-01-01
    • 2015-06-14
    • 1970-01-01
    • 1970-01-01
    • 2013-12-16
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 2013-03-31
    相关资源
    最近更新 更多