public bool CanMove(GameFieldGrid grid, GameMoveType move)
        {
            var emptylocation = grid.GetUnusedItemLocation ();

            // There always must be an empty field!
            if(emptylocation == null)
            {
                throw new InvalidOperationException ("Game field does not have an empty item location!");
            }

            switch(move)
            {
            case GameMoveType.MoveItemAbove:
                return emptylocation.Row > 0;

            case GameMoveType.MoveItemBelow:
                return emptylocation.Row < grid.Rows - 1;

            case GameMoveType.MoveItemLeft:
                return emptylocation.Column > 0;

            case GameMoveType.MoveItemRight:
                return emptylocation.Column < grid.Columns - 1;

            default:
                return false;
            }
        }
예제 #2
0
        /// <summary>
        /// Gets the accessible adjacent locations of a specified location.
        /// A location is accessible if it's top, bottom, left or right of the location within the grid's ranges and is not a treasure.
        /// </summary>
        /// <returns>The accessible adjacent locations.</returns>
        /// <param name="gameGrid">Game grid.</param>
        /// <param name="location">Location.</param>
        /// <param name="ignoreLocation">Ignore this location no matter what type of treasure it has.</param>
        /// <param name="currentActiveTreasure">The current active treasure is never considered a wall.</param> 
        public static List<GridLocation> GetAccessibleAdjacentLocations(GameFieldGrid gameGrid, GridLocation location, TreasureType currentActiveTreasure, GridLocation ignoreLocation = null)
        {
            var checkLocations = new List<GridLocation> ();

            // Check the item to the top.
            if(location.Row > 0)
            {
                var targetLocation = new GridLocation (location.Row - 1, location.Column);
                bool ignore = ignoreLocation != null && targetLocation.Equals (ignoreLocation);
                bool isActiveTreasure = gameGrid.GetItem (targetLocation).Treasure == currentActiveTreasure;

                if (isActiveTreasure || ignore || IsPassableTreasureType (targetLocation, gameGrid))
                {
                    checkLocations.Add (targetLocation);
                }
            }

            // Check the item below.

            if(location.Row < gameGrid.Rows - 1)
            {
                var targetLocation = new GridLocation (location.Row + 1, location.Column);
                var ignore = ignoreLocation != null && targetLocation.Equals (ignoreLocation);
                bool isActiveTreasure = gameGrid.GetItem (targetLocation).Treasure == currentActiveTreasure;

                if (isActiveTreasure || ignore || IsPassableTreasureType (targetLocation, gameGrid))
                {
                    checkLocations.Add (targetLocation);
                }
            }

            // Check the item to the left.
            if(location.Column > 0)
            {
                var targetLocation = new GridLocation (location.Row, location.Column - 1);
                var ignore = ignoreLocation != null && targetLocation.Equals (ignoreLocation);
                bool isActiveTreasure = gameGrid.GetItem (targetLocation).Treasure == currentActiveTreasure;

                if (isActiveTreasure || ignore || IsPassableTreasureType (targetLocation, gameGrid))
                {
                    checkLocations.Add (targetLocation);
                }
            }

            // Check the item to the right.
            if(location.Column < gameGrid.Columns - 1)
            {
                var targetLocation = new GridLocation (location.Row, location.Column + 1);
                var ignore = ignoreLocation != null && targetLocation.Equals (ignoreLocation);
                bool isActiveTreasure = gameGrid.GetItem (targetLocation).Treasure == currentActiveTreasure;

                if (isActiveTreasure || ignore || IsPassableTreasureType (targetLocation, gameGrid))
                {
                    checkLocations.Add (targetLocation);
                }
            }

            return checkLocations;
        }
예제 #3
0
        /// <summary>
        /// Returns a two dimensional grid where each field contains the distance from the start point (the empty field).
        /// </summary>
        /// <returns>The path.</returns>
        /// <param name="gameGrid">Game grid.</param>
        /// <param name="fromLocation">the location to start from.</param>
        /// <param name="currentActiveTreasure">The current active treasre can always be stepped onto.</param>
        public static Grid2D<int> PreparePathGrid(GameFieldGrid gameGrid, GridLocation fromLocation, TreasureType currentActiveTreasure)
        {
            if(gameGrid == null)
            {
                throw new ArgumentNullException ("gameGrid");
            }

            if(fromLocation == null)
            {
                throw new ArgumentNullException ("fromLocation");
            }

            // Create a grid as big as the game field. The content of each item is the distance from the staring point (the empty spot).
            var pathGrid = new Grid2D<int> (gameGrid.Rows, gameGrid.Columns, pathFindingMaxVal);

            // Use a Disjkstra algorithm to find a path from the empty spot to the current active treasure.
            // First, set the presumed target location to a distance of 0.
            pathGrid.SetItem (fromLocation, 0);

            // Start calculating distances.
            while(true)
            {
                // Set to true if at least one field was changed. If nothing is changed during one loop, we're done.
                bool gridChanged = false;

                // Loop all fields until each field contains a distance value.
                for (int row = 0; row < gameGrid.Rows; row++)
                {
                    for (int col = 0; col < gameGrid.Columns; col++)
                    {
                        // Distance is one more than it was at the current location. Set for the surrounding fields.
                        int newDistance = pathGrid.GetItem (row, col);
                        if(newDistance != pathFindingMaxVal)
                        {
                            newDistance++;
                        }

                        var checkLocations = AIHelpers.GetAccessibleAdjacentLocations(gameGrid, new GridLocation(row, col), currentActiveTreasure);
                        foreach(var checkLocation in checkLocations)
                        {
                            // Remember the distance to the start point.
                            int currentDistance = pathGrid.GetItem (checkLocation);
                            if(newDistance < currentDistance)
                            {
                                pathGrid.SetItem (checkLocation, newDistance);
                                gridChanged = true;
                            }
                        }
                    }
                }

                // Bail out of the algorithm if we visited all nodes.
                if(!gridChanged)
                {
                    break;
                }
            }
            return pathGrid;
        }
예제 #4
0
        /// <summary>
        /// Figures out if a specific item location is considered a "wall" for the path finding algorithm.
        /// A wall is defined as a treasure that is not the current active treasure card.
        /// The prediction is not 100% correct because the AI is not supposed to know the location of each and
        /// every treasure. Randomization and statistics are used to reduce the chance the AI will detect a wall.
        /// </summary>
        /// <returns><c>true</c> if the AI thinks it can move to the location otherwise, <c>false</c>.</returns>
        /// <param name="location">Location to check.</param>
        /// <param name="gameField">Game field to use.</param>
        public static bool IsPassableTreasureType(GridLocation location, GameFieldGrid gameField)
        {
            var item = gameField.GetItem (location.Row, location.Column);

            // A location can be moved to if the treasure is "None".
            if(item.Treasure != TreasureType.None)
            {
                // It's a wall. AI cannot go there.
                return false;
            }

            // It is not a wall. AI can go there.
            return true;
        }
예제 #5
0
 /// <summary>
 /// Gets a random move. Used if the AI does not know what to do and is guessing.
 /// </summary>
 /// <returns>The random move.</returns>
 /// <param name="gameGrid">Game grid.</param>
 /// <param name="fromLocation">From location.</param>
 /// <param name="currentActiveTreasure">The current active treasure. This can also be stepped onto.</param>
 public static GameMoveType GetRandomMove(GameFieldGrid gameGrid, GridLocation fromLocation, TreasureType currentActiveTreasure)
 {
     var possibleTargetLocations = AIHelpers.GetAccessibleAdjacentLocations (gameGrid, fromLocation, currentActiveTreasure);
     var platformServices = ServiceContainer.Resolve<IPlatformServices> ();
     // Pick a random target location.
     var toLocation = possibleTargetLocations [platformServices.GetRandomNumber (0, possibleTargetLocations.Count)];
     // Return the required move to get to the picked location.
     var randomMove = AIHelpers.GetMoveToAdjacentLocation (fromLocation, toLocation);
     return randomMove;
 }
예제 #6
0
        /// <summary>
        /// Gets the fields that have to be followed to come to a specific location.
        /// </summary>
        /// <returns>The path to location.</returns>
        /// <param name="fromLocation">the location from where to start path finding. Must be the same that was passed to PreparePathGrid()</param>
        /// <param name="toLocation">the location to find the path to starting at the unused location.</param>
        /// <param name="gameGrid">Game grid.</param>
        /// <param name="pathGrid">Populated path grid.</param>
        /// <param name="currentActiveTreasure">The current active treasre can always be stepped onto.</param>
        public static List<GridLocation> GetPathToLocation(GameFieldGrid gameGrid, Grid2D<int> pathGrid, GridLocation fromLocation, GridLocation toLocation, TreasureType currentActiveTreasure)
        {
            if(gameGrid == null)
            {
                throw new ArgumentNullException ("gameGrid");
            }

            if(pathGrid == null)
            {
                throw new ArgumentNullException ("pathGrid");
            }

            if(toLocation == null)
            {
                throw new ArgumentNullException ("toLocation");
            }

            // This will hold the reversed path.
            var path = new List<GridLocation> {
                // The target always belongs to the path.
                new GridLocation (toLocation.Row, toLocation.Column)
            };

            // Loop until the path has been completed.
            while(true)
            {
                // From the current target location check all possible moves. The algorithm works its way from the target to the start, so the path will be reversed.
                var checkLocations = AIHelpers.GetAccessibleAdjacentLocations (gameGrid, toLocation, currentActiveTreasure, fromLocation);

                // Loop the possible moves and remember the one with the lowest distance towards the start location.
                int lowestDistance = pathFindingMaxVal;
                GridLocation lowestLocation = null;

                foreach(var adjacentLocation in checkLocations)
                {
                    int distance = pathGrid.GetItem (adjacentLocation);
                    if(distance < lowestDistance)
                    {
                        lowestDistance = distance;
                        lowestLocation = adjacentLocation;
                        // The new target location is the lowest of the possible locations.
                        toLocation = lowestLocation;
                    }
                }

                if(lowestLocation != null)
                {
                    // Insert at 0 to have the path in the correct reversed order.
                    path.Insert(0, lowestLocation);
                    if(lowestLocation.Equals(fromLocation))
                    {
                        // Reached the start location.
                        break;
                    }
                }
                else
                {
                    // No lowest location found. Bail out.
                    break;
                }
            }

            return path;
        }
예제 #7
0
        /// <summary>
        /// Gets the location of the current active treasure card.
        /// Randomization and statistics are applied to prevent the prevention from being 100% correct. This makes the AI more human.
        /// If the AI cannot remember the location, a random location will be returned.
        /// </summary>
        /// <returns>The target treasure location.</returns>
        /// <param name="gameField">Game field.</param>
        /// <param name="activeTreaure">Active treaure.</param>
        public static GridLocation GetActiveTreasureLocation(GameFieldGrid gameField, TreasureType activeTreaure)
        {
            // Find the item that has the active treasure.
            var activeTreasureItem = gameField.FirstOrDefault (item => item.Treasure == activeTreaure);
            // Get the location of the item.
            var location = gameField.GetItemLocation (activeTreasureItem);

            return location;
        }