private static short ScoreNode(GameTree leaf) { short score = 0; switch (leaf.Data.State) { case GameState.Xwin: score = 10000; break; case GameState.Owin: score = -10000; break; case GameState.Draw: score = 0; break; case GameState.Open: var xWins = (short)(CountWins(Player.X, leaf.Data) * 10); var oWins = (short)(CountWins(Player.O, leaf.Data) * 10); score = (short)(xWins - oWins); score = leaf.Data.GameBoard.Aggregate(score, (current, board) => (short)(current + CalculateScore(board))); break; } leaf.Score = score; return(score); }
private UltimateTicTacToeMove MinMax() { var minMax = AlphaBeta(_gameTree, short.MinValue + 10, short.MaxValue - 10, Player == Player.X); Debug.WriteLine($"MinMax:{minMax}, Low:{_gameTree.GetLeafNodes().Min(node => node.Score)}, High:{_gameTree.GetLeafNodes().Max(node => node.Score)}"); Debug.WriteLine($"I'm looking ahead {_gameTree.GetTreeDepth()} moves!"); var possibleMoves = _gameTree.Children.Where(child => child.Score == minMax); // ReSharper disable PossibleMultipleEnumeration var index = new Random().Next(0, possibleMoves.Count()); _gameTree = possibleMoves.ElementAt(index); //Update the game state to the move chosen return(possibleMoves.ElementAt(index).Data.LastMove); // ReSharper restore PossibleMultipleEnumeration }
public UltimateTicTacToeMove TakeTurn(UltimateTicTacToeBoard ticTacToeBoard) { _taunt("Planning My Move..."); GameTree currentTree = _gameTree?.GetNode(ticTacToeBoard); Debug.WriteLine($"There were {_gameTree?.GetNodeCount()} nodes in the tree prior to this move."); //Taunt the player if they played well! if (_gameTree?.Children != null && _gameTree.Children.Count != 0) { var lowScore = _gameTree.Children.Min(node => node.Score); var highScore = _gameTree.Children.Max(node => node.Score); var bestMove = Player == Player.O ? highScore : lowScore; var worstMove = Player == Player.X ? highScore : lowScore; var bestPlayedMoves = _gameTree.Children.Where(child => child.Score == bestMove); var worstPlayedMoves = _gameTree.Children.Where(child => child.Score == worstMove); var playedMoves = bestPlayedMoves as IList <GameTree> ?? bestPlayedMoves.ToList(); if (playedMoves.Contains(currentTree) && playedMoves.Count == 1 && Math.Abs(bestMove - worstMove) > 250) { _taunt("Nice Move Kiddums!"); Thread.Sleep(timeout: new TimeSpan(0, 0, 5)); } else if (worstPlayedMoves.Contains(currentTree) && Math.Abs(bestMove - worstMove) > 250) { _taunt("What are you thinking?"); Thread.Sleep(timeout: new TimeSpan(0, 0, 5)); } } _gameTree = currentTree ?? new GameTree(data: ticTacToeBoard.Clone()); Debug.WriteLine($"Tree Triming reduced it to {_gameTree.GetNodeCount()} nodes."); Debug.WriteLine($"Memory Used {GC.GetTotalMemory(false)}"); GC.Collect(); GC.WaitForPendingFinalizers(); Debug.WriteLine($"Memory Used {GC.GetTotalMemory(false)}"); PopulateTree(); Debug.WriteLine($"Memory Used {GC.GetTotalMemory(false)}"); Debug.WriteLine($"Tree population increased it to {_gameTree.GetNodeCount()} nodes at a depth of {_gameTree.GetTreeDepth()}."); var move = MinMax(); return(move); }
private static short AlphaBeta(GameTree node, short alpha, short beta, bool maximizingPlayer) { if (node.Children.Count == 0) { return(ScoreNode(node)); } if (maximizingPlayer) { var value = short.MinValue; foreach (var child in node.Children) { value = Math.Max(value, AlphaBeta(child, alpha, beta, false)); alpha = Math.Max(alpha, value); if (beta < alpha) { break; } } node.Score = value; return(value); } else { var value = short.MaxValue; foreach (var child in node.Children) { value = Math.Min(value, AlphaBeta(child, alpha, beta, true)); beta = Math.Min(beta, value); if (beta < alpha) { break; } } node.Score = value; return(value); } }