一点背景:作为一种在C ++中学习多节点树的方法,我决定生成所有可能的TicTacToe板并将它们存储在树中,使得从节点开始的分支都是可以从该节点跟随的板,并且节点的子节点是一次移动的板。在那之后,我认为使用该树作为决策树来编写一个AI来玩TicTacToe会很有趣。
TTT是一个可以解决的问题,一个完美的玩家永远不会丢失,因此我第一次尝试AI时编码似乎很简单。现在,当我第一次实现AI时,我回过头来为每个节点添加了两个字段:X将赢得的次数& O将在该节点下的所有孩子中获胜的次数。我认为最好的解决方案就是让我的每一步动作都选择AI,然后选择最能赢得次数的子树。然后我发现虽然它在大部分时间都是完美的,但我找到了可以击败它的方法。这对我的代码来说不是问题,只是我让AI选择它的路径的问题。
然后我决定让它选择具有计算机最大胜利或人类最大损失的树,以较多者为准。这使它表现更好,但仍然不完美。我仍然可以击败它。
所以我有两个想法,我希望得到更好的输入:
1)而不是最大化赢或输,而是我可以为胜利分配1,为平局分配0,为亏损分配-1。然后选择具有最高值的树将是最佳移动,因为下一个节点不能是导致丢失的移动。这是电路板生成中的一个简单变化,但它保留了相同的搜索空间和内存使用量。还是......
2)在棋盘生成期间,如果有一个棋盘让X或O在下一步中获胜,则只会产生阻止该胜利的孩子。不会考虑其他子节点,然后生成将在此之后正常进行。它缩小了树的大小,但后来我必须实现一个算法来确定是否有一个移动获胜,我认为这只能在线性时间内完成(我认为让板子生成慢很多?)
哪个更好,还是有更好的解决方案?
答案 0 :(得分:14)
基于决策树实施AI的(通常)正确方法是使用“ Minimax ”算法:
按照自己的方式向上运行,将以下规则应用于每个节点:
当然,偶数和奇数可能需要反转,具体取决于你决定谁先行。
您可以在以下网址阅读更多内容:
答案 1 :(得分:8)
您现有的算法很好,除非您忘记了一件事。永远不要选择任何其他玩家移动导致你无法至少打成平手的路径。
所以基本上,丢弃玩家下次移动的任何分支都可能导致无法绑定的情况,然后运行现有的算法。这导致赢得对抗非完美对手的最高机会,同时消除失败的可能性。
答案 2 :(得分:4)
Tic-Tac-Toe可以使用greedy algorithm来解决,并不需要决策树。
如果您想继续使用当前算法,请按照顾客的意愿行事,并尽量减少每次决定失败的可能性。
如果您想要一种更简单的方法,AI每回合都会执行以下操作:
为每个方块的合意性评分,对于彼此在一条线上取正方形(由AI),为该方块添加一个合意点。对于对手占据的每个方格,删除一个可取的点。
例如,如果董事会目前是:
_|O|X
_|X|_
O| |
左上角的满意度为0(同一行中X为1,对角线为X为1,而每个Os为-1)。
在最理想的广场上玩。任意打破关系。
在上面的示例中,AI会选择右中方,因为它的合意性为2,这将导致下一轮获胜。
如果游戏刚刚开始,请播放中心广场,如果拍摄中心广场,请随意选择一个角落。
这是我的10年级Visual Basic学期项目。与存储决策树相比,它不可能击败并且需要的内存要少得多。
答案 3 :(得分:0)
执行此操作的“天真”方式(对于任意游戏,其中两个玩家轮流进行移动)是递归尝试每个可能的移动,直到你最终获得一个赢家的板,然后向上追溯在树中将节点标记为“O wins”,“X wins”或“draw”。
每次你加强(一个这样的步骤通常被称为一层),取决于谁的移动,假设玩家选择最适合他/她的移动。由于您正在从叶子向上移动,因此您将始终知道每个子节点的最佳可能结果。
当计算子树中可能的输赢板的数字时,你基本上假设每个玩家总是随机移动。正如您所指出的,如果您与智能播放器对战,这将不会非常有效。我上面概述的方案反而假设对手总是做出一个完美的举动,试图获胜。