public int MakeMove(IGameboard gameboard) { var mySymbol = gameboard.ActiveSymbol; var rootNode = new MinimaxNode(gameboard, -1); return Minimax(rootNode, true, -1, mySymbol).Move; }
public static Heuristic GetBestNode(MinimaxNode node, int depth) { if (node == null) { return(null); } return(alphaBeta(node, depth, double.MinValue, double.MaxValue, true)); }
/// <summary> /// Have the AI calculate its next move /// </summary> public void MakeMove() { //Set player = 1 as human generated current board state MinimaxNode rootNode = new MinimaxNode { Player = 1, GameState = Gameboard.BoardArray.Clone() as byte[, ] }; GenerateNode(ref rootNode); rootNode.Evaluate(out var bestNode, int.MinValue, int.MaxValue); //Get the index of the button in the board var index = bestNode.ModifiedCoordinate.Item2 * 3 + bestNode.ModifiedCoordinate.Item1; var childGameObject = GbO.transform.GetChild(index).gameObject; Gameboard.UpdateGameboard(childGameObject, 2); }
private int? EvaluateNode(MinimaxNode minimaxNode, string mySymbol) { if (IsWinner(mySymbol, minimaxNode.Board)) { return WIN; } if (IsWinner(GetOpponentsSymbol(mySymbol), minimaxNode.Board)) { return LOSS; } if (IsBoardComplete(minimaxNode.Board)) { return DRAW; } return null; }
private Evaluation Minimax(MinimaxNode node, bool maximizing, int firstMove, string mySymbol) { var eval = EvaluateNode(node, mySymbol); if (eval != null) { return new Evaluation() { Value = eval.Value, Move = firstMove }; } if (maximizing) { var best = new Evaluation() { Value = int.MinValue, Move = -1 }; for (int i = 0; i < 9; i++) { if (IsEmpty(node.Board.Tiles[i])) { var child = new MinimaxNode(node.Board, i); var val = Minimax(child, false, firstMove == -1 ? i : firstMove, mySymbol); if (val.Value > best.Value) { best = val; } } } return best; } else { var best = new Evaluation() { Value = int.MaxValue, Move = -1 }; for (int i = 0; i < 9; i++) { if (IsEmpty(node.Board.Tiles[i])) { var child = new MinimaxNode(node.Board, i); var val = Minimax(child, true, firstMove == -1 ? i : firstMove, mySymbol); if (val.Value < best.Value) { best = val; } } } return best; } }
/// <summary> /// Recursively populates the tree using the parent node as a base /// </summary> /// <param name="parentNode">current perm of the board</param> void GenerateNode(ref MinimaxNode parentNode) { //generate all possible perm of board and assign to nodes //check current depth of the current node, if = limit break, if !- limit recurse generate node on new node if (parentNode.Depth == DepthCap) { return; } for (var x = 0; x < 3; x++) { for (var y = 0; y < 3; y++) { if (parentNode.GameState[x, y] != 0) { continue; } MinimaxNode childNode = new MinimaxNode(); childNode.GameState = parentNode.GameState.Clone() as byte[, ]; if (childNode.GameState == null) { throw new Exception("Cannot clone Game State"); } childNode.GameState[x, y] = (byte)(parentNode.Player == 1 ? 2 : 1); childNode.Player = (byte)(parentNode.Player == 1 ? 2 : 1); childNode.ModifiedCoordinate = ((byte)x, (byte)y); childNode.Parent = parentNode; //Don't generate permutations of children that generate win conditions if (!(childNode.CheckForWinningMove(1) || childNode.CheckForWinningMove(2))) { GenerateNode(ref childNode); } parentNode.Children.Add(childNode); } } }
int AlphaBeta(ChessBoardSnapshot boardPosition, int depth, int alpha, int beta, bool maximizingPlayer) { int origAlpha = alpha; int origBeta = beta; MinimaxNode node = new MinimaxNode(); // Transposition Table Lookup; node is the lookup key for ttEntry ulong hash = boardPosition.board.ToZobristHash(); if (tTable.ContainsKey(hash)) { node = tTable[hash]; if (node.depth >= depth) { switch (node.flag) { case MinimaxNodeFlag.Exact: return(node.eval); case MinimaxNodeFlag.LowerBound: if (node.eval > alpha) { alpha = node.eval; } break; case MinimaxNodeFlag.UpperBound: if (node.eval < beta) { beta = node.eval; } break; } if (beta <= alpha) { return(node.eval); } } } // Minimax + Alpha Beta Pruning if (depth <= 0 || boardPosition.IsEndGame()) { return(GameManager.Instance.CalculateScore(boardPosition, playerType)); } int val = 0; if (maximizingPlayer) { val = int.MinValue; ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType); for (int i = 0; i < nextBoardPositions.Length; i++) { int newValue = AlphaBeta(nextBoardPositions[i], depth - 1, alpha, beta, false); if (newValue > val) { val = newValue; } if (val > alpha) { alpha = val; } if (beta <= alpha) { break; } } } else { val = int.MaxValue; ChessBoardSnapshot[] nextBoardPositions = FindPossibleMoves(boardPosition, playerType.ToOpposite()); for (int i = 0; i < nextBoardPositions.Length; i++) { int newValue = AlphaBeta(nextBoardPositions[i], depth - 1, alpha, beta, true); if (newValue < val) { val = newValue; } if (val < beta) { beta = val; } if (beta <= alpha) { break; } } } // Transposition Table Store; node is the lookup key for ttEntry node.hash = hash; node.eval = val; if (val <= origAlpha) { node.flag = MinimaxNodeFlag.UpperBound; } else if (val >= origBeta) { node.flag = MinimaxNodeFlag.LowerBound; } else { node.flag = MinimaxNodeFlag.Exact; } node.depth = depth; if (tTable.ContainsKey(hash)) { tTable[hash] = node; } else { tTable.Add(hash, node); } return(val); }
private static Heuristic alphaBeta(MinimaxNode node, int depth, double alpha, double beta, bool maximizingPlayer) { if (node.IsTerminal() || depth < 1) { return(new Heuristic { node = node, value = node.GetHeuristic() }); } List <MinimaxNode> subNodes = node.GetSubNodes(); if (maximizingPlayer) { Heuristic abNode = new Heuristic { value = double.MinValue }; Heuristic newAbNode; for (int i = 0; i < subNodes.Count; i++) { newAbNode = alphaBeta(subNodes[i], depth - 1, alpha, beta, false); if (newAbNode.value > abNode.value) { abNode = new Heuristic { node = subNodes[i], value = newAbNode.value } } ; if (abNode.value > alpha) { alpha = abNode.value; } if (alpha >= beta) { break; } } return(abNode); } else { Heuristic abNode = new Heuristic { value = double.MaxValue }; Heuristic newAbNode; for (int i = 0; i < subNodes.Count; i++) { newAbNode = alphaBeta(subNodes[i], depth - 1, alpha, beta, true); if (newAbNode.value < abNode.value) { abNode = new Heuristic { node = subNodes[i], value = newAbNode.value } } ; if (abNode.value < beta) { beta = abNode.value; } if (beta <= alpha) { break; } } return(abNode); } } }