// checks to see if capture move is profitable; if not, eliminate move from generated moves array public static void EvaluateCapture(ref List <ChessMove> moves, int player) { // get standing pat score of capturing move ChessMove theMove = moves[moves.Count - 1]; int see_score = StaticExchangeEvaluate(theMove, player); // if standing pat score is less than the minimum required score, eliminate move from moves list if (see_score < MIN_CAPTURE_SCORE) { moves.RemoveAt(moves.Count - 1); } else if (see_score >= MIN_CAPTURE_SCORE) { if (AI.board.myColor == ChessBoard.WHITE) { AI.history.whiteValue[theMove.GetFromSq()][theMove.GetToSq()] += see_score; } else if (AI.board.myColor == ChessBoard.BLACK) { AI.history.blackValue[theMove.GetFromSq()][theMove.GetToSq()] += see_score; } } }
// executes time-limited alpha/beta pruning depth-limited MiniMax search public static int PVABMiniMax(int depth, int alpha, int beta) { // initialize principal variant flag bool pvfound = false; // update evaluated node count AI.nodes++; // check to see if we have time left if (AI.timer.ElapsedMilliseconds > TIME_PER_MOVE) { AI.TIME_EXPIRED = true; return(Score.TIME_EXPIRED_SCORE); } // initialize variables List <ChessMove> moves = new List <ChessMove>(0); ChessMove bestAction = new ChessMove(); int value = SMALL_NUM, bestValue = SMALL_NUM, color = -1; if (depth % 2 != 0) { bestValue = LARGE_NUM; } // check for draws if (Score.IsDrawByHundredMoves()) // 100 move draw; update history table { return(Score.DRAW_SCORE); } if (Score.IsDrawByRepetition()) // repitition draw { return(Score.DRAW_SCORE); } if (Score.IsDrawByInsufficientMaterial()) // insufficient material draw { return(Score.DRAW_SCORE); } // set color of current player if (depth % 2 == 0) { color = AI.board.myColor; } else { color = AI.board.oppColor; } // if depth limit has been reached, return quiescence search value for this node if (depth >= MAX_DEPTH) { // set follow pv flag FOLLOW_PV = false; // go one node deeper if player to move is currently in check if (MoveGen.IsKingAttacked(color)) { MAX_DEPTH++; value = PVABMiniMax(depth, alpha, beta); MAX_DEPTH--; return(value); } // return quiescence search value return(QSearch(alpha, beta, color)); } // if allowed, try a null move to get an early prune if (NULLMOVE_ALLOWED && !FOLLOW_PV) { // if game could be in zugzwang for player to move or player to move is in check, do not attempt null move if (((color == ChessBoard.WHITE && Score.GetMaterialScore(color) > Score.NULLMOVE_LIMIT_SCORE) || (color == ChessBoard.BLACK && Score.GetMaterialScore(color) > Score.NULLMOVE_LIMIT_SCORE)) && !MoveGen.IsKingAttacked(color)) { // set allow null move flag NULLMOVE_ALLOWED = false; // if we are next player to move, run zero-window search for alpha; else use beta if (color == AI.board.oppColor) { value = PVABMiniMax(depth + NULLMOVE_DEPTH_GAP, alpha, alpha + 1); } else if (color == AI.board.myColor) { value = PVABMiniMax(depth + NULLMOVE_DEPTH_GAP, beta, beta + 1); } // reset allow null move flag NULLMOVE_ALLOWED = true; // if alpha/beta was not improved, prune if (color == AI.board.myColor && value >= beta) { return(value); } else if (color == AI.board.oppColor && value <= alpha) { return(value); } } } NULLMOVE_ALLOWED = true; // generate moves for player based on depth moves = MoveGen.GenerateMoves(color, false); // iterate through generated moves for (int i = 0; i < moves.Count; i++) { // order remaining moves according to history table AI.history.OrderMoves(ref moves, color, i); // make current move moves[i].DoMove(MAKE); // if move is illegal, unmake move; continue otherwise if (!MoveGen.IsKingAttacked(color)) { // if principal variant flag is set, run zero-window search; else, run normal search if (pvfound) { // if we are next player to move, use alpha for zero-window search; else, use beta if (depth % 2 != 0) { value = PVABMiniMax(depth + 1, alpha, alpha + 1); } else if (depth % 2 == 0) { value = PVABMiniMax(depth + 1, beta, beta + 1); } // if value returned falls within alpha/beta window, run normal search with normal alpha/beta window if (value > alpha && value < beta) { value = PVABMiniMax(depth + 1, alpha, beta); } } else { value = PVABMiniMax(depth + 1, alpha, beta); } // unmake current move moves[i].DoMove(UNMAKE); // check to see if search found checkmate if (value == Score.CHECKMATE_WIN_SCORE && depth == 0) { // update PV PV.Clear(); PV.Add(moves[i]); // return checkmate score return(Score.CHECKMATE_WIN_SCORE); } // check to see if time has expired if (value == Score.TIME_EXPIRED_SCORE) { return(Score.TIME_EXPIRED_SCORE); } // evaluate minimax search value if (depth % 2 == 0) // maximize { if (value >= beta) // fail-high, prune; update history table { if (color == ChessBoard.WHITE) { AI.history.whiteValue[moves[i].GetFromSq()][moves[i].GetToSq()] += (MAX_DEPTH - depth) * (MAX_DEPTH - depth); } else if (color == ChessBoard.BLACK) { AI.history.blackValue[moves[i].GetFromSq()][moves[i].GetToSq()] += (MAX_DEPTH - depth) * (MAX_DEPTH - depth); } return(value + 1); } if (value > alpha) // set new alpha, set principal variant flag { alpha = value; pvfound = true; } if (value >= bestValue) // set new best action, best value { // if alpha improves at root, update PV if (depth == 0 && value > bestValue) { PV.Clear(); PV.Add(moves[i]); } else if (depth == 0 && value == bestValue) { PV.Add(moves[i]); } bestAction = moves[i]; bestValue = value; } } else if (depth % 2 == 1) // minimize { if (value <= alpha) // fail-low, prune; update history table { if (color == ChessBoard.WHITE) { AI.history.whiteValue[moves[i].GetFromSq()][moves[i].GetToSq()] += (MAX_DEPTH - depth) * (MAX_DEPTH - depth); } else if (color == ChessBoard.BLACK) { AI.history.blackValue[moves[i].GetFromSq()][moves[i].GetToSq()] += (MAX_DEPTH - depth) * (MAX_DEPTH - depth); } return(value - 1); } if (value < beta) // set new beta, set principal variant flag { beta = value; pvfound = true; } if (value < bestValue) // set new best action, best value { bestAction = moves[i]; bestValue = value; } } } else { moves[i].DoMove(UNMAKE); } } // no legal moves for this state if (value == SMALL_NUM) { // if in check, checkmate; else stalemate if (MoveGen.IsKingAttacked(color)) { if (color == AI.board.myColor) // we are in checkmate { return(Score.CHECKMATE_LOSE_SCORE); } else if (color == AI.board.oppColor) // opp is in checkmate { return(Score.CHECKMATE_WIN_SCORE); } } else { return(Score.DRAW_SCORE); } } // return best value from current depth; update history table if (color == ChessBoard.WHITE) { AI.history.whiteValue[bestAction.GetFromSq()][bestAction.GetToSq()] += ((MAX_DEPTH - depth) * (MAX_DEPTH - depth) + 1); } else if (color == ChessBoard.BLACK) { AI.history.blackValue[bestAction.GetFromSq()][bestAction.GetToSq()] += ((MAX_DEPTH - depth) * (MAX_DEPTH - depth) + 1); } return(bestValue); }
// returns standing pat (static exchange evalutation) score of specified capturing move public static int StaticExchangeEvaluate(ChessMove move, int player) { // variables to track loss values int white_loss = 0, black_loss = 0, next_loss = 0; int wKing_loss = 0, bKing_loss = 0; // flags to check if either king is participating in attack bool wKingAttack = false, bKingAttack = false; // get current occupied BitBoard UInt64 occupied = AI.board.occupied_squares; // get target square of attack uint target = move.GetToSq(); // add piece on target square to color's material loss if (ChessBoard.GetPieceColor(move.GetCapture()) == ChessBoard.WHITE) { white_loss += Score.GetPieceValue(move.GetCapture()); } else if (ChessBoard.GetPieceColor(move.GetCapture()) == ChessBoard.BLACK) { black_loss += Score.GetPieceValue(move.GetCapture()); } // set next loss value equal to value of piece making initial capture next_loss = Score.GetPieceValue(move.GetPiece()); // remove piece making capture from occupied BitBoard occupied ^= ChessBoard.SQUARES[move.GetFromSq()]; // keep track of next player to move int playerToMove = AI.GetOtherPlayer(player); // loop until no more pieces are attacking the target square while (true) { // if either king is participating in an attack illegally, undo last capture, break loop if (wKingAttack && playerToMove == ChessBoard.WHITE) { black_loss -= wKing_loss; break; } else if (bKingAttack && playerToMove == ChessBoard.BLACK) { white_loss -= bKing_loss; break; } // reset attackers BitBoard UInt64 attackers = occupied; // check for attacking pieces of player to move if (playerToMove == ChessBoard.WHITE) { //--------------------// // WHITE PAWNS (NP) // //--------------------// // check for non-promoting white pawns if (ChessMove.GetRank(target) != 7) { // get all non-promoting white pawns attacking target square attackers = occupied & (ChessBoard.PAWN_CAPTURES[target] >> 16) & AI.board.white_pawn; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.W_PAWN); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } } //--------------------// // WHITE KNIGHTS // //--------------------// // get all white knights attacking target square attackers = occupied & (MoveGen.GetKnightMoves(target, occupied)) & AI.board.white_knight; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.W_KNIGHT); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // WHITE BISHOPS // //--------------------// // get all white bishops attacking target square attackers = occupied & (MoveGen.GetBishopMoves(target, occupied, occupied)) & AI.board.white_bishop; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.W_BISHOP); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // WHITE ROOKS // //--------------------// // get all white rooks attacking target square attackers = occupied & (MoveGen.GetRookMoves(target, occupied, occupied)) & AI.board.white_rook; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.W_ROOK); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // WHITE PAWNS (P) // //--------------------// // check for promoting white pawns if (ChessMove.GetRank(target) == 7) { // get all promoting white pawns attacking target square attackers = occupied & (ChessBoard.PAWN_CAPTURES[target - 16]) & AI.board.white_pawn; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.W_QUEEN) - Score.GetPieceValue(ChessBoard.W_PAWN); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } } //--------------------// // WHITE QUEENS // //--------------------// // get all white queens attacking target square attackers = occupied & (MoveGen.GetQueenMoves(target, occupied, occupied)) & AI.board.white_queen; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.W_QUEEN); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // WHITE KING // //--------------------// // get white king attacking target square attackers = occupied & (MoveGen.GetKingMoves(target, occupied)) & AI.board.white_king; // if attacking piece exists, update black loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values black_loss += next_loss; wKing_loss = next_loss; // set white king attack flag wKingAttack = true; // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } } else if (playerToMove == ChessBoard.BLACK) { //--------------------// // BLACK PAWNS (NP) // //--------------------// // check for non-promoting black pawns if (ChessMove.GetRank(target) != 0) { // get all non-promoting white pawns attacking target square attackers = occupied & (ChessBoard.PAWN_CAPTURES[target]) & AI.board.black_pawn; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.B_PAWN); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } } //--------------------// // BLACK KNIGHTS // //--------------------// // get all black knights attacking target square attackers = occupied & (MoveGen.GetKnightMoves(target, occupied)) & AI.board.black_knight; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.B_KNIGHT); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // BLACK BISHOPS // //--------------------// // get all black bishops attacking target square attackers = occupied & (MoveGen.GetBishopMoves(target, occupied, occupied)) & AI.board.black_bishop; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.B_BISHOP); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // BLACK ROOKS // //--------------------// // get all black rooks attacking target square attackers = occupied & (MoveGen.GetRookMoves(target, occupied, occupied)) & AI.board.black_rook; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.B_ROOK); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // BLACK PAWNS (P) // //--------------------// // check for promoting black pawns if (ChessMove.GetRank(target) == 0) { // get all promoting black pawns attacking target square attackers = occupied & (ChessBoard.PAWN_CAPTURES[target]) & AI.board.black_pawn; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.B_QUEEN) - Score.GetPieceValue(ChessBoard.B_PAWN); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } } //--------------------// // BLACK QUEENS // //--------------------// // get all black queens attacking target square attackers = occupied & (MoveGen.GetQueenMoves(target, occupied, occupied)) & AI.board.black_queen; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; next_loss = Score.GetPieceValue(ChessBoard.B_QUEEN); // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } //--------------------// // BLACK KING // //--------------------// // get black king attacking target square attackers = occupied & (MoveGen.GetKingMoves(target, occupied)) & AI.board.black_king; // if attacking piece exists, update white loss, next loss, and occupied BitBoard; repeat loop if (attackers != ChessBoard.EMPTY_BOARD) { // update loss values white_loss += next_loss; bKing_loss = next_loss; // set white king attack flag bKingAttack = true; // update occupied BitBoard occupied ^= ChessBoard.GetFirstBit(attackers); // switch player to move playerToMove = AI.GetOtherPlayer(playerToMove); // restart loop continue; } } // no more attacking pieces - break loop break; } // return net standing pat score based on player color if (player == ChessBoard.WHITE) { return(black_loss - white_loss); } else if (player == ChessBoard.BLACK) { return(white_loss - black_loss); } // something went wrong... return(-1); }
//This function is called each time it is your turn //Return true to end your turn, return false to ask the server for updated information public override bool run() { // Print out the current board state Console.WriteLine("+---+---+---+---+---+---+---+---+"); for (int rank = 8; rank > 0; rank--) { Console.Write("|"); for (int file = 1; file <= 8; file++) { bool found = false; // Loops through all of the pieces for (int p = 0; !found && p < pieces.Length; p++) { // determines if that piece is at the current rank and file if (pieces[p].getRank() == rank && pieces[p].getFile() == file) { found = true; // Checks if the piece is black if (pieces[p].getOwner() == 1) { Console.Write("*"); } else { Console.Write(" "); } // prints the piece's type Console.Write((char)pieces[p].getType() + " "); } } if (!found) { Console.Write(" "); } Console.Write("|"); } Console.WriteLine("\n+---+---+---+---+---+---+---+---+"); } // Looks through information about the players for (int p = 0; p < players.Length; p++) { Console.Write(players[p].getPlayerName()); // if playerID is 0, you're white, if its 1, you're black if (players[p].getId() == myID) { Console.Write(" (ME)"); // update timeRemaining timeRemaining = players[p].getTime(); } Console.WriteLine(" time remaining: " + players[p].getTime()); } // if there has been a move, print the most recent move if (moves.Length > 0) { Console.Write("Last Move Was: "); Console.WriteLine(files[moves[0].getFromFile() - 1] + "" + moves[0].getFromRank() + "-" + files[moves[0].getToFile() - 1] + "" + moves[0].getToRank()); } ///////////////////////////////////// // <-- END OF STOCK AI.cs CODE --> // ///////////////////////////////////// // print current move number Console.WriteLine("\nMove " + turnNumber().ToString("D3") + "\n========\n"); // add to GameState List and update ChessBoard if (moves.Length <= 1) { board = new ChessBoard(ref pieces, myID); states.Add(new GameState(null, null)); } else { ChessMove lastMove = ChessMove.GetChessMove(moves[0].getFromFile(), moves[0].getToFile(), moves[0].getFromRank(), moves[0].getToRank(), moves[0].getPromoteType(), states[states.Count - 1].enPassant); board = new ChessBoard(ref pieces, myID); states.Add(new GameState(states[states.Count - 1], lastMove)); } // display current score information for player Console.Write("Score for "); if (myID == ChessBoard.WHITE) { Console.WriteLine("WHITE:\n"); } else if (myID == ChessBoard.BLACK) { Console.WriteLine("BLACK:\n"); } int material = Score.GetMaterialScore(myID); int position = Score.GetPositionScore(myID); // int mobility = Score.GetPositionScore(myID); int pawn_structure = Score.GetPawnStructureScore(myID); int king_safety = Score.GetKingSafetyScore(myID); Console.WriteLine("Net Material = " + material); Console.WriteLine("Net Position = " + position); //Console.WriteLine("Net Mobility = " + mobility); Console.WriteLine("Net Pawn Structure = " + pawn_structure); Console.WriteLine("Net King Safety = " + king_safety + "\n"); Console.WriteLine("Overall Score = " + (material + position + /*mobility +*/ pawn_structure + king_safety) + "\n"); // if playing as human, get move from console prompt while (HUMAN_PLAYER) { // get legal moves for this position List <ChessMove> legalMoves = MoveGen.GenerateMoves(myID, false); // prompt user for move Console.Write("Enter a move ([from] [to] <promotion type>): "); string[] humanMove = Console.ReadLine().Split(' '); // get origin square int humanFromFile = 0, humanFromRank = 0; for (int i = 0; i < 8; i++) { if (humanMove[0][0] == files[i]) { humanFromFile = i + 1; break; } } humanFromRank = (int)Char.GetNumericValue(humanMove[0][1]); // get destination square int humanToFile = 0, humanToRank = 0; for (int i = 0; i < 8; i++) { if (humanMove[1][0] == files[i]) { humanToFile = i + 1; break; } } humanToRank = (int)Char.GetNumericValue(humanMove[1][1]); // if promotion type is specified, get the promotion piece from move int humanPromote = 0; if (humanMove.Length > 2) { humanPromote = (int)humanMove[2][0]; } // check for legality of human move bool isLegal = false; for (int i = 0; i < legalMoves.Count; i++) { ChessMove m = legalMoves[i]; if ((ChessMove.GetFile(m.GetFromSq()) + 1) == (uint)humanFromFile && (ChessMove.GetRank(m.GetFromSq()) + 1) == (uint)humanFromRank && (ChessMove.GetFile(m.GetToSq()) + 1) == (uint)humanToFile && (ChessMove.GetRank(m.GetToSq()) + 1) == (uint)humanToRank) { isLegal = true; break; } } // if move is legal, make move if (isLegal) { // get Piece associated with move Piece humanPiece = pieces[FindPiece(humanFromFile, humanFromRank)]; // make move humanPiece.move(humanToFile, humanToRank, humanPromote); return(true); } else if (!isLegal) { Console.WriteLine("ILLEGAL MOVE. Please input a legal move.\n"); } } // reset TIME_EXPIRED and timer TIME_EXPIRED = false; timer.Reset(); // reset history table history = new HistoryTable(); // run ABMiniMax int moveScore = 0, n = 0, toFile = -1, toRank = -1; uint fromSq = 0, toSq = 0, thePiece = 0; depth = 0; List <ChessMove> completeBestMoves = new List <ChessMove>(0); Search.UpdateTimePerMove(moves.Length); timer.Start(); while (!TIME_EXPIRED) { depth += 1; nodes = 0; Search.MAX_DEPTH = depth; Search.NULLMOVE_ALLOWED = true; Search.FOLLOW_PV = true; Search.PV = new List <ChessMove>(0); int score = Search.PVABMiniMax(0, Search.SMALL_NUM, Search.LARGE_NUM); if (score != Score.TIME_EXPIRED_SCORE) { moveScore = score; completeBestMoves = new List <ChessMove>(Search.PV); } // select random move from bestMoves List if (completeBestMoves.Count > 0) { n = generator.Next(0, completeBestMoves.Count - 1); // get bestMove info fromSq = completeBestMoves[n].GetFromSq(); thePiece = completeBestMoves[n].GetPiece(); toSq = completeBestMoves[n].GetToSq(); toFile = (int)((toSq % 8) + 1); toRank = (int)((toSq / 8) + 1); // print bestMove info Console.WriteLine("Best Move: " + completeBestMoves[n].GetMoveString() + ", Score: " + moveScore + ", Depth: " + depth + " (t = " + (timer.ElapsedMilliseconds / 1000.0).ToString("F3") + "s, nodes = " + nodes + ")"); } // if checkmate is found, stop searching if (score == Score.CHECKMATE_WIN_SCORE) { break; } } timer.Stop(); // output number of best moves Console.WriteLine("completeBestMoves = " + completeBestMoves.Count); // make bestMove pieces[FindPiece(fromSq, thePiece)].move(toFile, toRank, completeBestMoves[n].GetPromoteType()); // update ChessBoard and GameState List completeBestMoves[n].DoMove(Search.MAKE); return(true); }