/// <summary> /// Decides the best move for the player given by the color parameter, with a search /// of the given depth. /// </summary> public static ColoredBitBoard GetBestMove(ChessBoard board, int depth, ChessPieceColors color, GenerateMoveDelegate GenerateMoves, GetMaterialDelegate GetMaterialValue) { Debug.Assert(board != null); Debug.Assert(depth > 0); Debug.Assert(GenerateMoves != null); Debug.Assert(GetMaterialValue != null); //Alpha, have to add one to change symbol later otherwise overflow. double a = int.MinValue + 1; //Beta value. double b = int.MaxValue; double val = 0; ColoredBitBoard bestMove = null; List <ColoredBitBoard> moves = GenerateMoves(board, color); var ttEntry = new TranspositionEntry(board.BoardHash.Key, depth, val, false, EntryType.Exact); foreach (ColoredBitBoard move in moves) { board.Update(move); if (color == ChessPieceColors.Black) { val = -NegaMaxAlgorithm(board, depth - 1, -b, -a, ChessPieceColors.White, GenerateMoves, GetMaterialValue); } else if (color == ChessPieceColors.White) { val = -NegaMaxAlgorithm(board, depth - 1, -b, -a, ChessPieceColors.Black, GenerateMoves, GetMaterialValue); } if (val > a) { a = val; bestMove = move; ttEntry.BestMove = move; ttEntry.Score = a; } board.Undo(); } TranspositionTable.TranspositionCache.Add(ttEntry); return(bestMove); }
/// <summary> /// Implementation of the NegaMax algorithm. /// <param name="board"></param> /// <param name="depth"></param> /// <param name="a"></param> /// <param name="b"></param> /// <param name="color"></param> /// <param name="GenerateMoves"></param> /// <param name="GetMaterialValue"></param> /// <returns></returns> public static double NegaMaxAlgorithm( ChessBoard board, int depth, double a, double b, ChessPieceColors color, GenerateMoveDelegate GenerateMoves, GetMaterialDelegate GetMaterialValue ) { ColoredBitBoard bestMove = null; var hashBeforeTermCondition = board.BoardHash.Key; var termCondition = IsTerminal(board, color, GenerateMoves); Debug.Assert(hashBeforeTermCondition == board.BoardHash.Key); if (termCondition == TerminalConditions.Win || termCondition == TerminalConditions.Draw || depth == 0) { return((int)color * (GetMaterialValue(board))); } else { bool usingTT = false; ulong hash_beforeMove = board.BoardHash.Key; var ttEntry = TranspositionTable.TranspositionCache[board.BoardHash.Key]; if (ttEntry != null && ttEntry.Hash == board.BoardHash.Key && ttEntry.Depth >= depth ) { if (ttEntry.NodeType == EntryType.Alpha && ttEntry.Score <= a) { return(a); } if (ttEntry.NodeType == EntryType.Beta && ttEntry.Score >= b) { return(b); } if (ttEntry.NodeType == EntryType.Exact) { return(ttEntry.Score); } usingTT = true; } TranspositionEntry newEntry = new TranspositionEntry(board.BoardHash.Key, depth, 0, false, EntryType.Alpha); List <ColoredBitBoard> moves = new List <ColoredBitBoard>(); moves = GenerateMoves(board, color); foreach (ColoredBitBoard move in moves) { double val = 0; board.Update(move); var hashBeforeRec = board.BoardHash.Key; if (color == ChessPieceColors.White) { val = -NegaMaxAlgorithm(board, depth - 1, -b, -a, ChessPieceColors.Black, GenerateMoves, GetMaterialValue); } else if (color == ChessPieceColors.Black) { val = -NegaMaxAlgorithm(board, depth - 1, -b, -a, ChessPieceColors.White, GenerateMoves, GetMaterialValue); } Debug.Assert(hashBeforeRec == board.BoardHash.Key); board.Undo(); usingTT = false; if (val >= b) { if (!usingTT) { newEntry.NodeType = EntryType.Beta; newEntry.Score = val; TranspositionTable.TranspositionCache.Add(newEntry); Debug.Assert(hash_beforeMove == board.BoardHash.Key, "Board hash changed during update/undo"); } return(val); } if (val >= a) { a = val; bestMove = move; newEntry.NodeType = EntryType.Exact; } Debug.Assert(hash_beforeMove == board.BoardHash.Key, "Board hash changed during update/undo"); } if (!usingTT) { if (newEntry.NodeType == EntryType.Exact) { newEntry.BestMove = bestMove; } newEntry.Score = a; TranspositionTable.TranspositionCache.Add(newEntry); } return(a); } }