/// <summary> /// Find the best move for a player using alpha-beta /// </summary> /// <param name="board"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayer"> Player 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 board, SearchMode searchMode, ChessBoard.PlayerE ePlayer, List <Move> moveList, int[] arrIndex, ChessBoard.PosInfoS posInfo, ref Move moveBest, out int iPermCount, out int iCacheHit, out int iMaxDepth) { bool bRetVal = false; bool bMultipleThread; bool bUseTransTable; ChessBoard[] arrBoard; Task <AlphaBetaResult>[] taskArray; List <Move>[] 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 (ePlayer == ChessBoard.PlayerE.White) { posInfoWhite = posInfo; posInfoBlack = ChessBoard.s_posInfoNull; } else { posInfoWhite = ChessBoard.s_posInfoNull; posInfoBlack = posInfo; } searchMode.m_eOption &= ~SearchMode.OptionE.UseTransTable; bUseTransTable = (searchMode.m_eOption.HasFlag(SearchMode.OptionE.UseTransTable)); 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) { arrBoard = new ChessBoard[iThreadCount]; arrMoveList = new List <Move> [iThreadCount]; taskArray = new Task <AlphaBetaResult> [iThreadCount]; for (int iIndex = 0; iIndex < iThreadCount; iIndex++) { arrBoard[iIndex] = board.Clone(); arrMoveList[iIndex] = new List <Move>(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(arrBoard[iStep], searchMode, ePlayer, 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 <Move> moveListTmp; chessBoardTmp = board.Clone(); moveListTmp = new List <Move>(moveList.Count); for (int iIndex = 0; iIndex < moveList.Count; iIndex++) { moveListTmp.Add(moveList[arrIndex[iIndex]]); } alphaBetaRes = FindBestMoveUsingAlphaBetaAsync(chessBoardTmp, searchMode, ePlayer, 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 alpha-beta in a secondary thread /// </summary> /// <param name="board"> Chess board</param> /// <param name="searchMode"> Search mode</param> /// <param name="ePlayer"> Color doing the move</param> /// <param name="iThreadId"> Thread Id (0-n)</param> /// <param name="moveList"> List of move to try</param> /// <param name="posInfoWhite"> Information about pieces attacks for the white</param> /// <param name="posInfoBlack"> Information about pieces attacks for the black</param> /// <param name="iTotalMoveCount"> Total number of moves</param> /// <param name="iAlpha"> Alpha bound</param> /// <param name="iBeta"> Beta bound</param> /// <returns> /// Points /// </returns> private AlphaBetaResult FindBestMoveUsingAlphaBetaAsync(ChessBoard board, SearchMode searchMode, ChessBoard.PlayerE ePlayer, int iThreadId, List <Move> moveList, ChessBoard.PosInfoS posInfoWhite, ChessBoard.PosInfoS posInfoBlack, int iTotalMoveCount, int iAlpha, int iBeta) { AlphaBetaResult resRetVal; DateTime dtTimeOut; int iDepth; int iPermCountAtLevel; int iPoint; int iBestMoveIndex; int iDepthLimit; int[] arrPoints; System.Threading.ThreadPriority eThreadPriority; TransTable transTable; bool bTimeOut; bool bIterativeDepthFirst; resRetVal = new AlphaBetaResult(); eThreadPriority = System.Threading.Thread.CurrentThread.Priority; System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.BelowNormal; if ((searchMode.m_eOption & SearchMode.OptionE.UseTransTable) != 0) { transTable = TransTable.GetTransTable(iThreadId); transTable.Reset(); } else { transTable = null; } bIterativeDepthFirst = (searchMode.m_eOption.HasFlag(SearchMode.OptionE.UseIterativeDepthSearch)); resRetVal.movePosBest.StartPos = 255; resRetVal.movePosBest.EndPos = 255; resRetVal.movePosBest.OriginalPiece = ChessBoard.PieceE.None; resRetVal.movePosBest.Type = Move.TypeE.Normal; try { resRetVal.iPermCount = 0; if (searchMode.m_iSearchDepth == 0 || bIterativeDepthFirst) { dtTimeOut = (bIterativeDepthFirst) ? DateTime.MaxValue : DateTime.Now + TimeSpan.FromSeconds(searchMode.m_iTimeOutInSec); iDepthLimit = (bIterativeDepthFirst) ? searchMode.m_iSearchDepth : 999; iDepth = 1; resRetVal.iPts = FindBestMoveUsingAlphaBetaAtDepth(board, searchMode, ePlayer, moveList, posInfoWhite, posInfoBlack, iTotalMoveCount, iDepth, iAlpha, iBeta, transTable, DateTime.MaxValue, out iPermCountAtLevel, out iBestMoveIndex, out bTimeOut, out arrPoints); if (iBestMoveIndex != -1) { resRetVal.movePosBest = moveList[iBestMoveIndex]; } resRetVal.iPermCount += iPermCountAtLevel; resRetVal.iMaxDepth = iDepth; while (DateTime.Now < dtTimeOut && !m_bCancelSearch && !bTimeOut && iDepth < iDepthLimit) { moveList = SortMoveList(moveList, arrPoints); iDepth++; iPoint = FindBestMoveUsingAlphaBetaAtDepth(board, searchMode, ePlayer, moveList, posInfoWhite, posInfoBlack, iTotalMoveCount, iDepth, iAlpha, iBeta, transTable, dtTimeOut, out iPermCountAtLevel, out iBestMoveIndex, out bTimeOut, out arrPoints); if (!bTimeOut) { if (iBestMoveIndex != -1) { resRetVal.movePosBest = moveList[iBestMoveIndex]; } resRetVal.iPermCount += iPermCountAtLevel; resRetVal.iMaxDepth = iDepth; resRetVal.iPts = iPoint; } } } else { resRetVal.iMaxDepth = searchMode.m_iSearchDepth; resRetVal.iPts = FindBestMoveUsingAlphaBetaAtDepth(board, searchMode, ePlayer, moveList, posInfoWhite, posInfoBlack, iTotalMoveCount, resRetVal.iMaxDepth, iAlpha, iBeta, transTable, DateTime.MaxValue, out resRetVal.iPermCount, out iBestMoveIndex, out bTimeOut, out arrPoints); if (iBestMoveIndex != -1) { resRetVal.movePosBest = moveList[iBestMoveIndex]; } } } finally { System.Threading.Thread.CurrentThread.Priority = eThreadPriority; } return(resRetVal); }