protected virtual int QuiescenceSolve(BitBoard board, int alpha, int beta, int depth, bool prevmove = true) { searchResult.Nodes++; var cacheIndex = depth + Constants.MaxOpeningDepth + 1; var currentScore = Evaluation.Eval(board); if (depth == 0 || currentScore >= beta) { return(currentScore); } if (currentScore > alpha) { alpha = currentScore; } var moves = Rule.FindMoves(board); if (moves.Length == 0) { if (!prevmove) { //END return(EndGameEvaluation.Eval(board)); } else { var oppBoard = board.Switch(); var childScore = GetCachedScore(oppBoard, cacheIndex, () => - QuiescenceSolve(oppBoard, -beta, -alpha, depth, false)); return(BalanceScore(currentScore, childScore)); } } var nextScore = minimumScore; var foundPv = false; var orderedMoves = OrderMovesByMobility(moves, board); foreach (var pos in orderedMoves) { var eval = 0; var oppBoard = Rule.MoveSwitch(board, pos); if (foundPv) { //zero window eval = GetCachedScore(oppBoard, cacheIndex - 1, () => - QuiescenceSolve(oppBoard, -alpha - 1, -alpha, depth - 1)); if ((eval > alpha) && (eval < beta)) { eval = -QuiescenceSolve(oppBoard, -beta, -eval, depth - 1); } } else { eval = GetCachedScore(oppBoard, cacheIndex - 1, () => - QuiescenceSolve(oppBoard, -beta, -alpha, depth - 1)); } if (eval > nextScore) { nextScore = eval; if (eval > alpha) { if (eval >= beta) { //pruning return(nextScore); } alpha = eval; foundPv = true; } } } return(BalanceScore(currentScore, nextScore)); }
protected override int FastestFirstSolve(BitBoard board, int alpha, int beta, int depth, bool prevmove = true) { searchResult.Nodes++; //leaf node if (depth == 0) { return(QuiescenceSolve(board, alpha, beta, 2, prevmove)); } var moves = Rule.FindMoves(board); if (moves.Length == 0) { if (!prevmove) { //END return(EndGameEvaluation.Eval(board)); } else { var oppBoard = board.Switch(); return(GetCachedScore(oppBoard, depth, () => - FastestFirstSolve(oppBoard, -beta, -alpha, depth, false))); } } var score = minimumScore; var foundPv = false; var orderedMoves = OrderMovesByMobility(moves, board); foreach (var pos in orderedMoves) { var eval = 0; var oppBoard = Rule.MoveSwitch(board, pos); if (foundPv) { //zero window eval = GetCachedScore(oppBoard, depth - 1, () => - FastestFirstSolve(oppBoard, -alpha - 1, -alpha, depth - 1)); if ((eval > alpha) && (eval < beta)) { eval = -FastestFirstSolve(oppBoard, -beta, -eval, depth - 1); } } else { eval = GetCachedScore(oppBoard, depth - 1, () => - FastestFirstSolve(oppBoard, -beta, -alpha, depth - 1)); } if (eval > score) { score = eval; if (eval > alpha) { if (eval >= beta) { //pruning return(score); } alpha = eval; foundPv = true; } } } return(score); }
public override SearchResult Search(BitBoard board, int depth) { PrepareSearch(board); searchResult = new SearchResult(); if (depth > Constants.MaxEndGameDepth) { searchResult.Message = "too depth..."; return(searchResult); } var clock = new Clock(); clock.Start(); var moves = Rule.FindMoves(board); if (moves.Length == 0) { moves = Rule.FindMoves(board); if (moves.Length == 0) { //END var endScore = EndGameEvaluation.Eval(board); return(new SearchResult() { Move = -1, Score = endScore }); } else { var result = Search(board.Switch(), depth); result.Score = -result.Score; return(result); } } var alpha = Window.Alpha; var beta = Window.Beta; if (depth >= Constants.MaxEndGameDepth - 2) {//make search window small alpha = -1; beta = 1; } var score = minimumScore; var foundPv = false; var index = 0; var orderedMoves = OrderMovesByMobility(moves, board); foreach (var pos in orderedMoves) { //move var oppBoard = Rule.MoveSwitch(board, pos); var eval = 0; //check if (foundPv) { //zero window eval = -FastestFirstSolve(oppBoard, -alpha - 1, -alpha, depth - 1); if ((eval > alpha) && (eval < beta)) { eval = -FastestFirstSolve(oppBoard, -beta, -eval, depth - 1); } } else { eval = -FastestFirstSolve(oppBoard, -beta, -alpha, depth - 1); } //reback? searchResult.EvalList.Add(new EvalItem { Move = pos, Score = eval }); searchResult.Message += string.Format("({0}:{1})", pos, eval); if (eval > score) { score = eval; //update move searchResult.Move = pos; searchResult.Score = score; if (eval > alpha) { if (eval >= beta) { //pruning break; } alpha = eval; foundPv = true; } } searchResult.TimeSpan = clock.Elapsed; searchResult.Process = (++index) / (double)(moves.Length + 1); UpdateProgress?.Invoke(searchResult); } clock.Stop(); searchResult.TimeSpan = clock.Elapsed; var s = from q in scoreCaches.Select((e, i) => new { e.Count, Index = i }) where q.Count > 0 select new { q.Count, q.Index }; var cacheInfo = string.Join(",", s.Select(c => $"({c.Index}:{c.Count})")); searchResult.Message += $" (hits: {hits}, cache info:{cacheInfo})"; searchResult.Process = 1; return(searchResult); }
protected virtual int FastestFirstSolve(BitBoard board, int alpha, int beta, int depth, bool prevmove = true) { searchResult.Nodes++; //game over if (board.IsFull) { return(EndGameEvaluation.Eval(board)); } //leaf node if (depth == 0) { return(Evaluation.Eval(board)); } var moves = Rule.FindMoves(board); if (moves.Length == 0) { if (!prevmove) { //END return(EndGameEvaluation.Eval(board)); } else { var oppBoard = board.Switch(); //TODO: should it be +, not -? return(GetCachedScore(oppBoard, depth, () => - FastestFirstSolve(oppBoard, -beta, -alpha, depth, false))); } } var score = minimumScore; var foundPv = false; //moves = moves.OrderBy(i => squareDict[i]).ToArray(); var orderedMoves = OrderMovesByMobility(moves, board); foreach (var pos in orderedMoves) { var eval = 0; var oppBoard = Rule.MoveSwitch(board, pos); if (foundPv) { //zero window eval = GetCachedScore(oppBoard, depth, () => - FastestFirstSolve(oppBoard, -alpha - 1, -alpha, depth - 1)); if ((eval > alpha) && (eval < beta)) { eval = -FastestFirstSolve(oppBoard, -beta, -eval, depth - 1); } } else { eval = GetCachedScore(oppBoard, depth, () => - FastestFirstSolve(oppBoard, -beta, -alpha, depth - 1)); } //reback? if (eval > score) { score = eval; if (eval > alpha) { if (eval >= beta) { //pruning return(score); } alpha = eval; foundPv = true; } } } return(score); }
private int NoParitySearch(BitBoard board, int alpha, int beta, int empties, bool prevmove = true) { searchResult.Nodes++; var moves = Rule.FindMoves(board); if (moves.Length == 0) { if (!prevmove) { //END return(EndGameEvaluation.Eval(board)); } else { return(-NoParitySearch(board.Switch(), -beta, -alpha, empties, false)); } } var score = minimumScore; var diffCount = board.DiffCount(); foreach (var pos in moves) { var eval = 0; var flips = Rule.FindFlips(board, pos); var oppBoard = Rule.FlipSwitch(board, pos, flips); if (empties == 2) { var ownFlipsCount = flips.CountBits(); //the last move of opponent player var lastEmptySquare = oppBoard.EmptyPieces.Index(); if (lastEmptySquare < 0) { throw new Exception($"invalid square index:{lastEmptySquare}"); } var oppFlipsCount = Rule.CountFlips(oppBoard, lastEmptySquare); if (oppFlipsCount > 0) { //both done. eval = diffCount + 2 * (ownFlipsCount - oppFlipsCount); } else { //opp pass var ownLastFlipsCount = Rule.CountFlips(oppBoard, lastEmptySquare); if (ownLastFlipsCount > 0) { eval = diffCount + 2 * (ownFlipsCount + ownLastFlipsCount) + 2; } else { //all pass eval = diffCount + 2 * ownFlipsCount; //TODO: eval==0? if (eval >= 0) { eval += 2; } } } } else { //empties!=2 eval = -NoParitySearch(oppBoard, -beta, -alpha, empties - 1); } if (eval > score) { score = eval; if (eval > alpha) { if (eval >= beta) { return(score); } alpha = eval; } } } return(score); }
private int ParitySearch(BitBoard board, int alpha, int beta, int depth, bool prevmove = true) { searchResult.Nodes++; //game over if (board.IsFull) { return(EndGameEvaluation.Eval(board)); } //leaf node if (depth == 0) { return(Evaluation.Eval(board)); } var moves = Rule.FindMoves(board); if (moves.Length == 0) { if (!prevmove) { //END return(EndGameEvaluation.Eval(board)); } else { return(-ParitySearch(board.Switch(), -beta, -alpha, depth, false)); } } var score = minimumScore; var foundPv = false; //moves = moves.OrderBy(i => squareDict[i]).ToArray(); var orderedMoves = OrderMovesBySquares(moves); foreach (var pos in orderedMoves) { var eval = 0; var oppBoard = Rule.MoveSwitch(board, pos); if (depth <= Constants.NoParityDepth) { //Parity Search if (foundPv) { //zero window eval = -NoParitySearch(oppBoard, -alpha - 1, -alpha, depth - 1); if ((eval > alpha) && (eval < beta)) { eval = -NoParitySearch(oppBoard, -beta, -eval, depth - 1); } } else { eval = -NoParitySearch(oppBoard, -beta, -alpha, depth - 1); } } else { if (foundPv) { //zero window eval = -ParitySearch(oppBoard, -alpha - 1, -alpha, depth - 1); if ((eval > alpha) && (eval < beta)) { eval = -ParitySearch(oppBoard, -beta, -eval, depth - 1); } } else { eval = -ParitySearch(oppBoard, -beta, -alpha, depth - 1); } } //reback? if (eval > score) { score = eval; if (eval > alpha) { if (eval >= beta) { //pruning return(score); } alpha = eval; foundPv = true; } } } return(score); }