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); }
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); }
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); /* 用顶级人类选手的对局来测试评估审局函数的有效性。 * 理想情况下,双方分数应呈锯齿状交替上升,除去吃子的步骤,应该稳定渐变。 */ }
void HistoryGood(MOVE mv, int depth) { int i = GetHistoryIndex(mv); HistHit[i]++; HistTotal[i]++; History[i] += depth * depth; }
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); } }
//暂不支持(变招)识别 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); }
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; }
void HistoryBad(MOVE mv) { int i = GetHistoryIndex(mv); HistTotal[i]++; }
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); }
//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)
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); }
int Large2Small(MOVE a, MOVE b) { return(b.score.CompareTo(a.score)); }
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]); }
static int GetHistoryIndex(MOVE mv) { return(cnPieceHistIndex[mv.pcSrc] * 90 + cboard256[mv.sqDst]); }
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); }
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; } } }
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); }
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); }
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); }
//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); }
/*该函数首先返回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); } }
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); }