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); }
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); }
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)); }