using System; namespace Mehroz { /// /// Summary description for MazeSolver. /// Class name: MazeSolver /// Developed by: Syed Mehroz Alam /// Email: smehrozalam@yahoo.com /// URL: Programming Home "http://www.geocities.com/smehrozalam/" /// /// Constructors: /// ( int[,] ): takes 2D integer array /// ( int Rows, int Cols ) initializes the dimensions, indexers may be used /// to set individual elements' values /// /// Properties: /// Rows: returns the no. of rows in the current maze /// Cols: returns the no. of columns in the current maze /// Maze: returns the current maze as a 2D array /// PathCharacter: to get/set the value of path tracing character /// /// Indexers: /// [i,j] = used to set/get elements of maze /// /// Public Methods (Description is given with respective methods' definitions) /// int[,] FindPath(int iFromY, int iFromX, int iToY, int iToX) /// /// Private Methods /// void GetNodeContents(int[,] iMaze, int iNodeNo) /// void ChangeNodeContents(int[,] iMaze, int iNodeNo, int iNewValue) /// int[,] Search(int iBeginningNode, int iEndingNode) /// /// delegate void MazeChangedHandler(int iChanged, int jChanged); class MazeSolver { /// /// Class attributes/members /// int[,] m_iMaze; int m_iRows; int m_iCols; int iPath=100; public event MazeChangedHandler OnMazeChangedEvent; /// /// Constructor 1: takes a 2D integer array /// public MazeSolver(int[,] iMaze) { m_iMaze=iMaze; m_iRows=iMaze.GetLength(0); m_iCols=iMaze.GetLength(1); } /// /// Constructor 2: initializes the dimensions of maze, /// later, indexers may be used to set individual elements' values /// public MazeSolver(int iRows, int iCols) { m_iMaze=new int[iRows, iCols]; m_iRows=iRows; m_iCols=iCols; } /// /// Properites: /// public int Rows { get { return m_iRows; } } public int Cols { get { return m_iRows; } } public int[,] GetMaze { get { return m_iMaze; } } public int PathCharacter { get { return iPath; } set { if (value==0) throw new Exception("Invalid path character specified"); else iPath=value; } } /// /// Indexer /// public int this[int iRow, int iCol] { get { return m_iMaze[iRow,iCol]; } set { m_iMaze[iRow,iCol]=value; if (this.OnMazeChangedEvent!=null) this.OnMazeChangedEvent(iRow, iCol); } } /// /// The function is used to get the contents of a given node in a given maze, /// specified by its node no. /// private int GetNodeContents(int[,] iMaze, int iNodeNo) { int iCols=iMaze.GetLength(1); return iMaze[iNodeNo/iCols,iNodeNo-iNodeNo/iCols*iCols]; } /// /// The function is used to change the contents of a given node in a given maze, /// specified by its node no. /// private void ChangeNodeContents(int[,] iMaze, int iNodeNo, int iNewValue) { int iCols=iMaze.GetLength(1); iMaze[iNodeNo/iCols,iNodeNo-iNodeNo/iCols*iCols]=iNewValue; } /// /// This public function finds the shortest path between two points /// in the maze and return the solution as an array with the path traced /// by "iPath" (can be changed using property "PathCharacter") /// if no path exists, the function returns null /// public int[,] FindPath(int iFromY, int iFromX, int iToY, int iToX) { int iBeginningNode = iFromY*this.Cols + iFromX; int iEndingNode = iToY*this.Cols + iToX; return ( Search(iBeginningNode, iEndingNode) ) ; } /// /// Internal function for that finds the shortest path using a technique /// similar to breadth-first search. /// It assigns a node no. to each node(2D array element) and applies the algorithm /// private enum Status { Ready, Waiting, Processed } private int[,] Search(int iBeginningNode, int iEndingNode) { int iStart=iEndingNode, iStop=iBeginningNode; // the function displays the reverse path const int empty=0; int iRows=m_iRows; int iCols=m_iCols; int iMax=iRows*iCols; int[] Queue=new int[iMax]; int[] Origin=new int[iMax]; int iFront=0, iRear=0; //check if starting and ending points are valid (open) if ( GetNodeContents(m_iMaze, iStart)!=empty || GetNodeContents(m_iMaze, iStop)!=empty ) { return null; } //create dummy array for storing status int[,] iMazeStatus=new int[iRows,iCols]; //initially all nodes are ready for(int i=0;i=0 && iLeft/iCols==iCurrent/iCols) //if left node exists if ( GetNodeContents(m_iMaze, iLeft)==empty ) //if left node is open(a path exists) if (GetNodeContents(iMazeStatus, iLeft) == (int)Status.Ready) //if left node is ready { Queue[iRear]=iLeft; //add to Q Origin[iRear]=iCurrent; ChangeNodeContents(iMazeStatus, iLeft, (int)Status.Waiting); //change status to waiting iRear++; } iRight=iCurrent+1; if (iRight=0 ) //if top node exists if ( GetNodeContents(m_iMaze, iTop)==empty ) //if top node is open(a path exists) if (GetNodeContents(iMazeStatus, iTop) == (int)Status.Ready) //if top node is ready { Queue[iRear]=iTop; //add to Q Origin[iRear]=iCurrent; ChangeNodeContents(iMazeStatus, iTop, (int)Status.Waiting ); //change status to waiting iRear++; } iDown=iCurrent+iCols; if (iDown=0; i--) { if (Queue[i]==iCurrent) { iCurrent=Origin[i]; if (iCurrent==-1) // maze is solved return ( iMazeSolved ); ChangeNodeContents(iMazeSolved, iCurrent, iPath); } } //no path exists return null; } } }