public PVList Calculate(int depth, PVList prevPV, CancellationToken ct, SearchStats searchStats) { searchDepth = depth; this.searchStats = searchStats; this.ct = ct; if (searchDepth > 1 && prevPV != null) { currentPVNode = prevPV.First; } PVList pvList = new PVList(); int score = Search(depth, 0, ArtemisEngine.INITIAL_ALPHA, ArtemisEngine.INITIAL_BETA, pvList, false); pvList.Score = score; pvList.Depth = depth; return(pvList); }
public override string ToString() { List <Move> pv = new List <Move>(); PVNode node = First; while (node != null) { pv.Add(node.Move); node = node.Next; } string line = string.Join(", ", pv); string scoreStr = Score > 0 ? "+" + Score : Score.ToString(); string str = $"({Depth}) {scoreStr} {line}"; return(str); }
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); }
public void Replace(PVList lst) { First = lst.First; }
public void AddFirst(PVNode node) { node.Next = First; First = node; }
public PVNode(Move move, PVNode next = null) { Move = move; Next = next; }