#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) { //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); } } }; //Structure representing a spot on the grid. struct gridSpot { //A vector of tile pointers representing tiles that are //still in the domain for this spot on the grid. vector<tile*> domain; //True if this grid spot has a 'definite' tile placed in it. bool placed; }; //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 (gridSpot grid[5][5], int r, int c, int l) { //Mark another node visited. nodesVisited++; //Try each tile at (r,c) that's still in the domain. for (unsigned int i = 0; i < grid[r][c].domain.size(); i++) { //Create a new grid for the next level. gridSpot newGrid[5][5]; for (int x = 0; x < gridSize; x++) for (int y = 0; y < gridSize; y++) { newGrid[x][y].domain.assign(grid[x][y].domain.begin(), grid[x][y].domain.end()); newGrid[x][y].placed = grid[x][y].placed; } //Put this tile on the new grid. newGrid[r][c].domain.clear(); newGrid[r][c].domain.push_back(grid[r][c].domain[i]); newGrid[r][c].placed = true; //If this is the last level, we've finished! if (l == gridSize * gridSize) { // for (int x = 0; x < gridSize; x++) // { // for (int y = 0; y < gridSize; y++) // cout << newGrid[y][x].domain[0]->getId() + 1 << "." << newGrid[y][x].domain[0]->getOrientation() << " "; // cout << endl; // } return true; } //Start thinking. timer.startThink(); //First get rid of any tiles with the same id as this one. for (int x = 0; x < gridSize; x++) for (int y = 0; y < gridSize; y++) if (!newGrid[x][y].placed) for (unsigned int z = 0; z < newGrid[x][y].domain.size(); z++) { consistencyChecks++; if (newGrid[r][c].domain[0]->getId() == newGrid[x][y].domain[z]->getId()) { newGrid[x][y].domain.erase(newGrid[x][y].domain.begin() + z); z--; } } //If the tile west of us hasn't been placed yet. if (r > 0 && !newGrid[r-1][c].placed) for (unsigned int i = 0; i < newGrid[r-1][c].domain.size(); i++) { consistencyChecks++; if (!newGrid[r][c].domain[0]->matches(newGrid[r-1][c].domain[i], W)) { newGrid[r-1][c].domain.erase(newGrid[r-1][c].domain.begin() + i); i--; } } //If the tile east of us hasn't been placed yet. if (r < gridSize - 1 && !newGrid[r+1][c].placed) for (unsigned int i = 0; i < newGrid[r+1][c].domain.size(); i++) { consistencyChecks++; if (!newGrid[r][c].domain[0]->matches(newGrid[r+1][c].domain[i], E)) { newGrid[r+1][c].domain.erase(newGrid[r+1][c].domain.begin() + i); i--; } } //If the tile north of us hasn't been placed yet. if (c > 0 && !newGrid[r][c-1].placed) for (unsigned int i = 0; i < newGrid[r][c-1].domain.size(); i++) { consistencyChecks++; if (!newGrid[r][c].domain[0]->matches(newGrid[r][c-1].domain[i], N)) { newGrid[r][c-1].domain.erase(newGrid[r][c-1].domain.begin() + i); i--; } } //If the tile south of us hasn't been placed yet. if (c < gridSize - 1 && !newGrid[r][c+1].placed) for (unsigned int i = 0; i < newGrid[r][c+1].domain.size(); i++) { consistencyChecks++; if (!newGrid[r][c].domain[0]->matches(newGrid[r][c+1].domain[i], S)) { newGrid[r][c+1].domain.erase(newGrid[r][c+1].domain.begin() + i); i--; } } //The row and column to try next. int nR = 0, nC = 0; unsigned int minimumDomain = 1000000; //Look for the smallest domain. for (int x = 0; x < gridSize; x++) for (int y = 0; y < gridSize; y++) if (!newGrid[x][y].placed && newGrid[x][y].domain.size() < minimumDomain) { minimumDomain = newGrid[x][y].domain.size(); nR = x; nC = y; } //Start doing. timer.startDo(); //Try recursing deeper. if (recurse(newGrid, nR, nC, l+1)) 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; } //Thinking about initializing the constraints. timer.startThink(); //The initial grid. gridSpot grid[5][5]; for (int x = 0; x < gridSize; x++) for (int y = 0; y < gridSize; y++) { grid[x][y].placed = false; grid[x][y].domain.assign(availableTiles.begin(), availableTiles.end()); } //Start doing. timer.startDo(); //Start the recursion. recurse(grid, 0, 0, 1); //Stop the timer. timer.stop(); } //Stop the execution timer. executionTimer.stop(); cout << endl; cout << "Average total 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; }