private Move computeBestMove(Side side, Chessboard chessboard, int presentCumulativeScore) { currentDepth++; // First, get the set of available moves List <Move> moves = new List <Move>(); Move bestMoveForIteration = new Move(null, null); bool isPlayingSide = chessboard.CurrentMovingSide() == side; foreach (AbstractPiece piece in chessboard.getActivePieces()) { if (piece.side == chessboard.CurrentMovingSide()) { foreach (Position movePosition in piece.GetSafeMovesForCurrentPosition().GetPositions()) { Move newMove = MoveEvaluator.EvaluateMove(new Move(piece, movePosition), isPlayingSide); moves = InsertionSortMove(moves, newMove); } } } if (moves.Count > 0) { bestMoveForIteration = moves.ToArray()[0]; if (currentDepth < maxAllowableDepth) { foreach (Move move in moves) { Chessboard copyChessboard = Chessboard.MakeCopyOfChessboard(chessboard); copyChessboard.MoveTo(copyChessboard.GetPieceAtPosition(move.getPiece().GetCurrentPosition()), move.getPosition()); copyChessboard.ChangeMovingSide(); int followUpScore = computeBestMove(side, copyChessboard, presentCumulativeScore + move.getScore()).getScore(); if (move.getScore() + followUpScore > bestMoveForIteration.getScore()) { bestMoveForIteration = move; } } } } currentDepth--; return(bestMoveForIteration); }
private int Search(int depth, int ply, int alpha, int beta, PVList pvList, bool nullMoveReduction) { if (ct.IsCancellationRequested) { return(0); } searchStats.Nodes++; if (gameState.IsDraw()) { int score = evaluator.GetDrawScore(engine.EngineColor); if (score > alpha) { pvList.Replace(new PVList()); } return(score); } if (gameState.IsCheck() && depth < ArtemisEngine.MAX_DEPTH) { //check extension depth += 1; } int originalAlpha = alpha; ulong hash = gameState.GetIrrevState().ZobristHash; TTHit ttHit = transpositionTable.TryGetValue(hash, depth, alpha, beta, pvList); if (ttHit.HitType == HitType.Hit) { searchStats.TTHits++; return(ttHit.Score); } TranspositionNode ttNode = ttHit.TTNode; if (depth <= 0 || ply == ArtemisEngine.MAX_DEPTH) { int score = quietSearch.Search(alpha, beta); if (score > alpha) { pvList.Replace(new PVList()); } return(score); } Move bestMove = null; bool cutoff = false; PVList newPV = new PVList(); bool PVNode = alpha != beta - 1; if (!PVNode && ttHit.HitType != HitType.AvoidNullMove && !nullMoveReduction && engine.GameStage != GameStage.Endgame && !gameState.IsCheck()) { //null move pruning gameState.MakeNullMove(); int nextDepth = depth - 1 - config.NullMoveDepthReduction; int score = -Search(nextDepth, ply + 1, -beta, -beta + 1, newPV, true); gameState.UnmakeNullMove(); if (score >= beta) { searchStats.NullMoveCutoffs++; searchStats.AlphaBetaCutoffs++; TranspositionNode newNode = new TranspositionNode(NodeType.CutNode, score, nextDepth + 1, null, null); SaveNode(hash, ttNode, newNode); //alpha-beta cutoff return(beta); } } List <Move> moves = gameState.GetMoves(); Move pvMove = null; if (PVNode) { if (currentPVNode != null) { pvMove = currentPVNode.Move; currentPVNode = currentPVNode.Next; } else { PVNode = false; } } Move hashMove = null; if (ttNode != null) { hashMove = ttNode.BestMove; } Move[] killers = killerMoves.GetKillerMoves(ply); moves = moves.OrderByDescending(m => moveEvaluator.EvaluateMove(m, pvMove, hashMove, killers).Score).ToList(); int originalLen = moves.Count; int moveCount = 0; bool lmrCandidatePosition = depth >= 3 && !PVNode && !gameState.IsCheck(); for (int i = 0; i < moves.Count && !cutoff; i++) { Move move = moves[i]; gameState.MakeMove(move); if (move.IsLegal()) { int nextDepth = depth - 1; bool lmrReduction = false; if (lmrCandidatePosition && moveCount >= 4 && move.IsQuiet() && !gameState.IsCheck()) { lmrReduction = true; nextDepth--; searchStats.LMRReductions++; } int score; if (moveCount == 0 || searchDepth == 1) { score = -Search(nextDepth, ply + 1, -beta, -alpha, newPV, false); } else { ulong moveHash = gameState.GetIrrevState().ZobristHash; if (!config.Multithreading || i >= originalLen || searchedNodes.TryAdd(moveHash, true)) { score = -Search(nextDepth, ply + 1, -alpha - 1, -alpha, newPV, false); searchedNodes.TryRemove(moveHash, out _); if (score > alpha) { if (lmrReduction) { nextDepth++; } score = -Search(nextDepth, ply + 1, -beta, -alpha, newPV, false); } } else { moves.Add(move); gameState.UnmakeMove(move); continue; } } if (score >= beta) { //alpha-beta cutoff searchStats.AlphaBetaCutoffs++; alpha = beta; bestMove = move; cutoff = true; killerMoves.AddMove(move, ply); } else if (score > alpha) { alpha = score; bestMove = move; } moveCount++; } gameState.UnmakeMove(move); } if (moveCount == 0) { //player has no legal moves if (gameState.IsCheck()) { return(-PositionEvaluator.CHECKMATE_SCORE - depth); } else { return(0); } } NodeType nodeType = GetNodeType(originalAlpha, beta, alpha); PVList nodePV = null; if (nodeType == NodeType.PVNode) { searchStats.PVNodes++; PVNode node = new PVNode(bestMove); newPV.AddFirst(node); nodePV = newPV; pvList.Replace(newPV); } TranspositionNode updatedNode = new TranspositionNode(nodeType, alpha, depth, bestMove, nodePV); SaveNode(hash, ttNode, updatedNode); return(alpha); }