public void AiPonderMove() { Thinking = true; /* Fix added to prevent premature declaration of checkmate against computer's king. * * The original version only looked at squares that were available for the black king to move to PRIOR to white's latest move. * So... suppose you had a situation like this: * r4r2/pppb1p1k/8/3p2Q1/q4P2/2np4/6PP/5RK1/ w - 0 27 * ... and moved the white queen from G5 to H5. * At the start of white's move, the black king has 5 possible moves... 4 of which are attackable by white, and noted by * GenerateValidMoves in WhiteAttackBoard[]. When AiPonderMove executes, it immediately eliminates those 4 squares as options * for the black king, even though 2 of them are now safe because no white piece can attack them. However, square 7 is now * directly in the queen's attack path, so it's eliminated as an option as well. Boom, premature checkmate... game over. * * The time it takes to regenerate the valid moves is nontrivial under Android. To improve performance, * my fix regenerates the valid moves ONLY when it might prevent a game-ending premature declaration * of checkmate, unless the difficulty is set to Hard or VeryHard (in which case it runs every time). * Even a novice player is likely to get confused if the engine declares checkmate for no apparent reason, * but the bug's more insidious manifestations at the two easiest difficulty levels help to make the computer a tiny bit easier * to beat by giving it a blind spot that a player can exploit, the same way HUMAN opponents have blind spots and weaknesses of their own. */ if (ChessBoard.BlackMate || ChessBoard.WhiteMate || (GameDifficulty == Difficulty.VeryHard) || (GameDifficulty == Difficulty.Hard)) { ChessBoard.BlackMate = false; ChessBoard.WhiteMate = false; PieceValidMoves.GenerateValidMoves(ChessBoard); } NodesSearched = 0; var resultBoards = new ResultBoards(); resultBoards.Positions = new List <Board>(); if (CheckForMate(WhoseMove, ref ChessBoard)) { Thinking = false; return; } MoveContent bestMove = new MoveContent(); //If there is no playbook move search for the best move if (FindPlayBookMove(ref bestMove, ChessBoard, OpeningBook) == false || ChessBoard.FiftyMove > 45 || ChessBoard.RepeatedMove >= 2) { if (FindPlayBookMove(ref bestMove, ChessBoard, CurrentGameBook) == false || ChessBoard.FiftyMove > 45 || ChessBoard.RepeatedMove >= 2) { bestMove = Search.IterativeSearch(ChessBoard, PlyDepthSearched, ref NodesSearched, ref NodesQuiessence, ref pvLine, ref PlyDepthReached, ref RootMovesSearched, CurrentGameBook); } } //Make the move PreviousChessBoard = new Board(ChessBoard); RootMovesSearched = (byte)resultBoards.Positions.Count; Board.MovePiece(ChessBoard, bestMove.MovingPiecePrimary.SrcPosition, bestMove.MovingPiecePrimary.DstPosition, ChessPieceType.Queen); ChessBoard.LastMove.GeneratePGNString(ChessBoard); FileIO.SaveCurrentGameMove(ChessBoard, PreviousChessBoard, CurrentGameBook, bestMove); for (byte x = 0; x < 64; x++) { Square sqr = ChessBoard.Squares[x]; if (sqr.Piece == null) { continue; } sqr.Piece.DefendedValue = 0; sqr.Piece.AttackedValue = 0; } PieceValidMoves.GenerateValidMoves(ChessBoard); Evaluation.EvaluateBoardScore(ChessBoard); PieceTakenAdd(ChessBoard.LastMove); MoveHistory.Push(ChessBoard.LastMove); if (CheckForMate(WhoseMove, ref ChessBoard)) { Thinking = false; if (ChessBoard.WhiteMate || ChessBoard.BlackMate) { LastMove.PgnMove += "#"; } return; } if (ChessBoard.WhiteCheck || ChessBoard.BlackCheck) { LastMove.PgnMove += "+"; } Thinking = false; }
private static bool CheckForMate(ChessPieceColor whosTurn, ref Board chessBoard) { Search.SearchForMate(whosTurn, chessBoard, ref chessBoard.BlackMate, ref chessBoard.WhiteMate, ref chessBoard.StaleMate); return(chessBoard.BlackMate || chessBoard.WhiteMate || chessBoard.StaleMate); }
internal static MoveContent IterativeSearchOld(Board examineBoard, ChessEngine.Engine.Engine.TimeSettings gameTimeSettings, ref int nodesSearched, ref int nodesQuiessence, ref string pvLine, BackgroundWorker worker, ref byte plyDepthReached, ref byte rootMovesSearched, List <OpeningMove> currentGameBook) { Zobrist.MarkAncient(); MoveContent moveContent1 = new MoveContent(); MoveContent moveContent2 = new MoveContent(); string str1 = ""; List <Search.Position> positionList1 = new List <Search.Position>(); ResultBoards sortValidMoves = Search.GetSortValidMoves(examineBoard); rootMovesSearched = (byte)sortValidMoves.Positions.Count; int num1 = 30; int num2 = 40; byte depth = 1; if (gameTimeSettings == ChessEngine.Engine.Engine.TimeSettings.Moves40In10Minutes) { num1 = 15; } else if (gameTimeSettings == ChessEngine.Engine.Engine.TimeSettings.Moves40In20Minutes) { num1 = 30; } else if (gameTimeSettings == ChessEngine.Engine.Engine.TimeSettings.Moves40In30Minutes) { num1 = 45; } else if (gameTimeSettings == ChessEngine.Engine.Engine.TimeSettings.Moves40In40Minutes) { num1 = 60; } else if (gameTimeSettings == ChessEngine.Engine.Engine.TimeSettings.Moves40In60Minutes) { num1 = 90; } else if (gameTimeSettings == ChessEngine.Engine.Engine.TimeSettings.Moves40In90Minutes) { num1 = 135; } DateTime now = DateTime.Now; do { pvLine = ""; int num3 = -400000000; sortValidMoves.Positions.Sort(new Comparison <Board>(Search.Sort)); foreach (Board position1 in sortValidMoves.Positions) { if (DateTime.Now - now > TimeSpan.FromSeconds((double)num1)) { pvLine = str1; return(moveContent2); } if (worker != null) { worker.ReportProgress((int)((DateTime.Now - now).TotalSeconds / (double)num1 * 100.0)); } List <Search.Position> pvLine1 = new List <Search.Position>(); int num4 = -Search.AlphaBeta(position1, depth, -400000000, -num3, ref nodesSearched, ref nodesQuiessence, ref pvLine1, true); if (num4 >= (int)short.MaxValue) { pvLine = str1; return(position1.LastMove); } if ((int)examineBoard.RepeatedMove == 2) { string str2 = Board.Fen(true, position1); foreach (OpeningMove openingMove in currentGameBook) { if (openingMove.EndingFEN == str2) { num4 = 0; break; } } } position1.Score = num4; if (num4 > num3) { List <Search.Position> positionList2 = new List <Search.Position>(); pvLine = position1.LastMove.ToString(); foreach (Search.Position position2 in pvLine1) { pvLine = pvLine + " " + position2.ToString(); positionList2.Add(position2); } positionList2.Reverse(); num3 = num4; moveContent1 = position1.LastMove; } } moveContent2 = moveContent1; str1 = pvLine; plyDepthReached = depth; ++depth; }while (DateTime.Now - now < TimeSpan.FromSeconds((double)num1) && (int)plyDepthReached < 19); plyDepthReached = (byte)((uint)plyDepthReached + 1U); int num5 = num2 != 1 ? num2 - 1 : 40; return(moveContent2); }
private static int Quiescence(Board examineBoard, int alpha, int beta, ref int nodesSearched) { nodesSearched = nodesSearched + 1; Evaluation.EvaluateBoardScore(examineBoard); examineBoard.Score = Search.SideToMoveScore(examineBoard.Score, examineBoard.WhoseMove); if (examineBoard.Score >= beta) { return(beta); } if (examineBoard.Score > alpha) { alpha = examineBoard.Score; } List <Search.Position> positionList = examineBoard.WhiteCheck || examineBoard.BlackCheck ? Search.EvaluateMoves(examineBoard, (byte)0) : Search.EvaluateMovesQ(examineBoard); if (positionList.Count == 0) { return(examineBoard.Score); } positionList.Sort(new Comparison <Search.Position>(Search.Sort)); foreach (Search.Position position in positionList) { if (Search.StaticExchangeEvaluation(examineBoard.Squares[(int)position.DstPosition]) < 0) { Board board = examineBoard.FastCopy(); Board.MovePiece(board, position.SrcPosition, position.DstPosition, ChessPieceType.Queen); PieceValidMoves.GenerateValidMoves(board); if ((!board.BlackCheck || examineBoard.WhoseMove != ChessPieceColor.Black) && (!board.WhiteCheck || examineBoard.WhoseMove != ChessPieceColor.White)) { int num = -Search.Quiescence(board, -beta, -alpha, ref nodesSearched); if (num >= beta) { Search.KillerMove[2, 0].SrcPosition = position.SrcPosition; Search.KillerMove[2, 0].DstPosition = position.DstPosition; return(beta); } if (num > alpha) { alpha = num; } } } } return(alpha); }
private static int AlphaBeta(Board examineBoard, byte depth, int alpha, int beta, ref int nodesSearched, ref int nodesQuiessence, ref List <Search.Position> pvLine, bool extended) { nodesSearched = nodesSearched + 1; if ((int)examineBoard.FiftyMove >= 50 || (int)examineBoard.RepeatedMove >= 3) { return(0); } int?nullable1 = Zobrist.Search(examineBoard.ZobristHash, depth, alpha, beta); if (nullable1.HasValue) { return(nullable1.Value); } if ((int)depth == 0) { if (!extended && examineBoard.BlackCheck || examineBoard.WhiteCheck) { ++depth; extended = true; } else { int score = Search.Quiescence(examineBoard, alpha, beta, ref nodesQuiessence); if (score >= beta) { Zobrist.AddEntry(examineBoard.ZobristHash, depth, score, Zobrist.NodeType.Beta); } else if (score <= alpha) { Zobrist.AddEntry(examineBoard.ZobristHash, depth, score, Zobrist.NodeType.Alpha); } else { Zobrist.AddEntry(examineBoard.ZobristHash, depth, score, Zobrist.NodeType.Exact); } return(score); } } Zobrist.NodeType nodeType = Zobrist.NodeType.Alpha; List <Search.Position> moves = Search.EvaluateMoves(examineBoard, depth); if ((examineBoard.WhiteCheck || examineBoard.BlackCheck || moves.Count == 0) && Search.SearchForMate(examineBoard.WhoseMove, examineBoard, ref examineBoard.BlackMate, ref examineBoard.WhiteMate, ref examineBoard.StaleMate)) { if (examineBoard.BlackMate) { if (examineBoard.WhoseMove == ChessPieceColor.Black) { return(-32767 - (int)depth); } return((int)short.MaxValue + (int)depth); } if (!examineBoard.WhiteMate) { return(0); } if (examineBoard.WhoseMove == ChessPieceColor.Black) { return((int)short.MaxValue + (int)depth); } return(-32767 - (int)depth); } moves.Sort(new Comparison <Search.Position>(Search.Sort)); foreach (Search.Position position in moves) { List <Search.Position> pvLine1 = new List <Search.Position>(); Board board = examineBoard.FastCopy(); Board.MovePiece(board, position.SrcPosition, position.DstPosition, ChessPieceType.Queen); PieceValidMoves.GenerateValidMoves(board); if ((!board.BlackCheck || examineBoard.WhoseMove != ChessPieceColor.Black) && (!board.WhiteCheck || examineBoard.WhoseMove != ChessPieceColor.White)) { int?nullable2 = new int?(-Search.AlphaBeta(board, (byte)((uint)depth - 1U), -beta, -alpha, ref nodesSearched, ref nodesQuiessence, ref pvLine1, extended)); int?nullable3 = nullable2; int num1 = beta; if ((nullable3.GetValueOrDefault() >= num1 ? (nullable3.HasValue ? 1 : 0) : 0) != 0) { Search.KillerMove[Search.kIndex, (int)depth].SrcPosition = position.SrcPosition; Search.KillerMove[Search.kIndex, (int)depth].DstPosition = position.DstPosition; Search.kIndex = (Search.kIndex + 1) % 2; Zobrist.AddEntry(examineBoard.ZobristHash, depth, nullable2.Value, Zobrist.NodeType.Beta); return(beta); } nullable3 = nullable2; int num2 = alpha; if ((nullable3.GetValueOrDefault() > num2 ? (nullable3.HasValue ? 1 : 0) : 0) != 0) { pvLine1.Insert(0, new Search.Position() { SrcPosition = board.LastMove.MovingPiecePrimary.SrcPosition, DstPosition = board.LastMove.MovingPiecePrimary.DstPosition, Move = board.LastMove.ToString() }); pvLine = pvLine1; alpha = nullable2.Value; nodeType = Zobrist.NodeType.Exact; } } } Zobrist.AddEntry(examineBoard.ZobristHash, depth, alpha, nodeType); return(alpha); }
internal static MoveContent IterativeSearch(Board examineBoard, byte depth, ref int nodesSearched, ref int nodesQuiessence, ref string pvLine, BackgroundWorker worker, ref byte plyDepthReached, ref byte rootMovesSearched, List <OpeningMove> currentGameBook) { List <Search.Position> pvLine1 = new List <Search.Position>(); int num1 = -400000000; Zobrist.MarkAncient(); MoveContent moveContent = new MoveContent(); ResultBoards sortValidMoves = Search.GetSortValidMoves(examineBoard); rootMovesSearched = (byte)sortValidMoves.Positions.Count; if ((int)rootMovesSearched == 1) { return(sortValidMoves.Positions[0].LastMove); } foreach (Board position in sortValidMoves.Positions) { if (-Search.AlphaBeta(position, (byte)1, -400000000, -num1, ref nodesSearched, ref nodesQuiessence, ref pvLine1, true) >= (int)short.MaxValue) { return(position.LastMove); } } int num2 = 0; int num3 = -400000000; sortValidMoves.Positions.Sort(new Comparison <Board>(Search.Sort)); --depth; plyDepthReached = Search.ModifyDepth(depth, sortValidMoves.Positions.Count); foreach (Board position1 in sortValidMoves.Positions) { ++num2; if (worker != null) { worker.ReportProgress((int)((Decimal)num2 / (Decimal)sortValidMoves.Positions.Count * new Decimal(100))); } List <Search.Position> pvLine2 = new List <Search.Position>(); int num4 = -Search.AlphaBeta(position1, depth, -400000000, -num3, ref nodesSearched, ref nodesQuiessence, ref pvLine2, false); if (num4 >= (int)short.MaxValue) { return(position1.LastMove); } if ((int)examineBoard.RepeatedMove == 2) { string str = Board.Fen(true, position1); foreach (OpeningMove openingMove in currentGameBook) { if (openingMove.EndingFEN == str) { num4 = 0; break; } } } position1.Score = num4; if (num4 > num3 || num3 == -400000000) { pvLine = position1.LastMove.ToString(); foreach (Search.Position position2 in pvLine2) { pvLine = pvLine + " " + position2.ToString(); } num3 = num4; moveContent = position1.LastMove; } } plyDepthReached = (byte)((uint)plyDepthReached + 1U); return(moveContent); }