Ejemplo n.º 1
0
        protected override AnalysisResult DoAnalyze(Game clonedGame)
        {
            // If game is over then stop
            if (clonedGame.IsOver)
            {
                return(null);
            }

            // Get all the placed tiles to determine all the correct playable tiles
            IReadOnlyList <Tile> placedTiles = clonedGame.History;

            // If it is a new game, select the center most
            if (placedTiles.Count == 0)
            {
                return(new AnalysisResult(
                           clonedGame.Board[clonedGame.Board.Width / 2, clonedGame.Board.Height / 2]));
            }

            // Get hold of who player this analysis should be done for
            Player forPlayer = clonedGame.Manager.CurrentPlayer;

            // Generate nodes for all possible moves
            var possiblePositions = CandidateSearcher.Search(clonedGame).ToList();

            // make a list of pairs of positions - value
            var evaluations = new List <(IPositional positional, double value)>();

            // keep a record of max value
            var max = double.MinValue;

            // evaluate each possible position
            foreach (IPositional position in possiblePositions)
            {
                // play the board to get the next game state
                clonedGame.Play(position);

                // Evaluate the position using the algorithm
                // Note: because the possible position represents the maximizing node,
                // the next node should be done in minimizing phase
                var value = EvaluateMinimax(clonedGame, position, forPlayer, Level, isMaximizing: false);

                // If the value has reached max value, no need to evaluate further
                if (value == double.MaxValue)
                {
                    return(new AnalysisResult(position));
                }

                // Update the evaluations and max value accordingly
                if (value > max)
                {
                    evaluations.Clear();
                    evaluations.Add((position, value));
                    max = value;
                }
                else if (value == max)
                {
                    evaluations.Add((position, value));
                }

                // Evaluation is done, undo the game state
                clonedGame.Undo();
            }

            // Take out all the best choices
            var choices =
                (from evaluation in evaluations
                 select evaluation.positional)
                .ToList();

            // Randomly pick one result from the choices
            var choice = Random.Next(choices.Count);

            return(new AnalysisResult(choices[choice], choices));
        }
Ejemplo n.º 2
0
        protected IEnumerable <AINode> Search(Game game, int level)
        {
            // Get all the placed tiles to determine all the correct playable tiles
            IReadOnlyList <Tile> placedTiles = game.History;

            // If it is a new game, select the center most
            if (placedTiles.Count == 0)
            {
                return(new List <AINode>()
                {
                    new AINode(
                        game.Board[game.Board.Width / 2, game.Board.Height / 2],
                        0)
                });
            }

            // Get current player to determine which side to search for
            Player player      = game.Manager.CurrentPlayer;
            var    playerCount = game.Manager.Players.Length;

            var maxPoint       = double.MinValue;
            var candidateNodes = new List <AINode>();

            // Get all the playable tiles
            IEnumerable <IPositional> playableTiles = CandidateSearcher.Search(game);

            // Populate corresponding NTrees with each playable tile found.
            foreach (Tile tile in playableTiles)
            {
                // Play the new cloned board
                game.Play(tile.X, tile.Y);

                // Evaluate this tile
                var point = TileEvaluator.Evaluate(game, tile, player.Piece);
                point = Math.Abs(point);
                var aiNode = new AINode(tile, point);

                if (level < Level)
                {
                    if (aiNode.Point > maxPoint)
                    {
                        maxPoint = aiNode.Point;
                        candidateNodes.Clear();
                        candidateNodes.Add(aiNode);
                    }
                    else if (aiNode.Point == maxPoint)
                    {
                        candidateNodes.Add(aiNode);
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    candidateNodes.Add(aiNode);
                }

                game.Undo();
            }

            if (level == Level && placedTiles.Count <= 2)
            {
                return(candidateNodes);
            }

            if (level == 0)
            {
                return(candidateNodes);
            }

            foreach (AINode node in candidateNodes)
            {
                if (node.Point >= 1000.0)
                {
                    return(candidateNodes);
                }

                var childNodes = Search(game, level - 1).ToList();

                childNodes.Sort((x, y) => - 1 * x.CompareTo(y));
                AINode maxNode = childNodes.First();
                childNodes.RemoveAll((x) => x.Point < maxNode.Point);

                // If the current node's board's game is over, stop evaluating because
                // there is a chance this node will reach the end of game, so there is
                // no longer any need to continue evaluating
                //if (b.IsOver)
                //{
                //  break;
                //}

                node.Point -= maxNode.Point;

                // Minus the current node's point by the max point so if the children
                // node's point is high, this node is less likely to be selected.
                node.Point -= 0.01 * childNodes.Count;
            }

            candidateNodes.Sort((x, y) => - 1 * x.CompareTo(y));
            AINode maxNode2 = candidateNodes.First();

            candidateNodes.RemoveAll((x) => x.Point < maxNode2.Point);

            return(candidateNodes);
        }
Ejemplo n.º 3
0
        private double EvaluateMinimax(
            Game game,
            IPositional positional,
            Player forPlayer,
            int depth,
            double alpha      = double.MinValue,
            double beta       = double.MaxValue,
            bool isMaximizing = true)
        {
            // If is leaf node, evaluate it
            if (depth == 0)
            {
                // The leaf node is effectively created by the previous player so it
                // makes sense to evaluate it for the previous player
                return(EvaluateGame(game, positional, forPlayer, game.Manager.PreviousPlayer));
            }

            // If game is over, but not leaf node, evaluate for the current player instead
            if (game.IsOver)
            {
                return(EvaluateGame(game, positional, forPlayer, game.Manager.CurrentPlayer));
            }

            // Get all the playable tiles
            IEnumerable <IPositional> playableTiles = CandidateSearcher.Search(game);

            double value;

            if (isMaximizing)
            {
                var maxValue = double.MinValue;

                // Populate deeper nodes with each playable tile found
                foreach (Tile childTile in playableTiles)
                {
                    // play the board to get the next game state
                    game.Play(childTile);

                    // Prepare to evaluate the next state by getting the next position and
                    // recursively evaluate it
                    var childValue = EvaluateMinimax(game, childTile, forPlayer, depth - 1, alpha, beta, false);

                    // Evaluation is done, undo the game state
                    game.Undo();

                    // Negative inifity signifies a lose to our team. Marking this node as
                    // negative infinity so that the previous minimizing phase will not
                    // pick this node
                    if (childValue == double.MinValue)
                    {
                        maxValue = childValue;
                        break;
                    }

                    // Perform algorithmic updates
                    maxValue = Math.Max(maxValue, childValue);
                    alpha    = Math.Max(alpha, childValue);
                    if (beta <= alpha)
                    {
                        break;
                    }
                }
                value = maxValue;
            }
            else
            {
                var minValue = double.MaxValue;

                // Populate deeper nodes with each playable tile found
                foreach (Tile childTile in playableTiles)
                {
                    // play the board to get the next game state
                    game.Play(childTile);

                    // Prepare to evaluate the next state by getting the next position and
                    // recursively evaluate it
                    var childValue = EvaluateMinimax(game, childTile, forPlayer, depth - 1, alpha, beta, true);

                    // Evaluation is done, undo the game state
                    game.Undo();

                    // Positive inifity signifies a win to our team. Marking this node as
                    // positive infinity so that the previous maximizing phase will pick
                    // this node
                    if (childValue == double.MaxValue)
                    {
                        minValue = childValue;
                        break;
                    }

                    // Perform algorithmic updates
                    minValue = Math.Min(minValue, childValue);
                    beta     = Math.Min(beta, childValue);
                    if (beta <= alpha)
                    {
                        break;
                    }
                }
                value = minValue;
            }

            // Merging current state's value to child's value. Because the current
            // state is effectively created by the previous player so it makes sense
            // to evaluate it for the previous player
            value += EvaluateGame(game, positional, forPlayer, game.Manager.PreviousPlayer);
            return(value);
        }
Ejemplo n.º 4
0
        protected List <NTree <AINode> > Search(Game game, NTree <AINode> currentNode, int level)
        {
            // Get all the placed tiles to determine all the correct playable tiles
            IReadOnlyList <Tile> placedTiles = game.History;

            // If it is a new game, select the center most
            if (placedTiles.Count == 0)
            {
                return(new List <NTree <AINode> >()
                {
                    new NTree <AINode>(
                        new AINode(
                            game.Board[game.Board.Width / 2, game.Board.Height / 2],
                            0))
                });
            }

            // Get all the playable tiles
            IEnumerable <IPositional> playableTiles = CandidateSearcher.Search(game);

            // Get current player to determine which side to search for
            Player player      = game.Manager.CurrentPlayer;
            var    playerCount = game.Manager.Players.Length;

            // Populate corresponding NTrees with each playable tile found.
            var nTrees = new List <NTree <AINode> >();

            foreach (Tile tile in playableTiles)
            {
                // Play the board
                game.Play(tile.X, tile.Y);

                // Evalue this tile
                var point = TileEvaluator.Evaluate(game, tile, player.Piece);
                point = Math.Abs(point);
                var aiNode = new AINode(tile, point);

                // Add to the list of NTrees
                var nTree = new NTree <AINode>(currentNode, aiNode);
                nTrees.Add(nTree);

                // If the current node's board's game is over, stop evaluating because
                // there is a chance this node will reach the end of game, so there is
                // no longer any need to continue evaluating
                if (game.IsOver)
                {
                    game.Undo();
                    break;
                }

                // If the recursion of searching didn't reach the bottom (where level ==
                // 0) then, keep evaluating current node by evaluating its children
                // nodes, where the children's side is the next player's side.
                if (level > 0 && level <= Level)
                {
                    // Evaluate children nodes by recursion
                    nTree.Nodes = Search(game, nTree, level - 1);

                    if (nTree.Nodes.Count > 0)
                    {
                        // Get max point of the children nodes Take the first node because
                        // it is sorted by descending
                        NTree <AINode> firstNode = nTree.Nodes.First();
                        var            maxPoint  = firstNode.Value.Point;

                        // Minus the current node's point by the max point so if the
                        // children node's point is high, this node is less likely to be
                        // selected. use j as a factor for the point as it goes shallower
                        // back to its parent right before the original player's turn again
                        NTree <AINode> traverseNode = nTree;
                        for (var j = 1; j < playerCount && !(traverseNode is null); j++)
                        {
                            traverseNode.Value.Point -= j * maxPoint;
                            traverseNode              = traverseNode.ParentNode;
                        }

                        // Remove all chilren nodes with lower points
                        nTree.Nodes.RemoveAll(n => n.Value.Point < maxPoint);

                        // Call GC to free up memory
                        //GC.Collect();
                        //GC.WaitForPendingFinalizers();

                        // The more the chilren nodes are left, the less likely the node is
                        // to be selected. use j as a factor for the point as it goes
                        // shallower back to its parent right before the original player's
                        // turn again use 0.01 as a small factor to penalize same nodes left
                        traverseNode = nTree;
                        for (var j = 1; j < playerCount && !(traverseNode is null); j++)
                        {
                            traverseNode.Value.Point -= j * nTree.Nodes.Count * 0.01;
                            traverseNode              = traverseNode.ParentNode;
                        }
                    }
                }

                // Evaludation is done, undo the step
                game.Undo();
            }

            // Sort the NTrees list by descending where the first item has the largest point
            nTrees.Sort((x, y) => - 1 * x.Value.CompareTo(y.Value));
            return(nTrees);
        }