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