Example #1
0
        int SearchCut(int alpha, int beta, int depth, int height, int mate_threat, bool allowNull = true)
        {
            beta = Math.Min(beta, G.MATE - height);
            if (depth <= 0)
            {
                if (stepList[stepList.Count - 1].move.checking)
                {
                    //被照将时,推迟进入静态搜索
                    depth++;
                }
                else
                {
                    return(SearchQuiesce(alpha, beta, 0, height));
                }
            }

            stat.ZeroWindowNodes++;
            int best = HarmlessPruning(height);

            if (best >= beta)
            {
                return(best);
            }

#if USE_HASH
            HashStruct t = TT.ReadHash(Key);
            if (t.Move.sqSrc == 0)
            {
                TransKiller = null;
            }
            else
            {
                if (t.AlphaDepth >= depth)
                {
                    if (t.Alpha <= alpha)
                    {
                        return(t.Alpha);
                    }
                }
                if (t.BetaDepth >= depth)
                {
                    if (t.Beta >= beta)
                    {
                        return(t.Beta);
                    }
                }
                if (t.Alpha == t.Beta)
                {
                    return(t.Alpha);
                }
                TransKiller       = t.Move;
                TransKiller.pcSrc = pcSquares[TransKiller.sqSrc];
                TransKiller.pcDst = pcSquares[TransKiller.sqDst];
            }
#endif
            Debug.Assert(height < G.MAX_PLY);
            MOVE lastMove = stepList[stepList.Count - 1].move;
#if NULL_MOVE
            if (allowNull && depth >= G.NullDepth)
            {
                //for any opponent pure loss and bad capture, it's better to take it and see the accurate score
                //for any opponent good capture, we are already so bad, it makes no sense to do null move
                //for those equal material exchange, we must finish recapture
                //for mating and checking moves, we can not skip the moves
                if (!lastMove.checking && lastMove.score > HistoryScore && lastMove.score < GoodScore && lastMove.pcDst == 0 && Math.Abs(beta) < G.WIN)
                {
                    MakeNullMove();
                    int vl = -SearchCut(-beta, -alpha, depth - G.NullReduction - 1, height + 1, mate_threat, false);
                    UnmakeNullMove();
#if NULL_VERIFICATION
                    if (depth > G.VerReduction && vl >= beta)
                    {
                        vl = SearchCut(beta, depth - G.VerReduction, height + 1, false);
                    }
#endif
                    if (vl >= beta)
                    {
                        Debug.Assert(vl < G.WIN);   // do not return unproven mates
                        TT.WriteHash(Key, G.HASH_BETA, vl, depth, new MOVE());
                        stat.NullCutoffs++;
                        return(vl);
                    }
                }
            }
#endif
            IEnumerable <MOVE> moves = GetNextMove(7, height, mate_threat);
            MOVE        mvBest       = new MOVE();
            int         opt_value    = G.MATE;
            List <MOVE> mvPlayed     = new List <MOVE>();
            foreach (MOVE mv in moves)
            {
                int new_depth = depth - 1;
                if (mv.checking)
                {
                    new_depth++;
                }
                if (mate_threat < 0)
                {
                    if (best < -G.WIN && mvPlayed.Count > 0)
                    {
                        mate_threat = height;
                        alpha       = -G.WIN;
                    }
                }
                else
                {
                    MOVE matekiller = Killers[mate_threat + 1, 0];
                    if (mv == matekiller || mate_threat == height && best > -G.WIN || !IsLegalMove(matekiller.sqSrc, matekiller.sqDst))
                    {
                        mate_threat = -1;
                    }
                }
#if HISTORY_PRUNING
                bool reduced = false;
                if (depth >= G.HistoryDepth && !lastMove.checking && new_depth < depth && mvPlayed.Count >= G.HistoryMoveNb)
                {
                    int k = GetHistoryIndex(mv);
                    if ((float)(HistHit[k]) / HistTotal[k] < 0.6)
                    {
                        new_depth--;
                        reduced = true;
                        stat.HistoryReduced++;
                    }
                }
#endif
#if FUTILITY_PRUNING
                if (depth == 1 && !lastMove.checking && !mv.checking && new_depth == 0 && mv.pcDst == 0)
                {
                    if (opt_value == G.MATE)
                    {
                        opt_value = Simple_Evaluate() + G.FutilityMargin;
                    }
                    if (opt_value < beta)
                    {
                        continue;
                    }
                }
#endif
                //if the opponent is delaying mate by checking, do not enter quiesce until the mate killer move is tried
                if (new_depth == 0 && mate_threat > 0)
                {
                    new_depth = 1;
                }
                Debug.Write(new string('\t', height));
                Debug.WriteLine($"{mv} {alpha}, {beta}, {best} {mv.killer}");
                MakeMove(mv, false);
                int vl;
                //to avoid quiesce search beta cut off too early, use -best instead of -alpha as new beta
                if (new_depth == 0 && alpha < G.WIN)
                {
                    vl = -SearchCut(-beta, -best, 0, height + 1, mate_threat);
                }
                else
                {
                    vl = -SearchCut(-beta, -alpha, new_depth, height + 1, mate_threat);
                }
#if HISTORY_PRUNING
                if (vl >= beta && reduced)
                {
                    new_depth++;
                    vl = -SearchCut(-beta, -alpha, new_depth, height + 1, mate_threat);
                    stat.HistoryResearched++;
                }
#endif
                UnmakeMove();

                if (vl > best)
                {
                    best   = vl;
                    mvBest = mv;
                    if (vl >= beta)
                    {
                        SetBestMove(mvBest, best, depth, height);
                        foreach (MOVE m1 in mvPlayed)
                        {
                            HistoryBad(m1);
                        }
#if USE_HASH
                        TT.WriteHash(Key, G.HASH_BETA, best, depth, mvBest);
#endif
                        stat.BetaCutoffs++;
                        return(vl);
                    }
                    if (vl > alpha)
                    {
                        alpha = vl;
                    }
                }
                mvPlayed.Add(mv);
            }
#if USE_HASH
            TT.WriteHash(Key, G.HASH_ALPHA, best, depth, mvBest);
#endif
            return(best);
        }
Example #2
0
        int SearchPV(int alpha, int beta, int depth, int height, out List <MOVE> pvs)
        {
            beta = Math.Min(beta, G.MATE - height);
            pvs  = new List <MOVE>();
            if (depth <= 0)
            {
                if (stepList[stepList.Count - 1].move.checking)
                {
                    //被照将时,推迟进入静态搜索
                    depth++;
                }
                else
                {
                    return(SearchQuiesce(alpha, beta, 0, height));
                }
            }

            stat.PVNodes++;
            int best = HarmlessPruning(height);

            //distance pruning
            if (best >= beta)
            {
                return(best);
            }
            MOVE        mvBest   = new MOVE();
            int         hashFlag = 0;
            List <MOVE> subpv    = null;

            TransKiller = null;
#if USE_HASH
            HashStruct t = TT.ReadHash(Key);
            if (t.Move.sqSrc == 0)
            {
                TransKiller = null;
            }
            else
            {
                if (t.AlphaDepth >= depth)
                {
                    if (t.Alpha <= alpha)
                    {
                        return(t.Alpha);
                    }
                }
                if (t.BetaDepth >= depth)
                {
                    if (t.Beta >= beta)
                    {
                        return(t.Beta);
                    }
                }
                TransKiller       = t.Move;
                TransKiller.pcSrc = pcSquares[TransKiller.sqSrc];
                TransKiller.pcDst = pcSquares[TransKiller.sqDst];
            }
#endif
#if INTERNAL_ITERATIVE_DEEPENING
            if (depth >= G.IIDDepth && TransKiller is null)
            {
                int new_depth = depth - G.IIDReduction;
                Debug.Assert(new_depth > 0);
                int vl = SearchPV(alpha, beta, new_depth, height, out subpv);
                if (vl < alpha)
                {
                    vl = SearchPV(-G.MATE, beta, new_depth, height, out subpv);
                }
                if (subpv.Count > 0)
                {
                    TransKiller = subpv[0];
                }
            }
#endif

            IEnumerable <MOVE> moves = GetNextMove(7, height);
            foreach (MOVE mv in moves)
            {
                Debug.Write(new string('\t', height));
                Debug.WriteLine($"{mv} {alpha}, {beta}, {best},{mv.killer}");
                int new_depth = depth - 1;
                if (mv.sqDst == stepList[stepList.Count - 1].move.sqDst && mv.score > 0 ||
                    mv.checking)
                {
                    new_depth++;
                }
                MakeMove(mv, false);
                int vl;
                if (mvBest.sqSrc == 0)
                {
                    vl = -SearchPV(-beta, -alpha, new_depth, height + 1, out subpv);
                }
                else
                {
                    //当depth == 0, 且best < alpha时,传给quiesce的beta用 -best代替 -alpha,可以避免eval() > beta过早返回。
                    //返回的值和最佳着法只是因为搜索深度浅而貌似高于best。后续重新搜索时这个非最佳着法被首先搜索,降低了效率。
                    if (new_depth == 0 && alpha < G.WIN)
                    {
                        vl = -SearchCut(-alpha - 1, -best, new_depth, height + 1, -1);
                    }
                    else
                    {
                        vl = -SearchCut(-alpha - 1, -alpha, new_depth, height + 1, -1);
                    }
                    if (vl > alpha) // && vl < beta
                    {
                        Debug.WriteLine("Re-search");
                        Debug.Write(new string('\t', height));
                        Debug.WriteLine($"{mv} {alpha}, {beta}, {vl}");
                        vl = -SearchPV(-beta, -vl, new_depth, height + 1, out subpv);
                        stat.PVChanged++;
                    }
                }
                UnmakeMove();
                if (vl > best)
                {
                    best = vl;
                    if (vl >= beta)
                    {
                        stat.BetaCutoffs++;
                        mvBest   = mv;
                        hashFlag = G.HASH_BETA;
                        break;
                    }
                    if (vl > alpha)
                    {
                        alpha    = vl;
                        mvBest   = mv;
                        hashFlag = G.HASH_PV;
                        pvs.Clear();
                        pvs.Add(mv);
                        pvs.AddRange(subpv);
                    }
                }
                else
                {
                    HistoryBad(mv);
                }
            }
            if (best > -G.WIN)
            {
#if USE_HASH
                TT.WriteHash(Key, hashFlag, best, depth, mvBest);
#endif
                if (mvBest.pcSrc != 0)
                {
                    SetBestMove(mvBest, best, depth, height);
                }
            }
            return(best);
        }