// Make an automatic move private void DoAutoPlay() { // Player to play IPlayer player = Turn == CellState.X ? PlayerX : PlayerO; // Log, to be optionally filled by the AI players string log = ""; // Keep start time DateTime startTime = DateTime.Now; // Ask player for a move Pos pos = player.Play(gameBoard, ref log); // Print log, including how much time it took Debug.LogFormat("{0} took {1} ms\n{2}", player.GetType().Name, (DateTime.Now - startTime).TotalMilliseconds, log); // Is this an invalid move? if (gameBoard.GetStateAt(pos) != CellState.Undecided) { overrideWinner = Turn.Other(); return; } // Perform the actual move Move(pos); }
// Get board score for the specified player, ignoring the other player private float ScoreFor(Board board, CellState player) { // Current score float score = 0; // Search all corridors in the board foreach (Pos[] corridor in Board.winCorridors) { // By default we assume a line is available bool lineAvailable = true; // Cycle through all the positions in the current corridor foreach (Pos position in corridor) { // Check if there's an opponent piece at this position if (board.GetStateAt(position) == player.Other()) { // If so, it means there's no line available here, so // let's take a note of that and skip the rest of the line lineAvailable = false; break; } } // Was the previous line available for our player? if (lineAvailable) { // If so, increment score score++; } } // Return the score return(score); }
public Pos Play(Board gameBoard, ref string log) { // Populate a list with available board positions IList <Pos> emptyPositions = new List <Pos>(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { Pos pos = new Pos(i, j); if (gameBoard.GetStateAt(pos) == CellState.Undecided) { emptyPositions.Add(pos); } } } // Return a random empty board position return(emptyPositions[random.Next(emptyPositions.Count)]); }
// Helper method which obtains valid moves for the given board private static IList <Pos> ValidMovesFromBoard(Board board) { // List of valid moves List <Pos> validMoves = new List <Pos>(); // Search for valid moves for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { // Is current position a valid move? Pos move = new Pos(i, j); if (board.GetStateAt(move) == CellState.Undecided) { // If so, add it to list of valid moves validMoves.Add(move); } } } return(validMoves); }
// Process given board with ABNegamax private (float score, Pos move) Negamax(Board board, int depth) { // Increment number of evaluations (recursive ABNegamax calls) numEvals++; // Check what's the status of this board if (board.Status().HasValue) { // GAME OVER! Who won? if (board.Status().Value == CellState.Undecided) { // It's a tie, return 0 return(0, Board.NoMove); } else if (board.Status().Value == board.Turn) { // Current player wins, return max heuristic value return(heuristic.WinScore, Board.NoMove); } else { // The other player won, return min heuristic value return(-heuristic.WinScore, Board.NoMove); } } else if (depth == maxDepth) { // We reached the max depth, return the heuristic value for this // board return(heuristic.Evaluate(board, board.Turn), Board.NoMove); } else { // Game is not over and we haven't reached maximum depth, so let's // recursively call Negamax on all possible moves // Declare best move, which for now is no move at all (float score, Pos move)bestMove = (float.NegativeInfinity, Board.NoMove); // Try to play on all board positions for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { // Get the current board position Pos pos = new Pos(i, j); // Only consider making a move at this position if it's // not already occupied if (board.GetStateAt(pos) == CellState.Undecided) { // Score for current move float score; // Make a virtual move at this position board.DoMove(pos); // Get score for this move score = -Negamax(board, depth + 1).score; // Undo the move we just evaluated board.UndoMove(); // Is this the best move so far? if (score > bestMove.score) { // If so, keep it bestMove = (score, pos); } } } } // Return the best move found among all the tested moves return(bestMove); } }