Beispiel #1
0
        static void Search(
            State root,
            MtdfIds searcher,
            GoSettings settings,
            Program program,
            CancellationToken ct)
        {
            ISearchInfo info;

            {
                using var _ = searcher.IterationCompleted.Subscribe(icInfo =>
                {
                    // todo: even if we have cached tt info, we should be outputting the full pv
                    // todo: output other info fields (see stockfish)
                    var output = $"info depth {icInfo.Depth} time {(int)icInfo.Elapsed.TotalMilliseconds} nodes {icInfo.NodesSearched} score cp {icInfo.Score} pv {string.Join(' ', icInfo.Pv)}";
                    WriteLine(output);
                });

                info = searcher.Search(root, ct);
            }

            // if we finished but we're in ponder or infinite mode, wait until we receive "ponderhit" or "stop"

            while (!ct.IsCancellationRequested && (settings.Infinite || program._ponder))
            {
                Thread.Sleep(500);
            }

            // output the best move. if the pv contains more than 1 move (eg. there's not a mate in 1 and we searched more than depth 1),
            // output that too as the next move we expect the user to play.

            Debug.Assert(!info.Pv.IsEmpty);
            var output = $"bestmove {info.Pv[0]}";

            if (info.Pv.Length > 1)
            {
                output += $" ponder {info.Pv[1]}";
            }
            WriteLine(output);
        }
Beispiel #2
0
        void Go(Stack <string> tokens)
        {
            if (_root == null)
            {
                Error.WriteLine("Position not set, use the 'position' command to set it");
                return;
            }

            var settings = new GoSettings();

            while (tokens.TryPop(out var paramName))
            {
                switch (paramName)
                {
                case "searchmoves":     // needs to be the last command on the line
                    var searchMoves = new List <Move>();
                    while (tokens.TryPop(out var move))
                    {
                        searchMoves.Add(Move.ParseLong(move));
                    }
                    settings.SearchMoves = searchMoves.ToImmutableArray();
                    break;

                case "ponder":
                    _ponder = true;
                    break;

                case "depth":
                    settings.Depth = int.Parse(tokens.Pop());
                    break;

                case "nodes":
                    settings.Nodes = int.Parse(tokens.Pop());
                    break;

                case "mate":     // todo
                    settings.Mate = int.Parse(tokens.Pop());
                    break;

                case "movetime":     // todo
                    settings.MoveTime = TimeSpan.FromMilliseconds(int.Parse(tokens.Pop()));
                    break;

                case "infinite":
                    settings.Infinite = true;
                    break;
                }
            }

            // go ahead with the search

            var searcher = new MtdfIds();

            searcher.Depth    = settings.Depth ?? ((settings.Infinite || settings.Nodes != null) ? int.MaxValue : DefaultSearchDepth);
            searcher.MaxNodes = settings.Nodes ?? int.MaxValue;

            int ttCapacity = _options.Get <int>("Hash") * EntriesPerMb;

            if (_tt == null || _tt.Capacity != ttCapacity)
            {
                _tt = searcher.MakeTt(ttCapacity);
            }
            searcher.Tt = _tt;

            _ponder = false;
            var cts = new CancellationTokenSource();

            // define the task and either run it straight away, or add it to the queue if there's one in progress

            var rootCopy   = _root; // if a new search is requested before this one is started, we don't want to pick up on a new position
            var searchTask = new Task(() =>
            {
                try
                {
                    _cts = cts;
                    Search(rootCopy, searcher, settings, this, cts.Token);
                }
                // exceptions don't propagate to the main thread unless they are explicitly handled like this
                catch (Exception e)
                {
                    Error.WriteLine(e);
                }
                finally
                {
                    cts.Dispose();
                    _cts = null;

                    // run the next task if one is queued

                    if (_searchQueue.TryDequeue(out var nextTask))
                    {
                        nextTask.Start();
                    }
                    else
                    {
                        _searchInProgress = false;
                    }
                }
            });

            if (!_searchInProgress)
            {
                Debug.Assert(_searchQueue.IsEmpty);
                _searchInProgress = true;
                searchTask.Start();
            }
            else
            {
                _searchQueue.Enqueue(searchTask);
            }
        }