#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; }