/// <summary> /// Expand the tree out to our maximum depth, only expanding leaf nodes with the best-possible score /// </summary> /// <param name="maxDepth"></param> public void Expand(int maxDepth) { NumLeaves = 0; ConsideredBoardStates = new List <TakBoard>(); List <TakMove> nextMoves = PlayerAI.EnumerateMoves(MyColor, Root.InitialState); foreach (TakMove m in nextMoves) { MoveNode n = new MoveNode(Root.InitialState, m); if (!ConsideredBoardStates.Contains(n.ResultingState.Board)) { ConsideredBoardStates.Add(n.ResultingState.Board); n.Depth = 1; n.Parent = null; n.Score = n.ResultingState.Evaluate(MyColor); Root.Children.Add(n); } } // recursively expand the tree MaxDepth = maxDepth; foreach (MoveNode n in Root.Children) { n.Score = ABPrune(n, (double)int.MinValue - 1, (double)int.MaxValue + 1, true); } Root.Children.Sort(); PrintDebug(string.Format("Explored {0} board states", ConsideredBoardStates.Count)); PrintDebug(string.Format("Explored {0} leaves", NumLeaves)); }
/// <summary> /// Expand a node using AB pruning /// </summary> /// <param name="root"></param> /// <returns></returns> private double ABPrune(MoveNode root, double alpha, double beta, bool isMaxing) { // don't re-expand if we hit an automatic win or loss, or if we're at the depth limit if (root.Depth >= MaxDepth || root.ResultingState.GameOver) { NumLeaves++; root.Score = root.ResultingState.Evaluate(MyColor); return(root.Score); } else { List <TakMove> nextMoves = PlayerAI.EnumerateMoves(isMaxing ? MyColor : TheirColor, root.ResultingState); double v; if (isMaxing) { v = (double)int.MinValue - 1; foreach (TakMove m in nextMoves) { MoveNode n = new MoveNode(root.ResultingState, m); n.Depth = root.Depth + 1; root.Children.Add(n); n.Parent = root; if (!ConsideredBoardStates.Contains(n.ResultingState.Board)) { //Console.WriteLine("Considering\n{0}", n.ResultingState.Board); ConsideredBoardStates.Add(n.ResultingState.Board); v = Math.Max(v, ABPrune(n, alpha, beta, false)); alpha = Math.Max(alpha, v); if (beta <= alpha) { break; } } } } else { v = (double)int.MaxValue + 1; foreach (TakMove m in nextMoves) { MoveNode n = new MoveNode(root.ResultingState, m); n.Depth = root.Depth + 1; root.Children.Add(n); n.Parent = root; if (!ConsideredBoardStates.Contains(n.ResultingState.Board)) { //Console.WriteLine("Considering\n{0}", n.ResultingState.Board); ConsideredBoardStates.Add(n.ResultingState.Board); v = Math.Max(v, ABPrune(n, alpha, beta, true)); beta = Math.Min(beta, v); if (beta <= alpha) { break; } } } } root.Children.Sort(); if (isMaxing) { root.Children.Reverse(); } return(v); } }