├── 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 |
--------------------------------------------------------------------------------