private int QuiesceneSearch(int alpha, int beta) { searchInfo.Nodes++; searchInfo.QNodes++; int currentEval = Evaluation.Evaluate(board); if (currentEval >= beta) { searchInfo.BetaCutoffs++; return(beta); } if (currentEval > alpha) { alpha = currentEval; } generator.Setup(); var moves = generator.GetMoves(true); foreach (var move in moves.OrderByDescending(m => MoveImportance(m, -1, null))) { board.SubmitMove(move); var eval = -QuiesceneSearch(-beta, -alpha); board.UndoMove(); if (eval >= beta) { searchInfo.BetaCutoffs++; return(beta); } if (eval > alpha) { alpha = eval; } } return(alpha); }
private int DeepEval(int depth, int alpha, int beta, bool nullMoveAllowed, Move[] pv, IDictionary <Move, int> orderedTopLevelMoves = null) { searchInfo.Nodes++; if (transpositions.ContainsKey(board.positionKey)) { searchInfo.Transpositions++; var score = LookupEvaluation(alpha, beta); if (score != LookupFailed) { return(score); } } generator.Setup(); var moves = generator.GetMoves(false); if (!moves.Any()) { //When in Check: Checkmate: if (generator.InCheck) { //Compute checkmate score: var currentDepth = board.states.Count; //TODO add ply variable to board return(-CHECKMATE + currentDepth); } else //Stalemate { return(DRAW); } } else if (board.states.Any(s => s.positionKey == board.positionKey)) { //Repetition return(DRAW); } //TODO Add 50 moves rule if (depth == 0) { return(QuiesceneSearch(alpha, beta)); } if (depth == 1) { //Futility pruning var currentEval = Evaluation.Evaluate(board); if (currentEval + Evaluation.BishopValue < alpha) { searchInfo.FutilityPrunes++; //Prune this node, i.e. go directly to QuiesceneSearch return(QuiesceneSearch(alpha, beta)); } } if (nullMoveAllowed && !generator.InCheck && depth >= 1 + R) { searchInfo.NullMoves++; //Null move pruning var npv = new Move[depth]; //irellevant here board.SubmitNullMove(); int eval = -DeepEval(depth - 1 - R, -beta, -beta + 1, false, npv); board.UndoNullMove(); if (eval >= beta) { searchInfo.NullMovesSuccess++; return(eval); } } TranspositionEntryType ttType = TranspositionEntryType.UpperBound; int movenumber = 1; bool searchPV = true; foreach (var move in moves.OrderByDescending(m => MoveImportance(m, depth, orderedTopLevelMoves))) { if (depth == searchInfo.Depth) //Upper level { Console.WriteLine($"info currmove {move.ToAlgebraicNotation()} currmovenumber {movenumber++} depth {depth}"); } var npv = new Move[depth - 1]; board.SubmitMove(move); int eval; if (searchPV) { eval = -DeepEval(depth - 1, -beta, -alpha, true, npv); } else { searchInfo.Scouts++; //Scout zw search eval = -ZWSerach(depth - 1, -alpha); if (eval > alpha) { eval = -DeepEval(depth - 1, -beta, -alpha, true, npv); // re-search } else { searchInfo.ScoutRemovals++; } } board.UndoMove(); if (depth == searchInfo.Depth) { searchInfo.TopLevelMoveOrder.Add(move, eval); } if (eval >= beta) { searchInfo.BetaCutoffs++; //transposition is an upper bound StoreEvaluation(eval, TranspositionEntryType.LowerBound); if (move.CapturedPiece == Piece.NONE) { KillerMoves[board.Ply][2] = KillerMoves[board.Ply][1]; KillerMoves[board.Ply][1] = KillerMoves[board.Ply][0]; KillerMoves[board.Ply][0] = move; } return(beta); } else if (eval > alpha) { alpha = eval; searchPV = false; pv[0] = move; Array.Copy(npv, 0, pv, 1, npv.Length); ttType = TranspositionEntryType.Exact; } } StoreEvaluation(alpha, ttType); return(alpha); }
//ZW search with multicut private int ZWSerach(int depth, int beta, bool cut = true, bool nullMoveAllowed = true) { searchInfo.Nodes++; generator.Setup(); var moves = generator.GetMoves(false); if (transpositions.ContainsKey(board.positionKey)) { searchInfo.Transpositions++; var score = LookupEvaluation(beta - 1, beta); if (score != LookupFailed) { return(score); } } if (!moves.Any()) { //When in Check: Checkmate: if (generator.InCheck) { return(beta - 1); } else //Stalemate { if (0 >= beta) { return(beta); } else { return(beta - 1); } } } else if (board.states.Any(s => s.positionKey == board.positionKey)) { //Repetition if (0 >= beta) { return(beta); } else { return(beta - 1); } } if (depth <= 0) { return(QuiesceneSearch(beta - 1, beta)); } if (depth == 1) { //Futility pruning var currentEval = Evaluation.Evaluate(board); if (currentEval + Evaluation.BishopValue < beta - 1) { searchInfo.FutilityPrunes++; //Prune this node, i.e. go directly to QuiesceneSearch return(QuiesceneSearch(beta - 1, beta)); } } if (nullMoveAllowed && !generator.InCheck && depth >= 1 + R) { searchInfo.NullMoves++; //Null move pruning board.SubmitNullMove(); int eval = -ZWSerach(depth - 1 - R, 1 - beta, cut, false); board.UndoNullMove(); if (eval >= beta) { searchInfo.NullMovesSuccess++; return(beta); } } var orderedMoves = moves.OrderByDescending(m => MoveImportance(m, depth, null)).ToList(); var moveIndex = 0; if (depth >= MC_R && cut) { int c = 0; for (int m = 0; m < MC_M && moveIndex < orderedMoves.Count; m++) { board.SubmitMove(orderedMoves[moveIndex]); int eval = -ZWSerach(depth - 1 - MC_R, 1 - beta, !cut, true); board.UndoMove(); moveIndex++; if (eval >= beta) { c++; if (c == MC_C) { searchInfo.MCPrunes++; return(beta); // mc-prune } } } } for (moveIndex = 0; moveIndex < orderedMoves.Count; moveIndex++) { board.SubmitMove(orderedMoves[moveIndex]); int eval = -ZWSerach(depth - 1, 1 - beta, !cut, true); board.UndoMove(); if (eval >= beta) { return(beta); } } return(beta - 1); }