#include <iostream>
#include <vector>
#include <sys/timeb.h>
#include <time.h>

using namespace std;

//The four possible colors that the ropes can take.
enum color {R, G, B, Y};
//The four directions.
enum dir {N, S, E, W};

//The tile definitions, starting from the left rope on the top
//and preceding clockwise around the tile.  Tiles 1 - 9 are the
//3x3 tiles.  Only the first four colors on the tile are listed
//since the remaining four can be inferred.
color tileDef[25][4] =
{
	{ R, G, Y, B },
	{ R, B, Y, G },
	{ R, B, G, Y },
	{ G, B, Y, R },
	{ G, Y, B, R },
	{ B, R, G, Y },
	{ Y, R, G, B },
	{ Y, G, R, B },
	{ Y, G, B, R },
	{ R, G, B, Y },
	{ R, Y, G, B },
	{ R, Y, B, G },
	{ G, R, B, Y },
	{ G, R, Y, B },
	{ G, B, R, Y },
	{ G, Y, R, B },
	{ B, R, Y, G },
	{ B, G, R, Y },
	{ B, G, Y, R },
	{ B, Y, R, G },
	{ B, Y, G, R },
	{ Y, R, B, G },
	{ Y, B, R, G },
	{ Y, B, G, R },
	{ R, B, G, Y } //The repeated tile.
};

//Records the time spend thinking and the time spent doing.
class doThinkClock
{
private:
	//Variables to keep track of the state of the timer.
	bool doing;
	bool thinking;
	unsigned long lastSecond;
	unsigned long lastMillisecond;
	
	//The time in milliseconds spent thinking and doing.
	__int64 doingTime;
	__int64 thinkingTime;

public:
	//Constructor.
	doThinkClock() : doing(false), thinking(false), lastSecond(-1), lastMillisecond(-1), doingTime(0), thinkingTime(0) {}

	//Start doing.
	void startDo()
	{
		//Container for the time information.
		struct __timeb64 timeBuffer;

		//Retrieve time information.
		_ftime64( &timeBuffer );
		unsigned long second = timeBuffer.time;
		unsigned long millisecond = timeBuffer.millitm;

		if (doing)
			return;
		//If we were thinking, add time to the thinking counter.
		if (thinking)
		{
			thinkingTime += (second - lastSecond) * 1000;
			if (millisecond < lastMillisecond)
				thinkingTime -= lastMillisecond - millisecond;
			else
				thinkingTime += millisecond - lastMillisecond;
			thinking = false;
		}
		doing = true;

		//Remember the current time.
		lastSecond = second;
		lastMillisecond = millisecond;
	}

	//Start thinking.
	void startThink()
	{
		//Container for the time information.
		struct __timeb64 timeBuffer;

		//Retrieve time information.
		_ftime64( &timeBuffer );
		unsigned long second = timeBuffer.time;
		unsigned long millisecond = timeBuffer.millitm;

		if (thinking)
			return;
		//If we were doing, add time to the doing counter.
		if (doing)
		{
			doingTime += (second - lastSecond) * 1000;
			if (millisecond < lastMillisecond)
				doingTime -= lastMillisecond - millisecond;
			else
				doingTime += millisecond - lastMillisecond;
			doing = false;
		}
		thinking = true;

		//Remember the current time.
		lastSecond = second;
		lastMillisecond = millisecond;
	}

	//Stop.
	void stop()
	{
		//Container for the time information.
		struct __timeb64 timeBuffer;

		//Retrieve time information.
		_ftime64( &timeBuffer );
		unsigned long second = timeBuffer.time;
		unsigned long millisecond = timeBuffer.millitm;

		//If we were thinking, add time to the thinking counter.
		if (thinking)
		{
			thinkingTime += (second - lastSecond) * 1000;
			if (millisecond < lastMillisecond)
				thinkingTime -= lastMillisecond - millisecond;
			else
				thinkingTime += millisecond - lastMillisecond;
			thinking = false;
		}
		//If we were doing, add time to the doing counter.
		else if (doing)
		{
			doingTime += (second - lastSecond) * 1000;
			if (millisecond < lastMillisecond)
				doingTime -= lastMillisecond - millisecond;
			else
				doingTime += millisecond - lastMillisecond;
			doing = false;
		}

		//Remember the current time.
		lastSecond = second;
		lastMillisecond = millisecond;
	}

	//Return the time spent thinking.
	unsigned long getDoingTime()
	{ return doingTime; }

	//Return the time spent doing.
	unsigned long getThinkingTime()
	{ return thinkingTime; }
} timer;

//Keeps track of the number of nodes visited.
__int64 nodesVisited = 0;

//Keeps track of the number of consistency checks.
__int64 consistencyChecks = 0;

class tile
{
private:
	int id; //The ID of this tile, to make easier the process of
	//recognizing two tiles which are the same but rotated.
	int orientation; //The orientation of this tile.  It is
	//purely for testing and output purposes.
	color pattern[8]; //The pattern of colors on this tile,
	//starting at the left color on the top and proceeding
	//clockwise around the tile.
public:
	//The constructor for new tiles.
	tile (int newId, color newPattern[4]) : id(newId), orientation(1)
	{
		//Copy the first four colors.
		for (int i = 0; i < 4; i++)
			pattern[i] = newPattern[i];
		//Assign the last four colors.
		pattern[4] = pattern[0];
		pattern[5] = pattern[3];
		pattern[6] = pattern[1];
		pattern[7] = pattern[2];
	}

	//The constructor for rotated tiles.
	tile (int newId, int newOrientation, color newPattern[8]) : id(newId), orientation(newOrientation)
	{
		for (int i = 0; i < 8; i++)
			pattern[i] = newPattern[i];
	}

	//The id accessor function.
	int getId ()
	{ return id; }

	//The orientation accessor function.
	int getOrientation ()
	{ return orientation; }

	//Returns pointers to all possible rotations of this tile.
	void getRotations (tile *&t1, tile *&t2, tile *&t3)
	{
		//The new color patterns.
		color p1[8], p2[8], p3[8];

		for (int i = 0; i < 8; i++)
		{
			p1[i] = pattern[(i + 6) % 8];
			p2[i] = pattern[(i + 4) % 8];
			p3[i] = pattern[(i + 2) % 8];
		}

		//Create new tiles with the same id as this tile.
		t1 = new tile(id, 2, p1);
		t2 = new tile(id, 3, p2);
		t3 = new tile(id, 4, p3);
	}

	//Returns true if this tile matches the given tile in the
	//given direction.
	bool matches (tile *other, dir d)
	{
		consistencyChecks++;

		//Compare different things depending on what direction
		//this tile is in relation to the other tile.
		switch (d)
		{
			//The other tile is north of this tile.
			case N:
				if (this->pattern[0] == other->pattern[5]
				&& this->pattern[1] == other->pattern[4])
					return true;
				return false;
			//The other tile is south of this tile.
			case S:
				if (this->pattern[4] == other->pattern[1]
				&& this->pattern[5] == other->pattern[0])
					return true;
				return false;
			//The other tile is east of this tile.
			case E:
				if (this->pattern[2] == other->pattern[7]
				&& this->pattern[3] == other->pattern[6])
					return true;
				return false;
			//The other tile is west of this tile.
			case W:
				if (this->pattern[6] == other->pattern[3]
				&& this->pattern[7] == other->pattern[2])
					return true;
				return false;
			default:
				cout << "Illegal direction in 'match' function." << endl;
				exit(1);
		}
	}
};

//A list of the available tiles, including all rotations.
vector<tile*> availableTiles;

//The size of one side of the grid.
int gridSize;

//The recursive function to do backtracking.
bool recurse (tile *grid[5][5], bool usedList[25], int r, int c)
{
	//Mark another node visited.
	nodesVisited++;

	//Try each tile we could possibly put at (r,c).
	for (unsigned int i = 0; i < availableTiles.size(); i++)
		//Don't try a tile that's already on the grid somewhere.
		if (!usedList[availableTiles[i]->getId()])
		{
			//If there is a tile west of this one, see if we
			//match it.  If there is a tile north of this one,
			//see if we match it.  If everything works out, use
			//this tile and go to the next level.
			if ((r == 0 || availableTiles[i]->matches(grid[r-1][c], W))
				&& (c == 0 || availableTiles[i]->matches(grid[r][c-1], N)))
			{
				//Put this tile on the grid.
				grid[r][c] = availableTiles[i];

				//If this is the last grid spot than we've found
				//a solution!
				if (r == gridSize - 1 && c == gridSize - 1)
				{
//					for (int x = 0; x < gridSize; x++)
//					{
//						for (int y = 0; y < gridSize; y++)
//							cout << grid[y][x]->getId() + 1 << "." << grid[y][x]->getOrientation() << " ";
//						cout << endl;
//					}
					return true;
				}

				//Start thinking.
				timer.startThink();

				//Make a new usedList for the next level.
				bool newUsedList[25];
				for (int j = 0; j < 25; j++)
					newUsedList[j] = usedList[j];

				//Mark our tile on the new usedList.
				newUsedList[availableTiles[i]->getId()] = true;

				//Start doing.
				timer.startDo();

				//Calculate next grid spot to try.
				int nR = (r + 1) % gridSize, nC = c;
				if (nR == 0)
					nC++;

				//Try recursing deeper.
				if (recurse(grid, newUsedList, nR, nC))
					return true;
			}
		}

	return false;
}

int main (int argv, char** argc)
{
	const int trials = 100;

	//This timer times the entire execution of the program.
	doThinkClock executionTimer;
	executionTimer.startDo();

	//Check the command line for large puzzle switch.
	if (argv == 2 && argc[1][0] == '5')
		gridSize = 5;
	else
		gridSize = 3;

	//Initialize the availableTiles vector.
	for (int i = 0; i < gridSize * gridSize; i++)
	{
		//Pointers to tiles which will be rotated.
		tile *t0 = NULL, *t1 = NULL, *t2 = NULL, *t3 = NULL;

		//Create a new tile from the tile definition list.
		t0 = new tile(i, tileDef[i]);

		//Create new tiles by rotating the first tile.
		t0->getRotations(t1, t2, t3);

		//Put tiles into availableTiles vector.
		availableTiles.push_back(t0);
		availableTiles.push_back(t1);
		availableTiles.push_back(t2);
		availableTiles.push_back(t3);
	}

	//Seed the RNG via the time.
	srand((unsigned) time (NULL));

	for (int n = 0; n < trials; n++)
	{
		//Randomize the availableTiles vector.
		for (int i = 0; i < availableTiles.size(); i++)
		{
			//Random index into the availableTiles vector.
			int idx = rand() % availableTiles.size();

			//Temporary tile pointer.
			tile *temp = availableTiles[i];
			availableTiles[i] = availableTiles[idx];
			availableTiles[idx] = temp;
		}

		//The initial grid.
		tile *grid[5][5] = 
		{
			{ NULL, NULL, NULL, NULL, NULL },
			{ NULL, NULL, NULL, NULL, NULL },
			{ NULL, NULL, NULL, NULL, NULL },
			{ NULL, NULL, NULL, NULL, NULL },
			{ NULL, NULL, NULL, NULL, NULL }
		};

		//The initial list of used tiles (all unused).
		bool usedList[25];
		for (int i = 0; i < 25; i++)
			usedList[i] = false;

		//Start the timer.
		timer.startDo();

		//Start the recursion.
		recurse(grid, usedList, 0, 0);

		//Stop the timer.
		timer.stop();
	}

	//Stop the execution timer.
	executionTimer.stop();

	cout << endl;
	cout << "Average execution time: " << executionTimer.getDoingTime() / trials << "ms" << endl;
	cout << "Average time spent doing: " << timer.getDoingTime() / trials << "ms" << endl;
	cout << "Average time spent thinking: " << timer.getThinkingTime() / trials << "ms" << endl;
	cout << "Average nodes visited: " << nodesVisited / trials << endl;
	cout << "Average consistency checks: " << consistencyChecks / trials << endl;

	return 1;
}