使用四行(connect4)游戏实现和使用MinMax

我正在尝试连续四个(或connect4或连接四个)游戏实现MinMax算法。

我想我已经明白了它,它应该建立一个可能的板到一定深度的树,评估它们并返回它们的分数,然后我们只取这些分数的最大值。

因此, aiChooseCol()通过调用MinMax() aiChooseCol()检查每个可能列的分数,并返回具有最大分数的列。

现在我不确定,这是调用MinMax()的正确方法吗?

检查temp = Math.Max(temp, 1000);是否正确temp = Math.Max(temp, 1000);

我仍然没有制作启发式function,但这至少应该识别一个获胜列并选择它,但目前它只是从左边选择第一个免费列…我无法弄清楚我做错了什么。

 private int AiChooseCol() { int best = -1000; int col=0; for (int i = 0; i  best) { best = t; col = i; } m_Board.RemoveTopCoinFromCol(i); } } return col; } private int MinMax(int Depth, board Board, char PlayerSign) { int temp=0; if (Depth <= 0) { // return from heurisitic function return temp; } char otherPlayerSign = board.GetOtherPlayerSign(PlayerSign); char checkBoard = Board.CheckBoardForWin(); if (checkBoard == PlayerSign) { return 1000; } else if (checkBoard == otherPlayerSign) { return -1000; } else if (!Board.CheckIfBoardIsNotFull()) { return 0; // tie } if (PlayerSign == m_Sign) // maximizing Player is myself { temp = -1000; for (int i = 0; i < Board.Cols; i++) { if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column { var v = MinMax(Depth - 1, Board, otherPlayerSign); temp = Math.Max(temp, v); Board.RemoveTopCoinFromCol(i); } } } else { temp = 1000; for (int i = 0; i < Board.Cols; i++) { if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column { var v = MinMax(Depth - 1, Board, otherPlayerSign); temp = Math.Min(temp, v); Board.RemoveTopCoinFromCol(i); } } } return temp; } 

一些说明:

FillSignInBoardAccordingToCol()如果成功则返回一个布尔值。

board类型有一个char[,]数组,其中包含实际的电路板和播放器的标志。

此代码位于AI Player类中。

因此我决定编写自己的MinMax Connect 4.我使用深度来确定赢或输的价值,以便让您更接近获胜或阻止损失的举动优先。 我还决定如果不止一个具有相同的启发式,我会随机选择移动。 最后我将深度扩展到6,因为从一开始就需要多少次移动才能找到可能的胜利路径。

 private static void Main(string[] args) { var board = new Board(8,7); var random = new Random(); while (true) { Console.WriteLine("Pick a column 1 -8"); int move; if (!int.TryParse(Console.ReadLine(), out move) || move < 1 || move > 8) { Console.WriteLine("Must enter a number 1-8."); continue; } if (!board.DropCoin(1, move-1)) { Console.WriteLine("That column is full, pick another one"); continue; } if (board.Winner == 1) { Console.WriteLine(board); Console.WriteLine("You win!"); break; } if (board.IsFull) { Console.WriteLine(board); Console.WriteLine("Tie!"); break; } var moves = new List>(); for (int i = 0; i < board.Columns; i++) { if (!board.DropCoin(2, i)) continue; moves.Add(Tuple.Create(i, MinMax(6, board, false))); board.RemoveTopCoin(i); } int maxMoveScore = moves.Max(t => t.Item2); var bestMoves = moves.Where(t => t.Item2 == maxMoveScore).ToList(); board.DropCoin(2, bestMoves[random.Next(0,bestMoves.Count)].Item1); Console.WriteLine(board); if (board.Winner == 2) { Console.WriteLine("You lost!"); break; } if (board.IsFull) { Console.WriteLine("Tie!"); break; } } Console.WriteLine("DONE"); Console.ReadKey(); } private static int MinMax(int depth, Board board, bool maximizingPlayer) { if (depth <= 0) return 0; var winner = board.Winner; if (winner == 2) return depth; if (winner == 1) return -depth; if (board.IsFull) return 0; int bestValue = maximizingPlayer ? -1 : 1; for (int i = 0; i < board.Columns; i++) { if (!board.DropCoin(maximizingPlayer ? 2 : 1, i)) continue; int v = MinMax(depth - 1, board, !maximizingPlayer); bestValue = maximizingPlayer ? Math.Max(bestValue, v) : Math.Min(bestValue, v); board.RemoveTopCoin(i); } return bestValue; } public class Board { private readonly int?[,] _board; private int? _winner; private bool _changed; public Board(int cols, int rows) { Columns = cols; Rows = rows; _board = new int?[cols, rows]; } public int Columns { get; } public int Rows { get; } public bool ColumnFree(int column) { return !_board[column, 0].HasValue; } public bool DropCoin(int playerId, int column) { int row = 0; while (row < Rows && !_board[column,row].HasValue) { row++; } if (row == 0) return false; _board[column, row - 1] = playerId; _changed = true; return true; } public bool RemoveTopCoin(int column) { int row = 0; while (row < Rows && !_board[column, row].HasValue) { row++; } if (row == Rows) return false; _board[column, row] = null; _changed = true; return true; } public int? Winner { get { if (!_changed) return _winner; _changed = false; for (int i = 0; i < Columns; i++) { for (int j = 0; j < Rows; j++) { if (!_board[i, j].HasValue) continue; bool horizontal = i + 3 < Columns; bool vertical = j + 3 < Rows; if (!horizontal && !vertical) continue; bool forwardDiagonal = horizontal && vertical; bool backwardDiagonal = vertical && i - 3 >= 0; for (int k = 1; k < 4; k++) { horizontal = horizontal && _board[i, j] == _board[i + k, j]; vertical = vertical && _board[i, j] == _board[i, j + k]; forwardDiagonal = forwardDiagonal && _board[i, j] == _board[i + k, j + k]; backwardDiagonal = backwardDiagonal && _board[i, j] == _board[i - k, j + k]; if (!horizontal && !vertical && !forwardDiagonal && !backwardDiagonal) break; } if (horizontal || vertical || forwardDiagonal || backwardDiagonal) { _winner = _board[i, j]; return _winner; } } } _winner = null; return _winner; } } public bool IsFull { get { for (int i = 0; i < Columns; i++) { if (!_board[i, 0].HasValue) return false; } return true; } } public override string ToString() { var builder = new StringBuilder(); for (int j = 0; j < Rows; j++) { builder.Append('|'); for (int i = 0; i < Columns; i++) { builder.Append(_board[i, j].HasValue ? _board[i,j].Value.ToString() : " ").Append('|'); } builder.AppendLine(); } return builder.ToString(); } }