//node constructors public Node(Node p, Move m, int a, int b) { //create a new board with the move provided CBoard = Move.CopyAndMove(p.CBoard, m); Alpha = a; Beta = b; }
//construct node from just a board state public Node(Piece[,] b) { Parent = null; CBoard = b; Alpha = int.MinValue; Beta = int.MaxValue; }
static bool Terminal(Node p, bool turn) { return (Move.GenerateAllMoves(p.CBoard, turn).Count == 0); }
public bool Equals(Node other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Game1.Check(this.CBoard, other.CBoard); }
public static Piece[,] AlphaBetaSearch(Piece[,] cboard) { Console.WriteLine("Starting"); int depth = 0; int v = int.MinValue; counter c; c.nodes = 1; c.minprune = 0; c.maxprune = 0; var startTime = DateTime.Now; Node p = new Node(cboard); List<Move> moves = Move.GenerateAllMoves(p.CBoard, false); if (moves.Count() > 0) if (moves[0].Jump) { var pairs = new List<Tuple<Piece[,], int>>(); foreach (Piece[,] b in GenerateJumps(moves, p.CBoard, false)) { Node tNode = new Node(b); tNode.Alpha = p.Alpha; tNode.Beta = p.Beta; tNode.Parent = p; v = Math.Max(v, MinValue(ref tNode, ref c, depth)); if (v >= p.Beta) { Console.WriteLine("Time Elapsed: " + (DateTime.Now - startTime)); return b; } p.Alpha = Math.Max(p.Alpha, v); pairs.Add(new Tuple<Piece[,], int>(b, v)); } foreach (var d in pairs) { if (d.Item2 == v) { Console.WriteLine("Time Elapsed: " + (DateTime.Now - startTime)); return d.Item1; } } } else { var pairs = new List<Tuple<Move, int>>(); foreach (Move m in moves) { Node tNode = new Node(p, m, p.Alpha, p.Beta); v = Math.Max(v,MinValue(ref tNode, ref c, depth)); if (v >= p.Beta) { Console.WriteLine("Time Elapsed: " + (DateTime.Now - startTime)); return Move.CopyAndMove(cboard, m); } p.Alpha = Math.Max(p.Alpha, v); pairs.Add(new Tuple<Move, int>(m, v)); } foreach (var d in pairs) { if (d.Item2 == v) { Console.WriteLine("Time Elapsed: "+ (DateTime.Now - startTime)); return Move.CopyAndMove(cboard, d.Item1); } } } return Move.CopyAndMove(cboard,new Move(new Vector2(1), new Vector2(1)) ); }
static int MinValue(ref Node p, ref counter c, int depth) { c.nodes++; depth++; if (c.nodes % 10000 == 0) Console.WriteLine("Nodes: " + c.nodes + "\n" + "Min Prune: " + c.minprune + "\n" + "Max Prune: " + c.maxprune); //if(count%10== 0) Console.WriteLine(count); if (Terminal(p, true)) { return 20; } if (depth == MAX_DEPTH) { return Node.UtilityFunc(p.CBoard); } int v = int.MaxValue; List<Move> moves = Move.GenerateAllMoves(p.CBoard, true); if (moves.Count() > 0) if (moves[0].Jump) { foreach (Piece[,] b in GenerateJumps(moves, p.CBoard, false)) { Node tNode = new Node(b); tNode.Alpha = p.Alpha; tNode.Beta = p.Beta; tNode.Parent = p; v = Math.Min(v, MaxValue(ref tNode, ref c, depth)); if (v <= p.Alpha) { c.minprune++; return v; } p.Beta = Math.Min(p.Beta, v); } } else { foreach (Move m in moves) { Node tNode = new Node(p, m, p.Alpha, p.Beta); v = Math.Min(v, MaxValue(ref tNode, ref c, depth)); if (v <= p.Alpha) { c.minprune++; return v; } p.Beta = Math.Min(p.Beta, v); } } return v; }
//the main alpha-beta search function //takes in a board state, the current turn, and the maximum depth that the function should go to //returns a board state public static Piece[,] AlphaBetaSearch(Piece[,] cboard, int turn, int maxDepth) { //write some info to the console Console.WriteLine("Starting Alpha-Beta Search"); Console.WriteLine("Turn: " + turn); //depth counter int depth = 0; //value of best node Value v; //counter class holding statistics Counter c; //set values c.Nodes = 1; c.Minprune = 0; c.Maxprune = 0; c.Maxdepth = 0; //start counting time var startTime = DateTime.Now; //if the difficulty is set to very hard, increase depth as the //number of turns increases if(maxDepth == 18) { switch (turn) { case 0: _currentdepth = maxDepth; break; case 1: _currentdepth = maxDepth; break; case 2: _currentdepth = maxDepth + 2; break; case 3: _currentdepth = maxDepth + 3; break; case 4: _currentdepth = maxDepth + 5; break; case 5: _currentdepth = maxDepth + 7; break; default: _currentdepth = maxDepth + 100; break; } } else _currentdepth = maxDepth; //create root node Node p = new Node(cboard); //start the search v = MaxValue(ref p, ref c, depth); //go up the chain of parents until reaching the proper move to choose p = v.Nod; while(p.Parent.Parent != null) { p = p.Parent; } //write out the statistics Console.WriteLine( "Nodes: " + c.Nodes + "\n" + "Min Prune: " + c.Minprune + "\n" + "Max Prune: " + c.Maxprune + "\n" + "Maximum Depth: " + c.Maxdepth); Console.WriteLine("Time Elapsed: " + (DateTime.Now - startTime) + "\n"); return p.CBoard; }
//essentially the same as MaxValue, but with //alpha and beta flipped around static Value MinValue(ref Node p, ref Counter c, int depth) { c.Nodes++; if (depth == _currentdepth) { return new Value { Nod = p, Val = Node.UtilityFunc(p.CBoard, true) }; } List<Move> moves = Move.GenerateAllMoves(p.CBoard, true); if (!moves.Any()) { return new Value { Nod = p, Val = 40 }; } Value v = new Value { Nod = p, Val = int.MaxValue }; depth++; if (depth > c.Maxdepth) c.Maxdepth = depth; if (moves.Count() > 0) { List<Piece[,]> boards; if (moves[0].Jump) boards = Move.GenerateJumps(moves, p.CBoard, true); else boards = GenerateBoards(moves, p.CBoard); foreach(var b in boards) { Node tNode = new Node(b); tNode.Alpha = p.Alpha; tNode.Beta = p.Beta; tNode.Parent = p; v = Min(v, MaxValue(ref tNode, ref c, depth)); if (v.Val <= p.Alpha) { c.Minprune++; return v; } p.Beta = Math.Min(p.Beta, v.Val); } } return v; }
//max value function, part of the alpha-beta search static Value MaxValue(ref Node p, ref Counter c, int depth) { //increase node counter c.Nodes++; //if the depth is the maximum allowed, if (depth == _currentdepth) { return new Value{Nod = p, Val = Node.UtilityFunc(p.CBoard, false)}; } //generate all possible moves List<Move> moves = Move.GenerateAllMoves(p.CBoard, false); //if there anre't any moves, this is a terminating state if (!moves.Any()) { return new Value { Nod = p, Val = -40 }; } //create new value holder Value v = new Value { Nod = p, Val = int.MinValue }; //increase depth depth++; //if the depth is greater than the one currently in the counter //replace it if (depth > c.Maxdepth) c.Maxdepth = depth; //if moves exist if (moves.Count() > 0) { List<Piece[,]> boards; //if the first child is a jump, generate all jump outcomes if (moves[0].Jump) boards = Move.GenerateJumps(moves, p.CBoard, false); //otherwise generate regular board moves else boards = GenerateBoards(moves, p.CBoard); //for each child foreach (var b in boards) { //create a new node and set values Node tNode = new Node(b); tNode.Alpha = p.Alpha; tNode.Beta = p.Beta; tNode.Parent = p; //run minvalue on the new node v = Max(v, MinValue(ref tNode, ref c, depth)); //handle pruning if (v.Val >= p.Beta) { c.Maxprune++; return v; } p.Alpha = Math.Max(p.Alpha, v.Val); } } return v; }