public Board Move(BoardSpace move) { if (PlayerToMove == Player.X) { MoveX(move); PlayerToMove = Player.O; } else { MoveO(move); PlayerToMove = Player.X; } LastMove = move; return(this); }
// ask user for next opponent move private static BoardSpace GetOpponentMove() { Console.WriteLine("Enter opponent move (row col):"); BoardSpace move = null; while (move == null) { try { move = new BoardSpace(Console.ReadLine()); } catch { Console.WriteLine("Error reading move, use the format (row col):"); } } return(move); }
private static IEnumerable <BoardSpace> GetSurroundingSpaces(BoardSpace space) { var higherRow = (byte)(space.Row + 1); var lowerRow = (byte)(space.Row - 1); var higherCol = (byte)(space.Col + 1); var lowerCol = (byte)(space.Col - 1); if (space.Row > 0) { yield return(new BoardSpace(lowerRow, space.Col)); if (space.Col > 0) { yield return(new BoardSpace(lowerRow, lowerCol)); yield return(new BoardSpace(space.Row, lowerCol)); } if (space.Col < 7) { yield return(new BoardSpace(lowerRow, higherCol)); yield return(new BoardSpace(space.Row, higherCol)); } } if (space.Row < 7) { yield return(new BoardSpace(higherRow, space.Col)); if (space.Col > 0) { yield return(new BoardSpace(higherRow, lowerCol)); } if (space.Col < 7) { yield return(new BoardSpace(higherRow, higherCol)); } } }
public BestMoveResultWithStats(int score, BoardSpace bestMove) { Move = bestMove; Score = score; NodesGeneratedByDepth = new Dictionary <int, int>(); }
public BestMoveResult(int score, BoardSpace bestMove) { Move = bestMove; Score = score; }
// recursive alpha beta private BestMoveResultWithStats BestMoveInternal(Board board, int depth, int alpha, int beta, CancellationToken cancelToken) { // if we reached the bottom, return if (depth == 0) { _numNodesAtDepthLimit++; return(new BestMoveResultWithStats(_config.Heuristic.Evaluate(board), null)); } var isMaxTurn = board.MyPlayer == board.PlayerToMove; var validMoves = board.GetValidMoves(); // if we hit game over before the depth limit, return infinity/-infinity if it's our/their turn if (!validMoves.Any()) { return(new BestMoveResultWithStats(isMaxTurn ? int.MinValue : int.MaxValue, null)); } BoardSpace bestMove = null; // generate new boards for each move and evaluate them so we can sort var validMovesWithBoard = validMoves.Select(x => { var newBoard = board.Copy().Move(x); _nodesGeneratedByDepth[depth]++; var score = _config.Heuristic.Evaluate(newBoard); return(new { move = x, newBoard, score }); }); // if we're maxing, sort with largest first, otherwise sort with smallest first if (isMaxTurn) { validMovesWithBoard = validMovesWithBoard.OrderByDescending(x => x.score); } else { validMovesWithBoard = validMovesWithBoard.OrderBy(x => x.score); } // evaluate this board because we'll need to for quiessence search var boardScore = _config.Heuristic.Evaluate(board); foreach (var move in validMovesWithBoard) { BestMoveResultWithStats childResult; // if we're doing a quiessence search, check to see if heuristic score change is interesting if (IsInterestingMove(boardScore, move.score)) { // extend search depth because this move looks interesting _numNodesQuiessenceSearched++; childResult = BestMoveInternal(move.newBoard, depth, alpha, beta, cancelToken); } else { // normal evaluation childResult = BestMoveInternal(move.newBoard, depth - 1, alpha, beta, cancelToken); } // if we're near timeout or asked to cancel, just bail :( if (_timer.Timeout() || cancelToken.IsCancellationRequested) { _timedOut = true; break; } if (isMaxTurn) // if it's a max turn, we want to check alpha { if (childResult.Score > alpha) { alpha = childResult.Score; bestMove = move.move; } } else // else it's a min turn, so we want to check beta { if (childResult.Score < beta) { beta = childResult.Score; bestMove = move.move; } } // alpha-beta trim if (alpha >= beta) { break; } } // if we didn't find anything good, just return the first one if (bestMove == null) { bestMove = validMoves.First(); } return(new BestMoveResultWithStats(isMaxTurn ? alpha : beta, bestMove)); }
public IEnumerable <BoardSpace> GetMoves(BoardSpace currentPosition) { #region vertical moves // walk down from currentPosition for (var i = currentPosition.Row + 1; i < 8; i++) { if (_board[i][currentPosition.Col] == BoardSpaceValue.Empty) { yield return(new BoardSpace((byte)i, currentPosition.Col)); } else { break; } } // walk up from currentPosition for (var i = currentPosition.Row - 1; i >= 0; i--) { if (_board[i][currentPosition.Col] == BoardSpaceValue.Empty) { yield return(new BoardSpace((byte)i, currentPosition.Col)); } else { break; } } #endregion #region horizontal moves // walk right from currentPosition for (var j = currentPosition.Col + 1; j < 8; j++) { if (_board[currentPosition.Row][j] == BoardSpaceValue.Empty) { yield return(new BoardSpace(currentPosition.Row, (byte)j)); } else { break; } } // walk left from currentPosition for (var j = currentPosition.Col - 1; j >= 0; j--) { if (_board[currentPosition.Row][j] == BoardSpaceValue.Empty) { yield return(new BoardSpace(currentPosition.Row, (byte)j)); } else { break; } } #endregion #region diagonal moves // walk down-right from currentPosition for (int i = currentPosition.Row + 1, j = currentPosition.Col + 1; i < 8 && j < 8; i++, j++) { if (_board[i][j] == BoardSpaceValue.Empty) { yield return(new BoardSpace((byte)i, (byte)j)); } else { break; } } // walk down-left from currentPosition for (int i = currentPosition.Row + 1, j = currentPosition.Col - 1; i < 8 && j >= 0; i++, j--) { if (_board[i][j] == BoardSpaceValue.Empty) { yield return(new BoardSpace((byte)i, (byte)j)); } else { break; } } // walk up-right from currentPosition for (int i = currentPosition.Row - 1, j = currentPosition.Col + 1; i >= 0 && j < 8; i--, j++) { if (_board[i][j] == BoardSpaceValue.Empty) { yield return(new BoardSpace((byte)i, (byte)j)); } else { break; } } // walk up-left from currentPosition for (int i = currentPosition.Row - 1, j = currentPosition.Col - 1; i >= 0 && j >= 0; i--, j--) { if (_board[i][j] == BoardSpaceValue.Empty) { yield return(new BoardSpace((byte)i, (byte)j)); } else { break; } } #endregion }
public void MoveO(BoardSpace move) { _board[Oposition.Row][Oposition.Col] = BoardSpaceValue.Filled; _board[move.Row][move.Col] = BoardSpaceValue.PlayerO; Oposition = move; }
public void MoveX(BoardSpace move) { _board[Xposition.Row][Xposition.Col] = BoardSpaceValue.Filled; _board[move.Row][move.Col] = BoardSpaceValue.PlayerX; Xposition = move; }