#ifndef _MAZE_H #define _MAZE_H #include #include #include "Array.h" #include "Hash.h" #include "Graph.h" #include "test_utilities.h" ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // DOCUMENTATION OF MAZE CLASS: // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // A Maze object contains two parts: // the textual representation of the maze, // the representation as a Graph. // // The Graph has a vertex for every non-blank character in the textual representation. // Two vertices have an edge if their characters are adjacent (left,right,up, or down) // in the textual representation. // // To build a maze, read in the textual representation from a stream. // The Maze class will automatically build the corresponding Graph for you. // Access the graph and the textual representation using the member functions of Maze: // // Maze() -- constructor // // print textual representation of maze to ostream: // friend std::ostream & operator<< (std::ostream &, const Maze &); // // read textual representation of maze from istream, and build the graph: // friend std::istream & operator>> (std::istream &, Maze &); // // TEXT FUNCTIONS: // const Array > & text() const -- Return the text representation of the maze. // int text_height() const -- Return the number of rows in the maze. // int text_width(int i) const -- Return the number of columns in the ith row. // bool text_exists_at(int i, int j) const -- Is there an i'th row of text and does it have a j'th column? // char text_at(int i, int j) const -- Return the character in the j'th column of the i'th row of the text. // void set_text_at(int i, int j, char c) -- change that character // GRAPH FUNCTIONS: // const Graph & graph() const -- Return the graph associated with the maze. // int get_start_vertex() const -- Return the id of the start vertex. // int get_end_vertex() const -- Return the id of the end vertex. // int vertex_exists_at(int i, int j) const -- is there a vertex associated with j'th col of i'th row of text? // int vertex_of_loc(int i, int j) const -- if there is, return the id of that vertex // int vertex_at(int i, int j) const -- same as vertex_of_loc // Array loc_of_vertex(int v) const -- return Array of size 2, giving row and col of vertex ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // IMPLEMENTATION OF MAZE CLASS: // uncomment the first line if you want debugging, // uncomment the second one if you don't: //#define DEBUG(x) x #define DEBUG(x) class Maze { Graph G; Array > _text; // _text[i][j] is j'th char in i'th row of textual representation of maze HashTable > loc_to_vertex; // loc_to_vertex[i][j] is vertex id of vertex HashTable > vertex_to_loc; // vertex_to_loc[v][0], vertex_to_loc[v][1] give location of v int start_vertex, end_vertex; // id's of start and end vertices ("S" and "E" in text) void assoc_vertex(int v, int i, int j) // utility function to associate a vertex v with a location (i,j) { loc_to_vertex[i][j] = v; vertex_to_loc[v][0] = i; vertex_to_loc[v][1] = j; } public: Maze() : start_vertex(-1), end_vertex(-1) {} const Graph & graph() const { return G; } const Array > & text() const { return _text; } int text_height() const { return _text.size(); } int text_width(int i) const { if (0 <= i && i < _text.size()) return _text[i].size(); else die("Maze::text_width called for non-existent row"); } // does the textual representation have any character at location (i,j) bool text_exists_at(int i, int j) const { return 0 <= i && i < _text.size() && 0 <= j && j < _text[i].size(); } // returns the character at position i, j in the textual representation char text_at(int i, int j) const { if (text_exists_at(i,j)) return _text[i][j]; else die("Maze::text_at called for location without text"); } // returns the character at position i, j in the textual representation void set_text_at(int i, int j, char c) { if ((text_at(i,j) == ' ') != (c == ' ')) std::cerr << "Maze::set_text_at: warning, changing status of text" << std::endl; _text[i][j] = c; } // returns id of START vertex in the graph int get_start_vertex() const { if (start_vertex != -1) return start_vertex; else die("Maze:get_start_vertex called when no start vertex defined"); } // returns id of END vertex in the graph int get_end_vertex() const { if (end_vertex != -1) return end_vertex; else die("Maze:get_end_vertex called when no end vertex defined"); } // return whether there is a vertex at position i,j int vertex_exists_at(int i, int j) const { return (loc_to_vertex.exists(i) && loc_to_vertex[i].exists(j)); } // return id of a vertex at position i,j // die if no such vertex int vertex_of_loc(int i, int j) const { if (vertex_exists_at(i,j)) return loc_to_vertex[i][j]; else die("Maze::vertex_of_loc given location without vertex"); } int vertex_at(int i, int j) const { return vertex_of_loc(i,j); } // returns coordinates i,j represented by a vertex // (returns Array A with A[0]=i, A[1]=j) Array loc_of_vertex(int v) const { if (vertex_to_loc.exists(v)) return vertex_to_loc[v]; else die("Maze::loc_of_vertex called with non-existent vertex"); } // print textual representation of maze friend std::ostream & operator<< (std::ostream &, const Maze &); // read maze in from textual representation friend std::istream & operator>> (std::istream &, Maze &); }; // input operator for Maze // // reads until eof // std::istream & operator>> (std::istream &in, Maze &m) { int i, j; char ch; DEBUG(std::cerr << "reading maze";); m.start_vertex = m.end_vertex = -1; m.G.clear(); m._text.clear(); m.loc_to_vertex.clear(); m.vertex_to_loc.clear(); i = j = 0; while(!in.eof()) { ch = ' '; in.get(ch); if ((ch == '\n') or (ch == '\r')) { ++i; j = 0; DEBUG(std::cerr << ".";); } else if (ch == '\t') { std::cerr << "Maze::operator>>: warning, tabs in input" << std::endl; do { m._text[i][j++] = ' '; } while (j % 8 != 0); } else if (! isprint(ch)) { std::cerr << "Maze::operator>>: warning, ignoring unprintable character in input" << std::endl; } else { m._text[i][j] = ch; if (ch != ' ') { int v = m.G.add_vertex(); m.assoc_vertex(v, i, j); if (m.vertex_exists_at(i-1,j)) m.G.add_edge(v, m.vertex_at(i-1,j)); if (m.vertex_exists_at(i,j-1)) m.G.add_edge(v, m.vertex_at(i,j-1)); if (ch == 'S') if (m.start_vertex != -1) std::cerr << "Maze::operator>>: warning, ignoring redundant start vertex" << std::endl; else m.start_vertex = v; if (ch == 'E') if (m.end_vertex != -1) std::cerr << "Maze::operator>>: warning, ignoring redundant end vertex" << std::endl; else m.end_vertex = v; } ++j; } } DEBUG(std::cerr << std::endl;); if (m.start_vertex == -1) std::cerr << "Maze::operator>>: warning, no start point defined" << std::endl; if (m.end_vertex == -1) std::cerr << "Maze::operator>>: warning, no end point defined" << std::endl; return in; } std::ostream & operator<<(std::ostream & out, const Maze & M) { int i,j; for (i = 0; i < M.text_height(); i++) { for (j = 0; j < M.text_width(i); j++) out << M.text_at(i,j); out << std::endl; } return out; } #endif