/// <summary> /// Generates a random, unpredictable move. /// </summary> /// <returns>Returns a move structure with defined X, Y coordinates.</returns> private Move GenerateMediumDifficultyMove() { bool isValidMoveFound = false; Move mediumDifficultyMove = new Move(); while (!isValidMoveFound) { // Generate random coordinates mediumDifficultyMove.X = RandomGenerator.Next(0, Tictactoe.GetBoardHorizontalSize()); mediumDifficultyMove.Y = RandomGenerator.Next(0, Tictactoe.GetBoardVerticalSize()); // Check if the field is empty if (IsBoardFieldEmpty(Board, mediumDifficultyMove.X, mediumDifficultyMove.Y)) { isValidMoveFound = true; } } return(mediumDifficultyMove); }
/// <summary> /// Generates the best move available using the negamax algorithm. /// </summary> /// <param name="board">Board on which to do the calculations.</param> /// <param name="playerMark">Mark of the player who is calling the function.</param> /// <returns>An optimal move for the given board and player.</returns> /// Can be upgraded with alpha-beta pruning to increase performance. private Move GenerateImpossibleDifficultyMove(string[,] board, string playerMark) { // Set the oponent players mark string oponentPlayerMark = (playerMark == Resources.BoardCrossMark) ? Resources.BoardCircleMark : Resources.BoardCrossMark; // Check if the player calling the function has won if (Tictactoe.HasPlayerWon(board, playerMark)) { return(new Move() { Value = 1 }); } // Check if the player calling the function has lost if (Tictactoe.HasPlayerWon(board, oponentPlayerMark)) { return(new Move() { Value = -1 }); } // Check for a draw if (Tictactoe.HavePlayersDraw(board)) { return(new Move() { Value = 0 }); } // Set initial bestMove value to -2 because thats lower than the lowest possible value you can generate for a valid move from this implementation (which is -1) var bestMove = new Move() { Value = -2 }; // Loop through the board for (int i = 0; i < Tictactoe.GetBoardHorizontalSize(); i++) { for (int j = 0; j < Tictactoe.GetBoardVerticalSize(); j++) { if (IsBoardFieldEmpty(board, i, j)) { // Mark the board field board[i, j] = playerMark; // Assign a new value to the opposite of a recursive function call int value = -GenerateImpossibleDifficultyMove(board, oponentPlayerMark).Value; // Store move information if it has better value than the current best move if (value > bestMove.Value) { bestMove.Value = value; bestMove.X = i; bestMove.Y = j; } // Change the board field back to empty board[i, j] = Resources.BoardEmptyField; } } } return(bestMove); }