/// <summary> /// Calculate the best position for game AI to move according to current chessboard state. /// </summary> /// <param name="currentState">Current state of the chessboard.</param> /// <param name="playerMark">Chess piece mark of the player to move.</param> /// <param name="rowIndex">Row index of the best position to move calculated by Min-Max-Pruning.</param> /// <param name="columnIndex">Column index of the best position to move calculated by Min-Max-Pruning.</param> /// <remarks> /// The "player" here doesn't mean the human player of this game, it only means either AI or the player of this game. /// </remarks> public static void GetBestPosition(Chessboard currentState, ChessPiece playerMark, out int rowIndex, out int columnIndex) { // "out" parameter must be assigned. rowIndex = 0; columnIndex = 0; // Get the opponent's mark. ChessPiece opponentMark = playerMark == ChessPiece.X ? ChessPiece.O : ChessPiece.X; // Initialize the "max" to int.MinValue. int max = int.MinValue; // Iterate through every blank cell for current player. for (int playerRowIndex = 0; playerRowIndex < Chessboard.Size; playerRowIndex++) { for (int playerColumnIndex = 0; playerColumnIndex < Chessboard.Size; playerColumnIndex++) { if (currentState[playerRowIndex, playerColumnIndex] == ChessPiece.Blank) { // Make a deep copy of current state and put the player mark in the blank cell of the deep copy. Chessboard nextStep = currentState.Copy(); nextStep.AddChessPiece(playerRowIndex, playerColumnIndex, playerMark); // Initialize the "min" to int.MaxValue. int min = int.MaxValue; // Iterate through every blank cell for current player's opponent. for (int opponentRowIndex = 0; opponentRowIndex < Chessboard.Size; opponentRowIndex++) { for (int opponentColumnIndex = 0; opponentColumnIndex < Chessboard.Size; opponentColumnIndex++) { if (nextStep[opponentRowIndex, opponentColumnIndex] == ChessPiece.Blank) { // Make a deep copy of current state and put the opponent's mark in the blank cell of the deep copy. Chessboard nextNextStep = nextStep.Copy(); nextNextStep.AddChessPiece(opponentRowIndex, opponentColumnIndex, opponentMark); // Evaluate the state according to the player score and opponent score. int stateScore = PlayerScore(nextNextStep, playerMark) - OpponentScore(nextNextStep, opponentMark); // Keep tracking the min value. if (stateScore < min) { min = stateScore; } } } } // If min > max, then current (playerRowIndex, playerColumnIndex) is better than any other cell searched before. if (min > max) { max = min; rowIndex = playerRowIndex; columnIndex = playerColumnIndex; } } } } }
/// <summary> /// Returns a deep copy of the input <see cref="Chessboard" /> will all blank cells are filled with the specified mark. /// </summary> /// <param name="chessboard">The input (original) <see cref="Chessboard" />.</param> /// <param name="mark">The chess piece mark to fill the blank cells.</param> /// <returns>A deep copy of the input <see cref="Chessboard" /> will all blank cells are filled with the specified mark.</returns> private static Chessboard FillChessboard(Chessboard chessboard, ChessPiece mark) { Chessboard filledChessboard = chessboard.Copy(); for (int i = 0; i < Chessboard.Size; i++) { for (int j = 0; j < Chessboard.Size; j++) { if (filledChessboard[i, j] == ChessPiece.Blank) { filledChessboard.AddChessPiece(i, j, mark); } } } return(filledChessboard); }