Image IGraphicalMazeSolver.GenerateSolution(GraphicalMaze maze)
        {
            if (maze == null)
            {
                throw new ArgumentNullException(nameof(maze));
            }

            // Find start pixel
            var startPixel = FindStartPixel(maze);

            if (startPixel == null)
            {
                return(maze.MazeImage); // No start pixel found
            }

            var solutionImage = new Bitmap(maze.MazeImage);

            // Move up from start pixel until wall reached
            var currPixel = startPixel;
            var nextPixel = maze.GetPixelTop(currPixel);

            while (!nextPixel.IsColor(maze.WallColor))
            {
                currPixel = nextPixel;
                nextPixel = maze.GetPixelTop(currPixel);

                if (nextPixel.IsColor(maze.FinishColor))
                {
                    return(solutionImage);
                }

                solutionImage.SetPixel(currPixel.X, currPixel.Y, maze.SolutionColor);
            }

            var startWallPixel  = currPixel;
            var travelDirection = Direction.Right;
            var wallDirection   = Direction.Up;

            while (!currPixel.IsColor(maze.FinishColor))
            {
                var nextStep = FollowWall(maze, currPixel, travelDirection, wallDirection);
                solutionImage.SetPixel(currPixel.X, currPixel.Y, maze.SolutionColor);
                currPixel       = nextStep.Item1;
                travelDirection = nextStep.Item2;
                wallDirection   = nextStep.Item3;

                if (currPixel.Equals(startWallPixel))
                {
                    return(null); // No solution found if looped around to the first wall pixel
                }
            }

            solutionImage.SetPixel(startPixel.X, startPixel.Y, maze.StartColor); // Reapply start pixel color
            return(solutionImage);
        }
Пример #2
0
        public void Execute(string[] args)
        {
            // Validate arguments
            if (args.Length != 2)
            {
                Console.Error.WriteLine("Incorrect number of arguments found.");
                Exit(-1);
            }

            // Process arguments
            var currentDirectory = Directory.GetCurrentDirectory();
            var sourceFile       = Path.Combine(currentDirectory, args[0]);
            var outputFile       = Path.Combine(currentDirectory, args[1]);

            // TODO: Validate file formats

            // Get source image
            try
            {
                var sourceImage   = Image.FromFile(sourceFile);
                var graphicalMaze = GraphicalMaze.Create(sourceImage, DefaultWallColor, DefaultStartColor,
                                                         DefaultFinishColor,
                                                         DefaultSolutionColor);
                // Generate cleaned image
                var cleanedImage = CleanMaze(graphicalMaze);

                // Write solution image, creating the directory if necessary
                var ouputDirectory = Path.GetDirectoryName(outputFile);
                if (ouputDirectory != null)
                {
                    Directory.CreateDirectory(ouputDirectory);
                    cleanedImage.Save(outputFile);
                }
                else
                {
                    Console.Error.WriteLine("Cannot retrieve directoy information for output file path ("
                                            + outputFile + ".");
                    Exit(-1);
                }
            }
            catch (FileNotFoundException)
            {
                Console.Error.WriteLine("Source image not found (" + sourceFile + ").");
                Exit(-1);
            }

            // Solution generated successfully
            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.WriteLine("Image cleaned and saved (" + outputFile + ".");
            }

            Exit(0);
        }
        private static GraphicalMazePixel FindStartPixel(GraphicalMaze maze)
        {
            var startingY = 0;
            var endingY   = maze.Height;
            var startingX = 0;
            var endingX   = maze.Width;

            // Spiral iterate, since the start point is most likely on the outside of the maze
            while (startingY < endingY && startingX < endingX)
            {
                // Iterate over the first row from the remaining rows
                for (var x = startingX; x < endingX; x++)
                {
                    if (maze.GetPixel(x, startingY).IsColor(maze.StartColor))
                    {
                        return(maze.GetPixel(x, startingY));
                    }
                }
                startingY++;

                // Iterate over the last column from the remaining columns
                for (var y = startingY; y < endingY; y++)
                {
                    if (maze.GetPixel(endingX - 1, y).IsColor(maze.StartColor))
                    {
                        return(maze.GetPixel(endingX - 1, y));
                    }
                }
                endingX--;

                // Iterate over the last row from the remaining rows
                for (var x = startingX; x < endingX; x++)
                {
                    if (maze.GetPixel(x, endingY - 1).IsColor(maze.StartColor))
                    {
                        return(maze.GetPixel(x, endingY - 1));
                    }
                }
                endingY--;

                // Iterate over the first column from the remaining columns
                for (var y = startingY; y < endingY; y++)
                {
                    if (maze.GetPixel(startingX, y).IsColor(maze.StartColor))
                    {
                        return(maze.GetPixel(startingX, y));
                    }
                }
                startingX++;
            }

            return(null);
        }
Пример #4
0
        public static Image CleanMaze(GraphicalMaze maze)
        {
            // TODO Consider use of ColorMask for more efficient operation
            var palette      = new[] { maze.WallColor, maze.StartColor, maze.FinishColor, Color.White };
            var cleanedImage = new Bitmap(maze.MazeImage);

            for (var y = 0; y < maze.Height; y++)
            {
                for (var x = 0; x < maze.Width; x++)
                {
                    cleanedImage.SetPixel(x, y, GetNearestColor(palette, maze.MazeImage.GetPixel(x, y)));
                }
            }

            return(cleanedImage);
        }
        private static List <Point> BestFirstSearch(GraphicalMaze maze, Point startPoint, Point endPoint)
        {
            var visited = new HashSet <Point>();

            // Set of pending nodes to be evaluated, sorted by estimated cost from start to end through the point
            var discovered = new Queue <Point>();

            discovered.Enqueue(startPoint);
            var costMap = new Dictionary <Point, int> {
                { startPoint, 0 }
            };
            var accessMap = new Dictionary <Point, Point>();

            while (discovered.Count > 0)
            {
                var currentPoint = discovered.Dequeue();
                if (currentPoint.Equals(endPoint))
                {
                    return(ReconstructPath(accessMap, currentPoint));
                }

                visited.Add(currentPoint);
                var cost = costMap[Point.Create(currentPoint.X, currentPoint.Y)] + 1;
                foreach (var neighbor in maze.GetNeighbors(currentPoint))
                {
                    if (neighbor.IsColor(maze.WallColor) || visited.Contains(neighbor))
                    {
                        continue;
                    }

                    if (costMap.ContainsKey(neighbor) && cost >= costMap[neighbor])
                    {
                        continue;                                                             // Not a better path
                    }
                    // Record better path
                    accessMap[neighbor] = currentPoint;
                    costMap[neighbor]   = cost;
                    discovered.Enqueue(neighbor);
                }
            }

            // No solution found
            return(null);
        }
        public Image GenerateSolution(GraphicalMaze maze)
        {
            // Find start and end pixels
            Point startPoint = null;
            Point endPoint   = null;

            for (var y = 0; y < maze.Height; y++)
            {
                for (var x = 0; x < maze.Width; x++)
                {
                    var pixel = maze.GetPixel(x, y);
                    if (pixel.IsColor(maze.StartColor))
                    {
                        startPoint = pixel;
                    }
                    else if (pixel.IsColor(maze.FinishColor))
                    {
                        endPoint = pixel;
                    }
                }
            }

            if (startPoint == null || endPoint == null)
            {
                return(null); // No start or end points found
            }

            // Run best first search
            var path = BestFirstSearch(maze, startPoint, endPoint);

            if (path == null)
            {
                return(null);              // No solution found
            }
            var solutionImage = new Bitmap(maze.MazeImage);

            foreach (var point in path)
            {
                solutionImage.SetPixel(point.X, point.Y, maze.SolutionColor);
            }

            solutionImage.SetPixel(startPoint.X, startPoint.Y, maze.StartColor); // Reapply start pixel color
            return(solutionImage);
        }
        private static Tuple <GraphicalMazePixel, Direction, Direction> FollowWall(GraphicalMaze maze,
                                                                                   Point currentPoint, Direction travelDirection, Direction wallDirection)
        {
            // Left-hand rule conditions only
            while (maze.GetPixel(currentPoint, travelDirection).IsColor(maze.WallColor))
            {
                // Turn at interior corners
                switch (wallDirection)
                {
                case Direction.Up:
                    travelDirection = Direction.Down;
                    wallDirection   = Direction.Right;
                    break;

                case Direction.Right:
                    travelDirection = Direction.Left;
                    wallDirection   = Direction.Down;
                    break;

                case Direction.Down:
                    travelDirection = Direction.Up;
                    wallDirection   = Direction.Left;
                    break;

                case Direction.Left:
                    travelDirection = Direction.Right;
                    wallDirection   = Direction.Up;
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(wallDirection), wallDirection, null);
                }
            }

            var nextPixel = maze.GetPixel(currentPoint, travelDirection);

            // Wrap-around if moving away from a wall
            switch (travelDirection)
            {
            case Direction.Up:
                if (!maze.GetPixel(nextPixel, Direction.Left).IsColor(maze.WallColor))
                {
                    travelDirection = Direction.Left;
                    wallDirection   = Direction.Down;
                }
                break;

            case Direction.Right:
                if (!maze.GetPixel(nextPixel, Direction.Up).IsColor(maze.WallColor))
                {
                    travelDirection = Direction.Up;
                    wallDirection   = Direction.Left;
                }
                break;

            case Direction.Down:
                if (!maze.GetPixel(nextPixel, Direction.Right).IsColor(maze.WallColor))
                {
                    travelDirection = Direction.Right;
                    wallDirection   = Direction.Up;
                }
                break;

            case Direction.Left:
                if (!maze.GetPixel(nextPixel, Direction.Down).IsColor(maze.WallColor))
                {
                    travelDirection = Direction.Down;
                    wallDirection   = Direction.Right;
                }
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(travelDirection), travelDirection, null);
            }

            return(new Tuple <GraphicalMazePixel, Direction, Direction>(nextPixel, travelDirection, wallDirection));
        }