Example #1
0
        /*! 负极大值搜索,与Minimax算法相比,算法描述更简洁一些
         * 这种搜索拿到实战中是不可行的,只是理论上的一个实验,计算5层都要用42秒。
         * \param depth 搜索深度
         * \return 评估值
         */
        private int NegaMax(int depth)
        {
            /* 在着法生成中有将军的判断,这里就不需要再进行判断了。否则还要进行终止局面的判断。
             * 是不是还有其它终止局面?现在还不得知。
             */

            if (depth == 0)
            {
                return(Evaluator.EvaluateAllWithSide(board));
            }

            int bestScore = Evaluator.MIN_EVAL_VALUE + (MaxSearchDepth - depth);  //这种写法可以搜索深度最短的杀着

            // 着法生成中要进行将军的判断,也就是轮到红方走棋时,红方的走完后,帅不能被将军
            // 在终止局面时,即被将死的局面时,这个countMove返回0
            Move[] moveList   = new Move[200];
            int    countMoves = MoveGenerator.GenAllMoveList(board, moveList);

            for (int i = 0; i < countMoves; i++)
            {
                board.MakeMove(moveList[i]);
                int score = -NegaMax(depth - 1);
                board.UnmakeMove(moveList[i]);
                bestScore = (score > bestScore) ? score : bestScore;
            }
            return(bestScore);
        }
Example #2
0
        /// <summary>
        /// 输出开局库中元素的散列分布图,用图示的办法来查看散列的效果
        /// 2048行、2048列,2^11 * 2^11 * 4 = 16M 正好是16M个盘面情况
        /// 每4个元素为一组,未占用是白色,占用1个是黄色,占用2个是绿色,全部占用(3个)是蓝色
        /// 通过输出的位图可以看到散列效果还是不错的
        /// </summary>
        //public void SaveHashPicture(string filename)
        //{
        //    using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(2048, 2048, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
        //    {
        //        fs.Seek(0, SeekOrigin.Begin);
        //        for (int h = 0; h < 2048; h++)
        //            for (int w = 0; w < 2048; w++)
        //            {
        //                int count = 0;
        //                for (int i = 0; i < 4; i++)
        //                {
        //                    //从文件中取出6个字节,前4个字节是zobrist值,后2字节是相同盘面统计数
        //                    byte[] bytes6 = new byte[6];
        //                    fs.Read(bytes6, 0, 6);
        //                    int zobristInOpening = BitConverter.ToInt32(bytes6, 0);   //前4字节
        //                    //countSameBoard = BitConverter.ToUInt16(bytes6, 4); //后2个字节
        //                    if (zobristInOpening != 0)
        //                    {
        //                        ++count;
        //                    }
        //                }
        //                if (count == 0)
        //                    bmp.SetPixel(w, h, System.Drawing.Color.White);
        //                else if (count == 1)
        //                    bmp.SetPixel(w, h, System.Drawing.Color.Yellow);
        //                else if (count == 2)
        //                    bmp.SetPixel(w, h, System.Drawing.Color.Green);
        //                else
        //                    bmp.SetPixel(w, h, System.Drawing.Color.Blue);
        //            }
        //        bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
        //    }

        //}


        public Move FindBestMove(Board board)
        {
            Move maxUsedMove = new Move(0, 0, 0, 0); // 使用率最高的着法
            int  maxCount    = 0;                    // 保存使用得最多的次数

            // 在所有的可行的着法里查找一遍,可以发现这种办法效率有点低
            Move[] movelist = new Move[200];
            MoveGenerator.GenAllMoveList(board, movelist);
            foreach (Move m in movelist)
            {
                UInt16 countSameBoard;
                board.MakeMove(m);
                FindBoardHash(Zobrist.ZoristHash(board), out countSameBoard);

                //if (countSameBoard != 0)
                //    Console.WriteLine(m + ":  " + countSameBoard);
                if (countSameBoard > maxCount)
                {
                    maxCount    = countSameBoard;
                    maxUsedMove = new Move(m);
                }
                board.UnmakeMove(m);  // 要恢复原始的盘面
            }
            return(maxUsedMove);
        }
Example #3
0
        /// <summary>
        /// 最简单的AlphaBeta剪枝搜索,没有用置换表。
        /// 比MinMax算法要好多了,C++64位计算5层只需0.8秒,6层为6.8秒,7层为151秒。
        /// C#版本5层要10秒
        /// 把搜索到的最好的着法保存在BestMove中。
        /// TODO: 如何能够结束计算???
        /// </summary>
        /// <param name="depth">当前搜索深度</param>
        /// <param name="alpha">初始值可以设置为MIN_EVAL_VALUE</param>
        /// <param name="beta">初始值可以设置为MAX_EVAL_VALUE</param>
        /// <returns>评估值</returns>
        private int NegaAlphaBeta(int depth, int alpha, int beta)
        {
            /* 要进行被将死情况的检测
             * if (board.BlackTurn && board.IsRedKingCheckmated())
             * {
             * return int.MinValue;
             * }
             * else if (board.RedTurn && board.IsBlackKingCheckmated())
             * {
             * return int.MaxValue;
             * }
             */

            if (depth == 0)
            {
                return(Evaluator.EvaluateAllWithSide(board));
            }
            Move[] moveList = new Move[200];

            int countMove = MoveGenerator.GenAllMoveList(board, moveList);

            if (countMove == 0 && depth == 0)
            {
                bestMove = new Move(0, 0, 0, 0);
            }

            int bestScore = Evaluator.MIN_EVAL_VALUE + (MaxSearchDepth - depth);


            for (int i = 0; i < countMove; i++)
            {
                board.MakeMove(moveList[i]);
                int max_alpha_bestscore = (alpha > bestScore) ? alpha : bestScore;
                int score = -NegaAlphaBeta(depth - 1, -beta, -max_alpha_bestscore);
                board.UnmakeMove(moveList[i]);
                if (depth == 0 && engine != null)
                {
                    engine.Output.WriteLine("info depth 0 score " + score + " pv " + moveList[i]);
                }
                if (score > bestScore)
                {
                    bestScore = score;
                    if (depth == 0)
                    {
                        bestMove = moveList[i];
                    }
                    if (bestScore >= beta)
                    {
                        return(bestScore);
                    }
                }
            }
            return(bestScore);
        }
Example #4
0
        /// <summary>
        /// 极小极大值搜索
        /// 这种搜索拿到实战中是不可行的,只是理论上的一个实验,用C++语言计算5层都要用42秒。
        /// </summary>
        /// <param name="depth">当前搜索深度</param>
        /// <param name="maxDepth">最大搜索深度</param>
        /// <returns>评估值</returns>
        private int MiniMax(int depth)
        {
            /* 要进行将死的判断
             * if (board.BlackTurn && board.IsRedKingCheckmated())
             * {
             * return int.MinValue;
             * }
             * else if (board.RedTurn && board.IsBlackKingCheckmated())
             * {
             * return int.MaxValue;
             * }
             */

            if (depth == 0)
            {
                return(Evaluator.EvaluateAll(board));
            }

            int bestScore = board.IsRedTurn ?
                            Evaluator.MIN_EVAL_VALUE + (MaxSearchDepth - depth)
                : Evaluator.MAX_EVAL_VALUE - (MaxSearchDepth - depth);

            Move[] moveList = new Move[200];

            // 在终止局面时,即被将死的局面时,这个countMove返回0
            int countMove = MoveGenerator.GenAllMoveList(board, moveList);

            for (int i = 0; i < countMove; i++)
            {
                board.MakeMove(moveList[i]);
                int score = MiniMax(depth - 1);
                board.UnmakeMove(moveList[i]);
                if (board.IsRedTurn)
                {
                    bestScore = (score > bestScore) ? score : bestScore;
                }
                else
                {
                    bestScore = (score < bestScore) ? score : bestScore;
                }
            }
            return(bestScore);
        }
Example #5
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);
        }
Example #6
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);
        }
Example #7
0
        /// <summary>
        /// 最简单的AlphaBeta剪枝搜索,没有用置换表。
        /// 比MinMax算法要好多了,C++64位计算5层只需0.8秒,6层为6.8秒,7层为151秒。
        /// C#版本5层要18秒
        /// 把搜索到的最好的着法保存在BestMove中。
        /// TODO: 这里没有检测胜利局面
        /// </summary>
        /// <param name="depth">当前搜索深度</param>
        /// <param name="maxDepth">最大搜索深度</param>
        /// <param name="alpha">初始值可以设置为MIN_EVAL_VALUE</param>
        /// <param name="beta">初始值可以设置为MAX_EVAL_VALUE</param>
        /// <returns>评估值</returns>
        private int AlphaBeta(int depth, int alpha, int beta)
        {
            /* 要进行被将死情况的检测
             * if (board.BlackTurn && board.IsRedKingCheckmated())
             * {
             * return int.MinValue;
             * }
             * else if (board.RedTurn && board.IsBlackKingCheckmated())
             * {
             * return int.MaxValue;
             * }
             */

            if (depth == 0)
            {
                return(Evaluator.EvaluateAll(board));
            }
            Move[] moveList = new Move[200];
            int    n_moves  = MoveGenerator.GenAllMoveList(board, moveList);

            if (!board.IsRedTurn)
            {
                for (int i = 0; i < n_moves; i++)
                {
                    board.MakeMove(moveList[i]);
                    int score = AlphaBeta(depth - 1, alpha, beta);
                    board.UnmakeMove(moveList[i]);
                    if (score < beta)
                    {
                        beta = score;
                        if (depth == 0)
                        {
                            bestMove = moveList[i];
                        }
                        if (alpha >= beta)
                        {
                            return(alpha);
                        }
                    }
                }
                return(beta);
            }
            else
            {
                for (int i = 0; i < n_moves; i++)
                {
                    board.MakeMove(moveList[i]);
                    int score = AlphaBeta(depth - 1, alpha, beta);
                    board.UnmakeMove(moveList[i]);
                    if (score > alpha)
                    {
                        alpha = score;
                        if (depth == 0)
                        {
                            bestMove = moveList[i];
                        }
                        if (alpha >= beta)
                        {
                            return(beta);
                        }
                    }
                }
                return(alpha);
            }
        }