Example #1
0
        bool IsChecking(MOVE mv)
        {
            //discovered check
            int discover = DiscoverAttack[mv.pcSrc];

            if (discover > 0)
            {
                if (discover == 3)
                {
                    return(true);
                }
                if (discover == 1 && !SAME_FILE(mv.sqSrc, mv.sqDst))
                {
                    return(true);
                }
                if (discover == 2 && !SAME_RANK(mv.sqSrc, mv.sqDst))
                {
                    return(true);
                }
            }

            //direct check
            int side      = SIDE(mv.pcSrc);
            int sqOppking = sqPieces[OPP_SIDE_TAG(side) + KING_FROM];

            MovePiece(mv);
            if (IsLegalMove(mv.sqDst, sqOppking))
            {
                UndoMovePiece(mv);
                mv.checking = true;
                return(true);
            }
            UndoMovePiece(mv);
            return(false);
        }
Example #2
0
        void SetBestMove(MOVE mv, int score, int depth, int height)
        {
            Debug.Assert(mv.sqSrc != 0);
            Debug.Assert(score > -G.MATE && score < G.MATE);
            Debug.Assert(depth >= 0);

            //吃送吃的子没有必要记录
            if (mv.pcDst > 0 && mv.sqDst == stepList[stepList.Count - 1].move.sqDst)
            {
                return;
            }
#if USE_MATEKILLER
            if (score > G.RULEWIN)
            {
                Debug.Assert(sdPlayer == SIDE(mv.pcSrc));
                MateKiller[height].sqSrc = mv.sqSrc;
                MateKiller[height].sqDst = mv.sqDst;
                MateKiller[height].pcSrc = mv.pcSrc;
                MateKiller[height].pcDst = mv.pcDst;
            }
            else
#endif
            if (Killers[height, 0] != mv)
            {
                Killers[height, 1] = Killers[height, 0];
                Killers[height, 0] = mv;
            }
            HistoryGood(mv, depth);
        }
Example #3
0
        private void MenuContinuousEval_Click(object sender, EventArgs e)
        {
            App_inGame = false;
            string        fileName = @"G:\象棋\全局\1-23届五羊杯\第01届五羊杯象棋赛(1981)\第01局-胡荣华(红先负)柳大华.PGN";
            PgnFileStruct pgn      = pos.ReadPgnFile(fileName);

            pos.FromFEN(pgn.StartFEN);
            engine.FromFEN(pgn.StartFEN);
            engine.SearchQuiesce(-5000, 5000, 10, 0);
            for (int i = 1; i < pgn.MoveList.Count; i++)
            {
                MOVE step = pgn.MoveList[i];
                engine.MakeMove(step);
                int score = -engine.SearchQuiesce(-5000, 5000, 10, 0);
                if (i % 2 == 1)
                {
                    Console.Write("{0}. {1}  ", (i + 1) / 2, score);
                }
                else
                {
                    Console.WriteLine(score);
                }
            }

            //Write2Csv(@"G:\xqtest\eval.csv", pos.ivpc, totalMoves, 48);

            /* 用顶级人类选手的对局来测试评估审局函数的有效性。
             * 理想情况下,双方分数应呈锯齿状交替上升,除去吃子的步骤,应该稳定渐变。
             */
        }
Example #4
0
        void HistoryGood(MOVE mv, int depth)
        {
            int i = GetHistoryIndex(mv);

            HistHit[i]++;
            HistTotal[i]++;

            History[i] += depth * depth;
        }
Example #5
0
        async Task GoAsync()
        {
            //相当于go depth指令和bestmove反馈
            while (App_inGame && (pos.sdPlayer == 1 && MenuAIBlack.Checked || pos.sdPlayer == 0 && MenuAIRed.Checked))
            {
                Task <MOVE> GetBestMove = Task <MOVE> .Run(() => engine.SearchMain(G.MAX_Depth));

                MOVE bestmove = await GetBestMove;
                MakeMove(bestmove.sqSrc, bestmove.sqDst);
            }
        }
 public void WriteHash(ulong key, int flag, int vl, int depth, MOVE mv)
 {
     Debug.Assert(vl <G.MATE && vl> -G.MATE);
     if (vl > G.WIN && vl <= G.RULEWIN || vl < -G.WIN && vl >= -G.RULEWIN)
     {
         return;
     }
     //Mate value is accurate regardless of the searching depth.
     if (vl > G.WIN || vl < -G.WIN)
     {
         depth = G.MAX_PLY;
     }
     nWrite++;
     if (Trans.TryGetValue(key, out HashStruct entry))
     {
         nWriteHit++;
         if (mv.sqDst != 0)
         {
             entry.Move = mv;
         }
         if ((flag & G.HASH_ALPHA) != 0 && (depth > entry.AlphaDepth))
         {
             entry.Alpha     = vl;
             entry.BetaDepth = depth;
         }
         if ((flag & G.HASH_BETA) != 0 && (depth > entry.BetaDepth))
         {
             entry.Beta      = vl;
             entry.BetaDepth = depth;
         }
         Trans[key] = entry;
         return;
     }
     else
     {
         entry.Move  = mv;
         entry.Alpha = G.MATE;
         entry.Beta  = -G.MATE;
         if ((flag & G.HASH_ALPHA) != 0)
         {
             entry.AlphaDepth = depth;
             entry.Alpha      = vl;
         }
         if ((flag & G.HASH_BETA) != 0)
         {
             entry.BetaDepth = depth;
             entry.Beta      = vl;
         }
         Trans.Add(key, entry);
     }
 }
Example #7
0
        //暂不支持(变招)识别
        public PgnFileStruct ReadPgnFile(string szFileName)
        {
            Debug.Assert(szFileName != null);
            PgnFileStruct PGN = new PgnFileStruct();

            FromFEN(cszStartFen);
            PGN.StartFEN    = cszStartFen;
            PGN.MoveList    = new List <MOVE>();
            PGN.CommentList = new List <string>();

            using (StreamReader fp = new StreamReader(szFileName, Encoding.GetEncoding("GB2312")))
            {
                string pattern = @"\A\[(\w+)\s""(.*)""\]\Z";
                Regex  reg     = new Regex(pattern);
                Match  m;
                string line = "";
                while (fp.Peek() > -1)
                {
                    line = fp.ReadLine();
                    m    = reg.Match(line);
                    if (m.Success)
                    {
                        switch (m.Groups[1].Value)
                        {
                        case "Format":
                            PGN.Format = m.Groups[2].Value;
                            break;

                        case "Game":
                            if (m.Groups[2].Value != "Chinese Chess")
                            {
                                Console.WriteLine("非中国象棋棋谱");
                                return(PGN);
                            }
                            break;

                        case "Event":
                            PGN.Event = m.Groups[2].Value;
                            break;

                        case "Date":
                            PGN.Date = m.Groups[2].Value;
                            break;

                        case "Round":
                            PGN.Round = m.Groups[2].Value;
                            break;

                        case "FEN":
                            FromFEN(m.Groups[2].Value);
                            PGN.StartFEN = m.Groups[2].Value;
                            break;

                        case "Result":
                            PGN.Result = m.Groups[2].Value;
                            break;

                        case "Site":
                            PGN.Site = m.Groups[2].Value;
                            break;

                        case "RedTeam":
                            PGN.RedTeam = m.Groups[2].Value;
                            break;

                        case "BlackTeam":
                            PGN.BlackTeam = m.Groups[2].Value;
                            break;

                        case "Red":
                            PGN.Red = m.Groups[2].Value;
                            break;

                        case "Black":
                            PGN.Black = m.Groups[2].Value;
                            break;

                        case "ECCO":
                            PGN.ECCO = m.Groups[2].Value;
                            break;

                        case "Opening":
                            PGN.Opening = m.Groups[2].Value;
                            break;

                        case "Variation":
                            PGN.Variation = m.Groups[2].Value;
                            break;

                        case "BlackElo":
                            PGN.BlackElo = m.Groups[2].Value;
                            break;

                        case "RedElo":
                            PGN.RedElo = m.Groups[2].Value;
                            break;

                        default:
                            Debug.WriteLine("Unknown label {0}", m.Groups[2].Value);
                            break;
                        }
                    }
                    else
                    {
                        //Now comes the move and comment list
                        line += "\n" + fp.ReadToEnd();
                    }
                }
                int    index;
                string comment = "";
                //int phase = 2; //phase = 0是序号,1是move#1,2是 move#2
                do
                {
                    //get move list till next comment
                    index = line.IndexOf('{');
                    string content;
                    if (index < 0)
                    {
                        content = line;
                    }
                    else if (index == 0)
                    {
                        int index1 = line.IndexOf('}');
                        if (index1 > 0)
                        {//is comment
                            comment = line.Substring(1, index1 - 1);
                            line    = line.Substring(index1 + 1);
                        }
                        continue;
                    }
                    else
                    {
                        content = line.Substring(0, index);
                        line    = line.Substring(index);
                    }
                    string[] words = content.Split(new char[] { ' ', '\n', '\r', '.' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string s in words)
                    {
                        pattern = @"\A(\d+)\Z";
                        m       = Regex.Match(s, pattern);
                        if (m.Success)
                        {//is a move number
                         //Debug.WriteLine(m.Groups[1].Value);
                         //if (phase != 2 && m.Groups[1].Value != "2")//第一回合如果黑方先走,只有move#2
                         //{
                         //    Console.WriteLine("棋谱错误,缺少着法");
                         //    return false;
                         //}
                         //phase = 0;
                        }
                        else if (s.Length == 4)
                        {//is a Chinese move
                            //Debug.WriteLine(s);
                            MOVE mv = ParseWord(s);
                            if (mv.pcSrc == 0)
                            {
                                Console.WriteLine("警告:棋谱错误!");
                                return(PGN);
                            }
                            MakeMove(mv);
                            PGN.CommentList.Add(comment);
                            PGN.MoveList.Add(mv);
                            comment = null;
                            //phase++;
                        }
                        else if (s.Length == 5)
                        {// is a ICCS format move
                            Tuple <int, int> coord = ICCS2Move(s);
                            MOVE             mv    = new MOVE(coord.Item1, coord.Item2, pcSquares[coord.Item1], pcSquares[coord.Item2]);
                            MakeMove(mv);
                            PGN.CommentList.Add(comment);
                            PGN.MoveList.Add(mv);
                            comment = null;
                        }
                        else
                        {
                            PGN.CommentList.Add(comment);
                            if (s != PGN.Result)
                            {
                                Debug.WriteLine(s);
                            }

                            goto CHECK_RESULT;
                        }
                    }
                } while (line.Length > 0 & index >= 0);
            }

CHECK_RESULT:
            //Sometime the result is recorded in the last comment
            if (PGN.Result == "*")
            {
                string s = PGN.CommentList[PGN.CommentList.Count - 1];
                if (s != null)
                {
                    if (s.Contains("红胜") || s.Contains("黑负"))
                    {
                        PGN.Result = "1-0";
                    }
                    else if (s.Contains("和"))
                    {
                        PGN.Result = "1/2-1/2";
                    }
                    else if (s.Contains("红负") || s.Contains("黑胜"))
                    {
                        PGN.Result = "0-1";
                    }
                }
            }
            return(PGN);
        }
Example #8
0
        private void MenuOpen_Click(object sender, EventArgs e)
        {
            PgnFileStruct PGN;

            if (openPGNDialog.ShowDialog() == DialogResult.OK)
            {
                PGN         = pos.ReadPgnFile(openPGNDialog.FileName);
                MoveList    = PGN.MoveList;
                CommentList = PGN.CommentList;
            }
            else
            {
                return;
            }
            labelEvent.Text = PGN.Event;
            string result;

            switch (PGN.Result)
            {
            case "1-0":
                result = "胜";
                break;

            case "0-1":
                result = "负";
                break;

            case "1/2-1/2":
                result = "和";
                break;

            default:
                result = "*";
                break;
            }
            StringBuilder sb = new StringBuilder();

            sb.Append(PGN.RedTeam);
            sb.Append(' ');
            sb.Append(PGN.Red);
            sb.Append(" (先");
            sb.Append(result);
            sb.Append(") ");
            sb.Append(PGN.BlackTeam);
            sb.Append(' ');
            sb.Append(PGN.Black);
            labelPlayer.Text   = sb.ToString();
            labelDateSite.Text = PGN.Date + " 弈于 " + PGN.Site;


            ListboxMove.Items.Clear();

            if (string.IsNullOrEmpty(CommentList[0]))
            {
                ListboxMove.Items.Add("==开始==");
            }
            else
            {
                ListboxMove.Items.Add("==开始==*");
            }

            for (FENStep = 0; FENStep < MoveList.Count; FENStep++)
            {
                MOVE   step  = MoveList[FENStep];
                string label = step.ToString();
                if (FENStep % 2 == 0)
                {
                    label = ((FENStep / 2 + 1).ToString() + "." + label);
                }
                label = label.PadLeft(8);
                if (!string.IsNullOrEmpty(CommentList[FENStep + 1]))
                {
                    label += "*";
                }
                ListboxMove.Items.Add(label);
            }
            pos.FromFEN(PGN.StartFEN);
            engine.FromFEN(PGN.StartFEN);
            FENStep = 0;
            ListboxMove.SelectedIndex = 0;
            PanelBoard.Refresh();
            App_inGame = false;
        }
Example #9
0
        void HistoryBad(MOVE mv)
        {
            int i = GetHistoryIndex(mv);

            HistTotal[i]++;
        }
Example #10
0
        int SearchCut(int alpha, int beta, int depth, int height, int mate_threat, bool allowNull = true)
        {
            beta = Math.Min(beta, G.MATE - height);
            if (depth <= 0)
            {
                if (stepList[stepList.Count - 1].move.checking)
                {
                    //被照将时,推迟进入静态搜索
                    depth++;
                }
                else
                {
                    return(SearchQuiesce(alpha, beta, 0, height));
                }
            }

            stat.ZeroWindowNodes++;
            int best = HarmlessPruning(height);

            if (best >= beta)
            {
                return(best);
            }

#if USE_HASH
            HashStruct t = TT.ReadHash(Key);
            if (t.Move.sqSrc == 0)
            {
                TransKiller = null;
            }
            else
            {
                if (t.AlphaDepth >= depth)
                {
                    if (t.Alpha <= alpha)
                    {
                        return(t.Alpha);
                    }
                }
                if (t.BetaDepth >= depth)
                {
                    if (t.Beta >= beta)
                    {
                        return(t.Beta);
                    }
                }
                if (t.Alpha == t.Beta)
                {
                    return(t.Alpha);
                }
                TransKiller       = t.Move;
                TransKiller.pcSrc = pcSquares[TransKiller.sqSrc];
                TransKiller.pcDst = pcSquares[TransKiller.sqDst];
            }
#endif
            Debug.Assert(height < G.MAX_PLY);
            MOVE lastMove = stepList[stepList.Count - 1].move;
#if NULL_MOVE
            if (allowNull && depth >= G.NullDepth)
            {
                //for any opponent pure loss and bad capture, it's better to take it and see the accurate score
                //for any opponent good capture, we are already so bad, it makes no sense to do null move
                //for those equal material exchange, we must finish recapture
                //for mating and checking moves, we can not skip the moves
                if (!lastMove.checking && lastMove.score > HistoryScore && lastMove.score < GoodScore && lastMove.pcDst == 0 && Math.Abs(beta) < G.WIN)
                {
                    MakeNullMove();
                    int vl = -SearchCut(-beta, -alpha, depth - G.NullReduction - 1, height + 1, mate_threat, false);
                    UnmakeNullMove();
#if NULL_VERIFICATION
                    if (depth > G.VerReduction && vl >= beta)
                    {
                        vl = SearchCut(beta, depth - G.VerReduction, height + 1, false);
                    }
#endif
                    if (vl >= beta)
                    {
                        Debug.Assert(vl < G.WIN);   // do not return unproven mates
                        TT.WriteHash(Key, G.HASH_BETA, vl, depth, new MOVE());
                        stat.NullCutoffs++;
                        return(vl);
                    }
                }
            }
#endif
            IEnumerable <MOVE> moves = GetNextMove(7, height, mate_threat);
            MOVE        mvBest       = new MOVE();
            int         opt_value    = G.MATE;
            List <MOVE> mvPlayed     = new List <MOVE>();
            foreach (MOVE mv in moves)
            {
                int new_depth = depth - 1;
                if (mv.checking)
                {
                    new_depth++;
                }
                if (mate_threat < 0)
                {
                    if (best < -G.WIN && mvPlayed.Count > 0)
                    {
                        mate_threat = height;
                        alpha       = -G.WIN;
                    }
                }
                else
                {
                    MOVE matekiller = Killers[mate_threat + 1, 0];
                    if (mv == matekiller || mate_threat == height && best > -G.WIN || !IsLegalMove(matekiller.sqSrc, matekiller.sqDst))
                    {
                        mate_threat = -1;
                    }
                }
#if HISTORY_PRUNING
                bool reduced = false;
                if (depth >= G.HistoryDepth && !lastMove.checking && new_depth < depth && mvPlayed.Count >= G.HistoryMoveNb)
                {
                    int k = GetHistoryIndex(mv);
                    if ((float)(HistHit[k]) / HistTotal[k] < 0.6)
                    {
                        new_depth--;
                        reduced = true;
                        stat.HistoryReduced++;
                    }
                }
#endif
#if FUTILITY_PRUNING
                if (depth == 1 && !lastMove.checking && !mv.checking && new_depth == 0 && mv.pcDst == 0)
                {
                    if (opt_value == G.MATE)
                    {
                        opt_value = Simple_Evaluate() + G.FutilityMargin;
                    }
                    if (opt_value < beta)
                    {
                        continue;
                    }
                }
#endif
                //if the opponent is delaying mate by checking, do not enter quiesce until the mate killer move is tried
                if (new_depth == 0 && mate_threat > 0)
                {
                    new_depth = 1;
                }
                Debug.Write(new string('\t', height));
                Debug.WriteLine($"{mv} {alpha}, {beta}, {best} {mv.killer}");
                MakeMove(mv, false);
                int vl;
                //to avoid quiesce search beta cut off too early, use -best instead of -alpha as new beta
                if (new_depth == 0 && alpha < G.WIN)
                {
                    vl = -SearchCut(-beta, -best, 0, height + 1, mate_threat);
                }
                else
                {
                    vl = -SearchCut(-beta, -alpha, new_depth, height + 1, mate_threat);
                }
#if HISTORY_PRUNING
                if (vl >= beta && reduced)
                {
                    new_depth++;
                    vl = -SearchCut(-beta, -alpha, new_depth, height + 1, mate_threat);
                    stat.HistoryResearched++;
                }
#endif
                UnmakeMove();

                if (vl > best)
                {
                    best   = vl;
                    mvBest = mv;
                    if (vl >= beta)
                    {
                        SetBestMove(mvBest, best, depth, height);
                        foreach (MOVE m1 in mvPlayed)
                        {
                            HistoryBad(m1);
                        }
#if USE_HASH
                        TT.WriteHash(Key, G.HASH_BETA, best, depth, mvBest);
#endif
                        stat.BetaCutoffs++;
                        return(vl);
                    }
                    if (vl > alpha)
                    {
                        alpha = vl;
                    }
                }
                mvPlayed.Add(mv);
            }
#if USE_HASH
            TT.WriteHash(Key, G.HASH_ALPHA, best, depth, mvBest);
#endif
            return(best);
        }
Example #11
0
 //Static Exchange Evaluation
 //SEE is basically as same as SubSEE. But it copy the attack and defend list to make them mutable
 //SEE can return negative value. SubSEE only return positive value.
 int SEE(MOVE mv, in List <int> attackers, List <int> defenders)
Example #12
0
        public int SearchQuiesce(int alpha, int beta, int qheight, int height)
        {
            beta = Math.Min(beta, G.WIN);
            stat.QuiesceNodes++;
            MOVE mvLast    = stepList[stepList.Count - 1].move;
            bool isChecked = mvLast.checking;
            int  best;
            IEnumerable <MOVE> moves;

            //偶数层是主动方,奇数层是被动方
            TransKiller = null;
            if (qheight % 2 == 0)
            {
                if (isChecked)
                {
                    best  = -G.MATE + height - 1;
                    moves = GetNextMove(7, height);
                }
                else
                {
                    best  = Simple_Evaluate();
                    moves = GetNextMove(3, height);

                    if (best > beta && mvLast.pcDst == 0 && stepList[stepList.Count - 2].move.pcDst == 0)
                    {
                        stat.BetaCutoffs++;
                        return(best);
                    }
                    if (qheight > G.MAX_QUEISCE_DEPTH && mvLast.pcDst == 0)
                    {
                        return(best);
                    }
                    if (best > alpha)
                    {
                        alpha = best;
                    }
                }
            }
            else
            {
                if (isChecked)    //避免长照
                {
                    RepititionResult rep = Repitition();
                    if (rep != RepititionResult.NONE)
                    {
                        return((int)rep);
                    }
                    best = height - G.MATE - 1;
                }
                else
                {
                    best = Simple_Evaluate();
                    if (best > beta && mvLast.pcDst == 0 && stepList[stepList.Count - 2].move.pcDst == 0)
                    {
                        stat.BetaCutoffs++;
                        return(best);
                    }
                    if (best > alpha)
                    {
                        alpha = best;
                    }
                }
                //only extend evade and re-capture
                if (isChecked)
                {
                    moves = GetNextMove(7, height);
                }
                else
                {
                    moves = GetNextMove(2, height);
                }
            }
            int vl;

            foreach (MOVE mv in moves)
            {
                //if (!isChecked && height >= RootDepth * 2 && mv.pcDst == 0)
                //    continue;
                Debug.Write(new string('\t', height));
                Debug.WriteLine($"{mv} {alpha}, {beta}, {best}, {height}");
                MakeMove(mv, false);
                if (qheight % 2 == 0)
                {
                    if (stepList[stepList.Count - 1].move.checking)
                    {
                        stat.CheckExtesions++;
                    }
                    else
                    {
                        stat.CaptureExtensions++;
                    }
                }
                vl = -SearchQuiesce(-beta, -alpha, qheight + 1, height + 1);
                UnmakeMove();
                if (vl >= beta)
                {
                    stat.BetaCutoffs++;
                    SetBestMove(mv, vl, 0, height);
                    return(vl);
                }
                if (vl >= best)
                {
                    best  = vl;
                    alpha = Math.Max(alpha, vl);
                }
            }
            return(best);
        }
Example #13
0
 int Large2Small(MOVE a, MOVE b)
 {
     return(b.score.CompareTo(a.score));
 }
Example #14
0
        public MOVE SearchMain(int maxDepth)
        {
            Debug.Assert(maxDepth > 0);

            Book.ReadBook(@"J:\C#\MoleXiangqi\Book.dat");
            MOVE book_move = SearchOpeningBook();

            if (book_move != null)
            {
                return(book_move);
            }

            stat   = new STATISTIC();
            PVLine = new List <MOVE>();
#if USE_MATEKILLER
            for (int i = 0; i < G.MAX_PLY; i++)
            {
                MateKiller[i] = new MOVE();
            }
#endif
            Array.Clear(Killers, 0, G.MAX_PLY * 2);
            Array.Clear(History, 0, 14 * 90);
            Array.Clear(HistHit, 0, 14 * 90);
            Array.Clear(HistTotal, 0, 14 * 90);
            TT.Reset();
            TransKiller = null;
            rootMoves   = new List <MOVE>(GetNextMove(7, 0));

            int vl = 0;
            // 6. 做迭代加深搜索
            for (RootDepth = 1; RootDepth <= maxDepth; RootDepth++)
            {
                Debug.WriteLine("---------------------------");
                Console.WriteLine("info depth {0}", RootDepth);

                stopwatch.Start();
                vl = SearchRoot(RootDepth);
                stopwatch.Stop();

                stat.ElapsedTime += stopwatch.ElapsedMilliseconds;
                Debug.WriteLine(stat);
                PopPVLine();
                if (rootMoves.Count == 1)
                {
                    Console.WriteLine("Single feasible move");
                    break;
                }

                // 10. 搜索到杀棋则终止搜索
                if (Math.Abs(vl) > G.WIN)
                {
                    break;
                }
            }
            if (vl < -G.WIN)
            {
                Console.WriteLine("Resign");
            }
            else if (vl > G.WIN)
            {
                Console.WriteLine("MATE in {0} steps!", G.MATE - vl);
            }
            ProbeHistory();
            return(PVLine[0]);
        }
Example #15
0
 static int GetHistoryIndex(MOVE mv)
 {
     return(cnPieceHistIndex[mv.pcSrc] * 90 + cboard256[mv.sqDst]);
 }
Example #16
0
        int SearchPV(int alpha, int beta, int depth, int height, out List <MOVE> pvs)
        {
            beta = Math.Min(beta, G.MATE - height);
            pvs  = new List <MOVE>();
            if (depth <= 0)
            {
                if (stepList[stepList.Count - 1].move.checking)
                {
                    //被照将时,推迟进入静态搜索
                    depth++;
                }
                else
                {
                    return(SearchQuiesce(alpha, beta, 0, height));
                }
            }

            stat.PVNodes++;
            int best = HarmlessPruning(height);

            //distance pruning
            if (best >= beta)
            {
                return(best);
            }
            MOVE        mvBest   = new MOVE();
            int         hashFlag = 0;
            List <MOVE> subpv    = null;

            TransKiller = null;
#if USE_HASH
            HashStruct t = TT.ReadHash(Key);
            if (t.Move.sqSrc == 0)
            {
                TransKiller = null;
            }
            else
            {
                if (t.AlphaDepth >= depth)
                {
                    if (t.Alpha <= alpha)
                    {
                        return(t.Alpha);
                    }
                }
                if (t.BetaDepth >= depth)
                {
                    if (t.Beta >= beta)
                    {
                        return(t.Beta);
                    }
                }
                TransKiller       = t.Move;
                TransKiller.pcSrc = pcSquares[TransKiller.sqSrc];
                TransKiller.pcDst = pcSquares[TransKiller.sqDst];
            }
#endif
#if INTERNAL_ITERATIVE_DEEPENING
            if (depth >= G.IIDDepth && TransKiller is null)
            {
                int new_depth = depth - G.IIDReduction;
                Debug.Assert(new_depth > 0);
                int vl = SearchPV(alpha, beta, new_depth, height, out subpv);
                if (vl < alpha)
                {
                    vl = SearchPV(-G.MATE, beta, new_depth, height, out subpv);
                }
                if (subpv.Count > 0)
                {
                    TransKiller = subpv[0];
                }
            }
#endif

            IEnumerable <MOVE> moves = GetNextMove(7, height);
            foreach (MOVE mv in moves)
            {
                Debug.Write(new string('\t', height));
                Debug.WriteLine($"{mv} {alpha}, {beta}, {best},{mv.killer}");
                int new_depth = depth - 1;
                if (mv.sqDst == stepList[stepList.Count - 1].move.sqDst && mv.score > 0 ||
                    mv.checking)
                {
                    new_depth++;
                }
                MakeMove(mv, false);
                int vl;
                if (mvBest.sqSrc == 0)
                {
                    vl = -SearchPV(-beta, -alpha, new_depth, height + 1, out subpv);
                }
                else
                {
                    //当depth == 0, 且best < alpha时,传给quiesce的beta用 -best代替 -alpha,可以避免eval() > beta过早返回。
                    //返回的值和最佳着法只是因为搜索深度浅而貌似高于best。后续重新搜索时这个非最佳着法被首先搜索,降低了效率。
                    if (new_depth == 0 && alpha < G.WIN)
                    {
                        vl = -SearchCut(-alpha - 1, -best, new_depth, height + 1, -1);
                    }
                    else
                    {
                        vl = -SearchCut(-alpha - 1, -alpha, new_depth, height + 1, -1);
                    }
                    if (vl > alpha) // && vl < beta
                    {
                        Debug.WriteLine("Re-search");
                        Debug.Write(new string('\t', height));
                        Debug.WriteLine($"{mv} {alpha}, {beta}, {vl}");
                        vl = -SearchPV(-beta, -vl, new_depth, height + 1, out subpv);
                        stat.PVChanged++;
                    }
                }
                UnmakeMove();
                if (vl > best)
                {
                    best = vl;
                    if (vl >= beta)
                    {
                        stat.BetaCutoffs++;
                        mvBest   = mv;
                        hashFlag = G.HASH_BETA;
                        break;
                    }
                    if (vl > alpha)
                    {
                        alpha    = vl;
                        mvBest   = mv;
                        hashFlag = G.HASH_PV;
                        pvs.Clear();
                        pvs.Add(mv);
                        pvs.AddRange(subpv);
                    }
                }
                else
                {
                    HistoryBad(mv);
                }
            }
            if (best > -G.WIN)
            {
#if USE_HASH
                TT.WriteHash(Key, hashFlag, best, depth, mvBest);
#endif
                if (mvBest.pcSrc != 0)
                {
                    SetBestMove(mvBest, best, depth, height);
                }
            }
            return(best);
        }
Example #17
0
        void MakeMove(int sqFrom, int sqTo)
        {
            Graphics g = PanelBoard.CreateGraphics();

            if (pos.IsLegalMove(sqFrom, sqTo))
            {
                int  pcCaptured = pos.pcSquares[sqTo];
                MOVE step       = new MOVE(sqFrom, sqTo, pos.pcSquares[sqFrom], pcCaptured);
                pos.MakeMove(step);
                if (pos.CheckedBy(1 - pos.sdPlayer) > 0)
                {
                    PlaySound("ILLEGAL");
                    pos.UnmakeMove();
                    return;
                }
                engine.MakeMove(step);
                MoveList.Add(step);
                CommentList.Add(textBoxComment.Text);

                if (FENStep > 0)
                {
                    //擦除上一步的起始和结束位置选择框
                    DrawBoard(ptLastFrom, g);
                    DrawBoard(ptLastTo, g);
                    DrawPiece(ptLastTo, pcLast, g);
                }

                ptLastFrom = POSITION.UI_Coord2XY(sqFrom, bFlipped);
                ptLastTo   = POSITION.UI_Coord2XY(sqTo, bFlipped);
                pcLast     = cnPieceImages[step.pcSrc];

                //擦除原来的位置
                DrawBoard(ptLastFrom, g);
                DrawSelection(ptLastFrom, g);
                //移动到新位置
                DrawSelection(ptLastTo, g);
                DrawPiece(ptLastTo, pcLast, g);
                bSelected = false;

                FENStep++;
                string label = step.ToString();
                if (FENStep % 2 == 1)
                {
                    label = ((FENStep / 2 + 1).ToString() + "." + label);
                }
                label = label.PadLeft(8);
                ListboxMove.Items.Add(label);
                if (pos.pcSquares[sqTo] > 0)
                {
                    PlaySound("CAPTURE");
                }
                else
                {
                    PlaySound("MOVE");
                }

                if (pos.IsMate())
                {//直接吃王或者绝杀
                    if (pos.sdPlayer == 1 && MenuAIBlack.Checked && !MenuAIRed.Checked ||
                        pos.sdPlayer == 0 && MenuAIRed.Checked && !MenuAIBlack.Checked)
                    {
                        PlaySound("WIN");
                    }
                    else if (pos.sdPlayer == 1 && !MenuAIBlack.Checked && MenuAIRed.Checked ||
                             pos.sdPlayer == 0 && !MenuAIRed.Checked && MenuAIBlack.Checked)
                    {
                        PlaySound("LOSS");
                    }
                    if (pos.sdPlayer == 0)
                    {
                        MessageBox.Show("黑方胜!");
                    }
                    else
                    {
                        MessageBox.Show("红方胜!");
                    }
                    App_inGame = false;
                }
            }
        }
Example #18
0
        MOVE ParseWord(string word)
        {
            MOVE mv = new MOVE();
            int  sq, file0 = 0, file1;

            if (PieceDict.TryGetValue(word[0], out int pcType))
            {//Normal case 炮八平五
                file0 = FindFile(word[1]);
                Tuple <int, int> t = FindPiece(pcType, file0);
                if (t is null)
                {
                    return(mv);
                }
                mv.pcSrc = t.Item1;
                mv.sqSrc = t.Item2;
            }
            else
            {//special case 前马退二
                int[] pc_x = new int[16];
                pcType = PieceDict[word[1]];
                int x = 0, y, pc;
                //find doubled pieces on the same file
                for (int i = pcFrom[pcType]; i <= pcTo[pcType]; i++)
                {
                    pc = i + SIDE_TAG(sdPlayer);
                    sq = sqPieces[pc];
                    if (sq > 0)
                    {
                        x = FILE_X(sq);
                        pc_x[x]++;
                        if (pc_x[x] > 1)
                        {
                            file0 = x;
                            break;
                        }
                    }
                }
                int dir = 1;
                if (word[0] == '前')
                {
                    dir = 1;
                }
                else if (word[0] == '后')
                {
                    dir = -1;
                }
                if (sdPlayer == 1)
                {
                    dir = -dir;
                }
                if (dir > 0)
                {
                    y = RANK_TOP;
                }
                else
                {
                    y = RANK_BOTTOM;
                }
                do
                {
                    sq = XY2Coord(x, y);
                    pc = pcSquares[sq];
                    y += dir;
                }while (cnPieceTypes[pc] != pcType + SIDE_TAG(sdPlayer));
                mv.sqSrc = sq;
                mv.pcSrc = pc;
            }
            if (word[2] == '平')
            {
                file1    = FindFile(word[3]);
                mv.sqDst = mv.sqSrc + file1 - file0;
            }
            else
            {
                int dir = 1;
                if (word[2] == '进')
                {
                    dir = 1;
                }
                else if (word[2] == '退')
                {
                    dir = -1;
                }
                else
                {
                    Debug.Fail("Unrecoginized move 2 {0}", word);
                }
                if (sdPlayer == 0)
                {
                    dir = -dir;
                }
                if (pcType == KNIGHT || pcType == BISHOP || pcType == GUARD)
                {
SecondHalf:
                    int rank0 = RANK_Y(mv.sqSrc);
                    int rank1;
                    file1 = FindFile(word[3]);
                    if (pcType == KNIGHT)
                    {
                        rank1 = rank0 + (3 - Math.Abs(file1 - file0)) * dir;
                    }
                    else if (pcType == BISHOP)
                    {
                        rank1 = rank0 + 2 * dir;
                    }
                    else //GUARD
                    {
                        rank1 = rank0 + dir;
                    }
                    mv.sqDst = XY2Coord(file1, rank1);
                    if (!IsLegalMove(mv.sqSrc, mv.sqDst))
                    {//有些棋谱会出现相、仕在同一列不用前后表示的情况
                        mv.pcSrc++;
                        mv.sqSrc = sqPieces[mv.pcSrc];
                        Debug.Assert(FILE_X(sqPieces[mv.pcSrc]) == file0);
                        goto SecondHalf;
                    }
                }
                else
                {
                    //rook, cannon, king or pawn
                    mv.sqDst = mv.sqSrc + 16 * FindRank(word[3]) * dir;
                }
            }

            mv.pcDst = pcSquares[mv.sqDst];
            return(mv);
        }
Example #19
0
        public void TestEval()
        {
            //int Compare(KeyValuePair<string, int> a, KeyValuePair<string, int> b)
            //{
            //    return a.Value.CompareTo(b.Value);
            //}
            string sourceDirectory        = @"G:\象棋\全局\1-23届五羊杯";
            IEnumerable <string> pgnFiles = Directory.EnumerateFiles(sourceDirectory, "*.pgn", SearchOption.AllDirectories);
            int           nFile           = 0;
            int           totalMoves      = 0;
            int           totalSteps      = 0;
            List <double> redDelta        = new List <double>();
            List <double> blackDelta      = new List <double>();
            List <double> seq             = new List <double>();

            foreach (string fileName in pgnFiles)
            {
                Console.WriteLine(fileName.Substring(sourceDirectory.Length + 1));
                PgnFileStruct pgn       = ReadPgnFile(fileName);
                List <MOVE>   iMoveList = pgn.MoveList;
                nFile++;
                int nSteps = iMoveList.Count;
                totalSteps += nSteps;
                bool[] captures = new bool[nSteps];
                FromFEN(pgn.StartFEN);
                ivpc = new int[nSteps, 48];
                Complex_Evaluate();
                List <KeyValuePair <string, int> > mv_vals = new List <KeyValuePair <string, int> >();
                for (int i = 1; i < nSteps; i++)
                {
                    MOVE step = iMoveList[i];
                    captures[i] = pcSquares[step.sqDst] > 0;
                    if (pcSquares[step.sqDst] == 0)
                    {
                        mv_vals.Clear();
                        //List<MOVE> moves = GenerateMoves();
                        int    bookmovevalue = 0;
                        string bookmovekey   = step.ToString();
                        foreach (MOVE move in GenerateMoves())
                        {
                            if (move.pcDst == 0)
                            {
                                MovePiece(move);
                                string key = move.ToString();
                                int    val = Complex_Evaluate();
                                mv_vals.Add(new KeyValuePair <string, int>(key, val));
                                UndoMovePiece(move);
                                if (key == bookmovekey)
                                {
                                    bookmovevalue = val;
                                }
                            }
                        }
                        if (i % 2 == 0) //从小到大排序
                        {
                            mv_vals.Sort(delegate(KeyValuePair <string, int> a, KeyValuePair <string, int> b)
                                         { return(a.Value.CompareTo(b.Value)); });
                        }
                        else  //从大到小排序
                        {
                            mv_vals.Sort(delegate(KeyValuePair <string, int> a, KeyValuePair <string, int> b)
                                         { return(b.Value.CompareTo(a.Value)); });
                        }
                        int index = mv_vals.IndexOf(new KeyValuePair <string, int>(bookmovekey, bookmovevalue));
                        seq.Add(index);
                        //totalMoves += moves.Count;
                        Console.WriteLine("{0}. Book move: {1} {2}", i, bookmovekey, index);
                        foreach (var m in mv_vals)
                        {
                            //Console.WriteLine("{0}. {1} {2} / {3}", j++, m.Key, m.Value, moves.Count);
                        }
                    }

                    MakeMove(step, true);
                    Console.WriteLine("-------------------");
                }
                for (int i = 1; i < nSteps; i += 2)
                {
                    if (!captures[i])
                    {
                        redDelta.Add(ivpc[i, 1] - ivpc[i - 1, 1]);
                    }
                }

                for (int i = 2; i < nSteps; i += 2)
                {
                    if (!captures[i])
                    {
                        blackDelta.Add(ivpc[i, 1] - ivpc[i - 1, 1]);
                    }
                }
                break;
            }
            double redMean = Statistics.Mean(redDelta);
            double redVar  = Statistics.Variance(redDelta);

            Console.WriteLine("Red mean:{0}, var:{1}", redMean, redVar);
            double blackMean = Statistics.Mean(blackDelta);
            double blackVar  = Statistics.Variance(blackDelta);

            Console.WriteLine("Black mean:{0}, var:{1}", blackMean, blackVar);
            Console.WriteLine("Score: red{0}, black{1}, average{2}", redVar / redMean, blackVar / blackMean, (redVar + blackVar) / (redMean - blackMean));
            Console.WriteLine("Move average sequence: {0} of {1}", Statistics.Mean(seq), totalMoves / totalSteps);
        }
Example #20
0
        public int Complex_Evaluate()
        {
            //举例:当头炮与对方的帅之间隔了自己的马和对方的相,
            //自己的马就放在DiscoveredAttack里,对方的相就在PinnedPieces里
            int[] tacticValue  = new int[2];
            int[] PinnedPieces = new int[48];       //0没有牵制,1纵向牵制,2横向牵制,3纵横牵制
            bool[,] BannedGrids = new bool[2, 256]; //空头炮与将之间不能走子
            int sqSrc, sqDst, pcDst, delta;

            int[,] AttackMap;

            int[] cDiscoveredAttack = { 0, 1, 25, 20, 20, 7, 3, 3 };
            //对阻挡将军的子进行判断
            void CheckBlocker(int side, int pcBlocker, int sqPinner, int direction)
            {
                int sdBlocker = SIDE(pcBlocker);
                int pcKind    = cnPieceKinds[pcBlocker];

                //未过河兵没有牵制和闪击
                if (pcKind == PAWN && HOME_HALF[sdBlocker, sqPieces[pcBlocker]])
                {
                    return;
                }
                if (sdBlocker == side)
                {
                    //闪击加分,根据兵种不同
                    tacticValue[side] += cDiscoveredAttack[pcKind];
                }
                else
                {
                    PinnedPieces[pcBlocker] |= direction;
                    //在形如红炮-黑车-红兵-黑将的棋型中,黑车是可以吃红炮的
                    if (IsLegalMove(sqPieces[pcBlocker], sqPinner))
                    {
                        AttackMap[sdBlocker, sqPinner] = pcBlocker;
                    }
                }
            }

            AttackMap = new int[2, 256];    //保存攻击该格的价值最低的棋子
            //find absolute pin. 0没有牵制,1纵向牵制,2横向牵制,3纵横牵制
            for (int sd = 0; sd < 2; sd++)
            {
                int bas       = SIDE_TAG(sd);
                int sqOppKing = sqPieces[OPP_SIDE_TAG(sd) + KING_FROM];

                for (int pc = bas + ROOK_FROM; pc <= bas + ROOK_TO; pc++)
                {
                    sqSrc = sqPieces[pc];
                    if (SAME_FILE(sqSrc, sqOppKing))
                    {
                        delta = Math.Sign(sqOppKing - sqSrc) * 16;
                        int pcBlocker = 0, nblock = 0;
                        for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                        {
                            if (pcSquares[sq] != 0)
                            {
                                pcBlocker = pcSquares[sq];
                                nblock++;
                            }
                        }
                        if (nblock == 1)
                        {
                            CheckBlocker(sd, pcBlocker, sqSrc, 1);
                        }
                    }

                    if (SAME_RANK(sqSrc, sqOppKing))
                    {
                        delta = Math.Sign(sqOppKing - sqSrc);
                        int pcBlocker = 0, nblock = 0;
                        for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                        {
                            if (pcSquares[sq] != 0)
                            {
                                pcBlocker = pcSquares[sq];
                                nblock++;
                            }
                        }
                        if (nblock == 1)
                        {
                            CheckBlocker(sd, pcBlocker, sqSrc, 2);
                        }
                    }
                }

                for (int pc = bas + CANNON_FROM; pc <= bas + CANNON_TO; pc++)
                {
                    sqSrc = sqPieces[pc];
                    if (SAME_FILE(sqSrc, sqOppKing))
                    {
                        delta = Math.Sign(sqOppKing - sqSrc) * 16;
                        int nblock = 0;
                        for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                        {
                            if (pcSquares[sq] != 0)
                            {
                                nblock++;
                            }
                        }
                        if (nblock == 2)
                        {
                            for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                            {
                                if (pcSquares[sq] > 0)
                                {
                                    CheckBlocker(sd, pcSquares[sq], sqSrc, 1);
                                }
                            }
                        }
                        else if (nblock == 0) //空心炮
                        {
                            for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                            {
                                BannedGrids[1 - sd, sq] = true;
                            }
                        }
                    }
                    if (SAME_RANK(sqSrc, sqOppKing))
                    {
                        delta = Math.Sign(sqOppKing - sqSrc);
                        int nblock = 0;
                        for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                        {
                            if (pcSquares[sq] != 0)
                            {
                                nblock++;
                            }
                        }
                        if (nblock == 2)
                        {
                            for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                            {
                                if (pcSquares[sq] > 0)
                                {
                                    CheckBlocker(sd, pcSquares[sq], sqSrc, 2);
                                }
                            }
                        }
                        else if (nblock == 0) //空心炮
                        {
                            for (int sq = sqSrc + delta; sq != sqOppKing; sq += delta)
                            {
                                BannedGrids[1 - sd, sq] = true;
                            }
                        }
                    }
                }

                // 3. 判断对方的将是否被马威胁(以仕(士)的步长当作马腿)
                for (int i = 0; i < 4; i++)
                {
                    int pcBlocker = pcSquares[sqOppKing + ccGuardDelta[i]];
                    if (pcBlocker != 0)
                    {
                        for (int j = 0; j < 2; j++)
                        {
                            pcDst = pcSquares[sqOppKing + ccKnightCheckDelta[i, j]];
                            if (cnPieceTypes[pcDst] == bas + KNIGHT)
                            {
                                CheckBlocker(sd, pcBlocker, sqPieces[pcDst], 3);
                            }
                        }
                    }
                }

                //4. Is there king face to face risk?
                sqSrc = sqPieces[32 + KING_FROM];
                sqDst = sqPieces[16 + KING_FROM];
                if (SAME_FILE(sqSrc, sqDst))
                {
                    int pcBlocker = 0, nblock = 0;
                    for (int i = sqSrc + 16; i < sqDst; i += 16)
                    {
                        if (pcSquares[i] > 0)
                        {
                            pcBlocker = i;
                            nblock++;
                        }
                    }
                    if (nblock == 1)
                    {
                        PinnedPieces[pcBlocker] |= 1;
                    }
                }
            }


            int totalPieces = 0;

            int[,] nP = new int[2, 8];  //每个兵种的棋子数量
            int[] materialValue = new int[2];
            int[] positionValue = new int[2];
            connectivityMap = new int[2, 256];

            //Generate attack map, from most valuable piece to cheap piece
            for (int sd = 0; sd < 2; sd++)
            {
                int bas       = SIDE_TAG(sd);
                int sqOppKing = sqPieces[OPP_SIDE_TAG(sd) + KING_FROM];
                for (int pc = bas; pc < bas + 16; pc++)
                {
                    sqSrc = sqPieces[pc];
                    if (sqSrc == 0)
                    {
                        continue;
                    }
                    int pcKind      = cnPieceKinds[pc];
                    int sqSrcMirror = sd == 0 ? sqSrc : SQUARE_FLIP(sqSrc);
                    int pin         = PinnedPieces[pc];
                    totalPieces++;
                    nP[sd, pcKind]++;
                    materialValue[sd] += cnPieceValue[pc];

                    //int posv0 = positionValue[sd];
                    switch (pcKind)
                    {
                    case KING:
                        for (int i = 0; i < 4; i++)
                        {
                            sqDst = sqSrc + ccKingDelta[i];
                            if (IN_FORT[sqDst])
                            {
                                AttackMap[sd, sqDst] = pc;
                            }
                        }
                        positionValue[sd] += cKingPawnValue[sqSrcMirror];
                        break;

                    case ROOK:
                        for (int j = 0; j < 4; j++)
                        {
                            if (ccPinDelta[pin, j])
                            {
                                continue;
                            }
                            delta = ccKingDelta[j];
                            for (sqDst = sqSrc + delta; IN_BOARD[sqDst]; sqDst += delta)
                            {
                                pcDst = pcSquares[sqDst];
                                AttackMap[sd, sqDst] = pc;
                                if (pcDst != 0)
                                {
                                    break;
                                }
                            }
                        }
                        positionValue[sd] += cRookValue[sqSrcMirror];
                        break;

                    case CANNON:
                        for (int j = 0; j < 4; j++)
                        {
                            if (ccPinDelta[pin, j])
                            {
                                continue;
                            }
                            int nDelta = ccKingDelta[j];
                            for (sqDst = sqSrc + nDelta; IN_BOARD[sqDst]; sqDst += nDelta)
                            {
                                if (pcSquares[sqDst] != 0)     //炮架
                                {
                                    for (sqDst += nDelta; IN_BOARD[sqDst]; sqDst += nDelta)
                                    {
                                        AttackMap[sd, sqDst] = pc;
                                        if (pcSquares[sqDst] != 0)     //直瞄点
                                        {
                                            goto NextFor;
                                        }
                                    }
                                }
                            }
                            NextFor :;
                        }
                        if (SAME_FILE(sqSrc, sqOppKing) || SAME_RANK(sqSrc, sqOppKing))
                        {
                            positionValue[sd] += 5;
                        }
                        break;

                    case KNIGHT:
                        if (pin > 0)
                        {
                            continue;
                        }
                        for (int j = 0; j < 4; j++)
                        {
                            if (pcSquares[sqSrc + ccKingDelta[j]] == 0)
                            {
                                AttackMap[sd, sqSrc + ccKnightDelta[j, 0]] = pc;
                                AttackMap[sd, sqSrc + ccKnightDelta[j, 1]] = pc;
                            }
                        }
                        positionValue[sd] += cKnightValue[sqSrcMirror];
                        break;

                    case PAWN:
                        if ((pin & 1) == 0)
                        {
                            AttackMap[sd, SQUARE_FORWARD(sqSrc, sd)] = pc;
                        }
                        if ((pin & 2) == 0)
                        {
                            if (HOME_HALF[1 - sd, sqSrc])
                            {
                                AttackMap[sd, sqSrc + 1] = pc;
                                AttackMap[sd, sqSrc - 1] = pc;
                            }
                        }
                        positionValue[sd] += cKingPawnValue[sqSrcMirror];
                        break;

                    case BISHOP:
                        if (pin > 0)
                        {
                            continue;
                        }
                        for (int j = 0; j < 4; j++)
                        {
                            sqDst = sqSrc + ccGuardDelta[j];
                            if (HOME_HALF[sd, sqDst] && pcSquares[sqDst] == 0)
                            {
                                AttackMap[sd, sqDst + ccGuardDelta[j]] = pc;
                            }
                        }
                        positionValue[sd] += cBishopGuardValue[sqSrcMirror];
                        break;

                    case GUARD:
                        if (pin > 0)
                        {
                            continue;
                        }
                        for (int j = 0; j < 4; j++)
                        {
                            sqDst = sqSrc + ccGuardDelta[j];
                            if (IN_FORT[sqDst])
                            {
                                AttackMap[sd, sqDst] = pc;
                            }
                        }
                        positionValue[sd] += cBishopGuardValue[sqSrcMirror];
                        break;

                    default:
                        Debug.Fail("Unknown piece type");
                        break;
                    }
                    //ivpc[nStep, pc] = positionValue[sd] - posv0;
                }
                //帅所在的格没有保护
                AttackMap[sd, sqPieces[bas + KING_FROM]] = 0;
            }

            int[] pair = new int[2];
            for (int sd = 0; sd < 2; sd++)
            {
                //棋盘上的子越多,炮的威力越大,马的威力越小
                pair[sd] += (int)(2.4 * totalPieces * nP[sd, CANNON]);  //可调系数
                pair[sd] -= (int)(0.9 * totalPieces * nP[sd, KNIGHT]);  //可调系数
                //兵卒的价值随着对方攻击子力的减少而增加(即增加了过河的可能性)
                int   enemyAttack         = nP[1 - sd, ROOK] * 2 + nP[1 - sd, CANNON] + nP[1 - sd, KNIGHT];
                int[] additionalPawnValue = { 28, 21, 15, 10, 6, 3, 2, 1, 0 };
                pair[sd] += nP[sd, PAWN] * additionalPawnValue[enemyAttack];
                //兵种不全扣分
                if (nP[sd, ROOK] == 0)
                {
                    pair[sd] -= 30; //有车胜无车
                }
                if (nP[sd, CANNON] == 0)
                {
                    pair[sd] -= 20;
                }
                if (nP[sd, KNIGHT] == 0)
                {
                    pair[sd] -= 20;
                }
                //缺相怕炮
                pair[sd] += (nP[sd, BISHOP] - nP[1 - sd, CANNON]) * 15;
                //缺仕怕车
                pair[sd] += (nP[sd, GUARD] - nP[1 - sd, ROOK]) * 15;
            }

            int[] connectivity = new int[2];
            captureMoves = new List <MOVE>();
            foreach (int sq in cboard90)
            {
                int conn00 = connectivity[0], conn01 = connectivity[1];
                sqDst = sq;
                pcDst = pcSquares[sqDst];
                int sd = SIDE(pcDst);
                if (sd != -1)
                {
                    int attack  = AttackMap[1 - sd, sqDst];
                    int protect = AttackMap[sd, sqDst];
                    if (attack > 0)
                    {
                        int[] cnAttackScore = { 0, 20, 12, 8, 8, 4, 6, 6 };
                        if (protect > 0)
                        {
                            if (sd == sdPlayer)
                            {
                                if (cnPieceValue[pcDst] > cnPieceValue[attack])
                                {
                                    connectivity[1 - sd] += cnAttackScore[cnPieceKinds[pcDst]];
                                }
                                else
                                {
                                    connectivity[1 - sd] += 5;
                                }
                            }
                            else
                            {
                                connectivity[1 - sd] += Math.Max(cnPieceValue[pcDst] - cnPieceValue[attack], 5);
                                MOVE mv = new MOVE(sqPieces[attack], sqDst, attack, pcDst);
                                mv.score = cnPieceValue[pcDst] - cnPieceValue[attack];
                                captureMoves.Add(mv);
                            }
                        }
                        else
                        {
                            if (sd == sdPlayer)
                            {
                                connectivity[1 - sd] += cnAttackScore[cnPieceKinds[pcDst]];
                            }
                            else  //如果轮到对方走棋,可以直接吃无根子
                            {
                                connectivity[1 - sd] += cnPieceValue[pcDst] * 3 / 4;
                                MOVE mv = new MOVE(sqPieces[attack], sqDst, attack, pcDst);
                                mv.score = cnPieceValue[pcDst];
                                captureMoves.Add(mv);
                            }
                        }
                    }
                    else if (protect > 0)
                    {
                        connectivity[sd] += 2;
                    }
                }
                else
                {
                    for (sd = 0; sd < 2; sd++)
                    {
                        if (BannedGrids[sd, sqDst])
                        {
                            tacticValue[1 ^ sd] += AttackMap[1 - sd, sqDst] > 0 ? cDiscoveredAttack[cnPieceKinds[AttackMap[1 - sd, sqDst]]] : 2;
                        }
                        //机动性, 不考虑炮的空射,因为炮的射界与活动范围不同,且炮架可能是对方的车、炮或兵、帅
                        else if (AttackMap[sd, sqDst] > 0 && cnPieceKinds[AttackMap[sd, sqDst]] != CANNON && AttackMap[1 - sd, sqDst] == 0)
                        {
                            connectivity[sd] += 2;
                        }
                    }
                }
                connectivityMap[0, sqDst] = connectivity[0] - conn00;
                connectivityMap[1, sqDst] = connectivity[1] - conn01;
            }

            int scoreRed   = materialValue[0] + positionValue[0] + pair[0] + connectivity[0] + tacticValue[0];
            int scoreBlack = materialValue[1] + positionValue[1] + pair[1] + connectivity[1] + tacticValue[1];

            int total = scoreRed - scoreBlack;

            //ivpc[nStep, 0] = nStep;
            //ivpc[nStep, 1] = total;
            //ivpc[nStep, 2] = scoreRed;
            //ivpc[nStep, 3] = scoreBlack;
            //ivpc[nStep, 4] = materialValue[0];
            //ivpc[nStep, 5] = materialValue[1];
            //ivpc[nStep, 6] = positionValue[0];
            //ivpc[nStep, 7] = positionValue[1];
            //ivpc[nStep, 8] = connectivity[0];
            //ivpc[nStep, 9] = connectivity[1];
            //ivpc[nStep, 10] = pair[0];
            //ivpc[nStep, 11] = pair[1];
            //ivpc[nStep, 12] = tacticValue[0];
            //ivpc[nStep, 13] = tacticValue[1];
            return(sdPlayer == 0 ? total : -total);
        }
Example #21
0
        //The move generated are after check test. So they are already legal.
        //if FindAbsolutePin has been done right before GenerateMoves, set pinDone true
        List <MOVE> GenerateMoves(bool pinDone = false)
        {
            int         sqSrc, sqDst, pcSrc, pcDst;
            int         myBase, oppBase;
            int         delta;
            List <MOVE> mvs = new List <MOVE>();

            myBase  = SIDE_TAG(sdPlayer);
            oppBase = OPP_SIDE_TAG(sdPlayer);
            if (!pinDone)
            {
                FindAbsolutePin();
            }
            void AddMove()
            {
                if (bannedGrids[sdPlayer, sqDst])
                {
                    return;
                }
                MOVE mv     = new MOVE(sqSrc, sqDst, pcSrc, pcDst);
                int  mySide = sdPlayer;

                if (PinnedPieces[pcSrc] == 0)
                {
                    //如果被照将,先试试走棋后,照将着法是否仍然成立
                    if (stepList[stepList.Count - 1].move.checking)
                    {
                        MovePiece(mv);
                        int sqKing = sqPieces[SIDE_TAG(mySide) + KING_FROM];
                        if (!IsLegalMove(stepList[stepList.Count - 1].move.sqDst, sqKing))
                        {
                            if (CheckedBy(mySide) == 0)
                            {
                                mvs.Add(mv);
                            }
                        }
                        UndoMovePiece(mv);
                    }
                    else
                    {
                        mvs.Add(mv);
                    }
                }
                else
                {
                    MovePiece(mv);
                    if (CheckedBy(mySide) == 0)
                    {
                        mvs.Add(mv);
                    }
                    UndoMovePiece(mv);
                }
            }

            for (int i = ROOK_FROM; i <= ROOK_TO; i++)
            {
                pcSrc = myBase + i;
                sqSrc = sqPieces[pcSrc];
                if (sqSrc == 0)
                {
                    continue;
                }

                for (int j = 0; j < 4; j++)
                {
                    if (ccPinDelta[PinnedPieces[pcSrc], j])
                    {
                        continue;
                    }
                    delta = ccKingDelta[j];
                    for (sqDst = sqSrc + delta; IN_BOARD[sqDst]; sqDst += delta)
                    {
                        pcDst = pcSquares[sqDst];
                        if (pcDst == 0)
                        {
                            AddMove();
                        }
                        else
                        {
                            if ((pcDst & oppBase) != 0)
                            {
                                AddMove();
                            }
                            break;
                        }
                    }
                }
            }
            for (int i = CANNON_FROM; i <= CANNON_TO; i++)
            {
                pcSrc = myBase + i;
                sqSrc = sqPieces[pcSrc];
                if (sqSrc == 0)
                {
                    continue;
                }
                for (int j = 0; j < 4; j++)
                {
                    if (ccPinDelta[PinnedPieces[pcSrc], j])
                    {
                        continue;
                    }
                    delta = ccKingDelta[j];
                    for (sqDst = sqSrc + delta; IN_BOARD[sqDst]; sqDst += delta)
                    {
                        pcDst = pcSquares[sqDst];
                        if (pcDst == 0)
                        {
                            AddMove();
                        }
                        else
                        {
                            for (sqDst += delta; IN_BOARD[sqDst]; sqDst += delta)
                            {
                                pcDst = pcSquares[sqDst];
                                if (pcDst != 0)
                                {
                                    if ((pcDst & oppBase) != 0)
                                    {
                                        AddMove();
                                    }
                                    goto NextFor1;
                                }
                            }
                        }
                    }
                    NextFor1 :;
                }
            }

            for (int i = KNIGHT_FROM; i <= KNIGHT_TO; i++)
            {
                pcSrc = myBase + i;
                sqSrc = sqPieces[pcSrc];
                if (sqSrc == 0 || PinnedPieces[pcSrc] > 0)
                {
                    continue;
                }
                for (int j = 0; (sqDst = csqKnightMoves[sqSrc, j]) > 0; j++)
                {
                    if (pcSquares[csqKnightPins[sqSrc, j]] == 0)
                    {
                        pcDst = pcSquares[sqDst];
                        if ((pcDst & myBase) == 0)
                        {
                            AddMove();
                        }
                    }
                }
            }

            for (int i = PAWN_FROM; i <= PAWN_TO; i++)
            {
                pcSrc = myBase + i;
                sqSrc = sqPieces[pcSrc];
                if (sqSrc == 0)
                {
                    continue;
                }
                sqDst = SQUARE_FORWARD(sqSrc, sdPlayer);
                if (IN_BOARD[sqDst])
                {
                    pcDst = pcSquares[sqDst];
                    if ((pcDst & myBase) == 0)
                    {
                        AddMove();
                    }
                }
                if ((PinnedPieces[pcSrc] & 2) == 0)
                {
                    sqDst = sqSrc - 1;
                    if (HOME_HALF[1 - sdPlayer, sqDst])
                    {
                        pcDst = pcSquares[sqDst];
                        if ((pcDst & myBase) == 0)
                        {
                            AddMove();
                        }
                    }
                    sqDst = sqSrc + 1;
                    if (HOME_HALF[1 - sdPlayer, sqDst])
                    {
                        pcDst = pcSquares[sqDst];
                        if ((pcDst & myBase) == 0)
                        {
                            AddMove();
                        }
                    }
                }
            }

            for (int i = BISHOP_FROM; i <= BISHOP_TO; i++)
            {
                pcSrc = myBase + i;
                sqSrc = sqPieces[pcSrc];
                if (sqSrc == 0 || PinnedPieces[pcSrc] > 0)
                {
                    continue;
                }
                for (int j = 0; (sqDst = csqBishopMoves[sqSrc, j]) > 0; j++)
                {
                    if (pcSquares[(sqDst + sqSrc) / 2] == 0)
                    {
                        pcDst = pcSquares[sqDst];
                        if ((pcDst & myBase) == 0)
                        {
                            AddMove();
                        }
                    }
                }
            }

            for (int i = GUARD_FROM; i <= GUARD_TO; i++)
            {
                pcSrc = myBase + i;
                sqSrc = sqPieces[pcSrc];
                if (sqSrc == 0 || PinnedPieces[pcSrc] > 0)
                {
                    continue;
                }
                for (int j = 0; (sqDst = csqAdvisorMoves[sqSrc, j]) > 0; j++)
                {
                    pcDst = pcSquares[sqDst];
                    if ((pcDst & myBase) == 0)
                    {
                        AddMove();
                    }
                }
            }

            pcSrc = myBase + KING_FROM;
            sqSrc = sqPieces[pcSrc];
            for (int i = 0; (sqDst = csqKingMoves[sqSrc, i]) != 0; i++)
            {
                pcDst = pcSquares[sqDst];
                if ((pcDst & myBase) == 0)
                {
                    MOVE mv = new MOVE(sqSrc, sqDst, myBase + KING_FROM, pcDst);
                    MovePiece(mv);
                    if (CheckedBy(1 - sdPlayer) == 0)
                    {
                        mvs.Add(mv);
                    }
                    UndoMovePiece(mv);
                }
            }

            foreach (Tuple <int, int> pin in pinexception)
            {
                if (SIDE(pcSquares[pin.Item1]) == sdPlayer)
                {
                    MOVE mv = new MOVE(pin.Item1, pin.Item2, pcSquares[pin.Item1], pcSquares[pin.Item2]);
                    MovePiece(mv);
                    if (CheckedBy(1 - sdPlayer) == 0)
                    {
                        mvs.Add(mv);
                    }
                    UndoMovePiece(mv);
                }
            }
            return(mvs);
        }
Example #22
0
        /*该函数首先返回Transkiller,然后生成所有着法,过滤不需要的着法后SEE,排序
         * 参数moveType:
         * 1 - 生成所有照将
         * 2 - 生成所有吃子
         * 3 - 生成所有照将和吃子
         * 7 - 生成所有合法着法
         */
        public IEnumerable <MOVE> GetNextMove(int moveType, int height, int mate_threat = -1)
        {
            Debug.Assert(moveType < 8);
            Debug.Assert(height >= 0);
            bool wantCheck   = (moveType & 0x01) > 0;
            bool wantCapture = (moveType & 0x02) > 0;
            bool wantAll     = moveType == 7;

            List <MOVE> movesDone = new List <MOVE>();

#if USE_HASH
            if (!(TransKiller is null) && TransKiller.sqDst != 0)
            {
                Debug.Assert(TransKiller.pcSrc == pcSquares[TransKiller.sqSrc]);
                Debug.Assert(TransKiller.pcDst == pcSquares[TransKiller.sqDst]);
                Debug.Assert(IsLegalMove(TransKiller.sqSrc, TransKiller.sqDst));
                TransKiller.killer = KILLER.Trans;
                TransKiller.score  = TransScore; //unnecessary
                movesDone.Add(TransKiller);
                yield return(TransKiller);
            }
#endif
            MOVE killer;
#if USE_MATEKILLER
            if (!stepList[stepList.Count - 1].move.checking)
            {
                killer = MateKiller[sdPlayer];
                if (!(killer is null) && killer.pcSrc == pcSquares[killer.sqSrc] && killer.pcDst == pcSquares[killer.sqDst] &&
                    IsLegalMove(killer.sqSrc, killer.sqDst))
                {
                    MovePiece(killer);
                    bool notChecked = (CheckedBy(1 - sdPlayer) == 0);
                    killer.checking = CheckedBy(sdPlayer) > 0;
                    UndoMovePiece(killer);
                    if (notChecked)
                    {
                        if (wantAll || wantCheck && IsChecking(killer) || wantCapture && killer.pcDst > 0)
                        {
                            movesDone.Add(killer);
                            killer.killer = KILLER.Mate;
                            yield return(killer);
                        }
                    }
                }
            }
#endif
            List <MOVE> moves = GenerateMoves(false);
            GenAttackMap(true);

            foreach (MOVE mv in movesDone)
            {
                moves.Remove(mv);
            }
            if (moveType == 2)
            {
                moves.RemoveAll(x => x.pcDst == 0);
            }

            for (int i = 0; i < moves.Count; i++)
            {
                MOVE mv = moves[i];
                mv.checking = IsChecking(mv);
                if (mv.checking)
                {
                    //encourage continuous check
                    if (stepList.Count >= 2 && stepList[stepList.Count - 2].move.checking)
                    {
                        mv.score += 30;
                    }
                    else
                    {
                        mv.score += 10;
                    }
                    //Avoid repeating check by the same piece
                    if (mv.pcDst == 0 && stepList.Count >= 2 && mv.sqSrc == stepList[stepList.Count - 2].move.sqDst)
                    {
                        mv.score -= 5;
                    }
                }
            }

            //usually, moveType is either 3 or 7
            switch (moveType)
            {
            case 1:
                moves.RemoveAll(x => x.checking == false);
                break;

            case 2:
                moves.RemoveAll(x => x.pcDst == 0);
                break;

            case 3:
                moves.RemoveAll(x => x.checking == false && x.pcDst == 0);
                break;

            default:
                break;
            }

            for (int i = 0; i < moves.Count; i++)
            {
                MOVE mv = moves[i];
                int  vl = SEE(mv, AttackMap[sdPlayer, mv.sqDst], AttackMap[1 - sdPlayer, mv.sqDst]);
                mv.score += vl;
                mv.score += PiecePositionValue(mv.pcSrc, mv.sqDst) - PiecePositionValue(mv.pcSrc, mv.sqSrc);
                if (vl > 0)
                {
                    mv.score += GoodScore;
                    mv.killer = KILLER.GoodCapture;
                }
                else if (vl == 0)
                {
                    int j = GetHistoryIndex(mv);
                    Debug.Assert(HistHit[j] <= HistTotal[j]);
                    mv.score += (HistHit[j] + 1) * History[j] / (HistTotal[j] + 1) + HistoryScore;
                    Debug.Assert(mv.score < 0);
                    mv.killer = KILLER.Normal;
                }
                else
                {
                    mv.score += BadScore;
                    Debug.Assert(mv.score < HistoryScore);
                    mv.killer = KILLER.BadCapture;
                }
            }
            //don't extend bad capture in quiescence search
            if (!wantAll)
            {
                moves.RemoveAll(x => x.score < BadScore);
            }

            //assign killer bonus
            if (mate_threat > 0)
            {
                killer = moves.Find(x => x == Killers[mate_threat + 1, 0]);
                if (!(killer is null))
                {
                    killer.score += TransScore - 1;
                    killer.killer = KILLER.Mate;
                }
            }
            if (!(Killers[height, 0] is null) && Killers[height, 0].sqSrc > 0)
            {
                killer = moves.Find(x => x == Killers[height, 0]);
                if (!(killer is null))
                {
                    killer.score += KillerScore;
                    killer.killer = KILLER.Killer1;
                    killer        = moves.Find(x => x == Killers[height, 1]);
                    if (!(killer is null))
                    {
                        killer.killer = KILLER.Killer2;
                        killer.score += KillerScore - 1;
                    }
                }
            }
            moves.Sort(Large2Small);
            foreach (var mv in moves)
            {
                yield return(mv);
            }

            int PiecePositionValue(int pc, int sq)
            {
                Debug.Assert(pc < 48 && pc > 15);
                int sd       = SIDE(pc);
                int sqMirror = sd == 0 ? sq : SQUARE_FLIP(sq);
                int sqOppKing;
                int vl;

                switch (cnPieceKinds[pc])
                {
                case ROOK:
                    sqOppKing = sqPieces[OPP_SIDE_TAG(sd) + KING_FROM];
                    vl        = cRookValue[sqMirror];
                    if (SAME_FILE(sq, sqOppKing) || SAME_RANK(sq, sqOppKing))
                    {
                        vl += 5;
                    }
                    return(vl);

                case CANNON:
                    sqOppKing = sqPieces[OPP_SIDE_TAG(sd) + KING_FROM];
                    if (SAME_FILE(sq, sqOppKing))
                    {
                        return(10);
                    }
                    else if (SAME_RANK(sq, sqOppKing))
                    {
                        return(8);
                    }
                    break;

                case KNIGHT:
                    vl = cKnightValue[sqMirror];
                    //检查绊马腿
                    for (int j = 0; j < 4; j++)
                    {
                        int sqPin = sq + ccKingDelta[j];
                        if (pcSquares[sqPin] != 0)
                        {
                            vl -= 8;
                        }
                    }
                    return(vl);

                case PAWN:
                    return(cKingPawnValue[sqMirror]);

                case KING:
                    return(cKingPawnValue[sqMirror]);

                case BISHOP:
                case GUARD:
                    return(cBishopGuardValue[sqMirror]);

                default:
                    Debug.Fail("Unknown piece type");
                    break;
                }
                return(0);
            }
        }
Example #23
0
        public int SearchRoot(int depth)
        {
            int         alpha     = -G.MATE;
            int         beta      = G.MATE;
            MOVE        mvBest    = new MOVE();
            List <MOVE> subpv     = null;
            bool        firstMove = true;

            for (int i = 0; i < rootMoves.Count; i++)
            {
                MOVE mv = rootMoves[i];
                //Console.WriteLine($"info currmove {mv}, currmovenumber {i}");
                Debug.WriteLine($"{mv} {alpha}, {beta}");
                int new_depth = depth - 1;
                if (mv.sqDst == stepList[stepList.Count - 1].move.sqDst && mv.score > 0 ||
                    mv.checking || stepList[stepList.Count - 1].move.checking && firstMove)
                {
                    new_depth++;
                }
                firstMove = false;
                MakeMove(mv, false);
                int vl;

                if (mvBest.sqSrc == 0)
                {
                    vl = -SearchPV(-beta, -alpha, new_depth, 1, out subpv);
                }
                else
                {
                    vl = -SearchCut(-alpha - 1, -alpha, new_depth, 1, -1);
                    if (vl > alpha)
                    {
                        stat.PVChanged++;
                        Debug.WriteLine("Root re-search");
                        Debug.WriteLine($"{mv} {alpha}, {beta}");
                        vl = -SearchPV(-beta, -alpha, new_depth, 1, out subpv);
                    }
                }
                UnmakeMove();

                mv.score = vl;

                if (vl > alpha)
                {
                    alpha  = vl;
                    mvBest = mv;
                    PVLine.Clear();
                    PVLine.Add(mvBest);
                    PVLine.AddRange(subpv);
                    Console.WriteLine("PV:" + PopPVLine());
                    if (vl >= beta)
                    {
                        stat.BetaCutoffs++;
                        break;
                    }
                }
            }
            rootMoves.RemoveAll(x => x.score < -G.WIN);
            rootMoves.Sort(Large2Small);

#if LATE_MOVE_REDUCTION
            int vlBase = Simple_Evaluate();
            //late move reduction
            for (int i = rootMoves.Count - 1; i >= 0 && vlBase - rootMoves[i].score > 50; i--)
            {
                //lose a cannon/knight for nothing
                Console.WriteLine($"Prune move: {rootMoves[i]}, score {rootMoves[i].score}");
                rootMoves.RemoveAt(i);
            }
#endif
            Console.WriteLine("Root move\tScore");
            foreach (MOVE mv in rootMoves)
            {
                Console.WriteLine($"{mv}\t{mv.score}");
            }
            Console.WriteLine($"Best move {mvBest}, score {alpha}");
            Console.WriteLine("PV:" + PopPVLine());
            TT.PrintStatus();
            return(alpha);
        }