private void GetBestMoveImpl(Game game, int depth, out Move bestMove, out Estimate bestEstimate) { Player player = game.State.NextMovePlayer; bool isMaximizeStage = player == Player.Maximizing; Move curBestMove = null; Estimate curBestEstimate = isMaximizeStage ? Estimate.MinInf : Estimate.MaxInf; foreach (Move move in game.GetAllowedMoves()) { MovesChecked += 1; game.DoMove(move); Estimate estimate = game.State.StaticEstimate; // force AI to win faster if (game.State.IsTerminate && estimate != Estimate.Zero) { EstimateHelper.AdjustTerminalStateEstimate(depth, ref estimate); } if (depth < 3) { Trace.WriteLine($"{Tab(depth - 1)}Consider {move}"); } if (game.State.IsTerminate || depth == MaxDepth) { // no longer go deeper UpdateBestMove(isMaximizeStage, move, estimate, ref curBestMove, ref curBestEstimate); } else { // go deeper Move bestMoveInternal; Estimate bestEstimateInternal; GetBestMoveImpl(game, depth + 1, out bestMoveInternal, out bestEstimateInternal); UpdateBestMove(isMaximizeStage, move, bestEstimateInternal, ref curBestMove, ref curBestEstimate); } game.UndoMove(move); } if (curBestMove == null) { throw new InvalidOperationException("No moves found"); } bestMove = curBestMove; bestEstimate = curBestEstimate; #if DEBUG if (depth <= 3) { Trace.WriteLine($"{Tab(depth-1)}RET: {depth}; BM: {bestMove}; E: {bestEstimate} ({(isMaximizeStage ? "max" : "min")})"); } #endif }
private Estimate FindImpl(Game game, Estimate alpha, Estimate beta, int depth, int maxDepth, Metadata meta) { Player player = game.State.NextMovePlayer; bool isMax = player == Player.Maximizing; if (maxDepth - depth == 0) { return(game.State.StaticEstimate); } if (game.State.IsTerminate) { // make AI pick shortest path to terminate state // otherwise there will be funny situation then AI seeing that it won // w/o chances for other side will not purse the winning Estimate terminateEstimate = game.State.StaticEstimate; EstimateHelper.AdjustTerminalStateEstimate(depth, ref terminateEstimate); return(terminateEstimate); } Estimate v = isMax ? Estimate.MinInf : Estimate.MaxInf; List <Move> moves = game.GetAllowedMoves() .OrderByDescending(x => x.Priority) .ToList(); foreach (Move move in moves) { meta.MovesChecked += 1; using (DisposableMoveHandle.New(game, move)) { Estimate curEstimate = FindImpl(game, alpha, beta, depth + 1, maxDepth, meta); bool betterMoveFound = isMax ? curEstimate > v : curEstimate < v; if (betterMoveFound) { v = curEstimate; if (depth == 1) { meta.BestMove = move; } } if (isMax) { alpha = Estimate.Max(alpha, v); } else { beta = Estimate.Min(beta, v); } #if DEBUG if (depth <= 3) { Trace.WriteLine($"{ new string(' ', depth) } - Move {move} for {player} -- {curEstimate} -- Term? {game.State.IsTerminate}"); } #endif if (alpha >= beta) { // alpha/beta cut-off //Trace.WriteLine($"cut-off on depth {depth}"); break; } } } return(v); }