├── .gitignore ├── CMakeLists.txt ├── README.md ├── config.h.in ├── scrpt ├── README.MD ├── buildAllMazes └── makefile └── src ├── main.cpp └── mazesolve ├── astar.hpp ├── disjointSets.cpp ├── disjointSets.hpp ├── mazeGen.cpp └── mazeGen.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX user preferences 2 | .DS_Store 3 | 4 | # Internal deps 5 | /build 6 | 7 | # Byproducts of running cmake and make 8 | CMakeCache.txt 9 | CMakeFiles/ 10 | Makefile 11 | MazeSolver 12 | cmake_install.cmake 13 | config.h 14 | mazeDataFile.txt 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #cmake version 2 | cmake_minimum_required(VERSION 2.8) 3 | project(MazeSolver) 4 | 5 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 6 | message(STATUS "Setting build type to 'Release as none was specified.") 7 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 8 | #set possible values of built type for cmake-gui 9 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Release" "Debug" 10 | "MinSizeRel" "RelWithDebInfo") 11 | endif() 12 | 13 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 14 | set(CMAKE_CXX_FLAGS "-std=c++1y") 15 | set(EXECUTABLE_NAME "MazeSolver") 16 | set(VERSION_MAJOR 1) 17 | set(VERSION_MINOR 1) 18 | configure_file( 19 | "${PROJECT_SOURCE_DIR}/config.h.in" 20 | "${PROJECT_BINARY_DIR}/config.h" 21 | ) 22 | 23 | set(HEADERS 24 | src/mazesolve/disjointSets.hpp 25 | src/mazesolve/mazeGen.hpp 26 | src/mazesolve/astar.hpp 27 | ) 28 | 29 | set(SOURCES 30 | src/mazesolve/disjointSets.cpp 31 | src/mazesolve/mazeGen.cpp 32 | src/main.cpp 33 | ) 34 | 35 | include_directories("${PROJECT_BINARY_DIR}") 36 | include_directories("${PROJECT_SOURCE_DIR}/src/mazesolve") 37 | 38 | add_executable(${EXECUTABLE_NAME} ${SOURCES} ${HEADERS}) 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MazeSolver 2 | ========== 3 | 4 | An application designed to generate random **solvable** mazes of *any* size, and solve them! The program uses an implementation of the A* algorithm to solve these mazes. Clone the repo and give it a try yourself! 5 | 6 | Unsolved | Solved 7 | :-------:|:------: 8 | ![img](http://i.imgur.com/AxVHDz5.png) | ![img](http://i.imgur.com/9ydpJcD.png) 9 | 10 | Your only limitation to the size of the maze is the sky itself (well, and the size of your console and monitor I guess). 11 | 12 | Building 13 | ---------------------------------------------------- 14 | There are no dependencies for this repo, *yay!* All you need to worry about is building the project. 15 | 16 | Windows 17 | --------- 18 | 1. Clone the master MazeSolver repository in a directory of your choice. 19 | 20 | 2. Create a build folder. This project requires and out-of-tree build. This means you ~will be unable to~ **should not** run CMake in the MazeGen folder. 21 | 22 | 3. Open up the CMake GUI. in the input box labeled "Where is the source code:", enter the full path to the source folder. In the input box labeled "Where to build the binaries" , enter the full path to the folder you created in step 2. 23 | 24 | 4. Press the "Configure" button. A window will pop up asking you which compiler to use. Selecter any version your heart desires but this tutorial will center around VS, so we're going to select to build a VS project. 25 | 26 | 5. hit "Generate". 27 | 28 | 6. Open up the build folder, double click the Solution file and build the project. All done! 29 | 30 | Linux 31 | --------- 32 | 1. Clone the repository: `git clone https://github.com/Syntaf/MazeSolver` 33 | 34 | 2. Navigate to the project: `cd MazeSolver` 35 | 36 | 3. Build internal depedencies with CMake: `cmake .` (or path to directory containing CMakeLists.txt) 37 | 38 | 4. Build executable: `make` 39 | 40 | Usage 41 | ------ 42 | `./MazeSolver -p` runs the program with print functionality, or just run it with `./MazeSolver` . Super simple! 43 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #define VERSION_MAJOR @VERSION_MAJOR@ 2 | #define VERSION_MINOR @VERSION_MINOR@ -------------------------------------------------------------------------------- /scrpt/README.MD: -------------------------------------------------------------------------------- 1 | This folder containts scripts that may be useful (when running on a linux 2 | distribution). These scripts may become outdated or arbitrary as the application 3 | progresses. -------------------------------------------------------------------------------- /scrpt/buildAllMazes: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for fname in mazeFile* 3 | do 4 | base=${fname%.txt} 5 | base=${base#mazeFile} 6 | echo Building maze${base}.jpg 7 | ./maze_ppm 5 < "$fname" | convert - "maze${base}.jpg" 8 | done 9 | -------------------------------------------------------------------------------- /scrpt/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | g++ main.cpp mazeGen.cpp disjointSets.cpp 3 | 4 | clean: 5 | rm a.out 6 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // Main program for testing 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "config.h" 11 | #include "mazeGen.hpp" 12 | 13 | using namespace std; 14 | 15 | bool parseInput(std::string input, int& row, int& col); 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | // always writes to "mazeDataFile.txt" 20 | const string fName = "mazeDataFile.txt"; 21 | bool doPrint = false; 22 | 23 | cout << "Maze Generator v" << VERSION_MAJOR << "." << VERSION_MINOR << endl; 24 | cout << endl; 25 | 26 | // shortcut to enable printing 27 | if (argc == 2){ 28 | if (string(argv[1]) == "-p"){ 29 | doPrint = true; 30 | std::cout << "Console print enabled\n"; 31 | }else{ 32 | // ignore the line feed read in by CIN so our first loop doesn't read a \n and exit right away IF user didnt pass in -p flag 33 | std::cin.ignore(256, '\n'); 34 | } 35 | }else{ 36 | // give the user another chance to enable console print 37 | char ans; 38 | std::cout << "Console print is disabled by default( -p cmd arg ), Enable?(Y/N): "; 39 | std::cin >> ans; 40 | if(toupper(ans) == 'Y'){ 41 | doPrint = true; 42 | std::cout << "Console print enabled\n"; 43 | }else if(toupper(ans) == 'N'){ 44 | std::cout << "Console print disabled\n"; 45 | }else 46 | std::cout << "Unrecognized input... disabling console print\n"; 47 | // ignore the line feed read in by CIN so our first loop doesn't read a \n and exit right away IF user didnt pass in -p flag 48 | std::cin.ignore(256, '\n'); 49 | } 50 | std::cout << "\n\n------------------------------------------------------------------\n"; 51 | 52 | int rows, cols; 53 | std::string input; 54 | // loop indefintely 55 | while(true){ 56 | // parse user input 57 | std::cout << "Enter the number of ROWS and COLS(return to exit): "; 58 | std::getline(cin, input); 59 | if(input == "") 60 | break; 61 | if(!parseInput(input, rows, cols)) { 62 | std::cout << "unrecognized input... exiting\n"; 63 | break; 64 | } 65 | if(rows < 5 || cols < 5){ 66 | while(true) { 67 | rows < 5 ? std::cout << "Rows cannot be less than 5" : std::cout << "Cols cannot be less than 5"; 68 | std::cout << ". Renter ROWS followed by the number of COLS(e.g. 5 15): "; 69 | std::getline(cin, input); 70 | if(!parseInput(input, rows, cols)) 71 | continue; 72 | if(rows >= 5 && cols >= 5) 73 | break; 74 | } 75 | } 76 | // create maze 77 | std::cout << "\ncreating maze...\n"; 78 | mazeGen obj(rows, cols); 79 | std::cout << "done, generating maze...\n"; 80 | // generate solvable maze 81 | obj.generate(); 82 | // if console print enabled print blank maze 83 | if(doPrint){ 84 | std::cout << std::endl; 85 | obj.printMazeText(); 86 | std::cout << std::endl; 87 | } 88 | // create file 89 | std::cout << "done, creating data file...\n"; 90 | obj.printMazeData(fName); 91 | // read file and print solution 92 | std::cout << "done, printing solution...\n\n"; 93 | obj.findPath(); 94 | std::cout << std::endl; 95 | } 96 | return 0; 97 | } 98 | 99 | bool parseInput(std::string input, int& row, int& col) 100 | { 101 | std::stringstream ss(input); 102 | ss >> row >> col; 103 | return !ss.fail(); 104 | } 105 | -------------------------------------------------------------------------------- /src/mazesolve/astar.hpp: -------------------------------------------------------------------------------- 1 | //astar.hpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // solver is only allowed to move up,down,left,right 11 | const int dir=4; 12 | static int dx[dir] = {1, 0, -1, 0}; 13 | static int dy[dir] = {0, 1, 0, -1}; 14 | 15 | class node 16 | { 17 | public: 18 | 19 | node(int xp, int yp, int d, int p): 20 | xPos(xp), 21 | yPos(yp), 22 | level(d), 23 | priority(p) 24 | {} 25 | 26 | // get functions 27 | int getxPos() const {return xPos;} 28 | int getyPos() const {return yPos;} 29 | int getLevel() const {return level;} 30 | int getPriority() const {return priority;} 31 | 32 | void updatePriority(const int &xDest, const int &yDest); 33 | void nextLevel(const int &i); //give better priority to going straight instead of diagonal 34 | const int &estimate(const int &xDest, const int &yDest); //estimate function for the remaining distance to the goal 35 | 36 | private: 37 | int xPos; //x position 38 | int yPos; //y position 39 | int level; //total distance already traveled to reach node 40 | int priority; //priority = level + remaining distance estimate 41 | 42 | }; 43 | 44 | void node::updatePriority(const int &xDest, const int &yDest) 45 | { 46 | priority = level + estimate(xDest, yDest) * 10; 47 | } 48 | 49 | void node::nextLevel(const int &i) 50 | { 51 | level += i % 2 == 0 ? 10 : 14; 52 | } 53 | 54 | const int& node::estimate(const int& xDest, const int& yDest) 55 | { 56 | static int xd, yd, d; 57 | xd = xDest - xPos; 58 | yd = yDest - yPos; 59 | 60 | d = static_cast(sqrt(xd * xd + yd * yd)); 61 | 62 | return d; 63 | } 64 | 65 | //for determining priority of queue 66 | bool operator<(const node &a, const node &b) 67 | { 68 | return a.getPriority() > b.getPriority(); 69 | } 70 | namespace AStar { 71 | //A-star algorithm 72 | //returns a string of direction digits 73 | std::string findPath( const int &xStart, const int &yStart, 74 | const int xFinish, const int &yFinish, 75 | std::vector< std::vector> map) 76 | { 77 | //shortcut for size of array entered 78 | const int n = map.size(); 79 | const int m = map[0].size(); 80 | //arrays used for A-star algorithm 81 | std::vector > closed_nodes_map(n, std::vector(m)); 82 | std::vector > open_nodes_map(n, std::vector(m)); 83 | std::vector > dir_map(n, std::vector(m)); 84 | 85 | //other variables needed during algorithm 86 | std::priority_queue pq[2]; 87 | int pqi=0; 88 | node* n0; 89 | node* m0; 90 | int i, j, x, y, xdx, ydy; 91 | char c; 92 | 93 | //set all node maps to zero 94 | for(y = 0; y < m; y++) { 95 | for(x = 0; x < n; x++) { 96 | dir_map[x][y] = 0; 97 | closed_nodes_map[x][y] = 0; 98 | open_nodes_map[x][y] = 0; 99 | } 100 | } 101 | x = 0; y = 0; 102 | //create start node and push into list of open nodes 103 | n0 = new node(xStart, yStart, 0, 0); 104 | n0->updatePriority(xFinish, yFinish); 105 | pq[pqi].push(*n0); 106 | open_nodes_map[x][y] = n0->getPriority(); //mark on map 107 | //run through priority queue 108 | while(!pq[pqi].empty()) { 109 | //get the current node w/ highest priority 110 | // from list of open nodes 111 | n0 = new node(pq[pqi].top().getxPos(), pq[pqi].top().getyPos(), 112 | pq[pqi].top().getLevel(), pq[pqi].top().getPriority()); 113 | 114 | //shortcuts 115 | x = n0->getxPos(); 116 | y = n0->getyPos(); 117 | 118 | //remove node from open list 119 | pq[pqi].pop(); 120 | open_nodes_map[x][y] = 0; 121 | //mark node as closed 122 | closed_nodes_map[x][y] = 1; 123 | 124 | //quit searching when the goal state is reached 125 | if(x == xFinish && y == yFinish) { 126 | //generate path from finish to start by 127 | //following the directions 128 | std::string path=""; 129 | while(!(x == xStart && y == yStart)) { 130 | j = dir_map[x][y]; 131 | c = '0' + (j + dir / 2) % dir; 132 | path = c + path; 133 | x += dx[j]; 134 | y += dy[j]; 135 | } 136 | //garbage collection 137 | delete n0; 138 | //empty lower leftover nodes 139 | while(!pq[pqi].empty()) 140 | pq[pqi].pop(); 141 | return path; 142 | } 143 | 144 | //generate modes in all possible directions 145 | for(i = 0; i < dir; i++) { 146 | //more shortcuts 147 | xdx = x + dx[i]; 148 | ydy = y + dy[i]; 149 | 150 | 151 | if(!(xdx < 0 || xdx > n-1 || ydy < 0 || ydy > m-1 || 152 | map[xdx][ydy] == 1 || closed_nodes_map[xdx][ydy] == 1)) { 153 | //create child node 154 | m0 = new node( xdx, ydy, n0->getLevel(), 155 | n0->getPriority()); 156 | m0->nextLevel(i); 157 | m0->updatePriority(xFinish, yFinish); 158 | 159 | //if not in open list, add into that 160 | if(open_nodes_map[xdx][ydy] == 0) { 161 | open_nodes_map[xdx][ydy] = m0->getPriority(); 162 | pq[pqi].push(*m0); 163 | //mark its parent node direction 164 | dir_map[xdx][ydy]=(i + dir / 2) % dir; 165 | }else if(open_nodes_map[xdx][ydy] > m0->getPriority()) { 166 | //update node priority 167 | open_nodes_map[xdx][ydy] = m0->getPriority(); 168 | //update parent direction info 169 | dir_map[xdx][ydy] = (i + dir / 2) % dir; 170 | 171 | //replace the node by emplacing one pq 172 | //to the other one except the node to be 173 | //replaced will be ignored and the new node 174 | //will be pushed instead 175 | while(!(pq[pqi].top().getxPos() == xdx && 176 | pq[pqi].top().getyPos() == ydy)) { 177 | pq[1-pqi].push(pq[pqi].top()); 178 | pq[pqi].pop(); 179 | } 180 | //remove wanted node 181 | pq[pqi].pop(); 182 | 183 | //empty the larger size pq to the smaller one 184 | if(pq[pqi].size() > pq[1- pqi].size()) 185 | pqi = 1 - pqi; 186 | while(!pq[pqi].empty()) { 187 | pq[1-pqi].push(pq[pqi].top()); 188 | pq[pqi].pop(); 189 | } 190 | pqi=1-pqi; 191 | //add better node instead 192 | pq[pqi].push(*m0); 193 | } 194 | else 195 | delete m0; 196 | } 197 | } 198 | delete n0; 199 | } 200 | return ""; 201 | } 202 | 203 | std::vector > makeReadyMap(const std::vector >& map) 204 | { 205 | // turn map of characters into map of 0's (open) and 1's (closed) 206 | std::vector > resmap(map.size()); 207 | for(std::size_t r = 0; r < map.size(); r++) { 208 | for(std::size_t c = 0; c < map[0].size(); c++) { 209 | if(map[r][c] != ' ') { 210 | resmap[r].push_back(1); 211 | }else{ 212 | resmap[r].push_back(0); 213 | } 214 | } 215 | } 216 | 217 | return resmap; 218 | } 219 | 220 | } 221 | 222 | 223 | -------------------------------------------------------------------------------- /src/mazesolve/disjointSets.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "disjointSets.hpp" 4 | 5 | disjointSets::disjointSets(int param): 6 | setSize(param) 7 | { 8 | //dont allow sets smaller than 10 9 | if(setSize < 10) 10 | setSize = 10; 11 | ranks = new int[setSize]; 12 | links = new int[setSize]; 13 | for(int i = 0; i < setSize; i++) { 14 | links[i] = -1; 15 | ranks[i] = 1; 16 | } 17 | } 18 | 19 | disjointSets::~disjointSets() 20 | { 21 | //free memory 22 | if(links != NULL) 23 | delete [] links; 24 | if(ranks != NULL) 25 | delete [] ranks; 26 | } 27 | 28 | int disjointSets::entries() const 29 | { 30 | //return amount of entires currently in data 31 | //structure, uses a simple loop to count 32 | int cnt = 0; 33 | for(int i = 0; i < setSize; i++) { 34 | if(links[i] != -1) 35 | cnt++; 36 | } 37 | return cnt; 38 | } 39 | 40 | bool disjointSets::isEmpty() const 41 | { 42 | //checks if anything is inside the 43 | //data structure 44 | for(int i = 0; i < setSize; i++) 45 | if(links[i] != -1) 46 | return false; 47 | return true; 48 | } 49 | 50 | void disjointSets::printSets() const 51 | { 52 | //format and print the set to console 53 | std::cout << std::right; 54 | std::cout << "index: "; 55 | for(int i = 0; i < setSize; i++) { 56 | std::cout << std::setw(3) << i; 57 | } 58 | std::cout << "\nlinks: "; 59 | for(int i = 0; i < setSize; i++) { 60 | std::cout << std::setw(3) << links[i]; 61 | } 62 | std::cout << "\nranks: "; 63 | for(int i = 0; i < setSize; i++) { 64 | std::cout << std::setw(3) << ranks[i]; 65 | } 66 | std::cout << std::endl; 67 | } 68 | 69 | int disjointSets::setUnion(int s1, int s2) 70 | { 71 | //join two sets together, using ranks 72 | int p, c; 73 | if(links[s1] != -1 || links[s2] != -1) { 74 | std::cout << "Union must be called on a set\n"; 75 | return -100; 76 | } 77 | if(ranks[s1] > ranks[s2]) { 78 | p = s1; 79 | c = s2; 80 | } else { 81 | p = s2; 82 | c = s1; 83 | } 84 | links[c] = p; 85 | if(ranks[s1] == ranks[s2]) 86 | ranks[p]++; 87 | return p; 88 | } 89 | 90 | int disjointSets::setFind(int p) 91 | { 92 | //recursivly find p, return root 93 | if(links[p] == -1) 94 | return p; 95 | links[p] = setFind(links[p]); 96 | return links[p]; 97 | } 98 | -------------------------------------------------------------------------------- /src/mazesolve/disjointSets.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DISJOINTSETS_H_ 2 | #define DISJOINTSETS_H_ 3 | 4 | class disjointSets { 5 | public: 6 | // Constructor ensures set size is no less than 10, and if less than 10 it will 7 | // Internally set it to 10 anyways. Also allocates space for the ranks and 8 | // links arrays as well as initializing them to their respective start values 9 | disjointSets(int=10); 10 | 11 | // Free memory from links and ranks 12 | ~disjointSets(); 13 | 14 | // Function to sum up the total count of links that are not equal to the value 15 | // Initialized to them at start up. Returns count 16 | int entries() const; 17 | 18 | // Tests whether any links have been made, if none made returns true 19 | bool isEmpty() const; 20 | 21 | // Display information on disjoint sets 22 | void printSets() const; 23 | 24 | // Joins two sets together using ranks, must ensure that the union be called on 25 | // two sets. If so it will then join the two sets 26 | int setUnion(int, int); 27 | 28 | // Finds the head node of the set passed 29 | int setFind(int); 30 | private: 31 | int setSize; // size of sets 32 | int *links; // set links 33 | int *ranks; // set ranks 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/mazesolve/mazeGen.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "astar.hpp" 7 | #include "mazeGen.hpp" 8 | #include "disjointSets.hpp" 9 | 10 | bool comp (int *i, int *j) { return i[0] < j[0]; } 11 | 12 | mazeGen::mazeGen(int t_rows, int t_cols): 13 | rows(t_rows), 14 | cols(t_cols) 15 | { 16 | if(rows < 5) 17 | rows = 5; 18 | if(cols < 5) 19 | cols = 5; 20 | 21 | //the amount of walls needed 22 | int wallsCount = rows * (cols-1) + cols * (rows-1); 23 | //create class variable arrSize to save calculation time 24 | arrSize = wallsCount; 25 | int horiz = rows * (cols-1); 26 | 27 | //initialize each element of walls with an array (x,y) 28 | walls = new int*[wallsCount]; 29 | for(int i = 0; i < wallsCount; i++){ 30 | walls[i] = new int[2]; 31 | } 32 | 33 | //place all horizonally connected walls into the array, 34 | //making sure edges do not connect with other edges on a 35 | //new row 36 | for(int r = 0; r < rows; r++){ 37 | for(int c = 0; c < cols-1; c++){ 38 | int s = r > 0 ? -1*r : 0; 39 | walls[r*cols+c+s][0] = r*cols+c; 40 | walls[r*cols+c+s][1] = r*cols+c+1; 41 | } 42 | } 43 | //place all vertically connected walls into the array, 44 | //make sure edges do not connect with other edges on a 45 | //new column 46 | for(int c = 0; c < cols; c++){ 47 | for(int r = 0; r < rows-1; r++){ 48 | int s = c > 0 ? -1*c : 0; 49 | walls[horiz+c*rows+r+s][0] = c+r*cols; 50 | walls[horiz+c*rows+r+s][1] = c+r*cols+cols; 51 | } 52 | } 53 | } 54 | 55 | mazeGen::~mazeGen() 56 | { 57 | //free memory 58 | for(int i = 0; i < rows; i++){ 59 | if(walls[i] != NULL) 60 | delete [] walls[i]; 61 | } 62 | if(walls != NULL) 63 | delete [] walls; 64 | } 65 | 66 | void mazeGen::getSize(int & t_rows, int & t_cols) const 67 | { 68 | t_rows = rows; 69 | t_cols = cols; 70 | } 71 | 72 | void mazeGen::generate() 73 | { 74 | //randomize walls array 75 | randomize(); 76 | //create disjoint set and loop through walls, if 77 | //the wall is not connect to the next wall, destory 78 | //the wall with (-1,-1) 79 | disjointSets d(arrSize); 80 | for(int i = 0; i+1 < arrSize; i++){ 81 | int s1 = d.setFind(walls[i][0]); 82 | int s2 = d.setFind(walls[i][1]); 83 | if(s1 != s2){ 84 | d.setUnion(s1, s2); 85 | walls[i][0] = walls[i][1] = -1; 86 | } 87 | } 88 | } 89 | 90 | bool mazeGen::printMazeData(const std::string filename) const 91 | { 92 | std::ofstream outFile(filename.c_str()); 93 | if(!outFile.is_open()) 94 | return false; 95 | 96 | outFile << "+ +"; 97 | for(int i = 0; i < cols-1; i++) 98 | outFile << "--+"; 99 | outFile << std::endl; 100 | 101 | //begin looking through every column and place a '|' when a wall appears 102 | for(int i = 1, r = 0; i < rows*2;r += (i-1) % 2, i++) { 103 | if(i % 2 == 0) { 104 | outFile << "+"; 105 | for(int j = 0; j < cols; j++) { 106 | bool fnd = false; 107 | for(int h = 0; h < arrSize; h++) { 108 | if(walls[h][0] == j+r*cols && walls[h][1] == j+r*cols+cols) { 109 | outFile << "--+"; 110 | fnd = true; 111 | break; 112 | } 113 | } 114 | if(!fnd) 115 | outFile << " +"; 116 | } 117 | }else{ 118 | outFile << "|"; 119 | for(int j = 0; j < cols-1; j++) { 120 | bool fnd = false; 121 | for(int h = 0; h < arrSize; h++) { 122 | if(walls[h][0] == r*cols+j && walls[h][1] == r*cols+j+1) { 123 | outFile << " |"; 124 | fnd = true; 125 | break; 126 | } 127 | } 128 | if(!fnd) 129 | outFile << " "; 130 | } 131 | } 132 | if(i % 2 != 0) { 133 | outFile << " |"; 134 | } 135 | outFile << std::endl; 136 | } 137 | 138 | //print out bottom bounding line 139 | for(int i = 0; i < cols-1; i++) 140 | outFile << "+--"; 141 | outFile << "+ +"; 142 | outFile << std::endl; 143 | 144 | return true; 145 | } 146 | 147 | void mazeGen::printMazeText() const 148 | { 149 | //print maze out in text form 150 | //print first row with one opening 151 | std::cout << "+ +"; 152 | for(int i = 0; i < cols-1; i++) 153 | std::cout << "--+"; 154 | std::cout << std::endl; 155 | 156 | //begin looking through every column and place a '|' when a wall appears 157 | for(int i = 1, r = 0; i < rows*2;r += (i-1) % 2, i++) { 158 | if(i % 2 == 0) { 159 | std::cout << "+"; 160 | for(int j = 0; j < cols; j++) { 161 | bool fnd = false; 162 | for(int h = 0; h < arrSize; h++) { 163 | if(walls[h][0] == j+r*cols && walls[h][1] == j+r*cols+cols) { 164 | std::cout << "--+"; 165 | fnd = true; 166 | break; 167 | } 168 | } 169 | if(!fnd) 170 | std::cout << " +"; 171 | } 172 | }else{ 173 | std::cout << "|"; 174 | for(int j = 0; j < cols-1; j++) { 175 | bool fnd = false; 176 | for(int h = 0; h < arrSize; h++) { 177 | if(walls[h][0] == r*cols+j && walls[h][1] == r*cols+j+1) { 178 | std::cout << " |"; 179 | fnd = true; 180 | break; 181 | } 182 | } 183 | if(!fnd) 184 | std::cout << " "; 185 | } 186 | } 187 | if(i % 2 != 0) { 188 | std::cout << " |"; 189 | } 190 | std::cout << std::endl; 191 | } 192 | 193 | //print out bottom bounding line 194 | for(int i = 0; i < cols-1; i++) 195 | std::cout << "+--"; 196 | std::cout << "+ +"; 197 | std::cout << std::endl; 198 | } 199 | 200 | void mazeGen::findPath() 201 | { 202 | // create vector for reading in file, 1 char = 1 block on maze 203 | std::vector< std::vector > maze_map; 204 | typedef std::istreambuf_iterator buf_iter; 205 | std::fstream file("mazeDataFile.txt"); 206 | std::vector tmp; // create a vector that we can use to create the boarders 207 | maze_map.push_back(tmp); // push back top wall, this will initially be empty 208 | tmp.push_back('|'); // create left wall for each row 209 | for(buf_iter i(file), e; i != e; ++i) { // loop file 210 | char c = *i; // get char 211 | if(c == '\n') { // if end of line, new row 212 | tmp.push_back('|'); // create right wall 213 | maze_map.push_back(tmp); // push row in 214 | tmp.clear(); // clear tmp 215 | tmp.push_back('|'); // re add new left wall 216 | } else { 217 | tmp.push_back(c); // push char into row 218 | } 219 | } 220 | tmp.clear(); // clear tmp 221 | std::fill_n(back_inserter(maze_map[0]), maze_map[1].size(), '-'); 222 | std::fill_n(back_inserter(tmp), maze_map[1].size(), '-'); 223 | maze_map.push_back(tmp); 224 | 225 | // call A* algorithm to determine most efficient path to end 226 | std::string route = AStar::findPath(2, 1, maze_map.size()-2, maze_map[0].size()-3, AStar::makeReadyMap(maze_map)); 227 | 228 | //parse the string and print contents 229 | int x = 0; 230 | int y = 0; 231 | for(std::size_t i = 0; i < route.length(); i++) { 232 | char c = route[i]; 233 | int j = atoi(&c); 234 | x = x + dx[j]; 235 | y = y + dy[j]; 236 | // 2 and 1 are our offsets since we started at 2 and 1 237 | maze_map[x+2][y+1] = 'x'; 238 | } 239 | maze_map[1][2] = 'x'; 240 | 241 | // print our solved map not including boarders 242 | for(std::size_t i = 1; i < maze_map.size()-1; i++) { 243 | for(std::size_t j = 1; j < maze_map[0].size()-1; j++) { 244 | std::cout << maze_map[i][j]; 245 | } 246 | std::cout << std::endl; 247 | } 248 | } 249 | 250 | void mazeGen::randomize() 251 | { 252 | //the array so generate() wont have to do 253 | //anything special 254 | for(int i = arrSize-1;i > 0;i--){ 255 | int j = rand() % (i+1); 256 | //swap 257 | int tmp = walls[i][0]; 258 | int tmp2 = walls[i][1]; 259 | 260 | walls[i][0] = walls[j][0]; 261 | walls[i][1] = walls[j][1]; 262 | 263 | walls[j][0] = tmp; 264 | walls[j][1] = tmp2; 265 | } 266 | 267 | } 268 | 269 | -------------------------------------------------------------------------------- /src/mazesolve/mazeGen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAZEGEN_H_ 2 | #define MAZEGEN_H_ 3 | 4 | #include 5 | 6 | class mazeGen 7 | { 8 | public: 9 | // Constructor assigns variables rows and cols to the parameters 10 | // passed and sets the disjoint set size as well as calculates 11 | // the amount of walls needed for the program. Most importantly 12 | // the constructor will allocate the walls array 13 | mazeGen(int=5, int=5); 14 | 15 | // Destructor frees memeory allocated by the constructor inside the 16 | // walls variable 17 | ~mazeGen(); 18 | 19 | // Get function sets the value of param 1 & 2 to the corresponding 20 | // rows and cols values. Constant as no values are to be changed 21 | void getSize(int &, int &) const; 22 | 23 | // Bread and butter functino of mazeGen. This function will randomize the 24 | // walls array (which was initially in order) and create a disjoint 25 | // set object. Then the function will loop through the disjoint set 26 | // size and join any sets together by checking the union between the 27 | // two. If they are not unioned then the disjoint set will join them. 28 | // this ensures that the maze IS solvable! 29 | void generate(); 30 | 31 | // This will print all maze data into a file, which is this case in the maze 32 | // itself. Opens a file defined at compile time and writes the maze to 33 | // that given file. 34 | bool printMazeData(const std::string) const; 35 | 36 | // Simple function to print the maze to the console, very similar to printMazeData 37 | // but instead uses cout as opposed to fstream 38 | void printMazeText() const; 39 | 40 | // The solver for this class, uses the A* implementation but does some very important 41 | // data manipulation first. Once the maze is 'ready' to be passed the A* is called 42 | // from astar.hpp and a string is returned containing the solved path to the end of 43 | // the maze 44 | void findPath(); 45 | 46 | // Takes the walls array and randomizes the array, needed for the creation of a solvable 47 | // maze through disjoint sets 48 | void randomize(); 49 | private: 50 | int arrSize; // size of disjoint sets object 51 | int rows; // amount of rows in maze 52 | int cols; // amount of cols in maze 53 | int **walls; // walls array for creation of maze 54 | }; 55 | 56 | #endif 57 | --------------------------------------------------------------------------------