#include <iostream>
#include <iomanip>
#include <string>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <utility>
#include <fstream>
#include <algorithm>
#include "matrix.h"

using namespace std;
/****************************************************************************************
*	class:		Site
*	purpose:	Hold the current site of the Monster/Rogue
****************************************************************************************/
class Site
{
	private:
		int i;
		int j;
		string character;
	public:
		Site(int i, int j, string character)
		{
			this->i = i;
			this->j = j;
			this->character = character;
		}
		
		~Site() {}
/****************************************************************************************
*	function:	get_i
* 	purpose:	get the i position of the Site
****************************************************************************************/		
		int get_i()
		{
			return i;
		}
/****************************************************************************************
*	function:	get_j
* 	purpose:	get the j position of the Site
****************************************************************************************/
		int get_j()
		{
			return j;
		}
/****************************************************************************************
*	function:	get_char
* 	purpose:	get the Site character
****************************************************************************************/
		string get_char()
		{
			return character;
		}
};
/****************************************************************************************
*	class:		Dungeon
*	purpose:	Object to hold our Dungeon
****************************************************************************************/
class Dungeon
{
	private:
		int N; //size of the dungeon
		matrix<string> board;
		int counter;
	public:
		Dungeon(int sizeOfDungeon)
		{
			N = sizeOfDungeon;
			//counter = 0;
		}
/****************************************************************************************
*	function:	set_dungeon
* 	purpose:	receives the dungeon from the file and puts it in a matrix
****************************************************************************************/
		void set_dungeon(int sod, istream* inFile)
		{
			int i, j;
			int start = 0;
			
			string current_line; //Begin getting input from file.
	
			board.resize(sod, sod * 2);
			
			for (i = 0; i < sod; i++)
			{
				getline(*inFile, current_line);
				for (j = 0; j < sod * 2; j++)
				{
					string temp = current_line.substr(start, 1);
					if (temp == " " || temp == "+" || temp == "." || temp == "@"
						|| temp == "A" || temp == "B" || temp == "C" || temp == "D"
						|| temp == "E" || temp == "F" || temp == "G" || temp == "H"
						|| temp == "I" || temp == "J" || temp == "K" || temp == "L"
						|| temp == "M" || temp == "N" || temp == "O" || temp == "P"
						|| temp == "Q" || temp == "R" || temp == "S" || temp == "T"
						|| temp == "U" || temp == "V" || temp == "W" || temp == "X"
						|| temp == "Y" || temp == "Z")
						board[i][j] = temp;
					else break;
					start++;
				}
				start = 0;
			}
		}
/****************************************************************************************
*	function:	out_dungeon
* 	purpose:	outputs the entire dungeon
****************************************************************************************/		
		void out_dungeon()
		{
			int i;
			int j ;
			
			for (i = 0; i < N; i++)
				for (j = 0; j < N * 2; j++)
				{
					cout << board[i][j];
					if (j+1 == N * 2)
						cout << endl;
				}	
		}
/****************************************************************************************
*	function:	out_specific_cell
* 	purpose:	returns the contents of a specific cell
****************************************************************************************/		
		string out_specific_cell(int i, int j)
		{
			return board[i][j];
		}
/****************************************************************************************
*	function:	get_entire_dungeon
* 	purpose:	self-explanatory
****************************************************************************************/		
		matrix<string> get_entire_dungeon()
		{
			return board;
		}
/****************************************************************************************
*	function:	gds
* 	purpose:	get the size of the dungeon
****************************************************************************************/
		int gds()
		{
			return N;
		}
/****************************************************************************************
*	function:	isLegalMove
*	purpose:	returns true if the move is legal, false if not
****************************************************************************************/		
		bool isLegalMove(Site v, Site w)
		{
			int i1 = v.get_i();
		        int j1 = v.get_j();
		        int i2 = w.get_i();
		        int j2 = w.get_j();
		        if (i1 < 0 || j1 < 0 || i1 >= N || j1 >= N) return false;
		        if (i2 < 0 || j2 < 0 || i2 >= N || j2 >= N) return false;
		        if (is_Wall(v) || is_Wall(w)) return false;
		        if (abs(i1 - i2) > 1)  return false;
		        if (abs(j1 - j2) > 1)  return false;
		        if (is_Room(v) && is_Room(w)) return true;
		        if (i1 == i2)               return true;
		        if (j1 == j2)               return true;

		        return false;
        	}
/****************************************************************************************
*	function:	is_Corridor
* 	purpose:	returns true if the site is a corridor
****************************************************************************************/
		bool is_Corridor(Site v)
		{
			int i = v.get_i();
		        int j = v.get_j();
		        if (i < 0 || j < 0 || i >= N || j >= N) return false;
		        return true;
       		}
/****************************************************************************************
*	function:	is_Room
* 	purpose:	returns true if the site is a room
****************************************************************************************/
		bool is_Room(Site v)
		{
			int i = v.get_i();
        		int j = v.get_j();
		        if (i < 0 || j < 0 || i >= N || j >= N) return false;
		        return true;
       		}
/****************************************************************************************
*	function:	is_Wall
* 	purpose:	checks if the site is a wall
****************************************************************************************/
		bool is_Wall(Site v)
		{
		        return (!is_Room(v) && !is_Corridor(v));
		}
};

void play_Game(matrix<string> dunn, int irpos, int jrpos, int impos, int jmpos, int dsize);
void wait ( int seconds );
void out_dunn(matrix<string> dunnn, int ddsize);
matrix<string> rogueMoves(int mipos, int mjpos, int ripos, int rjpos, matrix<string> dunR, int rsize);
matrix<string> monsterMoves(int mipos, int mjpos, int ripos, int rjpos, matrix<string> dunM, int rsize);
/****************************************************************************************
*	function:	main
* 	purpose:	start our program
****************************************************************************************/
int main()
{
	int mi = 0, mj = 0; //location of the monster
	int ri = 0, rj = 0; //location of the rogue
	
	matrix<string> entire_dungeon;
	ifstream inFile("dungeon.txt"); //open to file just to get the size of the dungeon

	do
	{
		string current_line; //Begin getting input from file.
		getline(inFile, current_line); //gets the size of the dungeon
	
		int dungeonSize = atoi(current_line.c_str());  //gets the size of the dungeon
	
		if(dungeonSize == -1) 
		{
		cout<<"PLAYED ALL GAMES  "<<endl;
		return 0;
		}
	
		entire_dungeon.resize(dungeonSize, dungeonSize); //instantiates a dungeon to be used inside main
	
		Dungeon dungeon(dungeonSize); //creates a dungeon object
	
	
		dungeon.set_dungeon(dungeonSize, &inFile);

		//outputs the original dungeon
 		dungeon.out_dungeon();	
 	
 		cout << endl;
 	
 		//entire_dungeon is a copy of the matrix dungeon from the file
 		entire_dungeon = dungeon.get_entire_dungeon();
 	
 		try
 		{
 			play_Game(entire_dungeon, ri, rj, mi, mj, dungeonSize);
 		}
 		catch(int x)
 		{
 			cout << "Please press enter to move to next dungeon";
 			cin.get();
 			continue;
 		}
	}
	while(true);
	return 0;
}
/****************************************************************************************
*	function:	play_Game
* 	purpose:	Plays the current Dungeon game
****************************************************************************************/
void play_Game(matrix<string> dunn, int irpos, int jrpos, int impos, int jmpos, int dsize)
{	
	int i = 0;
	//figures out the position of the rogue and the monster on the current location
	for(int i = 0; i < dsize; i++)
		for(int j = 0; j < dsize * 2; j++)
		{
			if (dunn[i][j] == "@") { irpos = i; jrpos = j; }
			if (dunn[i][j] == "A" || dunn[i][j] == "B" || dunn[i][j] == "C"
				|| dunn[i][j] == "D" || dunn[i][j] == "E" || dunn[i][j] == "F"
				|| dunn[i][j] == "G" || dunn[i][j] == "H" || dunn[i][j] == "I"
				|| dunn[i][j] == "J" || dunn[i][j] == "K" || dunn[i][j] == "L"
				|| dunn[i][j] == "M" || dunn[i][j] == "N" || dunn[i][j] == "O"
				|| dunn[i][j] == "P" || dunn[i][j] == "Q" || dunn[i][j] == "R"
				|| dunn[i][j] == "S" || dunn[i][j] == "T" || dunn[i][j] == "U"
				|| dunn[i][j] == "V" || dunn[i][j] == "W" || dunn[i][j] == "X"
				|| dunn[i][j] == "Y" || dunn[i][j] == "Z") 
				{impos = i; jmpos = j; }
		}
	
	//dummy while loop to test the program	
	while(i < 20)
	{
		wait (1);

	 	out_dunn(monsterMoves(impos, jmpos, irpos, jrpos, dunn, dsize), dsize);
	 	
		wait (1);
		
	 	out_dunn(rogueMoves(impos, jmpos, irpos, jrpos, monsterMoves(impos, jmpos, irpos, jrpos, dunn,dsize), dsize),dsize);
	 	
	 	dunn = rogueMoves(impos, jmpos, irpos, jrpos, monsterMoves(impos, jmpos, irpos, jrpos, dunn,dsize),dsize);
	 	
	 	for(int i = 0; i < dsize; i++)
			for(int j = 0; j < dsize * 2; j++)
			{
				if (dunn[i][j] == "@") { irpos = i; jrpos = j; }
				if (dunn[i][j] == "A" || dunn[i][j] == "B" || dunn[i][j] == "C"
				|| dunn[i][j] == "D" || dunn[i][j] == "E" || dunn[i][j] == "F"
				|| dunn[i][j] == "G" || dunn[i][j] == "H" || dunn[i][j] == "I"
				|| dunn[i][j] == "J" || dunn[i][j] == "K" || dunn[i][j] == "L"
				|| dunn[i][j] == "M" || dunn[i][j] == "N" || dunn[i][j] == "O"
				|| dunn[i][j] == "P" || dunn[i][j] == "Q" || dunn[i][j] == "R"
				|| dunn[i][j] == "S" || dunn[i][j] == "T" || dunn[i][j] == "U"
				|| dunn[i][j] == "V" || dunn[i][j] == "W" || dunn[i][j] == "X"
				|| dunn[i][j] == "Y" || dunn[i][j] == "Z")
				 {impos = i; jmpos = j; }
			}
		i++;
	}
//FINAL SWAP OF MONSTER AND ROGUE _ END PROGRAM
}
/****************************************************************************************
*	function:	wait
* 	purpose:	wait for a specific time specified by "seconds"
****************************************************************************************/
void wait ( int seconds )
{ 
	clock_t endwait; 
	endwait = clock () + seconds * CLOCKS_PER_SEC; 
	while (clock() < endwait) {} 
}
/****************************************************************************************
*	function:	out_dunn
* 	purpose:	outputs the "updated" dungeon
****************************************************************************************/
void out_dunn(matrix<string> dunnn, int ddsize)
{
	for (int i = 0; i < ddsize; i++)
		for (int j = 0; j < ddsize * 2; j++)
		{
			cout<<dunnn[i][j];
			if (j+1 == ddsize * 2)
				cout << endl;
		}
	cout << endl;
}
/****************************************************************************************
*	function:	rogueMoves
*	purpose:	decides which ROUGE moves to make
****************************************************************************************/
matrix<string> rogueMoves(int mipos, int mjpos, int ripos, int rjpos, matrix<string> dunR, int rsize)
{
	//THE WALLS
 	if(ripos== rsize-1 || ripos == 1 || rjpos == (2*rsize)-2 || rjpos == 0)
 	{
  		if(ripos == rsize -1 && rjpos!=0 && rjpos!=(2*rsize)-2 ) //bottom
	  	{
	  		 if(mipos<=ripos && mjpos>=rjpos) 
	  		 {
	  			swap(dunR[ripos][rjpos], dunR[ripos - 1][rjpos]);
	  		 }
	  	}
	  	if(ripos == rsize -1 && rjpos!=0 && rjpos!=(2*rsize)-2 )
	  	{
	  		 if(mipos<=ripos && mjpos<=rjpos) 
	  		 {
	  		 	swap(dunR[ripos][rjpos], dunR[ripos + 1][rjpos]);
	  		 }
	  	}
	 	if(rjpos == 0 && ripos!=0 && ripos!= rsize -1 ) // left
	  	{
	   		if(mipos<=ripos && mjpos>=rjpos) 
	   		{
	   			swap(dunR[ripos][rjpos], dunR[ripos + 1][rjpos]);
	  		}
	  	}
	  	if(rjpos == 0 && ripos!=0 && ripos!= rsize -1 )
	  	{
	   		if(mipos>=ripos && mjpos>=rjpos) 
	   		{
	   			swap(dunR[ripos][rjpos], dunR[ripos - 1][rjpos]);
	   		}
	  	}
	 	if(ripos == 0 && rjpos!=0 && rjpos!= (2*rsize)-2 ) // top
	  	{
	   		if(mjpos<=rjpos && mipos>=ripos) 
	   		{
	   			swap(dunR[ripos][rjpos], dunR[ripos][rjpos +2]);
	  		}
	  	}
	  
	  	if(ripos == 0 && rjpos!=0 && rjpos!= (2*rsize)-2 )
	  	{
	   		if(mjpos>=rjpos && mipos>=ripos) 
	   		{
	   			swap(dunR[ripos][rjpos], dunR[ripos][rjpos -2]);
	  		}
	  	}
		if(rjpos ==(2*rsize)-2 && ripos!=0 && ripos!= rsize -1 ) // right
	  	{
	   		if(mipos<=ripos && mjpos<=rjpos) 
	   		{
	   			swap(dunR[ripos][rjpos], dunR[ripos + 1][rjpos]);
	  		}		
	  	}
	  	if(rjpos ==(2*rsize)-2 && ripos!=0 && ripos!= rsize -1 )
	  	{
	   		if(mipos>=ripos && mjpos<=rjpos) 
	   		{
	   			swap(dunR[ripos][rjpos], dunR[ripos - 1][rjpos]);
	  		}
	  	}
	  	if((ripos == 0 && rjpos ==0) || (ripos == rsize -1 && rjpos ==0) || (ripos ==0 && rjpos == (2*rsize)-2) || (ripos == rsize - 1 && rjpos == (2*rsize)-2))
	  	{
	  		swap(dunR[ripos][rjpos], dunR[ripos][rjpos]);  //do nothing;
	  	}

	}//END OF WALLS

	else if ((ripos == mipos && rjpos > mjpos) && (dunR[ripos][rjpos +2] == "." /*|| dunR[ripos][rjpos +2] != "+")*/))
 	swap(dunR[ripos][rjpos], dunR[ripos][rjpos +2]);
 

	 else if ((ripos == mipos && rjpos < mjpos) && (dunR[ripos][rjpos -2] == "." /*|| dunR[ripos][rjpos -2] != "+")*/))
  	swap(dunR[ripos][rjpos], dunR[ripos][rjpos -2]);


 else if ((ripos > mipos && rjpos < mjpos) && (dunR[ripos + 1][rjpos -2] == "." /*|| dunR[ripos + 1][rjpos -2]!= "+")*/))
  swap(dunR[ripos][rjpos], dunR[ripos + 1][rjpos -2]); 


 else if ((ripos > mipos && rjpos > mjpos) && (dunR[ripos + 1][rjpos+ 2] == "." /*|| dunR[ripos + 1][rjpos+ 2] != "+")*/))
  swap(dunR[ripos][rjpos], dunR[ripos +1][rjpos+ 2]); 

 
 else if ((ripos < mipos && rjpos < mjpos) && (dunR[ripos - 1][rjpos- 2] == "." /*|| dunR[ripos - 1][rjpos- 2]!="+")*/))
  swap(dunR[ripos][rjpos], dunR[ripos - 1][rjpos- 2]); 
 
 
 else if ((ripos < mipos && rjpos > mjpos) && (dunR[ripos -1][rjpos +2] == "." /*|| dunR[ripos -1][rjpos +2]!="+")*/))
  swap(dunR[ripos][rjpos], dunR[ripos -1][rjpos +2]); 

	return dunR;
}
/****************************************************************************************
*	function:	monsterMoves
* 	purpose:	decides which MONSTER moves to make
****************************************************************************************/
matrix<string> monsterMoves(int mipos, int mjpos, int ripos, int rjpos, matrix<string> dunM, int rsize)
{
	if ((ripos == mipos && rjpos < mjpos) && dunM[mipos][mjpos - 2] == "@")
	{
		cout << "Monster catching the player is now inevitable!" << endl;
		throw	45;
	}
	else if ((ripos == mipos && rjpos > mjpos) && dunM[mipos][mjpos + 2] == "@")
	{
		cout << "Monster catching the player is now inevitable!" << endl;
		throw	45;
	}
	else if ((rjpos == mjpos - 2 && ripos > mipos) && dunM[mipos + 1][mjpos] == "@")
	{
		cout << "Monster catching the player is now inevitable!" << endl;
		throw	45;
	}
	else if ((rjpos == mjpos && ripos < mipos) && dunM[mipos - 1][mjpos] == "@")
	{
		cout << "Monster catching the player is now inevitable!" << endl;
		throw	45;
	}
	else if(ripos < mipos)
	{
		// if rogue is also to the right of the monster
		if(rjpos > mjpos)
		{
			swap(dunM[mipos][mjpos], dunM[mipos - 1][mjpos + 2]);
			return	dunM;
		}
		// if rogue is also to the left of the monster
		else if(rjpos < mjpos)
		{
			swap(dunM[mipos][mjpos], dunM[mipos - 1][mjpos - 2]);
			return	dunM;
		}
		// if rogue is at the same vertical location as the monster
		else
		{
			swap(dunM[mipos][mjpos], dunM[mipos - 1][mjpos]);
			return	dunM;
		}
	}
	// if rogue is below the monster
	else if(ripos > mipos)
	{
		// if rogue is also to the right of the monster
		if(rjpos > mjpos)
		{
			swap(dunM[mipos][mjpos], dunM[mipos + 1][mjpos + 2]);
			return	dunM;
		}
		// if rogue is also to the left of the monster
		else if(rjpos < mjpos)
		{
			swap(dunM[mipos][mjpos], dunM[mipos + 1][mjpos - 2]);
			return	dunM;
		}

		// if rogue is at the same vertical location as the monster
		else
		{
			swap(dunM[mipos][mjpos], dunM[mipos + 1][mjpos]);
			return	dunM;
		}
	}
	// same horizontal line
	else if(ripos == mipos)
	{
		if(rjpos > mjpos)
		{
			swap(dunM[mipos][mjpos],dunM[mipos][mjpos +2]);
			return	dunM;
		}
		else
		{
			swap(dunM[mipos][mjpos],dunM[mipos][mjpos -2]);
			return	dunM;
		}	
	}
	else
	{
		cout << "game over" << endl;
		
		return dunM;
	}
}
//other that 
