protected Player(BoardColors boardColor) { if ((boardColor != BoardColors.Black) && (boardColor != BoardColors.White)) { throw new ArgumentException("Invalid board color."); } this.boardColor = boardColor; }
/// <summary> /// Calculate the heuristic for the state of the board, recursively. /// </summary> /// <param name="board">The current state of the board in the search.</param> /// <param name="depth">The current depth of the search.</param> /// <param name="nodesVisitedCount">The total number of nodes visited.</param> /// <param name="move">The best move.</param> /// <returns>The heuristic for this node in the search.</returns> private int CalculateMiniMaxHeuristic(Board board, int depth, ref int nodesVisitedCount, out Move move) { // safety-check the board parameter if (board == null) { throw new ArgumentNullException("board"); } bool myTurn = board.CurrentColor == BoardColor; BoardColors currentColor = myTurn ? BoardColor : (BoardColor == BoardColors.White ? BoardColors.Black : BoardColors.White); int heuristic = myTurn ? Int32.MaxValue * -1 : Int32.MaxValue; nodesVisitedCount++; move = new Move(-1, -1); // cut short the algorithm if it's time to give up if (nodesVisitedCount >= nodesVisitedLimit) { return(0); } // at the bottom of the tree, just calculate the value if (depth <= 0) { return(CalculateMiniMaxHeuristic(board)); } for (int row = 0; row < board.BoardSize; row++) { for (int column = 0; column < board.BoardSize; column++) { Move newMove = new Move(row, column); Board newBoard = board.CheckMove(BoardColor, newMove); if (newBoard != null) { Move deeperMove; // we only care about moves in the top level int newHeuristic = CalculateMiniMaxHeuristic(newBoard, depth - 1, ref nodesVisitedCount, out deeperMove); if (nodesVisitedCount >= nodesVisitedLimit) { move = newMove; return(0); } if ((myTurn && (newHeuristic > heuristic)) || (!myTurn && (newHeuristic < heuristic))) { move = newMove; heuristic = newHeuristic; } } } } return(heuristic); }
/// <summary> /// Create a new LocalPlayer object. /// </summary> /// <param name="boardColor">The color of this player.</param> /// <param name="playerIndex">The player index for this color.</param> /// <param name="boardSize"></param> public LocalPlayer( BoardColors boardColor, PlayerIndex playerIndex, int boardSize ) : base( boardColor ) { if( ( playerIndex != PlayerIndex.One ) && ( playerIndex != PlayerIndex.Two ) ) { throw new ArgumentException( "Invalid player index." ); } this.playerIndex = playerIndex; // start in the center of the board cursorPosition = new Point( boardSize / 2, boardSize / 2 ); }
/// <summary> /// Create a new LocalPlayer object. /// </summary> /// <param name="boardColor">The color of this player.</param> /// <param name="playerIndex">The player index for this color.</param> /// <param name="boardSize"></param> public LocalPlayer(BoardColors boardColor, PlayerIndex playerIndex, int boardSize) : base(boardColor) { if ((playerIndex != PlayerIndex.One) && (playerIndex != PlayerIndex.Two)) { throw new ArgumentException("Invalid player index."); } this.playerIndex = playerIndex; // start in the center of the board cursorPosition = new Point(boardSize / 2, boardSize / 2); }
/// <summary> /// Get the number of pieces for the other color than the one passed. /// </summary> /// <param name="boardColor">The board color - black or white.</param> /// <returns>The number of spaces that don't the argument.</returns> public int GetOppositeColorCount(BoardColors boardColor) { switch (boardColor) { case BoardColors.Black: return(WhitePieceCount); case BoardColors.White: return(BlackPieceCount); default: throw new ArgumentException("Invalid board color."); } }
/// <summary> /// Checks if a player has any valid move on the board. /// </summary> /// <param name="boardColor">The color of the player to check.</param> /// <returns>If true, a valid move exists.</returns> public bool HasValidMove(BoardColors boardColor) { for (int row = 0; row < boardSize; row++) { for (int column = 0; column < boardSize; column++) { if (IsValidMove(boardColor, new Move(row, column))) { return(true); } } } return(false); }
/// <summary> /// Checks if a move is valid. /// </summary> /// <param name="boardColor">The color of the player making the move.</param> /// <param name="move">The move.</param> /// <returns>If true, the move is valid.</returns> public bool IsValidMove(BoardColors boardColor, Move move) { // if it's not the right turn, it's not a valid move if (boardColor != currentColor) { return(false); } // if it's not empty, then it's never a possible move if (this[move.Row, move.Column] != BoardColors.Empty) { return(false); } // check for possible lines BoardColors otherColor = (currentColor == BoardColors.White) ? BoardColors.Black : BoardColors.White; for (int directionRow = -1; directionRow <= 1; directionRow++) { for (int directionColumn = -1; directionColumn <= 1; directionColumn++) { int currentRow = move.Row + 1 + directionRow; int currentColumn = move.Column + 1 + directionColumn; while (spaces[currentRow, currentColumn] == otherColor) { if (spaces[currentRow + directionRow, currentColumn + directionColumn] == currentColor) { return(true); } else if (spaces[currentRow + directionRow, currentColumn + directionColumn] == otherColor) { currentRow += directionRow; currentColumn += directionColumn; } else { break; } } } } return(false); }
/// <summary> /// Create an alternate version of the board where a certain move was made. /// </summary> /// <param name="boardColor">The color of the player making the move.</param> /// <param name="move">The move.</param> /// <returns>The board as of the new move.</returns> /// <remarks>Useful for previewing and AI algorithms.</remarks> public Board CheckMove(BoardColors boardColor, Move move) { // make sure it's a valid move if (!IsValidMove(boardColor, move)) { return(null); } // create a copy of the existing board Board board = new Board(boardSize); board.blackPieceCount = blackPieceCount; board.whitePieceCount = whitePieceCount; board.currentColor = currentColor; // array is of value type, so shallow is fine board.spaces = spaces.Clone() as BoardColors[, ]; board.ApplyMove(boardColor, move); return(board); }
/// <summary> /// Pass to the next player instead of making a move. /// </summary> /// <param name="player">The color of the player passing.</param> public void Pass(BoardColors boardColor) { // safety-check the player if (boardColor != currentColor) { throw new InvalidOperationException("Move made out of turn."); } switch (boardColor) { case BoardColors.Black: blackPassed = true; currentColor = BoardColors.White; break; case BoardColors.White: whitePassed = true; currentColor = BoardColors.Black; break; } }
/// <summary> /// Create a new AI player object. /// </summary> /// <param name="boardColor">The color of this player.</param> public AiPlayer(BoardColors boardColor) : base(boardColor) { }
/// <summary> /// Apply a move to the board. /// </summary> /// <param name="boardColor">The color of the player making the move.</param> /// <param name="move">The move.</param> /// <remarks> /// The move is assumed to be valid before entering this function. /// </remarks> public void ApplyMove( BoardColors boardColor, Move move ) { // safety-check the player if( boardColor != currentColor ) { throw new InvalidOperationException( "Move made out of turn." ); } // set the new piece lastMove = move; this[ move.Row, move.Column ] = currentColor; // flip lines in all directions BoardColors otherColor = BoardColors.Black; switch( boardColor ) { case BoardColors.Black: blackPassed = false; otherColor = BoardColors.White; break; case BoardColors.White: whitePassed = false; otherColor = BoardColors.Black; break; } for( int directionRow = -1; directionRow <= 1; directionRow++ ) { for( int directionColumn = -1; directionColumn <= 1; directionColumn++ ) { // use the spaces array to check as it includes the illegal boundary int currentRow = move.Row + 1 + directionRow; int currentColumn = move.Column + 1 + directionColumn; while( spaces[ currentRow, currentColumn ] == otherColor ) { if( spaces[ currentRow + directionRow, currentColumn + directionColumn ] == currentColor ) { while( spaces[ currentRow, currentColumn ] == otherColor ) { this[ currentRow - 1, currentColumn - 1 ] = currentColor; currentRow -= directionRow; currentColumn -= directionColumn; } break; } else if( spaces[ currentRow + directionRow, currentColumn + directionColumn ] == otherColor ) { currentRow += directionRow; currentColumn += directionColumn; } else { break; } } } } // it's now the other player's turn currentColor = otherColor; }
/// <summary> /// Checks if a move is valid. /// </summary> /// <param name="boardColor">The color of the player making the move.</param> /// <param name="move">The move.</param> /// <returns>If true, the move is valid.</returns> public bool IsValidMove( BoardColors boardColor, Move move ) { // if it's not the right turn, it's not a valid move if( boardColor != currentColor ) { return false; } // if it's not empty, then it's never a possible move if( this[ move.Row, move.Column ] != BoardColors.Empty ) { return false; } // check for possible lines BoardColors otherColor = ( currentColor == BoardColors.White ) ? BoardColors.Black : BoardColors.White; for( int directionRow = -1; directionRow <= 1; directionRow++ ) { for( int directionColumn = -1; directionColumn <= 1; directionColumn++ ) { int currentRow = move.Row + 1 + directionRow; int currentColumn = move.Column + 1 + directionColumn; while( spaces[ currentRow, currentColumn ] == otherColor ) { if( spaces[ currentRow + directionRow, currentColumn + directionColumn ] == currentColor ) { return true; } else if( spaces[ currentRow + directionRow, currentColumn + directionColumn ] == otherColor ) { currentRow += directionRow; currentColumn += directionColumn; } else { break; } } } } return false; }
/// <summary> /// Checks if a player has any valid move on the board. /// </summary> /// <param name="boardColor">The color of the player to check.</param> /// <returns>If true, a valid move exists.</returns> public bool HasValidMove( BoardColors boardColor ) { for( int row = 0; row < boardSize; row++ ) { for( int column = 0; column < boardSize; column++ ) { if( IsValidMove( boardColor, new Move( row, column ) ) ) { return true; } } } return false; }
protected Player( BoardColors boardColor ) { if( ( boardColor != BoardColors.Black ) && ( boardColor != BoardColors.White ) ) { throw new ArgumentException( "Invalid board color." ); } this.boardColor = boardColor; }
/// <summary> /// Create an alternate version of the board where a certain move was made. /// </summary> /// <param name="boardColor">The color of the player making the move.</param> /// <param name="move">The move.</param> /// <returns>The board as of the new move.</returns> /// <remarks>Useful for previewing and AI algorithms.</remarks> public Board CheckMove( BoardColors boardColor, Move move ) { // make sure it's a valid move if( !IsValidMove( boardColor, move ) ) { return null; } // create a copy of the existing board Board board = new Board( boardSize ); board.blackPieceCount = blackPieceCount; board.whitePieceCount = whitePieceCount; board.currentColor = currentColor; // array is of value type, so shallow is fine board.spaces = spaces.Clone() as BoardColors[ , ]; board.ApplyMove( boardColor, move ); return board; }
/// <summary> /// Get the number of pieces for the other color than the one passed. /// </summary> /// <param name="boardColor">The board color - black or white.</param> /// <returns>The number of spaces that don't the argument.</returns> public int GetOppositeColorCount( BoardColors boardColor ) { switch( boardColor ) { case BoardColors.Black: return WhitePieceCount; case BoardColors.White: return BlackPieceCount; default: throw new ArgumentException( "Invalid board color." ); } }
/// <summary> /// Pass to the next player instead of making a move. /// </summary> /// <param name="player">The color of the player passing.</param> public void Pass( BoardColors boardColor ) { // safety-check the player if( boardColor != currentColor ) { throw new InvalidOperationException( "Move made out of turn." ); } switch( boardColor ) { case BoardColors.Black: blackPassed = true; currentColor = BoardColors.White; break; case BoardColors.White: whitePassed = true; currentColor = BoardColors.Black; break; } }
/// <summary> /// Apply a move to the board. /// </summary> /// <param name="boardColor">The color of the player making the move.</param> /// <param name="move">The move.</param> /// <remarks> /// The move is assumed to be valid before entering this function. /// </remarks> public void ApplyMove(BoardColors boardColor, Move move) { // safety-check the player if (boardColor != currentColor) { throw new InvalidOperationException("Move made out of turn."); } // set the new piece lastMove = move; this[move.Row, move.Column] = currentColor; // flip lines in all directions BoardColors otherColor = BoardColors.Black; switch (boardColor) { case BoardColors.Black: blackPassed = false; otherColor = BoardColors.White; break; case BoardColors.White: whitePassed = false; otherColor = BoardColors.Black; break; } for (int directionRow = -1; directionRow <= 1; directionRow++) { for (int directionColumn = -1; directionColumn <= 1; directionColumn++) { // use the spaces array to check as it includes the illegal boundary int currentRow = move.Row + 1 + directionRow; int currentColumn = move.Column + 1 + directionColumn; while (spaces[currentRow, currentColumn] == otherColor) { if (spaces[currentRow + directionRow, currentColumn + directionColumn] == currentColor) { while (spaces[currentRow, currentColumn] == otherColor) { this[currentRow - 1, currentColumn - 1] = currentColor; currentRow -= directionRow; currentColumn -= directionColumn; } break; } else if (spaces[currentRow + directionRow, currentColumn + directionColumn] == otherColor) { currentRow += directionRow; currentColumn += directionColumn; } else { break; } } } } // it's now the other player's turn currentColor = otherColor; }