我已经在网上搜索了伪代码、工作示例、堆栈溢出的问题/解决方案等,但到目前为止还没有找到任何有帮助的内容,所以我在这里寻求帮助。
我试图以一种非常基本的方式(没有alpha/beta剪枝)在我的井字游戏中实现极小化极大算法。让我先解释一下我有的每个辅助方法,然后如果需要,我可以分享我实际代码的部分。注意(我在检查终止状态时不使用深度,只检查胜利或平局)。
首先,我有一个名为MakeBestMove的实用程序,它会在电脑移动时被调用,然后在MakeBestMove中会调用MiniMax,MiniMax会一直调用自己直到boardState达到终止状态或没有剩余移动为止。
我希望MakeBestMove返回最佳移动,我的MiniMax函数返回boardState的分数。
如果你熟悉MiniMax,当状态是终止状态时,状态会被评分。我使用-100表示玩家O获胜,100表示玩家X获胜,0表示平局来评分我的游戏。这是在我的EvaluateScore辅助函数中完成的。我还有一个函数会根据游戏板的当前状态确定可用的移动,以帮助我遍历开放的移动。另外,值得注意的是,玩家“X”总是人类,而玩家“O”总是电脑。
我试图使用我在Python中实现MiniMax的方法来实现F#中的MiniMax,理论上它应该能工作,尽管它不忠于F#的函数式范式。
不知为何,它的行为与我预期的不一样。我花了几个小时检查每一行代码,确保没有奇怪的副作用,但没有成功。如果有人能指导我正确的方向,我将不胜感激。我知道我写的代码非常命令式,但我认为这样也可以工作,但我无法弄清楚为什么它不工作,以及我可能遗漏了什么。任何帮助都将不胜感激!
代码行为问题:1. MakeBestMove不能正确返回bestMove 2. 虽然Minimax函数看起来通过递归迭代不同的终止boardStates表现正确,但MakeBestMove不会阻止“O”的移动,但会在一步之遥时采取获胜的移动。
我希望我的代码表现如下:1. 能够正确评分boardState(我认为我已经在做了)2. 如果boardState是终止状态,或者没有更多的移动可以采取时,返回该分数(我正在做)3. 使用该分数来做出最佳移动选择(这是问题所在)
编辑 我想添加一个对我的程序中调用的“走查”,以防你想在Visual Studio中运行我的代码并测试它。
我的代码从Program.fs开始,当它调用ComputerPlayerTurn()
时,它会使用Game.fs,其中ComputerPlayerTurn()
被调用后会初始化变量let mutable computerMove
为MakeBestMove(board)
。在MakeBestMove(board)
被调用后,在函数内部会调用MiniMax(board) marker
。
以下代码是我遇到行为问题的地方:
let rec MiniMax(board) marker = let mutable bestValue = 0 let mutable board = board let mutable moves = GetListOfMoves(board) if GameWon(board) marker = true || GetListOfMoves(board).Count = 0 then bestValue <- EvaluateScore(board) else if marker = "X" then bestValue <- -100 for move in moves do board <- ModifyBoard(board) move marker bestValue <- max(MiniMax(board) "O")(bestValue) board <- ModifyBoard(board) move " " bestValue <- bestValue else bestValue <- 100 for move in moves do board <- ModifyBoard(board) move marker bestValue <- min(MiniMax(board) "X")(bestValue) board <- ModifyBoard(board) move " " bestValue <- bestValue bestValuelet MakeBestMove (board) marker = let mutable board = board let mutable bestMove = 0 let mutable bestValue = 0 let mutable bestMoves = ResizeArray<int>() let moves = GetListOfMoves(board) for move in moves do board <- ModifyBoard(board) move marker bestValue <- MiniMax(board) "X" board <- ModifyBoard(board) move " " if marker = "X" then if bestValue = 100 then bestMove <- move if bestValue > 0 then bestMoves.Add(move) else if bestValue = 0 then bestMoves.Add(move) elif marker = "O" then if bestValue = -100 then bestMove <- move if bestValue < 0 then bestMoves.Add(move) else if bestValue = 0 then bestMoves.Add(move) if bestMove = 0 && bestMoves.Count > 0 then bestMove <- bestMoves.[0] elif bestMove <> 0 then bestMove <- bestMove else bestMove <- GetListOfMoves(board).[0] bestMove
我的代码在Github上的我的MASTER分支的仓库中:https://github.com/sinnmk/fsharp-tictactoe
回答:
问题出在这里:
if ((GameWon(board) marker) = true || (moves.Count = 0)) then
它只考虑了平局和marker
获胜的游戏。它还应该考虑marker
输掉的游戏:
if GameWon board "O" || GameWon board "X" || moves.Count = 0 then
我删除了不必要的括号和条件,如= true
。