/// <summary> /// An implementation of the alpha-beta pruning algorithm. /// </summary> /// <param name="game">The position to solve.</param> /// <param name="depth">The amount of full moves to search through.</param> /// <param name="alpha">The value of alpha (maximising player).</param> /// <param name="beta">The value of beta (minimising player).</param> /// <returns></returns> private static (int?, int) AlphaBetaPruning(Game game, int depth, int alpha, int beta) { bool maxPlayer = game.Turn % 2 == 0; List <int> children = new(); for (int col = 0; col < game.Grid.Length; col++) { if (!game.IsFilled(col)) { children.Add(col); } } if (game.Draw) { return(null, 0); } foreach (int child in children) { if (game.IsWinningMove(child)) { return(child, maxPlayer ? int.MaxValue : int.MinValue); } } if (depth == 0) { return(null, game.Evaluation(maxPlayer ? 0 : 1)); } if (maxPlayer) { int value = int.MinValue; Dictionary <int, int> scores = new(); foreach (int child in children) { Game opp = new(game); opp.Play(child); int score = AlphaBetaPruning(opp, depth - 1, alpha, beta).Item2; scores.Add(child, score); if (score > value) { value = score; } alpha = Max(alpha, value); if (alpha >= beta) { break; } } return(scores.Aggregate((l, r) => l.Value > r.Value ? l : r).Key, value); } else { int value = int.MaxValue; Dictionary <int, int> scores = new(); foreach (int child in children) { Game opp = new(game); opp.Play(child); int score = AlphaBetaPruning(opp, depth - 1, alpha, beta).Item2; scores.Add(child, score); if (score < value) { value = score; } beta = Max(beta, value); if (alpha >= beta) { break; } } return(scores.Aggregate((l, r) => l.Value > r.Value ? l : r).Key, value); } }