Example #1
0
        private bool IsWallAdjacent(MazeGridpoint mazeGridpoint, DirectionEnum direction)
        {
            // Start by proceeding in a direction given away from mazeGridpoint
            var isWallAdjacent    = false;
            var nextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(mazeGridpoint, direction);

            // Now, check each direction around the nextMazeGridpoint
            foreach (var nextDirection in nextMazeGridpoint.DirectionsAvailable.OpenPaths)
            {
                if (!MazeToSolve.IsValidPositionInMazeAndNotBacktracking(mazeGridpoint, nextMazeGridpoint, nextDirection))
                {
                    continue;
                }

                var nextNextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(nextMazeGridpoint, nextDirection);

                if (nextNextMazeGridpoint.HasBeenVisited)
                {
                    continue;
                }

                if (nextNextMazeGridpoint.IsWall)
                {
                    isWallAdjacent = true;
                    break;
                }
            }

            return(isWallAdjacent);
        }
        /// <summary>
        /// Picks a direction that is consitent with the last direction proceeded, if possible.
        /// </summary>
        public override DirectionEnum PickDirectionToProceed(object mazeSolutionElements, MazeGridpoint mazeGridpoint)
        {
            var pathToSolveMaze = mazeSolutionElements as List <MazeSolutionElement>;

            if (pathToSolveMaze == null)
            {
                throw new Exception("Maze solution elements used are not supported by BaseDirectionPicker");
            }

            if (pathToSolveMaze.Any())
            {
                var lastMazeSolutionElement = pathToSolveMaze.Last();
                var lastMazeGridpoint       = lastMazeSolutionElement.MazeGridpoint;
                var lastDirectionProceeded  = lastMazeSolutionElement.DirectionProceeded;

                // Find all paths that do not lead back to the last position in the maze on the path traced so far
                // AND, are valid positions inside the boundaries of the maze
                var potentialDirectionsToProceed = mazeGridpoint.DirectionsAvailable.OpenPaths
                                                   .Where(direction =>
                                                          MazeToSolve.IsValidPositionInMazeAndNotBacktracking(lastMazeGridpoint, mazeGridpoint, direction)).ToList();

                // If it's possible to keep going in a straight line, continue to do so
                if (potentialDirectionsToProceed.Contains(lastDirectionProceeded))
                {
                    return(lastDirectionProceeded);
                }

                return(potentialDirectionsToProceed.FirstOrDefault());
            }

            // Take the first path that is valid
            return(mazeGridpoint.DirectionsAvailable.OpenPaths
                   .FirstOrDefault(direction =>
                                   MazeToSolve.IsValidPositionInMaze(mazeGridpoint, direction)));
        }
        private MazeGridpoint SearchForSolution(Queue <MazeGridpoint> queueToSolveMaze)
        {
            // As long as there are still elements in the queue to check, keep going
            while (queueToSolveMaze.Any())
            {
                var currentMazeGridpoint = queueToSolveMaze.Dequeue();
                currentMazeGridpoint.HasBeenVisited = true;
                MazeToSolve.NotifyMazeHasBeenUpdated();
                MazeToSolve.NotifyMazeToBeRedrawnUpdated();

                _foundMazeFinishPoint = MazeToSolve.CheckIfAtFinish(currentMazeGridpoint);

                // Quit now if the end of the maze was found
                if (_foundMazeFinishPoint)
                {
                    return(currentMazeGridpoint);
                }

                // Investigate each direction available at the current gridpoint
                foreach (var direction in currentMazeGridpoint.DirectionsAvailable.OpenPaths)
                {
                    if (!MazeToSolve.IsValidPositionInMaze(currentMazeGridpoint, direction))
                    {
                        continue;
                    }

                    var nextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(currentMazeGridpoint, direction);

                    if (MazeToSolve.CheckIfAtDeadEnd(nextMazeGridpoint) || nextMazeGridpoint.HasBeenVisited)
                    {
                        continue;
                    }

                    // Set up the parent gridpoint for each direction that is valid
                    var nextMazeSolutionElement = PathToSolveMaze.Single(m => m.MazeGridpoint == nextMazeGridpoint);
                    nextMazeSolutionElement.ParentMazeGridpoint = currentMazeGridpoint;

                    nextMazeGridpoint.HasBeenVisited = true;

                    // Make sure we haven't found the end of the maze for each direction
                    _foundMazeFinishPoint = MazeToSolve.CheckIfAtFinish(nextMazeGridpoint);

                    if (_foundMazeFinishPoint)
                    {
                        return(nextMazeGridpoint);
                    }

                    // If its not the end of the maze, queue it up for another round down the line
                    queueToSolveMaze.Enqueue(nextMazeGridpoint);

                    Console.WriteLine("X : " + nextMazeGridpoint.Position.X + ", Y : " + nextMazeGridpoint.Position.Y + " Direction : " + direction);
                }
            }

            throw new Exception("Maze Solver Error: Sorry, maze could not be solved. The maze image provided may be invalid.");
        }
Example #4
0
        private void RecursivelySearchForSolution(MazeGridpoint currentMazeGridpoint, MazeSolutionElementTree parentMazeSolutionElementTree)
        {
            // No need to keep going on this call if the finish has already been found
            if (_foundMazeFinishPoint)
            {
                return;
            }

            _mazeSolutionElementTree = parentMazeSolutionElementTree;
            _foundMazeFinishPoint    = MazeToSolve.CheckIfAtFinish(currentMazeGridpoint);

            currentMazeGridpoint.HasBeenVisited = true;
            MazeToSolve.NotifyMazeHasBeenUpdated();
            MazeToSolve.NotifyMazeToBeRedrawnUpdated();

            // Now that we've checked the current gridpoint, let's make sure we need to do more work
            if (_foundMazeFinishPoint)
            {
                return;
            }

            // For each direction available that is valid, add an element to the tree and recursively
            // call the search function again.
            foreach (var direction in currentMazeGridpoint.DirectionsAvailable.OpenPaths)
            {
                if (!MazeToSolve.IsValidPositionInMaze(currentMazeGridpoint, direction))
                {
                    continue;
                }

                var nextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(currentMazeGridpoint, direction);

                if (MazeToSolve.CheckIfAtDeadEnd(nextMazeGridpoint) || nextMazeGridpoint.HasBeenVisited)
                {
                    continue;
                }

                var nextMazeSolutionElement = new MazeSolutionElementTree
                {
                    ParentSolutionElement = parentMazeSolutionElementTree,
                    MazeGridpoint         = nextMazeGridpoint
                };

                Console.WriteLine("X : " + nextMazeGridpoint.Position.X + ", Y : " + nextMazeGridpoint.Position.Y + " Direction : " + direction);
                RecursivelySearchForSolution(nextMazeGridpoint, nextMazeSolutionElement);
            }
        }
Example #5
0
        /// <summary>
        /// Sets all points in a maze that have only one open path to visited, effectively removing
        /// them from needing to be explored for a solution.
        /// </summary>
        public override void PreTreatMazeToSolve()
        {
            var deadEndMazeGridpoints = MazeToSolve.MazeGridpoints
                                        .Where(m => !m.Value.IsWall && !m.Value.HasBeenVisited && m.Value.DirectionsAvailable.Count == 1);

            foreach (var mazeGridpoint in deadEndMazeGridpoints)
            {
                if (mazeGridpoint.Value.IsStartPoint || mazeGridpoint.Value.IsFinishPoint)
                {
                    continue;
                }

                mazeGridpoint.Value.HasBeenVisited = true;
                MazeToSolve.NotifyMazeHasBeenUpdated();
                MazeToSolve.NotifyMazeToBeRedrawnUpdated();
            }
        }
        /// <summary>
        /// Given a direction to proceed, this logic determines what the next maze gridpoint will be. In cases
        /// where we have reached a dead-end, this may mean backtracking to the last gridpoint where another
        /// choice was available.
        /// </summary>
        private MazeGridpoint DetermineNextMazeGridPoint(MazeGridpoint currentMazeGridpoint, DirectionEnum directionToProceed)
        {
            var nextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(currentMazeGridpoint, directionToProceed);

            if (MazeToSolve.CheckIfAtDeadEnd(nextMazeGridpoint) || nextMazeGridpoint.HasBeenVisited)
            {
                // We have arrived at a dead-end, so this cannot have been the right direction to proceed
                currentMazeGridpoint.DirectionsAvailable[directionToProceed] = false;

                if (currentMazeGridpoint.DirectionsAvailable.Count == 1)
                {
                    // Go back to the last gridpoint where there were more than two choices about the direction to proceed
                    var lastValidMazeSolutionElement = PathToSolveMaze.LastOrDefault(p => p.MazeGridpoint.DirectionsAvailable.Count > 2);
                    if (lastValidMazeSolutionElement == null)
                    {
                        throw new Exception("Maze Solver Error: Sorry, maze could not be solved. The maze image provided may be invalid.");
                    }

                    var directionProceededAtLastValidMazeGridPoint = lastValidMazeSolutionElement.DirectionProceeded;

                    // Since the path resulted in a dead-end, it cannot have been the reight direction to proceed
                    lastValidMazeSolutionElement.MazeGridpoint.DirectionsAvailable[directionProceededAtLastValidMazeGridPoint] = false;

                    // Truncate the path back to the last valid point to take a new direction
                    PathToSolveMaze = PathToSolveMaze.Where(p => p.StepNumber < lastValidMazeSolutionElement.StepNumber).ToList();

                    return(lastValidMazeSolutionElement.MazeGridpoint);
                }

                return(currentMazeGridpoint);
            }

            // The step numbers will be indexed starting with zero
            var stepNumber          = PathToSolveMaze.Count;
            var mazeSolutionElement = new MazeSolutionElement
            {
                StepNumber         = stepNumber,
                MazeGridpoint      = currentMazeGridpoint,
                DirectionProceeded = directionToProceed
            };

            PathToSolveMaze.Add(mazeSolutionElement);

            return(nextMazeGridpoint);
        }
        protected void MarkClosedPaths(Dictionary <CartesianCoordinate, MazeGridpoint> mazeGridpoints)
        {
            foreach (var mazeGridpoint in mazeGridpoints.Values)
            {
                // For each available direction, check what paths are closed and mark them
                foreach (var direction in mazeGridpoint.DirectionsAvailable.OpenPaths)
                {
                    if (MazeToSolve.IsValidPositionInMaze(mazeGridpoint, direction))
                    {
                        var nextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(mazeGridpoint, direction);

                        if (MazeToSolve.CheckIfAtDeadEnd(nextMazeGridpoint) ||
                            nextMazeGridpoint.HasBeenVisited)
                        {
                            mazeGridpoint.DirectionsAvailable[direction] = false;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Solves the maze using a brute force algorithm approach.
        /// </summary>
        public override void SolveMaze()
        {
            // Apply all pre-treament logics, if any
            PreTreatmentLogics.ForEach(p => p.PreTreatMazeToSolve());

            // Set all parent gridpoints to null at the start
            var currentMazeGridpoint = MazeToSolve.MazeGridpoints.Values.First(m => m.IsStartPoint && m.DirectionsAvailable.Count > 0);

            // Keep on looking so long as we haven't reached the finish
            while (!MazeToSolve.CheckIfAtFinish(currentMazeGridpoint))
            {
                currentMazeGridpoint.HasBeenVisited = true;

                // Picked a direction and go in that direction
                var directionToProceed = DirectionPickerLogic.PickDirectionToProceed(PathToSolveMaze, currentMazeGridpoint);

                if (directionToProceed == DirectionEnum.None)
                {
                    throw new Exception("Maze Solver Error: Sorry, maze could not be solved. The maze image provided may be invalid.");
                }

                currentMazeGridpoint = DetermineNextMazeGridPoint(currentMazeGridpoint, directionToProceed);

                Console.WriteLine("X : " + currentMazeGridpoint.Position.X + ", Y : " + currentMazeGridpoint.Position.Y + " Direction : " + directionToProceed);
                MazeToSolve.NotifyMazeHasBeenUpdated();
                MazeToSolve.NotifyMazeToBeRedrawnUpdated();
            }

            // Once out of the loop, the current gridpoint will be the final gridpoint
            var finalMazeSolutionElement = new MazeSolutionElement
            {
                StepNumber         = PathToSolveMaze.Count,
                MazeGridpoint      = currentMazeGridpoint,
                DirectionProceeded = DirectionEnum.None
            };

            PathToSolveMaze.Add(finalMazeSolutionElement);
        }
Example #9
0
        /// <summary>
        /// Removes redundant interior space in a maze.
        /// </summary>
        public override void PreTreatMazeToSolve()
        {
            var wallMazeGridpoints = MazeToSolve.MazeGridpoints
                                     .Where(m => m.Value.IsWall)
                                     .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            // One round of the outer-loop below takes about 170 ms in debug mode.
            // For 5,500 gridpoints, that's about 15 minutes.
            if (wallMazeGridpoints.Count() > _maxiumGridpointsLimit)
            {
                return;
            }

            // First, focus only on the gridpoints that are walls or have been visited
            foreach (var mazeGridpoint in wallMazeGridpoints)
            {
                // Now for each direction, find the wall-adjacent gridpoints that are not walls themselves
                foreach (var direction in mazeGridpoint.Value.DirectionsAvailable.OpenPaths)
                {
                    if (!MazeToSolve.IsValidPositionInMaze(mazeGridpoint.Value, direction))
                    {
                        continue;
                    }

                    var canAvoidThisGridpoint     = true;
                    var wallAdjacentMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(mazeGridpoint.Value, direction);

                    if (MazeToSolve.CheckIfAtDeadEnd(wallAdjacentMazeGridpoint))
                    {
                        continue;
                    }

                    // For each gridpoint that is wall-adjacent and not a wall itself, check the gridpoints
                    // at each of its four directions.
                    foreach (var nextDirection in wallAdjacentMazeGridpoint.DirectionsAvailable.OpenPaths)
                    {
                        if (!MazeToSolve.IsValidPositionInMaze(wallAdjacentMazeGridpoint, nextDirection))
                        {
                            continue;
                        }

                        var nextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(wallAdjacentMazeGridpoint, nextDirection);

                        if (MazeToSolve.CheckIfAtDeadEnd(nextMazeGridpoint))
                        {
                            wallAdjacentMazeGridpoint.DirectionsAvailable[nextDirection] = false;
                        }
                        else
                        {
                            // If not at a dead-end go out a second layer of gridpoints from the gridpoints that
                            // are around the wall-adjacent gridpoint
                            foreach (var nextNextDirection in nextMazeGridpoint.DirectionsAvailable.OpenPaths)
                            {
                                if (!MazeToSolve.IsValidPositionInMaze(nextMazeGridpoint, nextNextDirection))
                                {
                                    continue;
                                }

                                var nextNextMazeGridpoint = MazeToSolve.ProceedToNewGridpoint(nextMazeGridpoint, nextNextDirection);

                                if (MazeToSolve.CheckIfAtDeadEnd(nextNextMazeGridpoint))
                                {
                                    nextMazeGridpoint.DirectionsAvailable[nextNextDirection] = false;
                                }
                            }

                            // If there's not at least two paths avaiable this far out, the wall-adjacent gridpoint
                            // likely cannot be avoided without closing off a potential solution path
                            if (nextMazeGridpoint.DirectionsAvailable.Count < 2)
                            {
                                canAvoidThisGridpoint = false;
                                break;
                            }
                        }
                    }

                    // Assuming each of the second layer gridpoints had at least two open paths, make sure
                    // that the wall-adjacent gridpoint has at least tree open paths
                    if (!canAvoidThisGridpoint ||
                        wallAdjacentMazeGridpoint.DirectionsAvailable.Count < 3)
                    {
                        break;
                    }

                    // If everything above was satisfied, we can safely ignore the wall-adjacent gridpoint
                    // without fear of closing off a solution path
                    wallAdjacentMazeGridpoint.HasBeenVisited = true;
                    MazeToSolve.NotifyMazeHasBeenUpdated();
                    MazeToSolve.NotifyMazeToBeRedrawnUpdated();
                }
            }
        }