Tic Tac Toe递归算法

时间:2012-01-16 12:29:42

标签: c# recursion tic-tac-toe

我可以看到这个问题(或者类似的问题)已被问过几次,我已经搜索了很多谷歌,所以我可以尝试理解它,但我肯定会卡住。

我的任务是使用一个递归函数,它使用“goodness”变量来确定哪个是计算机可以做出的最佳移动,我甚至有一个文档可以帮助解决这个问题,但对于我的生活,我只是不明白。

如果有人可能需要一些时间来帮助我或打破我实际需要做的事情,我会非常感激,我会链接到目前为止我的代码,但这是一项任务,所以指导比直接更好答案。我看过MinMax解决方案,这看起来肯定在我的掌握之上,我对编程非常陌生(特别是在C#只有几个月的经验)所以就这么简单!

以下是我想要遵循的建议解决方案:

http://erwnerve.tripod.com/prog/recursion/tictctoe.htm

public partial class Form1 : Form
{
    public static string[,] Board = new string[3, 3] { { "1", "2", "3" }, { "4", "5", "6" }, { "7", "8", "9" } };
    public bool Winner = false;
    public string WinState;

    private void Reset()
    {
        WinState = "";
        Winner = false;
        Board[0, 0] = "1";
        Board[0, 1] = "2";
        Board[0, 2] = "3";
        Board[1, 0] = "4";
        Board[1, 1] = "5";
        Board[1, 2] = "6";
        Board[2, 0] = "7";
        Board[2, 1] = "8";
        Board[2, 2] = "9";
        btn1.Text = "";
        btn2.Text = "";
        btn3.Text = "";
        btn4.Text = "";
        btn5.Text = "";
        btn6.Text = "";
        btn7.Text = "";
        btn8.Text = "";
        btn9.Text = "";
    }

    private void checkWinner()
    {
        // Top Row
        if (Board[0, 0].Equals(Board[0, 1]) && Board[0, 1].Equals(Board[0, 2]))
        {
            Winner = true;
            WinState = Board[0, 0];
        }
        // Middle Row
        if (Board[1, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[1, 2]))
        {
            Winner = true;
            WinState = Board[1, 0];
        }
        // Bottom Row
        if (Board[2, 0].Equals(Board[2, 1]) && Board[2, 1].Equals(Board[2, 2]))
        {
            Winner = true;
            WinState = Board[2, 0];
        }
        // Left column
        if (Board[0, 0].Equals(Board[1, 0]) && Board[1, 0].Equals(Board[2, 0]))
        {
            Winner = true;
            WinState = Board[0, 0];
        }
        // Middle column
        if (Board[0, 1].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[2, 1]))
        {
            Winner = true;
            WinState = Board[0, 1];
        }
        // Right column
        if (Board[0, 2].Equals(Board[1, 2]) && Board[1, 2].Equals(Board[2, 2]))
        {
            Winner = true;
            WinState = Board[0, 2];
        }
        // Diagonal 1
        if (Board[0, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[2, 2]))
        {
            Winner = true;
            WinState = Board[0, 0];
        }
        // Diagonal 2
        if (Board[2, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[0, 2]))
        {
            Winner = true;
            WinState = Board[2, 0];
        }

        if (Winner == true)
        {
            if (WinState == "X")
            {
                MessageBox.Show("Congratulations you win!");
                Reset();
            }
            else if (WinState == "O")
            {
                MessageBox.Show("Sorry you lose!");
                Reset();
            }
        }
    }

    private void btn1_Click(object sender, EventArgs e)
    {
        btn1.Text = "X";
        Board[0, 0] = "X";
        checkWinner();
    }

    private void btn2_Click(object sender, EventArgs e)
    {
        btn2.Text = "X";
        Board[0, 1] = "X";
        checkWinner();
    }

    private void btn3_Click(object sender, EventArgs e)
    {
        btn3.Text = "X";
        Board[0, 2] = "X";
        checkWinner();
    }

    private void btn4_Click(object sender, EventArgs e)
    {
        btn4.Text = "X";
        Board[1, 0] = "X";
        checkWinner();
    }

    private void btn5_Click(object sender, EventArgs e)
    {
        btn5.Text = "X";
        Board[1, 1] = "X";
        checkWinner();
    }

    private void btn6_Click(object sender, EventArgs e)
    {
        btn6.Text = "X";
        Board[1, 2] = "X";
        checkWinner();
    }

    private void btn7_Click(object sender, EventArgs e)
    {
        btn7.Text = "X";
        Board[2, 0] = "X";
        checkWinner();
    }

    private void btn8_Click(object sender, EventArgs e)
    {
        btn8.Text = "X";
        Board[2, 1] = "X";
        checkWinner();
    }

    private void btn9_Click(object sender, EventArgs e)
    {
        btn9.Text = "X";
        Board[2, 2] = "X";
        checkWinner();
    }
}

1 个答案:

答案 0 :(得分:11)

不要因为阅读该文档而不理解递归而感觉太糟糕。它在解释递归方面做得不是很好。 (这是一个艰难的概念 - 我可能也不会做得那么好)。最终,你要做的就是尽量让你的程序做你想做的事。我将尝试从这个角度解释它。

递归很有用,因为它允许我们在解决方案中编码(一次)一个步骤,然后使用刚刚计算的结果作为输入重复该步骤。尝试从你的角度看你的问题,而不是一些任意的善意算法。你可能很难理解论文中的算法。

尝试这样思考:在游戏开始时玩家1进行游戏。你的程序必须为玩家2选择一个移动。但玩家2必须考虑游戏的其余部分(每次可能的移动)。

  1. 玩家2可以从8种可能的动作中进行选择。
  2. 玩家1可以选择7
  3. 玩家2可以选择6
  4. 玩家1可以选择5
  5. 玩家2可以选择4
  6. 玩家1可以选择3
  7. 玩家2可以选择2
  8. 玩家1占据最后一个方格。
  9. 你可以将其改为:
    当前玩家为2,为当前玩家的所有剩余选择赋予权重 当前玩家为1,为当前玩家的所有剩余选择赋予权重 当前玩家为2,为当前玩家的所有剩余选择赋予权重 当前玩家为1,为当前玩家的所有剩余选择赋予权重 当前玩家为2,为当前玩家的所有剩余选择赋予权重 当前玩家为1,为当前玩家的所有剩余选择赋予权重 当前玩家为2,为当前玩家的所有剩余选择赋予权重 当前玩家为1,为当前玩家的所有可能剩余选项赋予权重

    您可以将此改为: 给予当前的球员, 切换播放器和为当前播放器的所有可能选择赋予权重 重复直到不再有可能的选择

    您可以将此改为: 给予当前的球员, 切换播放器和CheckGoodness() 重复直到不再有可能的选择

    所以,回到你的写作。作者使用1和1的玩家。 -1。为什么?因为当你越来越深地传递动作时,你必须交换玩家,并且当你下降这样的等级时,很容易切换玩家(我只是在这里谈论玩家:

    public int CheckGoodness(bool playerID)
    {
        playerID = -playerID;
        if (!endConditionMet)
        {
            goodness = CheckGoodness(playerID);
        }
        return goodness;
    }
    

    与玩家一起,你必须传递代表剩余所有可能移动状态的东西。问题是,如果您传递的内容作为参考传递,您所做的任何更改都将反映在原始数据中。确保没有发生。这可能就是@CodeInChaos建议你克隆的原因。

    请注意,在递归中,您必须确保始终有办法结束调用序列。您必须修改您所依赖的最终条件。在这种情况下,您的可能移动数量正在下降。否则你永远打电话,内存不足。

    编辑:已添加的董事会类别说明。

    从大局考虑。类是现实世界事物(例如对象)的表示。一件事具有描述它的属性。这些是班级的数据。事也行动。这些是方法。我听过的一个类的另一个定义是类是对该数据的数据和操作。

    想想游戏中有哪些对象。 2名球员和一名董事会。没什么别的。

    玩家可以移动,并且具有唯一的标识符(在这种情况下,可以是' X' O' O'),可以是人或AI。我现在无法想到任何其他事情(重要的),但通常会有更多的事情可以存在但不会影响程序(如眼睛的颜色)。这也是您可以使用继承的地方。你有一个带有抽象移动方法的玩家类。从播放器继承的人类,使用覆盖移动方法接受来自UI的输入,从播放器继承的计算机/ AI类,并通过计算良好评级的移动来覆盖移动方法。

    董事会有数据:

    • 一个3乘3网格的可能游戏位置(顺便说一下,这也可能是位置对象的集合)
      • 可能需要代表玩家1& 2

    董事会的行动可能是:

    • 接受玩家的(人或AI)移动,如果有效则记录,确定胜利&返回一个好动,坏动,游戏结束或胜利的指标
    • 可以有方法返回当前游戏的获胜者
    • 可能需要重置方法
    • 可以有移动历史

    你可以有一个静态的GoodNess类(可能需要一个更好的名字) 没有数据,只有一个方法(或者这可能是董事会成员的另一种方法:

    • 接受董事会,计算&返回善良数组,或简单地返回最佳移动

    AI可以在移动之前调用Goodness GetBestMove方法 递归将与GetBestMove方法隔离。

    请注意,这些都不是一成不变的。类是由您认为应该在其中定义的。这完全取决于你如何成为解决问题的最佳方法。如果您仍然遇到问题,请使用您尝试过的代码更新您的问题。在开始布置代码时,绘制图表确实很有帮助。

    祝你好运,希望这会有所帮助,并且我会尝试更好地监控StackOverflow通知。