/// <summary> /// 搬一步棋的棋子 /// </summary> /// <param name="mv">走棋步骤</param> /// <returns>返回原来目标格子上的棋子</returns> public static int MovePiece(int mv) { //Debuger.LogWarning(string.Format("Loadutil->MovePiece( ) 开始, mv = {0}", mv)); int sqSrc, sqDst, pc, pcCaptured; sqSrc = Chess_LoadUtil.SRC(mv); // 得到起始位置的数组下标 sqDst = Chess_LoadUtil.DST(mv); // 得到目标位置的数组下标 pcCaptured = ucpcSquares[sqDst]; // 得到目的格子的棋子 Debuger.LogWarning(string.Format("Loadutil->MovePiece( ) 得到目的格子的棋子pcCaptured ={0}", pcCaptured)); if (pcCaptured != 0) // 目的地不为空 { DelPiece(sqDst, pcCaptured); // 删掉目标格子棋子 } pc = ucpcSquares[sqSrc]; // 得到初始格子上的棋子 Debuger.LogWarning(string.Format("Loadutil->MovePiece( ) 得到初始格子上的棋子pc ={0}", pc)); if (0 != pc) { DelPiece(sqSrc, pc); // 删掉初始格子上的棋子 Debuger.LogWarning(string.Format("Loadutil->MovePiece( ) 删掉初始格子上的棋子sqSrc={0}, pc ={1}", sqSrc, pc)); AddPiece(sqDst, pc); // 在目标格子上放上棋子 } Debuger.LogWarning(string.Format("Loadutil->MovePiece( ) pcCaptured={0}", pcCaptured)); return(pcCaptured);// 返回原来目标格子上的棋子 }
/// <summary> /// 从棋盘上拿走一枚棋子,当拿走一枚棋子时,要将拿走的该棋子对应该位子的子力价值从该方在该局下的局面价值上减去 /// </summary> /// <param name="sq">位置下标</param> /// <param name="pc">哪颗棋子</param> public static void DelPiece(int sq, int pc) { //Debuger.LogWarning(string.Format("GameLogic->DelPiece() sq = {0}, pc = {1}", sq, pc)); ucpcSquares[sq] = 0; // 红方减分,黑方(注意"cucvlPiecePos"取值要颠倒)加分 if (pc < 16) { vlWhite -= GameConstant.cucvlPiecePos[pc - 8][sq]; } else { vlBlack -= GameConstant.cucvlPiecePos[pc - 16][Chess_LoadUtil.SQUARE_FLIP(sq)]; } Debuger.LogWarning(string.Format("GameLogic->DelPiece()完成 vlWhite = {0}, vlBlack = {1}", vlWhite, vlBlack)); }
/// <summary> /// 在棋盘上放一枚棋子 /// </summary> /// <param name="sq">位置下标</param> /// <param name="pc">哪颗棋子代号</param> public static void AddPiece(int sq, int pc) { ucpcSquares[sq] = pc; Debuger.LogWarning(string.Format("Loadutil->AddPiece( ) sq = {0}, pc = {1}", sq, pc)); // 红方加分,黑方(注意"cucvlPiecePos"取值要颠倒)减分 // 小于16是红方棋子,大于16为黑方棋子,0表示没棋子 if (pc < 16) { vlWhite += GameConstant.cucvlPiecePos[pc - 8][sq]; Debuger.LogWarning(string.Format("Loadutil->AddPiece( ) vlWhite = {0}", vlWhite)); } else { vlBlack += GameConstant.cucvlPiecePos[pc - 16][Chess_LoadUtil.SQUARE_FLIP(sq)]; Debuger.LogWarning(string.Format("Loadutil->AddPiece( ) vlBlack = {0}", vlBlack)); } }
/// <summary> /// 撤消搬一步棋的棋子,该方法要传入走棋的步骤,以及该走棋步骤下的目标位置上的棋子。 /// 先将该棋子搬到初始位置,如果该步骤下的目标位置上的棋子不为空,即: 是吃子走法时, /// 把被吃掉的棋子重新搬到该位置,以此来实现撤销搬一步棋的功能。 /// </summary> /// <param name="mv">走棋步骤</param> /// <param name="pcCaptured">原来目标格子上的棋子</param> public static void UndoMovePiece(int mv, int pcCaptured) { int sqSrc, sqDst, pc; sqSrc = Chess_LoadUtil.SRC(mv); // 得到起始位置的数组下标 sqDst = Chess_LoadUtil.DST(mv); // 得到目标位置的数组下标 pc = ucpcSquares[sqDst]; // 得到目的格子的棋子 if (0 != pc) { DelPiece(sqDst, pc); // 删除目标格子的棋子 } AddPiece(sqSrc, pc); // 在起始格子上放棋子 if (pcCaptured != 0) // 如果目标格子上起初有棋子 { AddPiece(sqDst, pcCaptured); // 将该棋子放在目标格子上 } Debuger.LogWarning(string.Format("Loadutil->UndoMovePiece( )")); }
/// <summary> /// 判断走法是否合理,生成所有走法后,判断每个走法是否合理,只把合理的走法留下来。 /// </summary> /// <param name="mv"></param> /// <returns></returns> public static bool LegalMove(int mv) { int sqSrc, sqDst, sqPin; int pcSelfSide, pcSrc, pcDst, nDelta; // 判断走法是否合法,需要经过以下的判断过程: // 1. 判断起始格是否有自己的棋子 sqSrc = Chess_LoadUtil.SRC(mv); pcSrc = ucpcSquares[sqSrc]; pcSelfSide = Chess_LoadUtil.SIDE_TAG(sdPlayer); // 获得红黑标记(红子是8,黑子是16) Debuger.Log(string.Format("下棋方pcSelfSide = {0}, pcSrc = {1}", pcSelfSide, pcSrc)); if ((pcSrc & pcSelfSide) == 0) { return(false); } // 2. 判断目标格是否有自己的棋子 sqDst = Chess_LoadUtil.DST(mv); pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) != 0) { return(false); } // 3. 根据棋子的类型检查走法是否合理 switch (pcSrc - pcSelfSide) { case GameConstant.PIECE_KING: return(Chess_LoadUtil.IN_FORT(sqDst) && Chess_LoadUtil.KING_SPAN(sqSrc, sqDst)); case GameConstant.PIECE_ADVISOR: return(Chess_LoadUtil.IN_FORT(sqDst) && Chess_LoadUtil.ADVISOR_SPAN(sqSrc, sqDst)); case GameConstant.PIECE_BISHOP: return(Chess_LoadUtil.SAME_HALF(sqSrc, sqDst) && Chess_LoadUtil.BISHOP_SPAN(sqSrc, sqDst) && ucpcSquares[Chess_LoadUtil.BISHOP_PIN(sqSrc, sqDst)] == 0); case GameConstant.PIECE_KNIGHT: sqPin = Chess_LoadUtil.KNIGHT_PIN(sqSrc, sqDst); return(sqPin != sqSrc && ucpcSquares[sqPin] == 0); case GameConstant.PIECE_ROOK: case GameConstant.PIECE_CANNON: if (Chess_LoadUtil.SAME_RANK(sqSrc, sqDst)) { nDelta = (sqDst < sqSrc ? -1 : 1); } else if (Chess_LoadUtil.SAME_FILE(sqSrc, sqDst)) { nDelta = (sqDst < sqSrc ? -16 : 16); } else { return(false); } sqPin = sqSrc + nDelta; while (sqPin != sqDst && ucpcSquares[sqPin] == 0) { sqPin += nDelta; } if (sqPin == sqDst) { return(pcDst == 0 || pcSrc - pcSelfSide == GameConstant.PIECE_ROOK); } else if (pcDst != 0 && pcSrc - pcSelfSide == GameConstant.PIECE_CANNON) { sqPin += nDelta; while (sqPin != sqDst && ucpcSquares[sqPin] == 0) { sqPin += nDelta; } return(sqPin == sqDst); } else { return(false); } case GameConstant.PIECE_PAWN: Debuger.Log(string.Format("走的炮")); if (Chess_LoadUtil.AWAY_HALF(sqDst, sdPlayer) && (sqDst == sqSrc - 1 || sqDst == sqSrc + 1)) { return(true); } return(sqDst == Chess_LoadUtil.SQUARE_FORWARD(sqSrc, sdPlayer)); default: return(false); } }
/// <summary> /// 判断是否被将军 /// </summary> /// <returns></returns> public static bool Checked( ) { int i, j, sqSrc, sqDst; int pcSelfSide, pcOppSide, pcDst, nDelta; pcSelfSide = Chess_LoadUtil.SIDE_TAG(sdPlayer); // 获得红黑标记(红子是8,黑子是16) pcOppSide = Chess_LoadUtil.OPP_SIDE_TAG(sdPlayer); // 获得红黑标记,对方的 // 找到棋盘上的帅(将),再做以下判断: for (sqSrc = 0; sqSrc < 256; sqSrc++) { if (ucpcSquares[sqSrc] != pcSelfSide + GameConstant.PIECE_KING) { continue; } // 1. 判断是否被对方的兵(卒)将军 if (ucpcSquares[Chess_LoadUtil.SQUARE_FORWARD(sqSrc, sdPlayer)] == pcOppSide + GameConstant.PIECE_PAWN) { return(true); } for (nDelta = -1; nDelta <= 1; nDelta += 2) { if (ucpcSquares[sqSrc + nDelta] == pcOppSide + GameConstant.PIECE_PAWN) { return(true); } } // 2. 判断是否被对方的马将军(以仕(士)的步长当作马腿) for (i = 0; i < 4; i++) { if (ucpcSquares[sqSrc + GameConstant.ccAdvisorDelta[i]] != 0) { continue; } for (j = 0; j < 2; j++) { int pcDstt = ucpcSquares[sqSrc + GameConstant.ccKnightCheckDelta[i][j]]; if (pcDstt == pcOppSide + GameConstant.PIECE_KNIGHT) { return(true); } } } // 3. 判断是否被对方的车或炮将军(包括将帅对脸) for (i = 0; i < 4; i++) { nDelta = GameConstant.ccKingDelta[i]; sqDst = sqSrc + nDelta; while (Chess_LoadUtil.IN_BOARD(sqDst)) { pcDst = ucpcSquares[sqDst]; if (pcDst != 0) { if (pcDst == pcOppSide + GameConstant.PIECE_ROOK || pcDst == pcOppSide + GameConstant.PIECE_KING) { return(true); } break; } sqDst += nDelta; } sqDst += nDelta; while (Chess_LoadUtil.IN_BOARD(sqDst)) { pcDst = ucpcSquares[sqDst]; if (pcDst != 0) { if (pcDst == pcOppSide + GameConstant.PIECE_CANNON) { return(true); } break; } sqDst += nDelta; } } return(false); } return(false); }
/// <summary> /// 生成所有走法,该方法根据该局面来生成所有该方的走棋步骤 /// </summary> /// <param name="mvs"></param> /// <returns></returns> public static int GenerateMoves(int[] mvs) { int i, j, nGenMoves, nDelta, sqSrc, sqDst; int pcSelfSide, pcOppSide, pcSrc, pcDst; // 生成所有走法,需要经过以下2个步骤: nGenMoves = 0; pcSelfSide = Chess_LoadUtil.SIDE_TAG(sdPlayer); pcOppSide = Chess_LoadUtil.OPP_SIDE_TAG(sdPlayer); for (sqSrc = 0; sqSrc < 256; sqSrc++) { // 1. 找到一个本方棋子,再做以下判断: pcSrc = ucpcSquares[sqSrc]; if ((pcSrc & pcSelfSide) == 0) { continue; } // 2. 根据棋子确定走法 switch (pcSrc - pcSelfSide) { // 帅(将) case GameConstant.PIECE_KING: for (i = 0; i < 4; i++) { sqDst = sqSrc + GameConstant.ccKingDelta[i]; if (!Chess_LoadUtil.IN_FORT(sqDst)) { continue; } pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); // 根据起点和终点获得走法 //Debuger.LogWarning(string.Format("GameLogic->GenerateMoves(),帅(将)moves[{0}] = {1}", nGenMoves, mvs[nGenMoves])); nGenMoves++; } } break; // 士 case GameConstant.PIECE_ADVISOR: for (i = 0; i < 4; i++) { sqDst = sqSrc + GameConstant.ccAdvisorDelta[i]; if (!Chess_LoadUtil.IN_FORT(sqDst)) { continue; } pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } } break; // 象 case GameConstant.PIECE_BISHOP: for (i = 0; i < 4; i++) { sqDst = sqSrc + GameConstant.ccAdvisorDelta[i]; if (!(Chess_LoadUtil.IN_BOARD(sqDst) && Chess_LoadUtil.HOME_HALF(sqDst, sdPlayer) && ucpcSquares[sqDst] == 0)) { continue; } sqDst += GameConstant.ccAdvisorDelta[i]; pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } } break; //马 case GameConstant.PIECE_KNIGHT: for (i = 0; i < 4; i++) { sqDst = sqSrc + GameConstant.ccKingDelta[i]; if (ucpcSquares[sqDst] != 0) { continue; } for (j = 0; j < 2; j++) { sqDst = sqSrc + GameConstant.ccKnightDelta[i][j]; if (!Chess_LoadUtil.IN_BOARD(sqDst)) { continue; } pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } } } break; // 车 case GameConstant.PIECE_ROOK: for (i = 0; i < 4; i++) { nDelta = GameConstant.ccKingDelta[i]; sqDst = sqSrc + nDelta; while (Chess_LoadUtil.IN_BOARD(sqDst)) { pcDst = ucpcSquares[sqDst]; if (pcDst == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } else { if ((pcDst & pcOppSide) != 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } break; } sqDst += nDelta; } } break; // 炮 case GameConstant.PIECE_CANNON: for (i = 0; i < 4; i++) { nDelta = GameConstant.ccKingDelta[i]; sqDst = sqSrc + nDelta; while (Chess_LoadUtil.IN_BOARD(sqDst)) { pcDst = ucpcSquares[sqDst]; if (pcDst == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } else { break; } sqDst += nDelta; } sqDst += nDelta; while (Chess_LoadUtil.IN_BOARD(sqDst)) { pcDst = ucpcSquares[sqDst]; if (pcDst != 0) { if ((pcDst & pcOppSide) != 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } break; } sqDst += nDelta; } } break; //小兵 case GameConstant.PIECE_PAWN: sqDst = Chess_LoadUtil.SQUARE_FORWARD(sqSrc, sdPlayer); // 格子水平镜像 if (Chess_LoadUtil.IN_BOARD(sqDst)) // 判断棋子是否在棋盘中 { pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } } if (Chess_LoadUtil.AWAY_HALF(sqSrc, sdPlayer)) { for (nDelta = -1; nDelta <= 1; nDelta += 2) { sqDst = sqSrc + nDelta; if (Chess_LoadUtil.IN_BOARD(sqDst)) { pcDst = ucpcSquares[sqDst]; if ((pcDst & pcSelfSide) == 0) { mvs[nGenMoves] = Chess_LoadUtil.MOVE(sqSrc, sqDst); nGenMoves++; } } } } break; } } return(nGenMoves); }
/// <summary> /// 超出边界(Fail-Soft)的Alpha-Beta搜索过程,最主要的方法, /// 通过递归调用,遍历所有走法, /// 然后得出每个走法下的局面价值, /// 根据其价值的大小搜索出最佳下棋步骤。 /// </summary> /// <param name="vlAlpha"></param> /// <param name="vlBeta"></param> /// <param name="nDepth"></param> /// <returns></returns> private static int SearchFull(int vlAlpha, int vlBeta, int nDepth) { //Debuger.LogWarning("Loadutil->SearchFull( ) 开始"); int i = 0, nGenMoves = 0, pcCaptured = 0; int vl, vlBest, mvBest; vl = vlBest = mvBest = 0; // 每一层生成的所有走法 int[] moves = new int[GameConstant.MAX_GEN_MOVES]; // 一个Alpha-Beta完全搜索分为以下6个阶段: // 1. 到达水平线,则返回局面评价值 if (nDepth == 0) { int value = Evaluate( ); Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 局面评价={0}", value)); // 局面评价 return(value); } // 2. 初始化最佳值和最佳走法 vlBest = -GameConstant.MATE_VALUE; // 这样可以知道,是否一个走法都没走过(杀棋) mvBest = 0; // 这样可以知道,是否搜索到了Beta走法或PV走法,以便保存到历史表 // 3. 生成全部走法,并根据历史表排序 nGenMoves = GenerateMoves(moves); #region 根据历史表排序 s_sortTempList.Clear( ); // 只对产生的走法排序,其他不变 for (int a = 0; a < nGenMoves; a++) { s_sortTempList.Add(moves[a]); } // 在进行alpha-beta搜索过程时,对其局面下的所有走法进行排序,排序依据为历史表,历史表数组的下标代表走法, // 其值为在该步骤下的深度值,所以,每次搜索时就可以先搜索最好的走法。减少对后面的搜索次数。 s_sortTempList.Sort((index1, index2) => { int value = AiMoveSearch.nHistoryTable[index2] - AiMoveSearch.nHistoryTable[index1]; //Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 历史表排序={0}", value)); return(value); }); //s_list.CopyTo(mvs); for (int a = 0; a < nGenMoves; a++) { // 用List排序好后,赋值回数组。只对产生的走法排序,数组中其他值不变 moves[a] = s_sortTempList[a]; } //for ( int i2 = 0; i2 < moves.Length; i2++ ) { // Debuger.LogWarning(string.Format("moves[{0}] = {1}", i2, moves[i2])); //} #endregion 根据历史表排序 Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 生成走法个数={0}", nGenMoves)); // 4. 逐一走这些走法,并进行递归 for (i = 0; i < nGenMoves; i++) { pcCaptured = ucpcSquares[Chess_LoadUtil.DST(moves[i])]; //Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 第 {0} 个走法, moves[{1}] = {2}, pcCaptured={3}", (i + 1), i, moves[i], pcCaptured)); if (MakeMove(moves[i], pcCaptured)) { // 递归 vl = -SearchFull(-vlBeta, -vlAlpha, nDepth - 1); //Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 递归vl={0}", vl)); UndoMakeMove(moves[i], pcCaptured); // 5. 进行Alpha-Beta大小判断和截断 if (vl > vlBest) // 找到最佳值(但不能确定是Alpha、PV还是Beta走法) { vlBest = vl; // "vlBest"就是目前要返回的最佳值,可能超出Alpha-Beta边界 if (vl >= vlBeta) // 找到一个Beta走法 { mvBest = moves[i]; // Beta走法要保存到历史表 break; // Beta截断 } if (vl > vlAlpha) // 找到一个PV走法 { mvBest = moves[i]; // PV走法要保存到历史表 vlAlpha = vl; // 缩小Alpha-Beta边界 } } } } //Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 遍历所有走法完成!!")); // 6. 所有走法都搜索完了,把最佳走法(不能是Alpha走法)保存到历史表,返回最佳值 if (vlBest == -GameConstant.MATE_VALUE) { int value = nDistance - GameConstant.MATE_VALUE; //Debuger.LogWarning(string.Format("Loadutil->SearchFull( ) 如果是杀棋,就根据杀棋步数给出评价={0}" + value)); // 如果是杀棋,就根据杀棋步数给出评价 return(value); } if (mvBest != 0) { // 如果不是Alpha走法,就将最佳走法保存到历史表 nHistoryTable[mvBest] += nDepth * nDepth; if (nDistance == 0) { // 搜索根节点时,总是有一个最佳走法(因为全窗口搜索不会超出边界),将这个走法保存下来 mvResult = mvBest; } } //Debuger.LogWarning("Loadutil->SearchFull( ) over, vlBest = " + vlBest); return(vlBest); }