我创建了棋盘游戏 Santorini 的 2D 克隆以及 AI。 AI 使用带有 Alpha Beta 修剪的 Minimax 算法,我能够调整算法运行的深度。 AI 似乎工作正常,我能够“玩”并观看两个 AI 的相互对抗,深度达到 100,游戏运行得很好。
我遇到的问题发生在我尝试运行 AI 相互对战的“模拟”时。我在其中创建了一个新的 Unity 场景,其中所有 UI 从一些信息中删除了一部分,例如玩过的游戏,AI 赢得了什么等......游戏的核心功能没有被触及,但是每当我尝试运行在像 5 这样非常低的深度下进行模拟,团结是冻结的。我想也许如果我离开它足够长的时间它会回来,但似乎没有。我必须关闭并重新加载 Unity。
我不想一次运行多个游戏,模拟一次只运行一个游戏,我能够在随机 AI 上运行模拟就好了。所以我假设问题来自 Minimax 算法,但正如我之前提到的,它在我的其他“播放”场景中工作得很好,所以我不确定出了什么问题。
这是我的 minimax 算法的主要代码。
public (Builder, Square, Square) getAiMove(Board board, bool team)
{
//set best score to +/- inf depending on team
int bestScore = team ? int.MinValue : int.MaxValue;
playerSearching = team;
//generate all possible moves for team from current position
List<(Builder, Square)> moves = board.FindTeamMoves(team);
//create best move and builds (start as null)
(Builder, Square) bestMove = (null, null);
Square bestBuild = null;
//loop through every move generated, each returning a score, where the best score/move will be selected
foreach ((Builder,Square) move in moves)
{
// create copy of board and builder, copy move on new board
Board boardCopy = board.CopyBoard();
Builder builderCopy = boardCopy.squares[(int)move.Item1.BuilderSquare.Pos.x, (int)move.Item1.BuilderSquare.Pos.y].OccupiedBuilder;
Square moveCopy = boardCopy.squares[(int)move.Item2.Pos.x, (int)move.Item2.Pos.y];
int score = 0;
//make move on new board
boardCopy.MakeMove(builderCopy, moveCopy);
//find all possible builds from new position
List<Square> builds = boardCopy.FindBuilds(builderCopy.BuilderSquare.Pos);
//loop through all possible builds
foreach (Square build in builds)
{
// create another copy of board to try every possible build
Board boardCopyTwo = boardCopy.CopyBoard();
Square buildCopy = boardCopyTwo.squares[(int)build.Pos.x, (int)build.Pos.y];
//make build on new board
boardCopyTwo.Build(buildCopy);
//call minimaxAB to generate a score
score = Minimax(boardCopyTwo, maxDepth, !team);
//if minimax returns a score better than current, replace best score
//black team is aiming for a score as high as possible while white team is aimijng for a score as low as possible
if ((team && score > bestScore) || (!team && score < bestScore))
{
//update score, move and build
bestScore = score;
bestMove = move;
bestBuild = board.squares[(int)build.Pos.x, (int)build.Pos.y];
}
}
}
//format move and build to be returned
(Builder, Square, Square) AiTurn = (bestMove.Item1, bestMove.Item2, bestBuild);
return AiTurn;
}
//Minimax algorithm with Alpha Beta Pruning enabled
public int Minimax(Board board, int depth, bool maximizingPlayer)
{
//? WHITE = MIN PLAYER +inf
//? BLACK = MAX PLAYER -inf
bool checkWinVal = board.CheckWin(!maximizingPlayer);
bool checkLossVal = board.CheckLoss(maximizingPlayer);
if (checkWinVal || checkLossVal || depth == 0)
{
//generate score for given board position and return
if (aiHeuristic == 1)
{
return board.BasicEvaluation(maximizingPlayer, checkLossVal, depth);
}
else if (aiHeuristic == 2)
{
return board.evaluateBoard(maximizingPlayer, checkLossVal);
}
}
if (maximizingPlayer) //MAX PLAYER (BLACK) TURN
{
//set best move to negative inf
int maxScore = int.MinValue;
//Generate every possible move for black team
List<(Builder, Square)> moves = board.FindTeamMoves(true);
// Check every possible move at this depth
foreach ((Builder, Square) move in moves)
{
Board boardCopy = board.CopyBoard();
Builder builderCopy = boardCopy.squares[(int)move.Item1.BuilderSquare.Pos.x, (int)move.Item1.BuilderSquare.Pos.y].OccupiedBuilder;
Square moveCopy = boardCopy.squares[(int)move.Item2.Pos.x, (int)move.Item2.Pos.y];
boardCopy.MakeMove(builderCopy, moveCopy);
List<Square> builds = boardCopy.FindBuilds(builderCopy.BuilderSquare.Pos);
foreach (Square build in builds)
{
Board boardCopyTwo = boardCopy.CopyBoard();
Square buildCopy = boardCopyTwo.squares[(int)build.Pos.x, (int)build.Pos.y];
boardCopyTwo.Build(buildCopy);
//recursively call Minimax (flip flopping between players until max depth reached)
int score = Minimax(boardCopyTwo, depth - 1, false);
//get max score
maxScore = Math.Max(maxScore, score);
}
}
return maxScore;
}
else //MIN PLAYER (WHITE) TURN
{
// set best move to pos inf
int minScore = int.MaxValue;
//Generate every possible move for white team
List<(Builder, Square)> moves = board.FindTeamMoves(false);
// Check every possible move at this depth
foreach ((Builder, Square) move in moves)
{
Board boardCopy = board.CopyBoard();
Builder builderCopy = boardCopy.squares[(int)move.Item1.BuilderSquare.Pos.x, (int)move.Item1.BuilderSquare.Pos.y].OccupiedBuilder;
Square moveCopy = boardCopy.squares[(int)move.Item2.Pos.x, (int)move.Item2.Pos.y];
boardCopy.MakeMove(builderCopy, moveCopy);
List<Square> builds = boardCopy.FindBuilds(builderCopy.BuilderSquare.Pos);
foreach (Square build in builds)
{
Board boardCopyTwo = boardCopy.CopyBoard();
Square buildCopy = boardCopyTwo.squares[(int)build.Pos.x, (int)build.Pos.y];
boardCopyTwo.Build(buildCopy);
int score = Minimax(boardCopyTwo, depth - 1, true);
minScore = Math.Min(minScore, score);
}
}
return minScore;
}
}
还包括指向其他游戏文件的链接:
https://pastebin.com/qFfZt1NA -- 游戏管理器(核心游戏功能)
https://pastebin.com/MJ4kWAR6 -- 极小极大算法
https://pastebin.com/VE8k48iU -- SimulationUI(模拟场景)
https://pastebin.com/9Um1QdN7 -- boardUI(游戏场景)
任何帮助解决这个问题的人都将不胜感激!