#include "BinaryTree.h" #include "BFS.h" #include "Dijkstra.h" #include "AStar.h" #include "FileSystem.h" #include "PathGenerator.h" #include <iostream> #include <stdexcept> #include <fstream> #include <string> #include <functional> constexpr auto newl = '\n'; constexpr int Maze_Generation_Welcome = 0; constexpr int Maze_Generation_Details = 1; constexpr int Maze_Generation_Unsolved = 2; constexpr int Maze_Generation_Solve = 3; constexpr int Maze_Generation_LoadFile = 4; constexpr int Maze_Generation_SaveFile = 5; constexpr int Maze_Generation_Exit = 6; constexpr int Maze_Generation_SingleAStar = 7; void processEvents(std::function<void(char**, int, int, PathGenerator::Location, int , int )> someEvent, char** maze, int rows, int cols, PathGenerator::Location startpoint, int rowStart, int colStart) { someEvent(maze, rows, cols, startpoint, rowStart, colStart); } bool ValidateMazeStructure(char** maze, int mazeHeight, int mazeWidth); int main() { char** maze = nullptr; int mazeHeight = 0; int mazeWidth = 0; int mazeExits = 0; int mazeMaxExits = 0; int* exitsLocation = nullptr; int* playerLocation = nullptr; int* aStarTrace = nullptr; bool prorgamRuuning = true; bool mazeLoad = false; bool mazeNew = false; bool mazeSave = false; std::string input; BinaryTree* ptrBT = nullptr; int mazeOption = 0; int pathOption = 0; while (prorgamRuuning) { if (mazeOption == 0) { std::cout << "Welcome to Maze Generation program" << newl; std::cout << "Would you like to Load or create a New maze?" << newl; std::cout << "Please type New, Load or Exit" << newl; std::cout << "\t New" << newl; std::cout << "\t Load" << newl; std::cout << "\t Exit" << newl; std::cout << "\t SingleAStar" << newl; for (;;) { if ((std::cin >> input) && ((input == "New") || (input == "new")) || ((input == "Load") || (input == "load")) || ((input == "Exit") || (input == "exit")) || ((input == "SingleAStar") || (input == "singleastar"))) { if ((input == "New") || (input == "new")) mazeOption = Maze_Generation_Details; if ((input == "Load") || (input == "load")) mazeOption = Maze_Generation_LoadFile; if ((input == "Exit") || (input == "exit")) mazeOption = Maze_Generation_Exit; if ((input == "SingleAStar") || (input == "singleastar")) mazeOption = Maze_Generation_Details; break; } else { std::cout << "Please enter a valid option" << newl; std::cout << "Please type New or Load" << newl; std::cin.clear(); } } } if (mazeOption == Maze_Generation_Details) { std::cout << "Now you will create a new Maze " << newl; std::cout << "Please enter only odd numbers. " << newl << newl; std::cout << "Please Note: minimum acceptable size is 9 x 9, this will print maze with no Walls." << newl; std::cout << "Please Note: minimum acceptable size is 13 x 13, this will print maze with minimal number of Walls." << newl << newl; std::cout << "Please enter a Height between 9 and 17. " << newl; for (;;) { if ((std::cin >> mazeHeight) && ((mazeHeight % 2 != 0) && (mazeHeight >= 9) && (mazeHeight <= 17))) { break; } else { std::cout << "Please enter a valid integer" << newl; std::cout << "Minimum acceptable Maze Height size is between 9 and 17" << newl; std::cout << "The number needs to be odd" << newl; std::cin.clear(); } } std::cout << "Please enter a Width between 9 and 31. " << newl; for (;;) { if ((std::cin >> mazeWidth) && ((mazeWidth % 2 != 0) && (mazeWidth >= 9) && (mazeWidth <= 31))) { break; } else { std::cout << "Please enter a valid integer" << newl; std::cout << "Minimum acceptable Maze Width size is between 9 and 31" << newl; std::cout << "The number needs to be odd" << newl; std::cin.clear(); } } mazeMaxExits = (2 * (mazeHeight - 2)) + (2 * (mazeWidth - 2)); std::cout << "Please enter a number of exits between 1 and 3. " << newl; for (;;) { if ((std::cin >> mazeExits) && ((mazeExits >= 1) && (mazeExits <= 3))) { break; } else { std::cout << "Please enter a number of exits" << newl; std::cout << "Maze needs to have between 1 and 5 exits" << newl; std::cin.clear(); } } mazeOption = 2; if ((input == "SingleAStar") || (input == "singleastar")) mazeOption = 7; } if (mazeOption == Maze_Generation_Unsolved) { ptrBT = new BinaryTree(mazeHeight, mazeWidth); exitsLocation = new int[mazeExits * 2]; ptrBT->GenerateRandomExit(mazeExits, exitsLocation, playerLocation); ptrBT->Generate(); ptrBT->GenerateCentreSpace(); bool isMazeValid = ValidateMazeStructure(ptrBT->GetMaze(), ptrBT->GetHeight(), ptrBT->GetWidth()); if (isMazeValid == true) { std::cout << "The maze is valid, it has one Start and one or more Exits" << newl; } else { std::cout << "This maze is not valid" << newl; } std::cout << "This is the unsolved maze for: " << newl; std::cout << "Height: " << ptrBT->GetHeight() << " Width: " << ptrBT->GetWidth() << " Nr. of exits: " << mazeExits << newl << newl; ptrBT->PrintMaze(); std::cout << newl << newl; maze = ptrBT->GetMaze(); mazeOption = Maze_Generation_Solve; } if (mazeOption == Maze_Generation_Solve) { PathGenerator::Location startpointBFS; startpointBFS.row = ptrBT->Maze_Height_Half; startpointBFS.col = ptrBT->Maze_Width_Half; std::function<void(char** , int , int , PathGenerator::Location , int , int )> onBFSEvent = [&](char** maze, int rows, int cols, PathGenerator::Location startpoint, int rowStart, int colStart) { BFS bfs; for (int i = 0; i < mazeExits * 2; i += 2) { bfs.MazeSearch(maze, ptrBT->GetHeight(), ptrBT->GetWidth(), startpointBFS, exitsLocation[i], exitsLocation[i + 1]); } }; std::function<void(char**, int, int, PathGenerator::Location, int, int)> onDijkstraEvent = [&](char** maze, int rows, int cols, PathGenerator::Location startpoint, int rowStart, int colStart) { Dijkstra dijkstra; for (int i = 0; i < mazeExits * 2; i += 2) { dijkstra.MazeSearch(maze, ptrBT->GetHeight(), ptrBT->GetWidth(), startpointBFS, exitsLocation[i], exitsLocation[i + 1]); } for (int i = 0; i < mazeExits * 2; i += 2) { maze[exitsLocation[i]][exitsLocation[i + 1]] = 'E'; } maze[ptrBT->Maze_Height_Half][ptrBT->Maze_Width_Half] = 'S'; }; std::cout << "Please enter a name of path algorithm to se:" << newl; std::cout << "\t BFS" << newl; std::cout << "\t Dijkstra" << newl; for (;;) { if ((std::cin >> input) && ((input == "BFS") || (input == "Bfs")) || ((input == "Dijkstra") || (input == "dijkstra"))) { if ((input == "BFS") || (input == "Bfs")) pathOption = 0; if ((input == "Dijkstra") || (input == "dijkstra")) pathOption = 1; break; } else { std::cout << "Please enter a valid option" << newl; std::cout << "Please type New, Load or Save" << newl; std::cin.clear(); } } if (pathOption == 0) { std::cout << "Finding Shortest Path using Breadth-First Search algorithm" << newl; for (int i = 0; i < mazeExits * 2; i += 2) { processEvents(onDijkstraEvent, maze, ptrBT->GetHeight(), ptrBT->GetWidth(), startpointBFS, exitsLocation[i], exitsLocation[i + 1]); } } else if (pathOption == 1) { std::cout << "Finding Shortest Path using Dijkstra algorithm" << newl; for (int i = 0; i < mazeExits * 2; i += 2) { processEvents(onDijkstraEvent, maze, ptrBT->GetHeight(), ptrBT->GetWidth(), startpointBFS, exitsLocation[i], exitsLocation[i + 1]); } } std::cout << newl; std::cout << "This solution shows the shortest path for every random exit" << newl << newl; ptrBT->PrintMaze(); std::cout << "Would you like to Save this maze to a .txt file? Run the Maze Generator again with new inputs? Or load a .txt maze file? Or Exit?" << newl; std::cout << "Please type in one of the available options: New, Load or Save " << newl; std::cout << "\t New" << newl; std::cout << "\t Save" << newl; std::cout << "\t Load" << newl; std::cout << "\t Exit" << newl; for (;;) { if ((std::cin >> input) && ((input == "New") || (input == "new")) || ((input == "Load") || (input == "load")) || ((input == "Save") || (input == "save")) || ((input == "Exit") || (input == "exit"))) { if ((input == "New") || (input == "new")) mazeOption = Maze_Generation_Welcome; if ((input == "Load") || (input == "load")) mazeOption = Maze_Generation_LoadFile; if ((input == "Save") || (input == "save")) mazeOption = Maze_Generation_SaveFile; if ((input == "Exit") || (input == "exit")) mazeOption = Maze_Generation_Exit; break; } else { std::cout << "Please enter a valid option" << newl; std::cout << "Please type New, Load or Save" << newl; std::cin.clear(); } } if (mazeOption == Maze_Generation_Details || mazeOption == Maze_Generation_LoadFile) { delete ptrBT; } delete[] exitsLocation; } if (mazeOption == Maze_Generation_LoadFile) { FileSystem fs; system("dir /b *.txt > Dir.txt"); std::string dir; fs.LoadFile("Dir.txt", dir); std::cout << "Please enter a name of one of the loadavailable mazes to load. " << newl; std::cout << "Use the following format: nameOfFie.txt " << newl; std::cout << "Please do not use Dir.txt as this will only display all files in the current directory, not a maze. " << newl << newl; std::cout << "Files:" << newl << newl; std::cout << dir << newl << newl; std::cin >> input; for (;;) { if ((dir.find(input) != std::string::npos) && (input != "Dir.txt")) { std::cout << "File found: " << input << '\n'; break; } else { std::cout << "File not found " << input << '\n'; std::cin >> input; } } fs.LoadFile(input, mazeHeight, mazeWidth); BinaryTree ptrBT(mazeHeight, mazeWidth); maze = ptrBT.GetMaze(); bool fileLoaded = false; fs.LoadFile(input, maze, mazeHeight, mazeWidth); bool isMazeSolved = fs.IsMazeSolved(maze, mazeHeight, mazeWidth); std::string isMazeSolvedAnswer = (isMazeSolved) ? "Maze is solved" : "Maze is not solved"; std::cout << isMazeSolvedAnswer << newl; bool isMazeValid = ValidateMazeStructure(maze, mazeHeight, mazeWidth); if (isMazeValid == true) { std::cout << "The maze is valid, it has one Start and one or more Exits" << newl; } else { std::cout << "This maze is not valid" << newl; } std::cout << "Loaded Maze: " << input << " " << "\n" << "\n"; ptrBT.PrintMaze(); std::cout << "Would you like to run the Maze Generator again with new inputs? Load a .txt maze file? Or exit the program" << newl; std::cout << "Please type in one of the available options: New, Load or Exit " << newl; std::cout << "\t New" << newl; std::cout << "\t Load" << newl; std::cout << "\t Exit" << newl; for (;;) { if ((std::cin >> input) && ((input == "New") || (input == "new")) || ((input == "Load") || (input == "load")) || ((input == "Exit") || (input == "exit"))) { if ((input == "New") || (input == "new")) mazeOption = Maze_Generation_Welcome; if ((input == "Load") || (input == "load")) mazeOption = Maze_Generation_LoadFile; if ((input == "Exit") || (input == "exit")) mazeOption = Maze_Generation_Exit; break; } else { std::cout << "Please enter a valid option" << newl; std::cout << "Please type New, Load or Exit" << newl; std::cin.clear(); } } } if (mazeOption == Maze_Generation_SaveFile) { FileSystem fs; system("dir /b *.txt > Dir.txt"); std::string dir; fs.LoadFile("Dir.txt", dir); std::cout << "Please enter a name to save the maze. " << newl; std::cout << "Use the following format: nameOfFie.txt " << newl; std::cout << "Please do not use any of the following name files. " << newl << newl; std::cout << "Files:" << newl << newl; std::cout << dir << newl << newl; for (;;) { std::cin >> input; if ((dir.find(input) == std::string::npos)) { std::cout << "Saved Successfully: " << input << '\n'; break; } else { std::cout << "Please choose other name, this name is invalid: " << input << '\n'; } } bool fileSaved = fs.SaveMazeFile(input, maze, mazeHeight, mazeWidth); if(ptrBT) delete ptrBT; std::cout << "Would you like to run the Maze Generator again with new inputs? Load a .txt maze file? Or exit the program" << newl; std::cout << "Please type in one of the available options: New, Load or Exit " << newl; std::cout << "\t New" << newl; std::cout << "\t Load" << newl; std::cout << "\t Exit" << newl; for (;;) { if ((std::cin >> input) && ((input == "New") || (input == "new")) || ((input == "Load") || (input == "load")) || ((input == "Exit") || (input == "exit"))) { if ((input == "New") || (input == "new")) mazeOption = Maze_Generation_Welcome; if ((input == "Load") || (input == "load")) mazeOption = Maze_Generation_LoadFile; if ((input == "Exit") || (input == "exit")) mazeOption = Maze_Generation_Exit; break; } else { std::cout << "Please enter a valid option" << newl; std::cout << "Please type New, Load or Exit" << newl; std::cin.clear(); } } } if (mazeOption == Maze_Generation_SingleAStar) { ptrBT = new BinaryTree(mazeHeight, mazeWidth); exitsLocation = new int[mazeExits * 2]; playerLocation = new int[mazeExits * 2]; ptrBT->Generate(); ptrBT->GenerateCentreSpace(MazeGenerator::Maze_Char_Finish); ptrBT->GenerateRandomExit(mazeExits, exitsLocation, playerLocation, MazeGenerator::Maze_Char_Player); std::cout << "This is the unsolved maze for: " << newl; std::cout << "Height: " << ptrBT->GetHeight() << " Width: " << ptrBT->GetWidth() << " Nr. of exits: " << mazeExits << newl << newl; ptrBT->PrintMaze(); std::cout << newl << newl; maze = ptrBT->GetMaze(); // Creating a shortcut for int, int pair type typedef std::pair<int, int> Pair; // Creating a shortcut for pair<int, pair<int, int>> type typedef std::pair<double, std::pair<int, int>> pPair; AStar a; if (mazeExits == 1) { Pair dest = std::make_pair(ptrBT->Maze_Height_Half, ptrBT->Maze_Width_Half); Pair src = std::make_pair(playerLocation[0], playerLocation[1]); int aStarTraceCount = 0; a.aStarSearch(maze, src, dest, aStarTrace, aStarTraceCount); for (int i = 0; i < aStarTraceCount * 2; i += 2) { maze[aStarTrace[i]][aStarTrace[i + 1]] = MazeGenerator::Maze_Char_Path; ptrBT->PrintMaze(); } } else if (mazeExits == 2) { Pair src = std::make_pair(ptrBT->Maze_Height_Half, ptrBT->Maze_Width_Half); Pair dest = std::make_pair(playerLocation[0], playerLocation[1]); int aStarTraceCount = 0; a.aStarSearch(maze, src, dest, aStarTrace, aStarTraceCount); for (int i = 0; i < aStarTraceCount * 2; i += 2) { maze[aStarTrace[i]][aStarTrace[i + 1]] = MazeGenerator::Maze_Char_Path; ptrBT->PrintMaze(); } aStarTraceCount = 0; Pair src2 = std::make_pair(ptrBT->Maze_Height_Half, ptrBT->Maze_Width_Half); Pair dest2 = std::make_pair(playerLocation[2], playerLocation[3]); a.aStarSearch(maze, src2, dest2, aStarTrace, aStarTraceCount); for (int i = 0; i < aStarTraceCount * 2; i += 2) { maze[aStarTrace[i]][aStarTrace[i + 1]] = MazeGenerator::Maze_Char_Path; ptrBT->PrintMaze(); } } delete[] exitsLocation; delete[] playerLocation; delete[] aStarTrace; std::cout << "Would you like to run the Maze Generator again with new inputs? Or exit the program" << newl; std::cout << "Please type in one of the available options: New or Exit " << newl; std::cout << "\t New" << newl; std::cout << "\t Exit" << newl; for (;;) { if ((std::cin >> input) && ((input == "New") || (input == "new")) || ((input == "Exit") || (input == "exit"))) { if ((input == "New") || (input == "new")) mazeOption = Maze_Generation_Welcome; if ((input == "Exit") || (input == "exit")) mazeOption = Maze_Generation_Exit; break; } else { std::cout << "Please enter a valid option" << newl; std::cout << "Please type New, Load or Exit" << newl; std::cin.clear(); } } } if (mazeOption == Maze_Generation_Exit) { prorgamRuuning = false; } } return 0; } bool ValidateMazeStructure(char** maze, int mazeHeight, int mazeWidth) { bool startFound = false; bool exitFound = false; for (int i = 0; i < mazeHeight; i++) { for (int j = 0; j < mazeWidth; j++) { if (maze[i][j] == MazeGenerator::Maze_Char_Start) { startFound = true; } if (maze[i][j] == MazeGenerator::Maze_Char_Exit) { exitFound = true; } } } if ((startFound == true) && (exitFound == true)) { return true; } else { return false; } return false; }