class MazeSolver { public string ToString(int fromx, int fromy, int tox, int toy) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int y = 0; y < this.GetMaze.GetUpperBound(1); y++) { for (int x = 0; x < this.GetMaze.GetUpperBound(0); x++) { if (x == fromx && y == fromy) { sb.Append("S"); } else if (x == tox && y == toy) { sb.Append("E"); } else { sb.Append(this[x, y]); } } sb.Append(Environment.NewLine); } return sb.ToString(); } public string PathToString(int[,] solved) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); for (int y = 0; y < this.GetMaze.GetUpperBound(1); y++) { for (int x = 0; x < this.GetMaze.GetUpperBound(0); x++) { if (solved[x,y]==100) { sb.Append("+"); } else { sb.Append(solved[x, y]); } } sb.Append(Environment.NewLine); } return sb.ToString(); } /// /// Class attributes/members /// int[,] m_iMaze; int m_iRows; int m_iCols; int iPath=100; bool diagonal=false; 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_iCols; } } 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; } } public bool AllowDiagonals { get { return diagonal; } set { diagonal=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) // trigger event 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 iStart, int iStop) { 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 && iRightDown/iCols==iCurrent/iCols+1 ) //if bottom-right node exists if ( GetNodeContents(m_iMaze, iRightDown)==empty ) //if this node is open(a path exists) if (GetNodeContents(iMazeStatus, iRightDown) == (int)Status.Ready) //if this node is ready { Queue[iRear]=iRightDown; //add to Q Origin[iRear]=iCurrent; ChangeNodeContents(iMazeStatus, iRightDown, (int)Status.Waiting); //change status to waiting iRear++; } iRightUp=iCurrent-iCols+1; if (iRightUp>=0 && iRightUp=0 && iLeftDown/iCols==iCurrent/iCols+1) //if bottom-left node exists if ( GetNodeContents(m_iMaze, iLeftDown)==empty ) //if this node is open(a path exists) if (GetNodeContents(iMazeStatus, iLeftDown) == (int)Status.Ready) //if this node is ready { Queue[iRear]=iLeftDown; //add to Q Origin[iRear]=iCurrent; ChangeNodeContents(iMazeStatus, iLeftDown, (int)Status.Waiting); //change status to waiting iRear++; } iLeftUp=iCurrent-iCols-1; if (iLeftUp>=0 && iLeftUp=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; } }