├── Maze.suo ├── Binaries └── maze.exe ├── Maze.Tests ├── maze.png ├── maze2.PNG ├── maze.solution.png ├── maze2.solution.png ├── big_maze_with_different_colors.png └── big_maze_with_different_colors.solved.png ├── Maze ├── IMazeSolver.cs ├── IWalledMazeNode.cs ├── IWalledMaze.cs ├── WalledMazeNode.cs ├── Maze.csproj ├── Program.cs ├── BFSMazeSolver.cs └── WalledMaze.cs ├── Maze.sln └── README.txt /Maze.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.suo -------------------------------------------------------------------------------- /Binaries/maze.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Binaries/maze.exe -------------------------------------------------------------------------------- /Maze.Tests/maze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.Tests/maze.png -------------------------------------------------------------------------------- /Maze.Tests/maze2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.Tests/maze2.PNG -------------------------------------------------------------------------------- /Maze.Tests/maze.solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.Tests/maze.solution.png -------------------------------------------------------------------------------- /Maze.Tests/maze2.solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.Tests/maze2.solution.png -------------------------------------------------------------------------------- /Maze.Tests/big_maze_with_different_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.Tests/big_maze_with_different_colors.png -------------------------------------------------------------------------------- /Maze.Tests/big_maze_with_different_colors.solved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gogsbread/MazeSolver/master/Maze.Tests/big_maze_with_different_colors.solved.png -------------------------------------------------------------------------------- /Maze/IMazeSolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Maze 7 | { 8 | /// 9 | /// Represents a interface for solver. 10 | /// 11 | public interface IMazeSolver 12 | { 13 | void Solve(IWalledMaze maze, Action> solvedResultCallback); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Maze/IWalledMazeNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Maze 5 | { 6 | /// 7 | /// Represents a node for the maze model. 8 | /// 9 | 10 | public interface IWalledMazeNode 11 | { 12 | int RowPosition { get; }//'y' coordinate 13 | int ColPosition { get; }//'x' coordinate 14 | WalledMazeNodeState State { get; set; } //State information.Valid states for a walled maze are OPEN and CLOSED 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Maze.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Web Developer Express 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Maze", "Maze\Maze.csproj", "{E147BBCC-4819-46EA-863A-26BD8FD19851}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {E147BBCC-4819-46EA-863A-26BD8FD19851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {E147BBCC-4819-46EA-863A-26BD8FD19851}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {E147BBCC-4819-46EA-863A-26BD8FD19851}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {E147BBCC-4819-46EA-863A-26BD8FD19851}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Maze/IWalledMaze.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Maze 7 | { 8 | /// 9 | /// Represents a interface for a walled maze. Any solver should be able to use the interfaces provided to solve the maze puzzle. 10 | /// 11 | public interface IWalledMaze 12 | { 13 | int Height { get; } 14 | int Width { get; } 15 | IWalledMazeNode Start { get; } //Indicates the start point of the maze 16 | IWalledMazeNode Finish { get; } //Indicate the end point of the maze 17 | bool IsGoal(IWalledMazeNode curNode); //Used by a to determine if it has arrived at a destination. 18 | void Solve(IMazeSolver solver, Action> solvedResultCallback); //Used by the client to solve the maze. 19 | IEnumerable GetAdjacentNodes(IWalledMazeNode curNode); //Lists all adjacent nodes for a given node. Used by the solver to find neighboring nodes during scan. 20 | IWalledMazeNode GetNode(int row, int col); //Gets a specific node given the position. 21 | IEnumerator GetNodes();//Gets all nodes for the maze. 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Maze/WalledMazeNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Maze 7 | { 8 | /// 9 | /// Valid states for a walled maze. 10 | /// 11 | public enum WalledMazeNodeState : int 12 | { 13 | Open = 0, 14 | Blocked = 1 15 | } 16 | 17 | /// 18 | /// Implementaion of . 19 | /// 20 | /// 21 | public sealed class WalledMazeNode : IWalledMazeNode 22 | { 23 | const int DEFAULT_POSITION = 0; 24 | int _rowPosition, _colPosition = DEFAULT_POSITION; 25 | WalledMazeNodeState _state = WalledMazeNodeState.Open; 26 | 27 | public int RowPosition 28 | { 29 | get { return _rowPosition; } 30 | } 31 | 32 | public int ColPosition 33 | { 34 | get { return _colPosition; } 35 | } 36 | 37 | public WalledMazeNodeState State 38 | { 39 | get 40 | { 41 | return _state; 42 | } 43 | set 44 | { 45 | _state = value; 46 | } 47 | } 48 | 49 | public WalledMazeNode(int row, int col) 50 | { 51 | _rowPosition = row; 52 | _colPosition = col; 53 | } 54 | 55 | public override bool Equals(object obj) 56 | { 57 | WalledMazeNode mazeNode = obj as WalledMazeNode; 58 | if (mazeNode == null) 59 | return false; 60 | if(object.ReferenceEquals(mazeNode,this)) 61 | return true; 62 | return (mazeNode.ColPosition == this.ColPosition && mazeNode.RowPosition == this.RowPosition && mazeNode.State == this.State);//all necessary informations are considered for equality 63 | } 64 | 65 | public override int GetHashCode() 66 | { 67 | return base.GetHashCode() + ColPosition + RowPosition + (int)State;//changed according to the implementation of Equals(). 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This is a code for a maze puzzle solver using BFS. The solver solves any walled maze puzzle that defines a start and a finish. Refer to article for general ideas about maze solving algorithms http://en.wikipedia.org/wiki/Maze_solving_algorithm. I wrote the code in C# but you can take the idea and use it for any OO language. The solution files attached work with either VisualStudio or Monodevelop. 2 | 3 | Design 4 | ======= 5 | Any maze solving problem can be reduced to a single statement: "How will you find a path from source to destination"? and the answer there-in lies in Graph theory. 6 | 7 | General steps followed: 8 | 1) Convert the image to an object model 9 | 2) Run a path finding algorithms against the object model 10 | 3) Write the results back to the image. 11 | 12 | Most maze images in the web have an average resolution of 440*440 pixels. So, if I were to convert every pixel to a node I would be dealing with < 200,000 nodes(vertices). This is a relatively small dataset and you can use a basic search algorithm such as "Breadth First Search". 13 | 14 | 15 | You also have to think about abstracting the maze model from the algorithm that solves the maze. This is very imporatant because any path finding algorithm will alter the state of the maze object model and this action could be unwarranted. Also,future needs may warrant a better algorithm than BFS and hence I painted the puzzle in a OO canvas. 16 | 17 | Interfaces: 18 | ---------- 19 | Modelling the maze: 20 | ------------------- 21 | The following interfaces encapsulate the details of a maze. This model converts an image to an Maze so that any path finding algorithm can be applied. 22 | 23 | IWalledMazeNode: 24 | Represents a node in the maze. Has Positional and State information. 25 | IWalledMaze: 26 | The actual maze. Has all interface methods that a maze solver would need to solve this maze puzzle. 27 | 28 | Modelling the solver: 29 | --------------------- 30 | IMazeSolver 31 | This interface encapsulates the algorithm that would run on a maze defined by IWalledMaze. It can maintain its own internal representation of a node and act on those nodes instead of converting the state of the original maze. 32 | 33 | 34 | Runtime Analysis: 35 | ================ 36 | 37 | Since I used BFS the runtime is O(V+E). Sum of total number of vertices and edges. 38 | 39 | Usage: 40 | ====== 41 | maze source.[bmp,jpg,png] destination.[bmp,jpg,png] 42 | -------------------------------------------------------------------------------- /Maze/Maze.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {E147BBCC-4819-46EA-863A-26BD8FD19851} 9 | Exe 10 | Properties 11 | Maze 12 | maze 13 | v4.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 60 | -------------------------------------------------------------------------------- /Maze/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Drawing; 6 | 7 | namespace Maze 8 | { 9 | class Program 10 | { 11 | /// 12 | /// Program entry point. 13 | /// 14 | public static void Main(string[] args) 15 | { 16 | if (args.Length != 2)//input from command line 17 | { 18 | Console.WriteLine("Invalid number of arguments"); 19 | PrintUsage(); 20 | return; 21 | } 22 | string imagePath = args[0]; 23 | string outputImagePath = args[1]; 24 | 25 | try 26 | { 27 | IWalledMaze mazeProblem = new WalledMaze(imagePath, HexCode.BLACK, HexCode.WHITE, HexCode.RED, HexCode.BLUE);//configures the maze. 28 | IMazeSolver bfsSolver = new BFSWalledMazeSolver();//intializes the solver. BFS in this case 29 | mazeProblem.Solve(bfsSolver, (solvedPath) => //callback defines action to perform after the maze is solved. Here the action is to save in "destination.png". 30 | { 31 | Bitmap bitMap = new Bitmap(imagePath); 32 | foreach (var node in solvedPath) 33 | { 34 | bitMap.SetPixel(node.ColPosition, node.RowPosition, Color.Green); //Could also use lockbits() over SetPixel() for faster processing. 35 | } 36 | bitMap.Save(outputImagePath); 37 | Console.WriteLine(); 38 | Console.WriteLine("Completed:"); 39 | Console.WriteLine("========="); 40 | Console.WriteLine("See '{0}' for solution", outputImagePath); 41 | Console.WriteLine(); 42 | }); 43 | } 44 | catch (Exception e)//generic error handling. Specific errors are handled inside the main code. 45 | { 46 | Console.WriteLine(); 47 | Console.WriteLine("ERROR:"); 48 | Console.WriteLine("======"); 49 | Console.WriteLine("{0}", e.Message); 50 | } 51 | } 52 | 53 | /// 54 | /// Prints the usage of the tool. 55 | /// 56 | 57 | static void PrintUsage() 58 | { 59 | Console.WriteLine("Usage"); 60 | Console.WriteLine("====="); 61 | Console.WriteLine("maze source.[bmp,png,jpg] destination.[bmp,png,jpg]"); 62 | Console.WriteLine(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Maze/BFSMazeSolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Maze 5 | { 6 | /// 7 | /// Solves a walled maze puzzle using Breadth First Algorithm. 8 | /// 9 | class BFSWalledMazeSolver : IMazeSolver 10 | { 11 | //BFS'es internal node states. Every solver can have its own state. 12 | enum BFSNodeState 13 | { 14 | NotVisited = 0, //White 15 | Visited = 1, //Black 16 | Queued = 2, //Gray 17 | } 18 | 19 | /// 20 | /// BFS'es internal node representation. Every solver can have its own representation. 21 | /// 22 | class BFSNode 23 | { 24 | public int RowPosition { get; set; } 25 | public int ColPosition { get; set; } 26 | //Internal state of the BFS node. 27 | public BFSNodeState State 28 | { 29 | get; 30 | set; 31 | } 32 | //Distance from the source node. 33 | public int Distance 34 | { 35 | get; 36 | set; 37 | } 38 | //Pointer to the previous node. 39 | public BFSNode Predecessor 40 | { 41 | get; 42 | set; 43 | } 44 | 45 | public BFSNode(int row, int col) 46 | { 47 | RowPosition = row; 48 | ColPosition = col; 49 | } 50 | } 51 | 52 | BFSNode[,] _bfsNodes = null; 53 | 54 | /// 55 | /// Implementatin of the 's Solve() method. Uses the Breadth Fist Search Algorithm that tracks predecessors and distance from source. 56 | /// 57 | public void Solve(IWalledMaze maze, Action> solvedResultCallback) 58 | { 59 | InitializeSolver(maze); 60 | Queue frontierQueue = new Queue();//Queue that maintains the frontier to the explored. 61 | BFSNode startNode = GetBFSNode(maze.Start);//Start of the maze as defined by IWalledMaze. 62 | startNode.Distance = 0; 63 | startNode.Predecessor = null; 64 | startNode.State = BFSNodeState.Queued;//In BFS this is marked by the color GREY 65 | frontierQueue.Enqueue(startNode); 66 | 67 | while (frontierQueue.Count > 0) 68 | { 69 | BFSNode curBFSNode = frontierQueue.Dequeue(); 70 | IWalledMazeNode curMazeNode = GetMazeNode(maze, curBFSNode); 71 | if (maze.IsGoal(curMazeNode))//Uses the goal defined by the IWalledMaze as terminating point. 72 | { 73 | IEnumerable solvedPath = TraceSolvedPath(maze, curBFSNode); 74 | solvedResultCallback(solvedPath);//Calls the callback Action and returns. 75 | return; 76 | } 77 | foreach (IWalledMazeNode adjMazeNode in maze.GetAdjacentNodes(curMazeNode)) 78 | { 79 | //Just use the x & Y positions from the adjNode and use the internal representation to do comparision. 80 | BFSNode adjBFSNode = GetBFSNode(adjMazeNode); 81 | if (adjBFSNode.State == BFSNodeState.NotVisited) 82 | { 83 | adjBFSNode.State = BFSNodeState.Queued; 84 | adjBFSNode.Predecessor = curBFSNode; 85 | adjBFSNode.Distance = curBFSNode.Distance + 1; 86 | frontierQueue.Enqueue(adjBFSNode); 87 | } 88 | } 89 | curBFSNode.State = BFSNodeState.Visited;//In BFS this is marked by the color BLACK 90 | } 91 | 92 | solvedResultCallback(null); //if it comes this far then no solution found. 93 | } 94 | 95 | /// 96 | /// Conversion function. Converts a maze node to a internal BFS node. 97 | /// 98 | private BFSNode GetBFSNode(IWalledMazeNode mazeNode) 99 | { 100 | return _bfsNodes[mazeNode.RowPosition, mazeNode.ColPosition]; //Both BFS and Maze node have positional relationship. 101 | } 102 | 103 | /// 104 | /// Conversion function. Converts a BFS node to a maze node. 105 | /// 106 | private IWalledMazeNode GetMazeNode(IWalledMaze maze, BFSNode bfsNode) 107 | { 108 | return maze.GetNode(bfsNode.RowPosition, bfsNode.ColPosition);//Both BFS and Maze node have positional relationship. 109 | } 110 | 111 | /// 112 | /// Scans all maze's nodes and converts into an internal solver's Node . 113 | /// 114 | private void InitializeSolver(IWalledMaze maze) 115 | { 116 | _bfsNodes = new BFSNode[maze.Height, maze.Width]; 117 | IEnumerator mazeNodes = maze.GetNodes(); 118 | while (mazeNodes.MoveNext()) 119 | { 120 | IWalledMazeNode mazeNode = mazeNodes.Current; 121 | if (mazeNode.State == WalledMazeNodeState.Blocked)//Blocked cells are walls. This is internally represented as "Visited" nodes. 122 | _bfsNodes[mazeNode.RowPosition, mazeNode.ColPosition] = new BFSNode(mazeNode.RowPosition, mazeNode.ColPosition) { State = BFSNodeState.Visited, Distance = int.MaxValue }; 123 | else if (mazeNode.State == WalledMazeNodeState.Open)//Other cells are open. These nodes are represented as "Not-Visited" nodes. 124 | _bfsNodes[mazeNode.RowPosition, mazeNode.ColPosition] = new BFSNode(mazeNode.RowPosition, mazeNode.ColPosition) { State = BFSNodeState.NotVisited, Distance = int.MaxValue }; 125 | } 126 | } 127 | /// 128 | /// Traces the solved path and builds the internal BFS tree. 129 | /// 130 | private IEnumerable TraceSolvedPath(IWalledMaze maze, BFSNode endNode) 131 | { 132 | BFSNode curNode = endNode; 133 | ICollection pathTrace = new List(); 134 | while (curNode != null) 135 | { 136 | pathTrace.Add(GetMazeNode(maze, curNode)); 137 | curNode = curNode.Predecessor; 138 | } 139 | return pathTrace; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Maze/WalledMaze.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | 6 | namespace Maze 7 | { 8 | /// 9 | /// Encapsulation for all the colors defined in this maze. Should ideally be using the but faced some issues during color identification during implementation. 10 | /// 11 | public struct HexCode 12 | { 13 | public const string BLACK = "ff000000"; 14 | public const string WHITE = "ffffffff"; 15 | public const string RED = "ffff0000"; 16 | public const string BLUE = "ff0000ff"; 17 | public const string GREEN = "ff00ff00"; 18 | 19 | static public bool Contains(string color) 20 | { 21 | return (color == WHITE || color == BLACK || color == RED || color == BLUE || color == GREEN); 22 | } 23 | } 24 | 25 | /// 26 | /// Implementaion of . 27 | /// Implements all necessary information for maze model 28 | /// 29 | class WalledMaze : IWalledMaze 30 | { 31 | WalledMazeNode _source = null; 32 | WalledMazeNode _destination = null; 33 | WalledMazeNode[,] _mazeMap = null; 34 | 35 | int _width = 0; 36 | int _height = 0; 37 | 38 | //Indicates the total width of the maze. Used by the solver during its initialization. 39 | public int Width 40 | { 41 | get { return _width; } 42 | } 43 | 44 | //Indicates the total height of the maze. Used by the solver during its initialization. 45 | public int Height 46 | { 47 | get { return _height; } 48 | } 49 | 50 | //Indicates the start point of the maze 51 | public IWalledMazeNode Start 52 | { 53 | get { return _source; } 54 | } 55 | 56 | //Indicate the end point of the maze 57 | public IWalledMazeNode Finish 58 | { 59 | get { return _destination; } 60 | } 61 | 62 | //Used by a to determine if it has arrived at a destination. 63 | public bool IsGoal(IWalledMazeNode curNode) 64 | { 65 | if (curNode == null) 66 | return false; 67 | return curNode.Equals(_destination);//calls MazeNode's overridden Equals(). 68 | } 69 | 70 | public WalledMaze(string imagePath, string wallColor, string openColor, string startColor, string finishColor) 71 | { 72 | if (!File.Exists(imagePath)) 73 | throw new FileNotFoundException(string.Format("File {0} not found", imagePath), "imagePath"); 74 | if (!HexCode.Contains(wallColor) || !HexCode.Contains(openColor) || !HexCode.Contains(startColor) || !HexCode.Contains(finishColor)) 75 | throw new ArgumentException("Please check your color codes. One\\Many of them are wrong");//Color codes used by the maze should be defined in . 76 | InitializeMaze(imagePath, wallColor, openColor, startColor, finishColor); 77 | } 78 | 79 | /// 80 | /// Gets adjacents for a node. Any node can have at most 8 adjacents. 81 | /// 82 | public IEnumerable GetAdjacentNodes(IWalledMazeNode curNode) 83 | { 84 | int rowPosition = curNode.RowPosition; 85 | int colPosition = curNode.ColPosition; 86 | 87 | if (rowPosition < 0 || rowPosition >= _height || colPosition < 0 || colPosition >= _width) //if given node is out of bounds return a empty list as adjacents. 88 | return new List(0); 89 | 90 | List adjacents = new List(8); 91 | for (int i = rowPosition - 1; i <= rowPosition + 1; i++) 92 | { 93 | for (int j = colPosition - 1; j <= colPosition + 1; j++) 94 | { 95 | if (i < 0 || i >= _height || j < 0 || j >= _width || (i == rowPosition && j == colPosition))//eliminates out of bounds from being sent as adjacents. 96 | continue; 97 | adjacents.Add(_mazeMap[i, j]); 98 | } 99 | } 100 | return adjacents; 101 | } 102 | 103 | /// 104 | /// Gets a node. 105 | /// 106 | public IWalledMazeNode GetNode(int row, int col) 107 | { 108 | GuardPosition(row, col,"row\\col"); 109 | return _mazeMap[row, col]; 110 | } 111 | 112 | /// 113 | /// Gets all maze nodes. 114 | /// 115 | public IEnumerator GetNodes() 116 | { 117 | for (int i = 0; i < _height; i++) 118 | for (int j = 0; j < _width; j++) 119 | yield return _mazeMap[i, j]; 120 | } 121 | 122 | /// 123 | /// Function that solves the maze using the solver. 124 | /// 125 | public void Solve(IMazeSolver solver, Action> solvedResultCallback) 126 | { 127 | if (solver == null) 128 | throw new ArgumentNullException("Solver cannot be null", "solver"); 129 | if(solvedResultCallback == null) 130 | throw new ArgumentNullException("Please provide a callback action", "solvedResultCallback"); 131 | //calls solver's solve method. 132 | solver.Solve(this, (solvedPath) => 133 | { 134 | if (solvedPath == null) 135 | solvedResultCallback(new List(0));//return a empty path if the solver could not solve the maze. 136 | else 137 | solvedResultCallback(solvedPath); 138 | }); 139 | } 140 | 141 | /// 142 | /// Scans the image and stores each pixel information as . 143 | /// 144 | private void InitializeMaze(string imagePath, string wallColor, string openColor, string startColor, string finishColor) 145 | { 146 | if (!File.Exists(imagePath)) 147 | throw new FileNotFoundException(string.Format("{0} not found", imagePath)); 148 | 149 | Bitmap image = null; 150 | try 151 | { 152 | image = new Bitmap(imagePath); 153 | } 154 | catch (ArgumentException)//Image is not a valid bitmap(.jpg,.png etc..). Framework exception message should be appropriate 155 | { 156 | throw; 157 | } 158 | 159 | _mazeMap = new WalledMazeNode[image.Height, image.Width]; 160 | _height = image.Height; 161 | _width = image.Width; 162 | 163 | for (int i = 0; i < _height; i++) 164 | { 165 | for (int j = 0; j < _width; j++) 166 | { 167 | Color c = image.GetPixel(j, i);//Could also use lockbits() over GetPixel() for faster processing. 168 | 169 | if (c.Name == wallColor)//Black Color is a wall. Mark them as blocked 170 | { 171 | _mazeMap[i, j] = new WalledMazeNode(i, j) { State = WalledMazeNodeState.Blocked }; 172 | continue; 173 | } 174 | else if (c.Name == startColor) 175 | { 176 | _mazeMap[i, j] = new WalledMazeNode(i, j) { State = WalledMazeNodeState.Open }; 177 | if (_source == null)//pick the first red color as the source node. 178 | _source = _mazeMap[i, j]; 179 | } 180 | else if (c.Name == finishColor) 181 | { 182 | _mazeMap[i, j] = new WalledMazeNode(i, j) { State = WalledMazeNodeState.Open }; 183 | if (_destination == null)//pick the first blue color as the destination node. 184 | _destination = _mazeMap[i, j]; 185 | } 186 | else if (c.Name == openColor)//Open Color is mostly White. 187 | { 188 | _mazeMap[i, j] = new WalledMazeNode(i, j) { State = WalledMazeNodeState.Open }; 189 | } 190 | else 191 | { 192 | //Invalid color. 193 | //Eventhough anomalous colors could be defined as OPEN nodes, such implementations may confuse the solver when BLACK walls are defined with a hue. 194 | //I restricted such usage by throwing exceptions for unknown colors. Better safe than sorry. :) 195 | throw new Exception(string.Format("I found a color {0} that this maze was not configured to handle. Please use colors that you originally specified.", c.Name)); 196 | } 197 | } 198 | } 199 | } 200 | 201 | # region GuardMethods 202 | public void GuardPosition(IWalledMazeNode node, string exceptionParamName) 203 | { 204 | int rowPosition = node.RowPosition; 205 | int colPosition = node.ColPosition; 206 | 207 | GuardPosition(rowPosition, colPosition, exceptionParamName); 208 | } 209 | 210 | private void GuardPosition(int rowPosition, int colPosition, string exceptionParamName) 211 | { 212 | if (rowPosition < 0 || rowPosition >= _height || colPosition < 0 || colPosition >= _width) 213 | throw new ArgumentException("The supplied node is out of bounds", exceptionParamName); 214 | } 215 | # endregion 216 | } 217 | } 218 | --------------------------------------------------------------------------------