CSC8501_Advanced_Programming_For_Games / MazeGeneration / Game.cpp
Game.cpp
Raw
#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;
}