/// <summary> /// Debugging routine /// </summary> /// <param name="iDepth"> Actual search depth</param> /// <param name="ePlayerColor"> Color doing the move</param> /// <param name="move"> Move</param> /// <param name="iPts"> Points for this move</param> protected void TraceSearch(int iDepth, ChessBoard.PlayerColorE ePlayerColor, ChessBoard.MovePosS move, int iPts) { if (m_trace != null) { m_trace.TraceSearch(iDepth, ePlayerColor, move, iPts); } }
public void ToStringTest() { ChessBoard.MovePosS target = new ChessBoard.MovePosS(); target.OriginalPiece = ChessBoard.PieceE.Knight | ChessBoard.PieceE.Black; target.StartPos = 57; target.EndPos = 42; target.Type = ChessBoard.MoveTypeE.Normal; string expected = "Чёрный конь g8-f6"; string actual; actual = target.ToString(); Assert.AreEqual(expected, actual); }
/// <summary> /// Add a move to the stack. All redo move are discarded /// </summary> /// <param name="movePos"> New move</param> public void AddMove(ChessBoard.MovePosS movePos) { int iCount; int iPos; iCount = Count; iPos = m_iPosInList + 1; while (iCount != iPos) { m_listMovePos.RemoveAt(--iCount); } m_listMovePos.Add(movePos); m_iPosInList = iPos; }
/// <summary> /// Convert a list of PGN positions into a moving positions /// </summary> /// <param name="eColorToPlay"> Color to play</param> /// <param name="arrRawMove"> Array of PGN moves</param> /// <param name="piMoveList"> Returned array of moving position</param> /// <param name="listMovePos"> Returned the list of move if not null</param> /// <param name="iSkip"> Skipped count</param> /// <param name="iTruncated"> Truncated count</param> private void CnvRawMoveToPosMove(ChessBoard.PlayerColorE eColorToPlay, List <string> arrRawMove, out int[] piMoveList, List <ChessBoard.MovePosS> listMovePos, ref int iSkip, ref int iTruncated) { List <int> arrMoveList; ChessBoard.MovePosS movePos; int iPos; movePos = new ChessBoard.MovePosS(); movePos.StartPos = 0; movePos.EndPos = 0; movePos.Type = ChessBoard.MoveTypeE.Normal; arrMoveList = new List <int>(256); try { foreach (string strMove in arrRawMove) { if (strMove == "1-0" || strMove == "0-1" || strMove == "1/2-1/2" || strMove == "*" || strMove[0] == '(') { break; } else if (strMove == "..") { iSkip++; break; } CnvRawMoveToPosMove(eColorToPlay, strMove, out iPos, ref iTruncated, ref movePos); if (iPos != -1) { arrMoveList.Add(iPos); if (listMovePos != null) { listMovePos.Add(movePos); } eColorToPlay = (eColorToPlay == ChessBoard.PlayerColorE.Black) ? ChessBoard.PlayerColorE.White : ChessBoard.PlayerColorE.Black; } else { break; } } } catch (PgnParserException ex) { ex.MoveList = arrMoveList.ToArray(); MessageBox.Show(ex.ToString()); throw; } piMoveList = arrMoveList.ToArray(); }
/// <summary> /// Gets the description of a move /// </summary> /// <param name="movePos"> Move to describe</param> /// <returns> /// Move description /// </returns> private string GetMoveDesc(ChessBoard.MovePosS movePos) { string strRetVal; if (m_eDisplayMode == DisplayModeE.MovePos) { strRetVal = ChessBoard.GetHumanPos(movePos); } else { strRetVal = PgnUtil.GetPGNMoveFromMove(m_chessBoard, movePos, false); if ((movePos.Type & ChessBoard.MoveTypeE.MoveFromBook) == ChessBoard.MoveTypeE.MoveFromBook) { strRetVal = "(" + strRetVal + ")"; } } return(strRetVal); }
/// <summary> /// Deserialize from XML /// </summary> /// <param name="reader"> XML reader</param> void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { ChessBoard.MovePosS movePos; bool bIsEmpty; m_listMovePos.Clear(); if (reader.MoveToContent() != XmlNodeType.Element || reader.LocalName != "MoveList") { throw new SerializationException("Unknown format"); } else { bIsEmpty = reader.IsEmptyElement; m_iPosInList = Int32.Parse(reader.GetAttribute("PositionInList")); if (bIsEmpty) { reader.Read(); } else { if (reader.ReadToDescendant("Move")) { while (reader.IsStartElement()) { movePos = new ChessBoard.MovePosS(); movePos.OriginalPiece = (ChessBoard.PieceE)Enum.Parse(typeof(ChessBoard.SerPieceE), reader.GetAttribute("OriginalPiece")); movePos.StartPos = (byte)Int32.Parse(reader.GetAttribute("StartingPosition")); movePos.EndPos = (byte)Int32.Parse(reader.GetAttribute("EndingPosition")); movePos.Type = (ChessBoard.MoveTypeE)Enum.Parse(typeof(ChessBoard.MoveTypeE), reader.GetAttribute("MoveType")); m_listMovePos.Add(movePos); reader.ReadStartElement("Move"); } } reader.ReadEndElement(); } } }
/// <summary> /// Find the best move for a player using minmax search /// </summary> /// <param name="chessBoard"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayerColor"> Color doing the move</param> /// <param name="moveList"> Move list</param> /// <param name="arrIndex"> Order of evaluation of the moves</param> /// <param name="posInfo"> Information about pieces attacks</param> /// <param name="moveBest"> Best move found</param> /// <param name="iPermCount"> Nb of permutations evaluated</param> /// <param name="iCacheHit"> Nb of cache hit</param> /// <param name="iMaxDepth"> Maximum depth evaluated</param> /// <returns> /// true if a move has been found /// </returns> protected override bool FindBestMove(ChessBoard chessBoard, SearchEngine.SearchMode searchMode, ChessBoard.PlayerColorE ePlayerColor, List <ChessBoard.MovePosS> moveList, int[] arrIndex, ChessBoard.PosInfoS posInfo, ref ChessBoard.MovePosS moveBest, out int iPermCount, out int iCacheHit, out int iMaxDepth) { bool bRetVal = false; DateTime dtTimeOut; int iDepth; int iPermCountAtLevel; iPermCount = 0; iCacheHit = 0; if (searchMode.m_iSearchDepth == 0) { dtTimeOut = DateTime.Now + TimeSpan.FromSeconds(searchMode.m_iTimeOutInSec); iDepth = 0; do { bRetVal = FindBestMoveUsingMinMaxAtDepth(chessBoard, searchMode, ePlayerColor, moveList, arrIndex, iDepth + 1, ref moveBest, out iPermCountAtLevel); iPermCount += iPermCountAtLevel; iDepth++; } while (DateTime.Now < dtTimeOut); iMaxDepth = iDepth; } else { iMaxDepth = searchMode.m_iSearchDepth; bRetVal = FindBestMoveUsingMinMaxAtDepth(chessBoard, searchMode, ePlayerColor, moveList, arrIndex, iMaxDepth, ref moveBest, out iPermCount); } return(bRetVal); }
/// <summary> /// Convert a PGN position into a moving position /// </summary> /// <param name="ePlayerColor"> Color moving</param> /// <param name="strMove"> Move</param> /// <param name="iPos"> Returned moving position</param> /// <param name="iTruncated"> Truncated count</param> /// <param name="movePos"> Move position</param> private void CnvRawMoveToPosMove(ChessBoard.PlayerColorE ePlayerColor, string strMove, out int iPos, ref int iTruncated, ref ChessBoard.MovePosS movePos) { string strPureMove; int iIndex; int iStartCol; int iStartRow; int iEndPos; int iOfs; ChessBoard.PieceE ePiece; ChessBoard.MoveTypeE eMoveType; eMoveType = ChessBoard.MoveTypeE.Normal; iPos = 0; strPureMove = strMove.Replace("x", "").Replace("#", "").Replace("ep", "").Replace("+", ""); iIndex = strPureMove.IndexOf('='); if (iIndex != -1) { if (strPureMove.Length > iIndex + 1) { switch (strPureMove[iIndex + 1]) { case 'Q': eMoveType = ChessBoard.MoveTypeE.PawnPromotionToQueen; break; case 'R': eMoveType = ChessBoard.MoveTypeE.PawnPromotionToRook; break; case 'B': eMoveType = ChessBoard.MoveTypeE.PawnPromotionToBishop; break; case 'N': eMoveType = ChessBoard.MoveTypeE.PawnPromotionToKnight; break; case 'P': eMoveType = ChessBoard.MoveTypeE.PawnPromotionToPawn; break; default: iPos = -1; iTruncated++; break; } if (iPos != -1) { strPureMove = strPureMove.Substring(0, iIndex); } } else { iPos = -1; iTruncated++; } } if (iPos == 0) { if (strPureMove == "O-O") { iPos = FindCastling(ePlayerColor, true, ref iTruncated, strMove, ref movePos); } else if (strPureMove == "O-O-O") { iPos = FindCastling(ePlayerColor, false, ref iTruncated, strMove, ref movePos); } else { iOfs = 1; switch (strPureMove[0]) { case 'K': // King ePiece = ChessBoard.PieceE.King; break; case 'N': // Knight ePiece = ChessBoard.PieceE.Knight; break; case 'B': // Bishop ePiece = ChessBoard.PieceE.Bishop; break; case 'R': // Rook ePiece = ChessBoard.PieceE.Rook; break; case 'Q': // Queen ePiece = ChessBoard.PieceE.Queen; break; default: // Pawn ePiece = ChessBoard.PieceE.Pawn; iOfs = 0; break; } DecodeMove(strPureMove.Substring(iOfs), out iStartCol, out iStartRow, out iEndPos); iPos = FindPieceMove(ePlayerColor, ePiece, iStartCol, iStartRow, iEndPos, eMoveType, strMove, ref iTruncated, ref movePos); } } }
/// <summary> /// Find a move using the specification /// </summary> /// <param name="ePlayerColor"> Color moving</param> /// <param name="ePiece"> Piece moving</param> /// <param name="iStartCol"> Starting column of the move or -1 if not specified</param> /// <param name="iStartRow"> Starting row of the move or -1 if not specified</param> /// <param name="iEndPos"> Ending position of the move</param> /// <param name="eMoveType"> Type of move. Use for discriminating between different pawn promotion.</param> /// <param name="strMove"> Move</param> /// <param name="iTruncated"> Truncated count</param> /// <param name="movePos"> Move position</param> /// <returns> /// Moving position or -1 if error /// </returns> private int FindPieceMove(ChessBoard.PlayerColorE ePlayerColor, ChessBoard.PieceE ePiece, int iStartCol, int iStartRow, int iEndPos, ChessBoard.MoveTypeE eMoveType, string strMove, ref int iTruncated, ref ChessBoard.MovePosS movePos) { int iRetVal = -1; List <ChessBoard.MovePosS> arrMovePos; int iCol; int iRow; ePiece = ePiece | ((ePlayerColor == ChessBoard.PlayerColorE.Black) ? ChessBoard.PieceE.Black : ChessBoard.PieceE.White); arrMovePos = m_chessBoard.EnumMoveList(ePlayerColor); foreach (ChessBoard.MovePosS move in arrMovePos) { if ((int)move.EndPos == iEndPos && m_chessBoard[(int)move.StartPos] == ePiece) { if (eMoveType == ChessBoard.MoveTypeE.Normal || (move.Type & ChessBoard.MoveTypeE.MoveTypeMask) == eMoveType) { iCol = (int)move.StartPos & 7; iRow = (int)move.StartPos >> 3; if ((iStartCol == -1 || iStartCol == iCol) && (iStartRow == -1 || iStartRow == iRow)) { if (iRetVal != -1) { throw new PgnParserException("More then one piece found for this move - " + strMove, GetCodeInError()); } movePos = move; iRetVal = (int)move.StartPos + ((int)move.EndPos << 8); m_chessBoard.DoMove(move); } } } } if (iRetVal == -1) { if (m_bDiagnose) { throw new PgnParserException("Unable to find compatible move - " + strMove, GetCodeInError()); } iTruncated++; } return(iRetVal); }
/// <summary> /// Find a castle move /// </summary> /// <param name="ePlayerColor"> Color moving</param> /// <param name="bShortCastling"> true for short, false for long</param> /// <param name="iTruncated"> Truncated count</param> /// <param name="strMove"> Move</param> /// <param name="movePos"> Returned moved if found</param> /// <returns> /// Moving position or -1 if error /// </returns> private int FindCastling(ChessBoard.PlayerColorE ePlayerColor, bool bShortCastling, ref int iTruncated, string strMove, ref ChessBoard.MovePosS movePos) { int iRetVal = -1; int iWantedDelta; int iDelta; List <ChessBoard.MovePosS> arrMovePos; arrMovePos = m_chessBoard.EnumMoveList(ePlayerColor); iWantedDelta = bShortCastling ? 2 : -2; foreach (ChessBoard.MovePosS move in arrMovePos) { if ((move.Type & ChessBoard.MoveTypeE.MoveTypeMask) == ChessBoard.MoveTypeE.Castle) { iDelta = ((int)move.StartPos & 7) - ((int)move.EndPos & 7); if (iDelta == iWantedDelta) { iRetVal = (int)move.StartPos + ((int)move.EndPos << 8); movePos = move; m_chessBoard.DoMove(move); } } } if (iRetVal == -1) { if (m_bDiagnose) { throw new PgnParserException("Unable to find compatible move - " + strMove, GetCodeInError()); } iTruncated++; } return(iRetVal); }
/// <summary> /// Find the best move for a player using alpha-beta /// </summary> /// <param name="chessBoard"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayerColor"> Color doing the move</param> /// <param name="moveList"> Move list</param> /// <param name="arrIndex"> Order of evaluation of the moves</param> /// <param name="posInfo"> Information about pieces attacks</param> /// <param name="moveBest"> Best move found</param> /// <param name="iPermCount"> Total permutation evaluated</param> /// <param name="iCacheHit"> Number of moves found in the translation table cache</param> /// <param name="iMaxDepth"> Maximum depth to use</param> /// <returns> /// true if a move has been found /// </returns> protected override bool FindBestMove(ChessBoard chessBoard, SearchEngine.SearchMode searchMode, ChessBoard.PlayerColorE ePlayerColor, List <ChessBoard.MovePosS> moveList, int[] arrIndex, ChessBoard.PosInfoS posInfo, ref ChessBoard.MovePosS moveBest, out int iPermCount, out int iCacheHit, out int iMaxDepth) { bool bRetVal = false; bool bMultipleThread; bool bUseTransTable; ChessBoard[] arrChessBoard; Task <AlphaBetaResult>[] taskArray; List <ChessBoard.MovePosS>[] arrMoveList; AlphaBetaResult alphaBetaRes; ChessBoard.PosInfoS posInfoWhite; ChessBoard.PosInfoS posInfoBlack; int iAlpha; int iBeta; int iThreadCount; //TODO Enable transposition table when bug on 3 repetition move draw will be found. if (ePlayerColor == ChessBoard.PlayerColorE.White) { posInfoWhite = posInfo; posInfoBlack = ChessBoard.s_posInfoNull; } else { posInfoWhite = ChessBoard.s_posInfoNull; posInfoBlack = posInfo; } searchMode.m_eOption &= ~SearchMode.OptionE.UseTransTable; bUseTransTable = ((searchMode.m_eOption & SearchMode.OptionE.UseTransTable) != 0); iCacheHit = 0; iMaxDepth = 0; iPermCount = 0; iAlpha = -10000000; iBeta = +10000000; bMultipleThread = (searchMode.m_eThreadingMode == SearchMode.ThreadingModeE.OnePerProcessorForSearch); iThreadCount = System.Environment.ProcessorCount; if (bMultipleThread && iThreadCount < 2) { bMultipleThread = false; // No reason to go with multi-threading if only one processor } if (bMultipleThread) { arrChessBoard = new ChessBoard[iThreadCount]; arrMoveList = new List <ChessBoard.MovePosS> [iThreadCount]; taskArray = new Task <AlphaBetaResult> [iThreadCount]; for (int iIndex = 0; iIndex < iThreadCount; iIndex++) { arrChessBoard[iIndex] = chessBoard.Clone(); arrMoveList[iIndex] = new List <ChessBoard.MovePosS>(moveList.Count / iThreadCount + 1); for (int iStep = iIndex; iStep < moveList.Count; iStep += iThreadCount) { arrMoveList[iIndex].Add(moveList[arrIndex[iStep]]); } } for (int iIndex = 0; iIndex < iThreadCount; iIndex++) { taskArray[iIndex] = Task <AlphaBetaResult> .Factory.StartNew((param) => { int iStep = (int)param; return(FindBestMoveUsingAlphaBetaAsync(arrChessBoard[iStep], searchMode, ePlayerColor, iStep, arrMoveList[iStep], posInfoWhite, posInfoBlack, moveList.Count, iAlpha, iBeta)); }, iIndex); } iMaxDepth = 999; for (int iStep = 0; iStep < iThreadCount; iStep++) { alphaBetaRes = taskArray[iStep].Result; if (alphaBetaRes.movePosBest.StartPos != 255) { iPermCount += alphaBetaRes.iPermCount; iMaxDepth = Math.Min(iMaxDepth, alphaBetaRes.iMaxDepth); if (bUseTransTable) { iCacheHit += TransTable.GetTransTable(iStep).CacheHit; } if (alphaBetaRes.iPts > iAlpha) { iAlpha = alphaBetaRes.iPts; moveBest = alphaBetaRes.movePosBest; bRetVal = true; } } } if (iMaxDepth == 999) { iMaxDepth = -1; } } else { ChessBoard chessBoardTmp; List <ChessBoard.MovePosS> moveListTmp; chessBoardTmp = chessBoard.Clone(); moveListTmp = new List <ChessBoard.MovePosS>(moveList.Count); for (int iIndex = 0; iIndex < moveList.Count; iIndex++) { moveListTmp.Add(moveList[arrIndex[iIndex]]); } alphaBetaRes = FindBestMoveUsingAlphaBetaAsync(chessBoardTmp, searchMode, ePlayerColor, 0, // ThreadId moveListTmp, posInfoWhite, posInfoBlack, moveList.Count, iAlpha, iBeta); iPermCount = alphaBetaRes.iPermCount; iMaxDepth = alphaBetaRes.iMaxDepth; if (alphaBetaRes.movePosBest.StartPos != 255) { if (bUseTransTable) { iCacheHit += TransTable.GetTransTable(0).CacheHit; } moveBest = alphaBetaRes.movePosBest; bRetVal = true; } } return(bRetVal); }
/// <summary> /// Find the best move for a player using minmax search /// </summary> /// <param name="chessBoard"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayerColor"> Color doing the move</param> /// <param name="moveList"> Move list</param> /// <param name="arrIndex"> Order of evaluation of the moves</param> /// <param name="iDepth"> Maximum depth</param> /// <param name="moveBest"> Best move found</param> /// <param name="iPermCount"> Total permutation evaluated</param> /// <returns> /// true if a move has been found /// </returns> private bool FindBestMoveUsingMinMaxAtDepth(ChessBoard chessBoard, SearchMode searchMode, ChessBoard.PlayerColorE ePlayerColor, List <ChessBoard.MovePosS> moveList, int[] arrIndex, int iDepth, ref ChessBoard.MovePosS moveBest, out int iPermCount) { bool bRetVal = false; ChessBoard.MovePosS move; int iPts; int iWhiteMoveCount; int iBlackMoveCount; int iBestPts; ChessBoard.RepeatResultE eResult; iPermCount = 0; iBestPts = Int32.MinValue; if (ePlayerColor == ChessBoard.PlayerColorE.White) { iWhiteMoveCount = moveList.Count; iBlackMoveCount = 0; } else { iWhiteMoveCount = 0; iBlackMoveCount = moveList.Count; } foreach (int iIndex in arrIndex) { move = moveList[iIndex]; eResult = chessBoard.DoMoveNoLog(move); if (eResult == ChessBoard.RepeatResultE.NoRepeat) { iPts = -MinMax(chessBoard, searchMode, (ePlayerColor == ChessBoard.PlayerColorE.Black) ? ChessBoard.PlayerColorE.White : ChessBoard.PlayerColorE.Black, iDepth - 1, iWhiteMoveCount, iBlackMoveCount, ref iPermCount); } else { iPts = 0; } chessBoard.UndoMoveNoLog(move); if (iPts > iBestPts) { TraceSearch(iDepth, ePlayerColor, move, iPts); iBestPts = iPts; moveBest = move; bRetVal = true; } } return(bRetVal); }
/// <summary> /// Find the best move for a player using a specific method /// </summary> /// <param name="chessBoard"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayerColor"> Color doing the move</param> /// <param name="moveBest"> Best move found</param> /// <param name="iPermCount"> Total permutation evaluated</param> /// <param name="iCacheHit"> Number of moves found in the translation table cache</param> /// <param name="iMaxDepth"> Maximum depth reached</param> /// <returns> /// true if a move has been found /// </returns> public bool FindBestMove(ChessBoard chessBoard, SearchMode searchMode, ChessBoard.PlayerColorE ePlayerColor, out ChessBoard.MovePosS moveBest, out int iPermCount, out int iCacheHit, out int iMaxDepth) { bool bRetVal = false; List <ChessBoard.MovePosS> moveList; int[] arrIndex; int iSwapIndex; int iTmp; Random rnd; ChessBoard.PosInfoS posInfo; iCacheHit = 0; moveList = chessBoard.EnumMoveList(ePlayerColor, true, out posInfo); arrIndex = new int[moveList.Count]; m_bCancelSearch = false; for (int iIndex = 0; iIndex < moveList.Count; iIndex++) { arrIndex[iIndex] = iIndex; } if (searchMode.m_eRandomMode != SearchMode.RandomModeE.Off) { rnd = (searchMode.m_eRandomMode == SearchMode.RandomModeE.OnRepetitive) ? m_rndRep : m_rnd; for (int iIndex = 0; iIndex < moveList.Count; iIndex++) { iSwapIndex = rnd.Next(moveList.Count); iTmp = arrIndex[iIndex]; arrIndex[iIndex] = arrIndex[iSwapIndex]; arrIndex[iSwapIndex] = iTmp; } } moveBest.StartPos = 0; moveBest.EndPos = 0; moveBest.OriginalPiece = ChessBoard.PieceE.None; moveBest.Type = ChessBoard.MoveTypeE.Normal; bRetVal = FindBestMove(chessBoard, searchMode, ePlayerColor, moveList, arrIndex, posInfo, ref moveBest, out iPermCount, out iCacheHit, out iMaxDepth); return(bRetVal); }
/// <summary> /// Find the best move using a specific search method /// </summary> /// <param name="chessBoard"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayerColor"> Color doing the move</param> /// <param name="moveList"> Move list</param> /// <param name="arrIndex"> Order of evaluation of the moves</param> /// <param name="posInfo"> Position information</param> /// <param name="moveBest"> Best move found</param> /// <param name="iPermCount"> Total permutation evaluated</param> /// <param name="iCacheHit"> Number of moves found in the translation table cache</param> /// <param name="iMaxDepth"> Maximum depth to use</param> /// <returns> /// true if a move has been found /// </returns> protected abstract bool FindBestMove(ChessBoard chessBoard, SearchMode searchMode, ChessBoard.PlayerColorE ePlayerColor, List <ChessBoard.MovePosS> moveList, int[] arrIndex, ChessBoard.PosInfoS posInfo, ref ChessBoard.MovePosS moveBest, out int iPermCount, out int iCacheHit, out int iMaxDepth);