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 PVList Search(int startDepth, CancellationToken ct) { SearchStats = new SearchStats(); killerMoves.Prepare(gameState.IrrevStates); PVList pv = null; bool con; int depth = startDepth; do { PVList newPV = pvSearch.Calculate(depth, pv, ct, SearchStats); if (!ct.IsCancellationRequested) { pv = newPV; } depth++; con = !ct.IsCancellationRequested && (!config.ConstantDepth || depth <= config.Depth); } while (con); return(pv); }
public async Task <PVList> Search(CancellationToken ct) { Stopwatch watch = new Stopwatch(); watch.Start(); List <Task <PVList> > tasks = new List <Task <PVList> >(); int currentThreadsNum = config.Multithreading ? threadsNum : 1; for (int t = 0; t < currentThreadsNum; t++) { SearchThread thread = searchThreads[t]; thread.LoadState(gameState); Task <PVList> engineTask = Task.Run(() => thread.Search(1, ct)); tasks.Add(engineTask); } PVList[] foundPV = await Task.WhenAll(tasks); PVList bestPV = new PVList(); SearchThread bestThread = null; for (int i = 0; i < foundPV.Length; i++) { if (foundPV[i] != null && foundPV[i].First != null && foundPV[i] > bestPV) { bestPV = foundPV[i]; bestThread = searchThreads[i]; } } watch.Stop(); bestThread.SearchStats.Time = watch.ElapsedMilliseconds; string statsJson = JsonConvert.SerializeObject(bestThread.SearchStats); Console.WriteLine(statsJson); Console.WriteLine(bestPV); searchedNodes.Clear(); return(bestPV); }
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; }