Ejemplo n.º 1
0
        /// <summary>
        /// Executes the parsing.
        /// </summary>
        public static void Run()
        {
            Restrictions.Output = OutputType.UCI;
            Engine   engine   = new Engine();
            Position position = Position.Create(Position.StartingFEN);

            String command;

            while ((command = Console.ReadLine()) != null)
            {
                List <String> terms = new List <String>(command.Split(' '));

                switch (terms[0])
                {
                default:
                    Terminal.WriteLine("Unknown command: {0}", terms[0]);
                    Terminal.WriteLine("Enter \"help\" for assistance.");
                    break;

                case "uci":
                    Terminal.WriteLine("id name " + engine.Name);
                    Terminal.WriteLine("id author Zong Zheng Li");
                    Terminal.WriteLine("option name Hash type spin default " + Engine.DefaultHashAllocation + " min 1 max 2047");
                    Terminal.WriteLine("uciok");
                    break;

                case "ucinewgame":
                    engine.Reset();
                    break;

                case "setoption":
                    if (terms.Contains("Hash"))
                    {
                        engine.HashAllocation = Int32.Parse(terms[terms.IndexOf("value") + 1]);
                    }
                    break;

                case "position":
                    String fen = Position.StartingFEN;
                    if (terms[1] != "startpos")
                    {
                        fen = command.Substring(command.IndexOf("fen") + 4);
                    }
                    position = Position.Create(fen);

                    Int32 movesIndex = terms.IndexOf("moves");
                    if (movesIndex >= 0)
                    {
                        for (Int32 i = movesIndex + 1; i < terms.Count; i++)
                        {
                            position.Make(Move.Create(position, terms[i]));
                        }
                    }
                    break;

                case "go":
                    Restrictions.Reset();
                    for (Int32 i = 1; i < terms.Count; i++)
                    {
                        switch (terms[i])
                        {
                        default:
                        case "infinite":
                            break;

                        case "depth":
                            Restrictions.Depth           = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls = false;
                            break;

                        case "movetime":
                            Restrictions.MoveTime        = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls = false;
                            break;

                        case "wtime":
                            Restrictions.TimeControl[Colour.White] = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls           = true;
                            break;

                        case "btime":
                            Restrictions.TimeControl[Colour.Black] = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls           = true;
                            break;

                        case "winc":
                            Restrictions.TimeIncrement[Colour.White] = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls             = true;
                            break;

                        case "binc":
                            Restrictions.TimeIncrement[Colour.Black] = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls             = true;
                            break;

                        case "nodes":
                            Restrictions.Nodes           = Int32.Parse(terms[i + 1]);
                            Restrictions.UseTimeControls = false;
                            break;

                        case "ponder":
                            // TODO: implement command.
                            break;

                        case "mate":
                            // TODO: implement command.
                            break;

                        case "movestogo":
                            // TODO: implement command.
                            break;
                        }
                    }
                    new Thread(new ThreadStart(() => {
                        Int32 bestMove = engine.GetMove(position);
                        Terminal.WriteLine("bestmove " + Stringify.Move(bestMove));
                    }))
                    {
                        IsBackground = true
                    }.Start();
                    break;

                case "stop":
                    engine.Stop();
                    break;

                case "isready":
                    Terminal.WriteLine("readyok");
                    break;

                case "quit":
                    return;

                case "perft":
                    Perft.Iterate(position, Int32.Parse(terms[1]));
                    break;

                case "divide":
                    Perft.Divide(position, Int32.Parse(terms[1]));
                    break;

                case "draw":
                    Terminal.WriteLine(position);
                    break;

                case "fen":
                    Terminal.WriteLine(position.GetFEN());
                    break;

                case "ponderhit":
                    // TODO: implement command.
                    break;

                case "register":
                    // TODO: implement command.
                    break;

                case "help":
                    Terminal.WriteLine("Command             Function");
                    Terminal.WriteLine("-----------------------------------------------------------------------");
                    Terminal.WriteLine("position [fen]      Sets the current position to the position denoted");
                    Terminal.WriteLine("                    by the given FEN. \"startpos\" is accepted for the");
                    Terminal.WriteLine("                    starting position");
                    Terminal.WriteLine("go [type] [number]  Searches the current position. Search types include");
                    Terminal.WriteLine("                    \"movetime\", \"depth\", \"nodes\", \"wtime\", \"btime\",");
                    Terminal.WriteLine("                    \"winc\", and \"binc\"");
                    Terminal.WriteLine("perft [number]      Runs perft() on the current position to the given");
                    Terminal.WriteLine("                    depth");
                    Terminal.WriteLine("divide [number]     Runs divide() on the current position for the given");
                    Terminal.WriteLine("                    depth");
                    Terminal.WriteLine("fen                 Prints the FEN of the current position.");
                    Terminal.WriteLine("draw                Draws the current position");
                    Terminal.WriteLine("stop                Stops an ongoing search");
                    Terminal.WriteLine("quit                Exits the application");
                    Terminal.WriteLine("-----------------------------------------------------------------------");
                    break;
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Returns the dynamic value of the position as determined by a recursive
        /// search that terminates upon reaching a quiescent position.
        /// </summary>
        /// <param name="position">The position to search on.</param>
        /// <param name="ply">The number of plies from the root position.</param>
        /// <param name="alpha">The lower bound on the value of the best move.</param>
        /// <param name="beta">The upper bound on the value of the best move.</param>
        /// <returns>The value of the termination position given optimal play.</returns>
        private Int32 Quiescence(Position position, Int32 ply, Int32 alpha, Int32 beta)
        {
            _totalNodes++;
            _quiescenceNodes++;

            // Evaluate the position statically. Check for upper bound cutoff and lower
            // bound improvement.
            Int32 value = Evaluate(position);

            if (value >= beta)
            {
                return(value);
            }
            if (value > alpha)
            {
                alpha = value;
            }

            // Initialize variables and generate the pseudo-legal moves to be
            // considered. Perform basic move ordering and sort the moves.
            Int32 colour = position.SideToMove;

            Int32[] moves      = _generatedMoves[ply];
            Int32   movesCount = position.PseudoQuiescenceMoves(moves);

            for (Int32 i = 0; i < movesCount; i++)
            {
                _moveValues[i] = MoveOrderingValue(moves[i]);
            }
            Sort(moves, _moveValues, movesCount);

            // Go through the move list.
            for (Int32 i = 0; i < movesCount; i++)
            {
                _movesSearched++;
                Int32 move = moves[i];

                // Consider the move only if it doesn't immediately lose material. This
                // improves efficiency.
                if ((Move.Piece(move) & Piece.Mask) <= (Move.Capture(move) & Piece.Mask) || EvaluateStaticExchange(position, move) >= 0)
                {
                    // Make the move.
                    position.Make(move);

                    // Search the move if it is legal. This is equivalent to not leaving the
                    // king in check.
                    if (!position.InCheck(colour))
                    {
                        value = -Quiescence(position, ply + 1, -beta, -alpha);

                        // Check for upper bound cutoff and lower bound improvement.
                        if (value >= beta)
                        {
                            position.Unmake(move);
                            return(value);
                        }
                        if (value > alpha)
                        {
                            alpha = value;
                        }
                    }

                    // Unmake the move.
                    position.Unmake(move);
                }
            }
            return(alpha);
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Returns whether the given move is a dangerous pawn advance. A dangerous
 /// pawn advance is a pawn move that results in the pawn being in a position
 /// in which no enemy pawns can threaten or block it.
 /// </summary>
 /// <param name="move">The move to consider.</param>
 /// <param name="passedPawnPreventionBitboard">A bitboard giving the long term attack possibilities of the enemy pawns.</param>
 /// <returns>Whether the given move is a dangerous pawn advance.</returns>
 private Boolean IsDangerousPawnAdvance(Int32 move, UInt64 passedPawnPreventionBitboard)
 {
     return(Move.IsPawnAdvance(move) && ((1UL << Move.To(move)) & passedPawnPreventionBitboard) == 0);
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Returns the dynamic value of the position as determined by a recursive
        /// search to the given depth. This implements the main search algorithm.
        /// </summary>
        /// <param name="position">The position to search on.</param>
        /// <param name="depth">The depth to search to.</param>
        /// <param name="ply">The number of plies from the root position.</param>
        /// <param name="alpha">The lower bound on the value of the best move.</param>
        /// <param name="beta">The upper bound on the value of the best move.</param>
        /// <param name="inCheck">Whether the side to play is in check.</param>
        /// <param name="allowNull">Whether a null move is permitted.</param>
        /// <returns>The value of the termination position given optimal play.</returns>
        private Int32 Search(Position position, Int32 depth, Int32 ply, Int32 alpha, Int32 beta, Boolean inCheck, Boolean allowNull = true)
        {
            // Check whether to enter quiescence search and initialize pv length.
            _pvLength[ply] = 0;
            if (depth <= 0 && !inCheck)
            {
                return(Quiescence(position, ply, alpha, beta));
            }

            // Check for time extension and search termination. This is done once for
            // every given number of nodes for efficency.
            if (++_totalNodes > _referenceNodes)
            {
                _referenceNodes += NodeResolution;

                // Apply loss time extension. The value of the best move for the current
                // root position is compared with the value of the previous root position.
                // If there is a large loss, a time extension is given.
                Int32 loss = _finalAlpha - _rootAlpha;
                if (loss >= TimeControlsLossResolution)
                {
                    Int32 index = Math.Min(loss / TimeControlsLossResolution, TimeControlsLossExtension.Length - 1);
                    TryTimeExtension(TimeControlsLossThreshold, TimeControlsLossExtension[index]);
                }

                if (_stopwatch.ElapsedMilliseconds >= _timeLimit + _timeExtension || _totalNodes >= Restrictions.Nodes)
                {
                    _abortSearch = true;
                }
            }
            if (_abortSearch)
            {
                return(Infinity);
            }

            // Perform draw detection.
            Int32 drawValue       = ((ply & 1) == 0) ? DrawValue : -DrawValue;
            Int32 drawRepetitions = (ply > 2) ? 2 : 3;

            if (position.FiftyMovesClock >= 100 || position.InsufficientMaterial() || position.HasRepeated(drawRepetitions))
            {
                return(drawValue);
            }

            // Perform mate distance pruning.
            Int32 mateAlpha = Math.Max(alpha, -(CheckmateValue - ply));
            Int32 mateBeta  = Math.Min(beta, CheckmateValue - (ply + 1));

            if (mateAlpha >= mateBeta)
            {
                return(mateAlpha);
            }

            // Perform hash probe.
            _hashProbes++;
            Int32     hashMove = Move.Invalid;
            HashEntry hashEntry;

            if (_table.TryProbe(position.ZobristKey, out hashEntry))
            {
                hashMove = hashEntry.Move;
                if (hashEntry.Depth >= depth)
                {
                    Int32 hashType  = hashEntry.Type;
                    Int32 hashValue = hashEntry.GetValue(ply);
                    if ((hashType == HashEntry.Beta && hashValue >= beta) || (hashType == HashEntry.Alpha && hashValue <= alpha))
                    {
                        _hashCutoffs++;
                        return(hashValue);
                    }
                }
            }

            Int32 colour = position.SideToMove;

            // Apply null move heuristic.
            if (allowNull && !inCheck && position.Bitboard[colour] != (position.Bitboard[colour | Piece.King] | position.Bitboard[colour | Piece.Pawn]))
            {
                position.MakeNull();
                Int32 reduction = NullMoveReduction + (depth >= NullMoveAggressiveDepth ? depth / NullMoveAggressiveDivisor : 0);
                Int32 value     = -Search(position, depth - 1 - reduction, ply + 1, -beta, -beta + 1, false, false);
                position.UnmakeNull();
                if (value >= beta)
                {
                    return(value);
                }
            }

            // Generate legal moves and perform basic move ordering.
            Int32[] moves      = _generatedMoves[ply];
            Int32   movesCount = position.LegalMoves(moves);

            if (movesCount == 0)
            {
                return(inCheck ? -(CheckmateValue - ply) : drawValue);
            }
            for (Int32 i = 0; i < movesCount; i++)
            {
                _moveValues[i] = MoveOrderingValue(moves[i]);
            }

            // Apply single reply and check extensions.
            if (movesCount == 1 || inCheck)
            {
                depth++;
            }

            // Perform killer move ordering.
            _killerMoveChecks++;
            bool killerMoveFound = false;

            for (Int32 slot = 0; slot < KillerMovesAllocation; slot++)
            {
                Int32 killerMove = _killerMoves[ply][slot];
                for (Int32 i = 0; i < movesCount; i++)
                {
                    if (moves[i] == killerMove)
                    {
                        _moveValues[i] = KillerMoveValue + slot * KillerMoveSlotValue;
                        if (!killerMoveFound)
                        {
                            _killerMoveMatches++;
                        }
                        killerMoveFound = true;
                        break;
                    }
                }
            }

            // Perform hash move ordering.
            _hashMoveChecks++;
            if (hashMove != Move.Invalid)
            {
                for (Int32 i = 0; i < movesCount; i++)
                {
                    if (moves[i] == hashMove)
                    {
                        _moveValues[i] = HashMoveValue;
                        _hashMoveMatches++;
                        break;
                    }
                }
            }

            // Check for futility pruning activation.
            Boolean futileNode    = false;
            Int32   futilityValue = 0;

            if (depth < FutilityMargin.Length && !inCheck)
            {
                futilityValue = Evaluate(position) + FutilityMargin[depth];
                futileNode    = futilityValue <= alpha;
            }

            // Sort the moves based on their ordering values and initialize variables.
            Sort(moves, _moveValues, movesCount);
            Int32 irreducibleMoves = 1;

            while (irreducibleMoves < movesCount && _moveValues[irreducibleMoves] > 0)
            {
                irreducibleMoves++;
            }
            UInt64 preventionBitboard = PassedPawnPreventionBitboard(position);
            Int32  bestType           = HashEntry.Alpha;
            Int32  bestMove           = moves[0];

            // Go through the move list.
            for (Int32 i = 0; i < movesCount; i++)
            {
                _movesSearched++;

                Int32   move        = moves[i];
                Boolean causesCheck = position.CausesCheck(move);
                Boolean dangerous   = inCheck || causesCheck || alpha < -NearCheckmateValue || IsDangerousPawnAdvance(move, preventionBitboard);
                Boolean reducible   = i + 1 > irreducibleMoves;

                // Perform futility pruning.
                if (futileNode && !dangerous && futilityValue + PieceValue[Move.Capture(move)] <= alpha)
                {
                    _futileMoves++;
                    continue;
                }

                // Make the move and initialize its value.
                position.Make(move);
                Int32 value = alpha + 1;

                // Perform late move reductions.
                if (reducible && !dangerous)
                {
                    value = -Search(position, depth - 1 - LateMoveReduction, ply + 1, -alpha - 1, -alpha, causesCheck);
                }

                // Perform principal variation search.
                else if (i > 0)
                {
                    value = -Search(position, depth - 1, ply + 1, -alpha - 1, -alpha, causesCheck);
                }

                // Perform a full search.
                if (value > alpha)
                {
                    value = -Search(position, depth - 1, ply + 1, -beta, -alpha, causesCheck);
                }

                // Unmake the move and check for search termination.
                position.Unmake(move);
                if (_abortSearch)
                {
                    return(Infinity);
                }

                // Check for upper bound cutoff.
                if (value >= beta)
                {
                    _table.Store(new HashEntry(position, depth, ply, move, value, HashEntry.Beta));
                    if (reducible)
                    {
                        for (Int32 j = _killerMoves[ply].Length - 2; j >= 0; j--)
                        {
                            _killerMoves[ply][j + 1] = _killerMoves[ply][j];
                        }
                        _killerMoves[ply][0] = move;
                    }
                    return(value);
                }

                // Check for lower bound improvement.
                if (value > alpha)
                {
                    alpha    = value;
                    bestMove = move;
                    bestType = HashEntry.Exact;

                    // Collect the principal variation.
                    _pvMoves[ply][0] = move;
                    for (Int32 j = 0; j < _pvLength[ply + 1]; j++)
                    {
                        _pvMoves[ply][j + 1] = _pvMoves[ply + 1][j];
                    }
                    _pvLength[ply] = _pvLength[ply + 1] + 1;
                }
            }

            // Store the results in the hash table and return the lower bound of the
            // value of the position.
            _table.Store(new HashEntry(position, depth, ply, bestMove, alpha, bestType));
            return(alpha);
        }