public void SearchPosition(ref S_SearchInfo info) { // Iterative deepening int bestMove = Variables.NO_MOVE; int bestScore = -infinite; int currentDepth = 0; Clear(ref info); for (currentDepth = 1; currentDepth <= info.Depth; ++currentDepth) { bestScore = AlphaBeta(-infinite, infinite, currentDepth, ref info, true); if (info.Stopped) { break; } var pvMoves = PvTable.GetLine(currentDepth, board); bestMove = board.PvArray[0]; // First move in the PvArray, is the best move. Console.Write("info score cp {0} depth {1} nodes {2} time {3} ", bestScore, currentDepth, info.Nodes, Variables.Watch.ElapsedMilliseconds - info.StartTime); pvMoves = PvTable.GetLine(currentDepth, board); Console.Write("pv:"); for (int PvNum = 0; PvNum < pvMoves; ++PvNum) { int move = board.PvArray[PvNum]; Console.Write(" {0}", Io.MoveToString(move)); } Console.Write("\n"); /* * if (info.Fh != 0) { // Cannot divide by zero. * Console.Write("Ordering: {0}\n", info.Fhf / info.Fh); * } */ } // info score cp 13 depth 1 nodes 13 time 15 pv f1b5 Console.Write("bestmove {0}\n", Io.MoveToString(bestMove)); }
// Constructor. public Board() { Searcher = new Search.Search(this); pieces = new int[Variables.BRD_SQ_NUM]; Pawns = new ulong[3]; KingSq = new int[2]; EnPas = 99; // NO_SQ Side = -1; FiftyMoves = -1; Ply = 0; HistoryPly = 0; PceNum = new int[13]; Material = new int[2]; BigPieces = new int[2]; MajPieces = new int[2]; MinPieces = new int[2]; CastlePerm = -1; PList = new int[13, 10]; SearchHistory = new int[13, Variables.BRD_SQ_NUM]; SearchKillers = new int[2, Variables.MAX_DEPTH]; HistoryInit(); PvTable = new PvTable(20000); PvArray = new int[Variables.MAX_DEPTH]; }
/// <summary> /// Search all capture positions, to help avoid the Horizon effect. /// </summary> private int Quiescence(int alpha, int beta, ref S_SearchInfo info) { Debug.Assert(BoardOperations.CheckBoard(board)); if ((info.Nodes & 2047) == 0) { CheckUp(ref info); } info.Nodes++; // If position is a draw. if ((IsRepetition() || board.FiftyMoves >= 100) && board.Ply != 0) { return(0); } int score = Evaluate.Position(board); // Stand_pat. if (board.Ply > Variables.MAX_DEPTH - 1) { return(score); } if (score >= beta) { return(beta); } if (score > alpha) { alpha = score; } MoveList list = new MoveList(); MoveGen.GenerateAllMoves(board, list, true); // Only capture moves int oldAlpha = alpha; score = -infinite; int legal = 0; // Will increment when we find a legal move. int bestMove = Variables.NO_MOVE; int PvMove = PvTable.Probe(board); for (int i = 0; i < list.Count; ++i) { PickNextMove(i, list); var move = list.Moves[i].Move; if (!MakeMove.Make_Move(board, move)) { continue; } legal++; score = -Quiescence(-beta, -alpha, ref info); MakeMove.TakeMove(board); // Take back the made move. if (info.Stopped) { return(0); } // We have a new alpha or beta cutoff. if (score > alpha) { bool isCaptureMove = (move & MoveOperations.MoveFlagCapture) != 0; // beta cutoff? if (score >= beta) { if (legal == 1) { info.Fhf++; // We searched the best move first. } info.Fh++; return(beta); } // Alpha cutoff alpha = score; bestMove = move; } } if (alpha != oldAlpha) { PvTable.StoreMove(board, bestMove); } return(alpha); }
public int AlphaBeta(int alpha, int beta, int depth, ref S_SearchInfo info, bool DoNull) { Debug.Assert(BoardOperations.CheckBoard(board)); if (depth == 0) { return(Quiescence(alpha, beta, ref info)); } if ((info.Nodes & 2047) == 0) { CheckUp(ref info); } info.Nodes++; // If position is a draw. if ((IsRepetition() || board.FiftyMoves >= 100) && board.Ply != 0) { return(0); } if (board.Ply > Variables.MAX_DEPTH - 1) { return(Evaluate.Position(board)); } bool kingInCheck = Attack.IsSqAttacked(board.KingSq[board.Side], board.Side ^ 1, board); // If king is in check, search deeper to get out of check. if (kingInCheck) { depth++; // The two following lines are possibly ERROR. long timeInc = (info.StopTime - info.StartTime) * (1 / 2); info.StopTime += timeInc; } MoveList list = new MoveList(); MoveGen.GenerateAllMoves(board, list, false); int oldAlpha = alpha; int score = -infinite; int legal = 0; // Will increment when we find a legal move. int bestMove = Variables.NO_MOVE; int PvMove = PvTable.Probe(board); // Prioritize Principle Variation move if it's found. if (PvMove != Variables.NO_MOVE) { for (int i = 0; i < list.Count; ++i) { var move = list.Moves[i].Move; if (move == PvMove) { list.Moves[i].Score = 2000000; break; } } } for (int i = 0; i < list.Count; ++i) { PickNextMove(i, list); var move = list.Moves[i].Move; if (!MakeMove.Make_Move(board, move)) { continue; } legal++; score = -AlphaBeta(-beta, -alpha, depth - 1, ref info, true); MakeMove.TakeMove(board); // Take back the made move. if (info.Stopped) { return(0); // Back up to the root if times up. } // We have a new alpha or beta cutoff. if (score > alpha) { bool isCaptureMove = (move & MoveOperations.MoveFlagCapture) != 0; // beta cutoff? if (score >= beta) { if (legal == 1) { info.Fhf++; // We searched the best move first. } info.Fh++; // If beta cutoff, but no capture move. if (!isCaptureMove) { board.SearchKillers[1, board.Ply] = board.SearchKillers[0, board.Ply]; board.SearchKillers[0, board.Ply] = move; } return(beta); } // Alpha cutoff alpha = score; bestMove = move; if (!isCaptureMove) { int from = MoveOperations.FromSq(move); int to = MoveOperations.ToSq(move); board.SearchHistory[board[from], to] += depth; // Prioritizes move near the root of the tree. } } } // If we haven't had any legal moves. if (legal == 0) { // If in check with no legal moves checkmate. if (kingInCheck) { return(-mate + board.Ply); // Return the amount of moves it takes to mate. // Returning in this way, allows the method to "prefer" the fastest checkmate combination. } else { return(0); // Stalemate. } } if (alpha != oldAlpha) { PvTable.StoreMove(board, bestMove); } return(alpha); }