Beispiel #1
0
    // id_loop() is the main iterative deepening loop. It calls search() repeatedly
    // with increasing depth until the allocated thinking time has been consumed,
    // user stops the search, or the maximum search depth is reached.

    private static void id_loop(Position pos)
    {
        var stack = new Stack[_.MAX_PLY + 4];
        for (var idx = 0; idx < stack.Length; idx++)
        {
            stack[idx] = new Stack();
        }

        var ss = new StackArrayWrapper(stack, 2); // To allow referencing (ss-2) and (ss+2)

        ValueT alpha, delta;

        var easyMove = EasyMove.get(pos.key());
        EasyMove.clear();

        //TODO: need to memset?
        //Math.Memset(ss - 2, 0, 5 * sizeof(Stack));

        var depth = Depth.DEPTH_ZERO;
        BestMoveChanges = 0;
        var bestValue = delta = alpha = -Value.VALUE_INFINITE;
        var beta = Value.VALUE_INFINITE;

        TranspositionTable.new_search();

        var multiPV = uint.Parse(OptionMap.Instance["MultiPV"].v);
        var skill = new Skill(int.Parse(OptionMap.Instance["Skill Level"].v));

        // When playing with strength handicap enable MultiPV search that we will
        // use behind the scenes to retrieve a set of possible moves.
        if (skill.enabled())
        {
            multiPV = Math.Max(multiPV, 4);
            multiPV = Math.Max(multiPV, 4);
            multiPV = Math.Max(multiPV, 4);
        }

        multiPV = (uint) Math.Min(multiPV, RootMoves.Count);

        // Iterative deepening loop until requested to stop or target depth reached;
        while (++depth < _.MAX_PLY && !Signals.stop && (Limits.depth == 0 || depth <= Limits.depth))
        {
            // Age out PV variability metric
            BestMoveChanges *= 0.5;

            // Save the last iteration's scores before first PV line is searched and
            // all the move scores except the (new) PV are set to -VALUE_INFINITE.
            foreach (var rm in RootMoves)
            {
                rm.previousScore = rm.score;
            }

            // MultiPV loop. We perform a full root search for each PV line
            for (PVIdx = 0; PVIdx < multiPV && !Signals.stop; ++PVIdx)
            {
                // Reset aspiration window starting size
                if (depth >= 5*Depth.ONE_PLY_C)
                {
                    delta = Value.Create(16);
                    alpha = Value.Create(Math.Max(RootMoves[(int) PVIdx].previousScore - delta, -Value.VALUE_INFINITE));
                    beta = Value.Create(Math.Min(RootMoves[(int) PVIdx].previousScore + delta, Value.VALUE_INFINITE));
                }

                // Start with a small aspiration window and, in the case of a fail
                // high/low, re-search with a bigger window until we're not failing
                // high/low anymore.
                while (true)
                {
                    bestValue = search(NodeType.Root, false, pos, ss, alpha, beta, depth, false);

                    // Bring the best move to the front. It is critical that sorting
                    // is done with a stable algorithm because all the values but the
                    // first and eventually the new best one are set to -VALUE_INFINITE
                    // and we want to keep the same order for all the moves except the
                    // new PV that goes to the front. Note that in case of MultiPV
                    // search the already searched PV lines are preserved.

                    //TODO: Check for stable sort replacement
                    Utils.stable_sort(RootMoves, (int) PVIdx, RootMoves.Count);
                    //std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end());

                    // Write PV back to transposition table in case the relevant
                    // entries have been overwritten during the search.
                    for (var i = 0; i <= PVIdx; ++i)
                    {
                        RootMoves[i].insert_pv_in_tt(pos);
                    }

                    // If search has been stopped break immediately. Sorting and
                    // writing PV back to TT is safe because RootMoves is still
                    // valid, although it refers to previous iteration.
                    if (Signals.stop)
                    {
                        break;
                    }

                    // When failing high/low give some update (without cluttering
                    // the UI) before a re-search.
                    if (multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && TimeManagement.elapsed() > 3000)
                    {
                        Output.WriteLine(UCI.pv(pos, depth, alpha, beta));
                    }

                    // In case of failing low/high increase aspiration window and
                    // re-search, otherwise exit the loop.
                    if (bestValue <= alpha)
                    {
                        beta = (alpha + beta)/2;
                        alpha = Value.Create(Math.Max(bestValue - delta, -Value.VALUE_INFINITE));

                        Signals.failedLowAtRoot = true;
                        Signals.stopOnPonderhit = false;
                    }
                    else if (bestValue >= beta)
                    {
                        alpha = (alpha + beta)/2;
                        beta = Value.Create(Math.Min(bestValue + delta, Value.VALUE_INFINITE));
                    }
                    else
                    {
                        break;
                    }

                    delta += delta/2;

                    Debug.Assert(alpha >= -Value.VALUE_INFINITE && beta <= Value.VALUE_INFINITE);
                }

                // Sort the PV lines searched so far and update the GUI
                //TODO: Check for stable sort replacement
                Utils.stable_sort(RootMoves, 0, (int) PVIdx + 1);
                //std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1);

                if (Signals.stop)
                {
                    Output.WriteLine($"info nodes {RootPos.nodes_searched()} time {TimeManagement.elapsed()}");
                }

                else if (PVIdx + 1 == multiPV || TimeManagement.elapsed() > 3000)
                {
                    Output.WriteLine(UCI.pv(pos, depth, alpha, beta));
                }
            }

            // If skill level is enabled and time is up, pick a sub-optimal best move
            if (skill.enabled() && skill.time_to_pick(depth))
            {
                skill.pick_best(multiPV);
            }

            // Have we found a "mate in x"?
            if (Limits.mate != 0 && bestValue >= Value.VALUE_MATE_IN_MAX_PLY
                && Value.VALUE_MATE - bestValue <= 2*Limits.mate)
            {
                Signals.stop = true;
            }

            // Do we have time for the next iteration? Can we stop searching now?
            if (Limits.use_time_management())
            {
                if (!Signals.stop && !Signals.stopOnPonderhit)
                {
                    // Take some extra time if the best move has changed
                    if (depth > 4*Depth.ONE_PLY && multiPV == 1)
                    {
                        TimeManagement.pv_instability(BestMoveChanges);
                    }

                    // Stop the search if only one legal move is available or all
                    // of the available time has been used or we matched an easyMove
                    // from the previous search and just did a fast verification.
                    if (RootMoves.Count == 1 || TimeManagement.elapsed() > TimeManagement.available()
                        || (RootMoves[0].pv[0] == easyMove && BestMoveChanges < 0.03
                            && TimeManagement.elapsed() > TimeManagement.available()/10))
                    {
                        // If we are allowed to ponder do not stop the search now but
                        // keep pondering until the GUI sends "ponderhit" or "stop".
                        if (Limits.ponder)
                        {
                            Signals.stopOnPonderhit = true;
                        }
                        else
                        {
                            Signals.stop = true;
                        }
                    }
                }

                if (RootMoves[0].pv.Count >= 3)
                {
                    EasyMove.update(pos, RootMoves[0].pv);
                }
                else
                {
                    EasyMove.clear();
                }
            }
        }

        // Clear any candidate easy move that wasn't stable for the last search
        // iterations; the second condition prevents consecutive fast moves.
        if (EasyMove.stableCnt < 6 || TimeManagement.elapsed() < TimeManagement.available())
        {
            EasyMove.clear();
        }

        // If skill level is enabled, swap best PV line with the sub-optimal one
        if (skill.enabled())
        {
            var foundIdx = RootMoves.FindIndex(rootMove1 => rootMove1.pv[0] == skill.best_move(multiPV));
            Debug.Assert(foundIdx >= 0);
            var rootMove = RootMoves[0];
            RootMoves[0] = RootMoves[foundIdx];
            RootMoves[foundIdx] = rootMove;
        }
    }