Ejemplo n.º 1
0
        /// <summary>
        /// 主要变例搜索
        /// </summary>
        /// <param name="depth">最底部的叶子结点为第0层</param>
        /// <param name="alpha"></param>
        /// <param name="beta"></param>
        /// <returns></returns>
        private int PVS(int depth, int alpha, int beta)
        {
            bool haveFoundPV = false;


            ulong boardKey = board.ZobristKey;   // Zobrist.ZoristHash(board);

            BoardHistory[Ply] = boardKey;

            if (IsRepetition(boardKey, Ply, BoardHistory, HalfmoveClock[Ply]))
            {
                bool  redRepCheck   = true;
                bool  blackRepCheck = true;
                Board b             = new Board(board);
                for (int d = 0; d < HalfmoveClock[Ply]; d++)
                {
                    if (Ply - d == 0)
                    {
                        break;
                    }
                    // TODO: 逻辑不合理的代码:关于将军的判断应该放在Board类中
                    MoveGenerator g = new MoveGenerator(b);
                    if (b.IsRedTurn)
                    {
                        if (redRepCheck && g.IsBlackKingSafe())
                        {
                            redRepCheck = false;
                        }
                    }
                    else
                    {
                        if (blackRepCheck && g.IsRedKingSafe())
                        {
                            blackRepCheck = false;
                        }
                    }
                    b.UnmakeMove(MoveHistory[Ply - d]);
                    if (board.ZobristKey == b.ZobristKey)
                    {
                        break;
                    }
                }

                if (redRepCheck && blackRepCheck)
                {
                    return(0);                              //双方都长将,平局分数
                }
                if (redRepCheck)
                {
                    return(-30000 + 100);
                }
                if (blackRepCheck)
                {
                    return(30000 - 100);
                }
                return(0); // 双方都是允许着法时
            }


            // 探查置换表
            NodeOfSearch entry = transpositionTable.Probe(boardKey);

            // (depth=2)            a
            //                    /   \
            // (depth=1)   entry<b>     c       这里的b是已经搜索过的节点,已经保存在置换表中,entry.Depth = 1
            //                 / \      \
            // (depth=0)      d   e     [f]   (当前搜索的节点是f) depth = 0
            //              ..................
            // 假设b是以前搜索过的节点,已经保存在置换表中,entry.depth=1
            // 当前搜索的节点是f,depth=0,如果f与b的局面相同,由于b的评估值是经过了更深层的搜索得到的,
            // 所以f可以直接用b的评估值,这个结果只会好,不会差,所以应该判断entry.Depth >= depth
            if (entry.NodeType != NodeOfSearch.EMPTY_NODES && entry.Depth > depth)
            {
                switch (entry.NodeType)
                {
                case NodeOfSearch.PV_NODES:
                    return(entry.Score);

                case NodeOfSearch.CUT_NODES:
                    // -------------\         /-----------------\
                    // 评估值的范围 |         |  当前搜索窗口   |
                    //--------------+---------+-----------------+--
                    //      entry.Score  <=  alpha            beta
                    if (entry.Score <= alpha)      // 剪枝!
                    {
                        return(alpha);
                    }
                    //-------------------------\ 
                    //        评估值的范围     |
                    //       /-----------------+---------------\ 
                    //       |           当前搜| 索窗口        |
                    //-------+-----------------+---------------+-------
                    //      alpha          entry.Score   <    beta
                    if (entry.Score < beta)      //调整beta即可
                    {
                        beta = entry.Score;
                    }
                    break;

                case NodeOfSearch.ALL_NODES:
                    //      /-----------------\      /-------------
                    //      |  当前搜索窗口   |      |评估值的范围
                    //------+-----------------+------+--------------
                    //    alpha            beta <= entry.Score
                    if (beta <= entry.Score)      // 剪枝!
                    {
                        return(beta);
                    }
                    //                         /-----------------------
                    //                         |     评估值的范围
                    //       /-----------------+---------------\ 
                    //       |           当前搜| 索窗口        |
                    //-------+-----------------+---------------+-------
                    //      alpha    <     entry.Score       beta
                    if (alpha < entry.Score)      // 此时只要调整alpha即可
                    {
                        alpha = entry.Score;
                    }
                    break;
                }
            }

            // 到达叶子节点
            if (depth == 0)
            {
                int          valueLeaf = Evaluator.EvaluateAllWithSide(board);
                NodeOfSearch nodeLeaf  = new NodeOfSearch(boardKey, depth, NodeOfSearch.PV_NODES, valueLeaf);
                transpositionTable.RecordHash(nodeLeaf);
                return(valueLeaf);
            }


            int nodeType = NodeOfSearch.ALL_NODES;

            Move[] moveList  = new Move[200];
            int    countMove = MoveGenerator.GenAllMoveList(board, moveList);

            // 无着可走,说明是终止局面,即被将死
            if (countMove == 0)
            {
                int          scoreEndStatus = Evaluator.MIN_EVAL_VALUE + Ply;
                NodeOfSearch nodeEnd        = new NodeOfSearch(boardKey, depth, NodeOfSearch.PV_NODES, scoreEndStatus);
                transpositionTable.RecordHash(nodeEnd);
                return(scoreEndStatus);
            }

            // 利用了置换表中的历史评估数据,进行着法排序
            //            局面"9/4a4/3k5/3N5/3N5/r8/9/9/9/4K4 w"
            // 用迭代加深算法来测试效果:迭代加深计算到第8层
            // DEBUG
            // 不排序时1.7秒,探查并对着法排序时:17秒,代价很大
            // Release
            // 不排序时0.7秒,探查并对着法排序时:7秒

            // if(depth == 0)
            //     SortMovelist(moveList, countMove);

            Move bestMove = null;

            for (int i = 0; i < countMove; i++)
            {
                ++Ply; // 胜利局面中需要这个变量, -MAX + ply
                MoveHistory[Ply]   = moveList[i];
                HalfmoveClock[Ply] = moveList[i].Irreversible ? 0 : HalfmoveClock[Ply - 1] + 1;
                board.MakeMove(moveList[i]);

                int score;
                if (haveFoundPV)
                {
                    score = -PVS(depth - 1, -alpha - 1, -alpha);
                    if ((score > alpha) && (score < beta))
                    { // 检查失败
                        score = -PVS(depth - 1, -beta, -alpha);
                    }
                }
                else
                {
                    score = -PVS(depth - 1, -beta, -alpha);
                }
                board.UnmakeMove(moveList[i]);
                --Ply;

                if (score >= beta)
                {
                    //PrintDebugInfo("发生剪枝!bestScore >= beta: " + bestScore + " >= " + beta);
                    NodeOfSearch nodeBeta = new NodeOfSearch(boardKey, depth, NodeOfSearch.CUT_NODES, beta, moveList[i]);
                    transpositionTable.RecordHash(nodeBeta);
                    return(beta);
                }
                if (score > alpha)
                {
                    // 这时只是记录alpha值的变化情况,并不写置换表
                    nodeType = NodeOfSearch.PV_NODES;
                    alpha    = score;
                    bestMove = moveList[i];
                    //PrintDebugInfo("修改alpha: " + alpha);
                }

                if (engine != null && stopWatch.ElapsedMilliseconds > engine.timeLimit)
                {
                    break;
                }
            }

            NodeOfSearch node = new NodeOfSearch(boardKey, depth, nodeType, alpha, bestMove);

            transpositionTable.RecordHash(node);
            return(alpha);
        }
Ejemplo n.º 2
0
        /**********************************************************
         * 带置换表的Alphabeta搜索算法的示例代码供参考:
         * int AlphaBeta(int depth, int alpha, int beta) {
         *               int hashf = hashfALPHA;
         *               if ((val = ProbeHash(depth, alpha, beta)) != valUNKNOWN) {
         *                   // 【valUNKNOWN必须小于-INFINITY或大于INFINITY,否则会跟评价值混淆。】
         *                   return val;
         *               }
         *               if (depth == 0) {
         *                   val = Evaluate();
         *                   RecordHash(depth, val, hashfEXACT);
         *                   return val;
         *               }
         *               GenerateLegalMoves();
         *               while (MovesLeft()) {
         *                   MakeNextMove();
         *                   val = -AlphaBeta(depth - 1, -beta, -alpha);
         *                   UnmakeMove();
         *                   if (val >= beta) {
         *                       RecordHash(depth, beta, hashfBETA);
         *                       return beta;
         *                   }
         *                   if (val > alpha) {
         *                       hashf = hashfEXACT;
         *                       alpha = val;
         *                   }
         *                }
         *                RecordHash(depth, alpha, hashf);
         *                return alpha;
         * }
         *****************************************************************/

        /// <summary>
        ///
        /// </summary>
        /// <param name="depth">最底部的叶子结点为第0层</param>
        /// <param name="alpha"></param>
        /// <param name="beta"></param>
        /// <returns></returns>
        private int NegaAlphaBetaTT(int depth, int alpha, int beta)
        {
            ulong boardKey = board.ZobristKey;   // Zobrist.ZoristHash(board);

            BoardHistory[Ply] = boardKey;

            if (IsRepetition(boardKey, Ply, BoardHistory, HalfmoveClock[Ply]))
            {
                bool  redRepCheck   = true;
                bool  blackRepCheck = true;
                Board b             = board;
                for (int d = 0; d < HalfmoveClock[Ply]; d++)
                {
                    // TODO: 逻辑不合理的代码:关于将军的判断应该放在Board类中
                    MoveGenerator g = new MoveGenerator(b);
                    if (b.IsRedTurn)
                    {
                        if (redRepCheck && g.IsBlackKingSafe())
                        {
                            redRepCheck = false;
                        }
                    }
                    else
                    {
                        if (blackRepCheck && g.IsRedKingSafe())
                        {
                            blackRepCheck = false;
                        }
                    }
                    b.UnmakeMove(MoveHistory[Ply - d]);
                }

                if (redRepCheck && blackRepCheck)
                {
                    return(0);                              //双方都长将,平局分数
                }
                if (redRepCheck)
                {
                    return(-30000 + 100);
                }
                if (blackRepCheck)
                {
                    return(30000 - 100);
                }
                return(0); // 双方都是允许着法时
            }


            // 探查置换表
            //UInt64 code1, code2;
            NodeOfSearch entry = transpositionTable.Probe(boardKey);

            // (depth=2)            a
            //                    /   \
            // (depth=1)   entry<b>     c       这里的b是已经搜索过的节点,已经保存在置换表中,entry.Depth = 1
            //                 / \      \
            // (depth=0)      d   e     [f]   (当前搜索的节点是f) depth = 0
            //              ..................
            // 假设b是以前搜索过的节点,已经保存在置换表中,entry.depth=1
            // 当前搜索的节点是f,depth=0,如果f与b的局面相同,由于b的评估值是经过了更深层的搜索得到的,
            // 所以f可以直接用b的评估值,这个结果只会好,不会差,所以应该判断entry.Depth >= depth
            if (entry.NodeType != NodeOfSearch.EMPTY_NODES && entry.Depth > depth)
            {
                //PrintDebugInfo("Hash表命中!" + entry);
                switch (entry.NodeType)
                {
                case NodeOfSearch.PV_NODES:
                    return(entry.Score);

                case NodeOfSearch.CUT_NODES:
                    // -------------\         /-----------------\
                    // 评估值的范围 |         |  当前搜索窗口   |
                    //--------------+---------+-----------------+--
                    //      entry.Score  <=  alpha            beta
                    if (entry.Score <= alpha)      // 剪枝!
                    {
                        return(alpha);
                    }
                    //-------------------------\ 
                    //        评估值的范围     |
                    //       /-----------------+---------------\ 
                    //       |           当前搜| 索窗口        |
                    //-------+-----------------+---------------+-------
                    //      alpha          entry.Score   <    beta
                    if (entry.Score < beta)      //调整beta即可
                    {
                        beta = entry.Score;
                    }
                    break;

                case NodeOfSearch.ALL_NODES:
                    //      /-----------------\      /-------------
                    //      |  当前搜索窗口   |      |评估值的范围
                    //------+-----------------+------+--------------
                    //    alpha            beta <= entry.Score
                    if (beta <= entry.Score)      // 剪枝!
                    {
                        return(beta);
                    }
                    //                         /-----------------------
                    //                         |     评估值的范围
                    //       /-----------------+---------------\ 
                    //       |           当前搜| 索窗口        |
                    //-------+-----------------+---------------+-------
                    //      alpha    <     entry.Score       beta
                    if (alpha < entry.Score)      // 此时只要调整alpha即可
                    {
                        alpha = entry.Score;
                    }
                    break;
                }
            }

            // 到达叶子节点
            if (depth == 0)
            {
                int valueLeaf = Evaluator.EvaluateAllWithSide(board);
                // 应该肯定是EXACT节点吧?

                /* if(v0 <= alpha)
                 *  node.Type = NODE_ALPHA;
                 * else if(v0 >= beta)
                 *  node.Type = NODE_BETA;
                 * else  */
                NodeOfSearch nodeLeaf = new NodeOfSearch(boardKey, depth, NodeOfSearch.PV_NODES, valueLeaf);
                transpositionTable.RecordHash(nodeLeaf);
                //DEBUG(DBG_DEBUG, SPACES[depth] << "到达最大深度:" << " return score: "<< v0);

                return(valueLeaf);
            }


            int nodeType = NodeOfSearch.ALL_NODES;

            Move[] moveList  = new Move[200];
            int    countMove = MoveGenerator.GenAllMoveList(board, moveList);

            // 无着可走,说明是终止局面,即被将死
            if (countMove == 0)
            {
                // TODO: 杀棋分数调整 http://www.xqbase.com/computer/stepbystep5.htm
                // (1) 对于RecordHash:置换表项记录的杀棋步数 = 实际杀棋步数 - 置换表项距离根节点的步数;
                   // (2) 对于ProbeHash:实际杀棋步数 = 置换表项记录的杀棋步数 + 置换表项距离根节点的步数。
                int          scoreEndStatus = Evaluator.MIN_EVAL_VALUE + Ply;
                NodeOfSearch nodeEnd        = new NodeOfSearch(boardKey, depth, NodeOfSearch.PV_NODES, scoreEndStatus);
                transpositionTable.RecordHash(nodeEnd);
                return(scoreEndStatus);
            }

            // 利用了置换表中的历史评估数据,进行着法排序
            //            局面"9/4a4/3k5/3N5/3N5/r8/9/9/9/4K4 w"
            // 用迭代加深算法来测试效果:迭代加深计算到第8层
            // DEBUG
            // 不排序时1.7秒,探查并对着法排序时:17秒,代价很大
            // Release
            // 不排序时0.7秒,探查并对着法排序时:7秒

            // if(depth == 0)
            //     SortMovelist(moveList, countMove);


            //int bestScore = Evaluator.MIN_EVAL_VALUE + depth;
            Move bestMove = null;

            for (int i = 0; i < countMove; i++)
            {
                ++Ply; // 胜利局面中需要这个变量, -MAX + ply
                MoveHistory[Ply]   = moveList[i];
                HalfmoveClock[Ply] = moveList[i].Irreversible ? 0 : HalfmoveClock[Ply - 1] + 1;
                board.MakeMove(moveList[i]);
                int score = -NegaAlphaBetaTT(depth - 1, -beta, -alpha);
                board.UnmakeMove(moveList[i]);

                --Ply;
                //HalfmoveClock[Ply] = moveList[i].Irreversible ? 0 : HalfmoveClock[Ply - 1] + 1;

                // 这里负责记录最佳着法
                //if (score > bestScore)
                //{
                //    bestScore = score;
                //    if(depth == 0) bestMove = moveList[i];
                //}

                if (score >= beta)
                {
                    //PrintDebugInfo("发生剪枝!bestScore >= beta: " + bestScore + " >= " + beta);
                    // 这里记录refutation move
                    NodeOfSearch nodeBeta = new NodeOfSearch(boardKey, depth, NodeOfSearch.CUT_NODES, beta, moveList[i]);
                    transpositionTable.RecordHash(nodeBeta);
                    return(beta);
                }
                if (score > alpha)
                {
                    // alpha = bestScore;  // alpha = score????
                    // 这时只是记录alpha值的变化情况,并不写置换表
                    nodeType = NodeOfSearch.PV_NODES;
                    alpha    = score;
                    bestMove = moveList[i];
                    //PrintDebugInfo("修改alpha: " + alpha);
                }
            }

            NodeOfSearch node = new NodeOfSearch(boardKey, depth, nodeType, alpha, bestMove);

            transpositionTable.RecordHash(node);
            return(alpha);
        }