Wednesday, November 25, 2015

Solving a Puzzle

Earlier this year, I was given a puzzle as a gift.  It's a simple frame with Tetris-like blocks of different shapes, and the object is to get all nine blocks into the frame.  Making it more difficult, there will be leftover empty squares throughout the puzzle.  I played around with it for a while, and it wasn't too difficult to get to within one square of completing it, but I was probably nowhere close to actually solving it.

Being a programmer, I decided it would be more fun to program my computer to solve the puzzle.  I spent a couple of hours on it, and came up with a nice result.  I'll share the result below, along with some observations about the program and the puzzle.

First, here's a link to the puzzle:  http://www.palmettopuzzleworks.net/
You can buy it directly through Etsy for $20.


Before I delve into my program, let me start by suggesting that this might make a great programming assignment.  It's a very well-defined problem, but with a number of subtleties to watch out for.

So let me show you my solution.  My intuition was that this would be a computationally complex problem, and it looked like a good fit regardless, so my language of choice was C.

The first thing to do is define the data structures.  I've found that in the vast majority of problems, the data structures drive the code.  If you have the wrong data structures, you'll never get a good solution, but with the right data structures, the code mostly flows into place.

As you can see in the picture, the board is an 11x11 grid with some spaces filled in.  That makes the board an easy two-dimensional array.  The largest piece fits in a 4x5 grid, so I created a three-dimensional array for the pieces initialized to a 1 for the spots that are filled and a 0 for the squares not occupied.  Thinking ahead, I'm going to have to rotate each piece, so I need to use a square for each piece, not just a rectangle, so each piece is a 5x5 grid.


int grid[11][11];
int pieces[9][5][5] =
{
   // first four pieces are "big" and take four rows
   { { 0,1,1,0,0 },
     { 0,1,1,1,0 },
     { 1,1,1,1,1 },
     { 0,1,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,1,0,0 },
     { 0,1,1,1,0 },
     { 1,1,1,0,0 },
     { 0,1,0,0,0 },
     { 0,0,0,0,0 } },
   { { 0,1,0,0,0 },
     { 0,1,1,1,0 },
     { 0,1,1,1,1 },
     { 1,1,1,1,0 },
     { 0,0,0,0,0 } },
   { { 0,1,0,0,0 },
     { 1,1,1,1,0 },
     { 0,1,1,1,1 },
     { 0,1,0,1,0 },
     { 0,0,0,0,0 } },
   // remaining five pieces take three rows
   { { 0,1,1,1,0 },
     { 1,1,1,0,0 },
     { 1,1,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,1,1,0 },
     { 1,1,1,0,0 },
     { 1,0,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,1,0,0 },
     { 1,1,1,1,0 },
     { 1,0,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 0,1,1,1,0 },
     { 1,1,1,0,0 },
     { 1,0,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,0,0,0 },
     { 1,1,1,1,0 },
     { 0,1,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } }
};

Initializing the grid is pretty simple: a zero represents an open space, a number indicates a piece (1 through 9), and another constant represents the border (10).  Because the border is more filled than open, the code fills the borders, then opens up the spaces where a piece can intrude.


#define BORDER 10

void init_border(void)
{
   // Set grid to zero
   memset(grid,0,sizeof(grid));
   // Border defaults to blocked
   for(int i=0;i<11;++i)
   {
      grid[0][i] = grid[i][0] = grid[10][i] = grid[i][10] = BORDER;
   }
   // Clear spots
   grid[0][2] = grid[0][4] = grid[0][8] = 0;
   grid[10][1] = grid[10][4] = grid[10][6] = grid[10][8] = 0;
   grid[3][0] = grid[5][0] = grid[8][0] = 0;
   grid[1][10] = grid[5][10] = grid[7][10] = grid[9][10] = 0;
}

Now here we come to the key trick to make the whole thing work.  I have to try each piece in four different rotations.  If I rotate the pieces on the fly, the program will be spending most of its time doing rotations.  That's not a good idea for something that is already likely to be quite computationally intensive.  So why not save the work and just do it once?  This raises one subtlety to the problem: The pieces all fit in a 5x5 grid, but have at least one row or column empty.  The solving code will want to assume that the top row and left column are non-empty, so each rotation has to also shift to make that happen.  With that explained, there's nothing particularly fancy about the code, though I'll admit that on the first run, it mangled the pieces by doing some reflections as well as rotations.


int all_pieces[9][4][5][5];

void init_rotations(void)
{
   for (int piece=0; piece<9; ++piece)
   {
      for (int rotation=0; rotation<4; ++rotation )
      {
         if ( rotation == 0 )
         {
            for ( int r=0;r<5;++r )
            {
               for ( int c=0;c<5;++c )
               {
                  all_pieces[piece][rotation][r][c] = pieces[piece][r][c];
               }
            }
         }
         else
         {
            for ( int r=0;r<5;++r )
            {
               for ( int c=0;c<5;++c )
               {
                  all_pieces[piece][rotation][4-c][r] = all_pieces[piece][rotation-1][r][c];
               }
            }
         }
      }
   }
   // Now shift rotations to all be in the upper-left (no all-zero rows or all-zero columns
   for (int piece=0; piece<9; ++piece)
   {
      for (int rotation=1; rotation<4; ++rotation )
      {
         for ( int i=0; i<5; ++i )
         {
            if ( all_pieces[piece][rotation][0][i] ) goto next; // Something in the first row
         }
         for ( int r=0;r<4;++r )
         {
            for ( int c=0;c<5;++c )
            {
               all_pieces[piece][rotation][r][c] = all_pieces[piece][rotation][r+1][c];
            }
         }
         for ( int c=0;c<5;++c )
         {
            all_pieces[piece][rotation][4][c] = 0;
         }
         --rotation; continue;
      next: ;
      }
   }
   for (int piece=0; piece<9; ++piece)
   {
      for (int rotation=1; rotation<4; ++rotation )
      {
         for ( int i=0; i<5; ++i )
         {
            if ( all_pieces[piece][rotation][i][0] ) goto next2; // Something in the first column
         }
         for ( int r=0;r<5;++r )
         {
            for ( int c=0;c<4;++c )
            {
               all_pieces[piece][rotation][r][c] = all_pieces[piece][rotation][r][c+1];
            }
         }
         for ( int r=0;r<5;++r )
         {
            all_pieces[piece][rotation][r][4] = 0;
         }
         --rotation; continue;
      next2: ;
      }
   }
}

Before writing the code to try all the combinations and find the solution, I need to be able to display the board.  I also need to be able to display the pieces to check that I've coded and rotated them correctly.  This is a place where spending a few minutes extra to have nice readable output can save a lot of time in understanding the results.  Using a graphics toolkit or generating HTML would be overkill for this problem.  Simple printing to the console would be fine, but tossing in some ANSI color codes to make the pieces easier to visualize would be well worth the effort.  Fortunately, I have a handy set of ANSI color code defines:


#define _CN "\033[m"          // Reset to default color and attributes
#define _CRED "\033[1;31m"    // Set text to bold red
#define _Cred "\033[31m"      // Set text to red
#define _C_red "\033[4;31m"   // Set text to underlined and red
#define _C_RED "\033[4;1;31m" // Set text to underline and bold red
#define _Bred "\033[41m"      // Set background to red
#define _CGRN "\033[1;32m"
#define _Cgrn "\033[32m"
#define _C_grn "\033[4;32m"
#define _C_GRN "\033[4;1;32m"
#define _Bgrn "\033[42m"
#define _CYEL "\033[1;33m"
#define _Cyel "\033[33m"
#define _C_yel "\033[4;33m"
#define _C_YEL "\033[4;1;33m"
#define _Byel "\033[43m"
#define _CBLU "\033[1;34m"
#define _Cblu "\033[34m"
#define _C_blu "\033[4;34m"
#define _C_BLU "\033[4;1;34m"
#define _Bblu "\033[44m"
#define _CMAG "\033[1;35m"
#define _Cmag "\033[35m"
#define _C_mag "\033[4;35m"
#define _C_MAG "\033[4;1;35m"
#define _Bmag "\033[45m"
#define _CCYN "\033[1;36m"
#define _Ccyn "\033[36m"
#define _C_cyn "\033[4;36m"
#define _C_CYN "\033[4;1;36m"
#define _Bcyn "\033[46m"
#define _CWHT "\033[1;37m"
#define _Cwht "\033[37m"
#define _C_wht "\033[4;37m"
#define _C_WHT "\033[4;1;37m"
#define _Bwht "\033[47m"
#define _CBLK "\033[1;30m"
#define _Cblk "\033[30m"
#define _C_blk "\033[4;30m"
#define _C_BLK "\033[4;1;30m"
#define _Bblk "\033[40m"
/*
 * ANSI attribute strings
 */
#define _A_BOLD "\033[1m"         // Leave color and underline-status as-is, set BOLD
#define _A_UNDER "\033[4m"        // Leave color and bold-status as-is, set UNDERLINE
#define _A_BOLD_UNDER "\033[1;4m" // Just an optimized version of _A_BOLD _A_UNDER

So using those defines to display the grid and the pieces is fairly straightforward.  You'll notice that I take advantage of C's string concatenation--any two strings separated only by whitespace are treated as a single string.


void display_grid(void)
{
   printf(_Bblk"             "_CN"\n");
   for (int row=0;row<11;++row)
   {
      printf(_Bblk" "_CN);
      for (int col=0;col<11;++col)
      {
         char c;
         c = grid[row][col] + '0';
         switch(grid[row][col]) {
            case 0: c=' '; printf(_Bwht); break;
            case 1:
               printf(_Bcyn); break;
            case 2:
            case 3:
               printf(_Bblu); break;
            case 4:
            case 5:
               printf(_Bmag); break;
            case 6:
            case 7:
               printf(_Byel); break;
            case 8:
               printf(_Bred); break;
            case 9:
               printf(_Bgrn); break;
            case BORDER: c=' '; printf(_Bblk); break;
         }
         printf("%c"_CN,c);
      }
      printf(_Bblk" "_CN"\n");
   }
   printf(_Bblk"             "_CN"\n");
   printf("\n");
}

void display_pieces(void)
{
   for (int piece=0;piece<9;++piece)
   {
      printf("Piece %d rotations\n",piece+1);
      for (int row=0;row<5;++row)
      {
         for (int rotation=0;rotation<4;++rotation)
         {
            for (int col=0;col<5;++col)
            {
               char c;
               c = all_pieces[piece][rotation][row][col] ? '1' + piece : ' ';
              
               switch(all_pieces[piece][rotation][row][col] * (piece+1)) {
                  case 0: c=' '; printf(_Bwht); break;
                  case 1:
                     printf(_Bcyn); break;
                  case 2:
                  case 3:
                     printf(_Bblu); break;
                  case 4:
                  case 5:
                     printf(_Bmag); break;
                  case 6:
                  case 7:
                     printf(_Byel); break;
                  case 8:
                     printf(_Bred); break;
                  case 9:
                     printf(_Bgrn); break;
                  default:
                     printf("invalid"); break;
               }
               printf("%c"_CN,c);
            }
            printf("      ");
         }
         printf("\n");
      }
   }
}

With the above code, I have all that is needed to display the pre-rotated pieces (which proved vital for debugging mangled rotations) and the game board with any pieces placed in it.  Starting out, it looks like:

Piece 1 rotations
 11          1           1        1         
 111        11        11111      1111       
11111      111         111        111       
 1         1111         11        11        
             1                    1         
Piece 2 rotations
222         2           2         2 2       
 222       222         222       2222       
222        2222       222         222       
 2         2 2         222         2        
                                            
Piece 3 rotations
 3           3         3333      3          
 333        333       3333       3333       
 3333       333        333       333        
3333       3333          3       333        
              3                   3         
Piece 4 rotations
 4           4         4 4         4        
4444        444       4444       4444       
 4444       44         4444       44        
 4 4       4444          4       444        
            4                     4         
Piece 5 rotations
 555       5           555       55         
555        555         555       555        
555        555        555        555        
            55                     5        
                                            
Piece 6 rotations
6666       6           6 6       666        
666        666         666        66        
6 6        66         6666       666        
           666                     6        
                                            
Piece 7 rotations
777         7          7 7       777        
7777       777        7777        77        
7 7        77          777       777        
           777                    7         
                                            
Piece 8 rotations
 888       8           8 8       88         
888        888         888        88        
8 8        88         888        888        
            88                     8        
                                            
Piece 9 rotations
99          9          99         99        
9999        99        9999       999        
 99        999          99       99         
           99                     9         
                                            
Empty board
             
             
             
             
             
             
             
             
             
             
             
             
             

So the the code to actually go through the placements feels anticlimactic at this point.  It simply marches through the puzzle, trying every possible combination in much the way I would do if I were doing an exhaustive search by hand.  Here's the complete program:


/*
 * Find the solution or solutions to The Infuriater
 *
 * By Preston Crow
 *
 * gcc -std=c99 -W -Wall puzzle.c -o puzzle -O3 && puzzle
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _CN "\033[m"          // Reset to default color and attributes
#define _CRED "\033[1;31m"    // Set text to bold red
#define _Cred "\033[31m"      // Set text to red
#define _C_red "\033[4;31m"   // Set text to underlined and red
#define _C_RED "\033[4;1;31m" // Set text to underline and bold red
#define _Bred "\033[41m"      // Set background to red
#define _CGRN "\033[1;32m"
#define _Cgrn "\033[32m"
#define _C_grn "\033[4;32m"
#define _C_GRN "\033[4;1;32m"
#define _Bgrn "\033[42m"
#define _CYEL "\033[1;33m"
#define _Cyel "\033[33m"
#define _C_yel "\033[4;33m"
#define _C_YEL "\033[4;1;33m"
#define _Byel "\033[43m"
#define _CBLU "\033[1;34m"
#define _Cblu "\033[34m"
#define _C_blu "\033[4;34m"
#define _C_BLU "\033[4;1;34m"
#define _Bblu "\033[44m"
#define _CMAG "\033[1;35m"
#define _Cmag "\033[35m"
#define _C_mag "\033[4;35m"
#define _C_MAG "\033[4;1;35m"
#define _Bmag "\033[45m"
#define _CCYN "\033[1;36m"
#define _Ccyn "\033[36m"
#define _C_cyn "\033[4;36m"
#define _C_CYN "\033[4;1;36m"
#define _Bcyn "\033[46m"
#define _CWHT "\033[1;37m"
#define _Cwht "\033[37m"
#define _C_wht "\033[4;37m"
#define _C_WHT "\033[4;1;37m"
#define _Bwht "\033[47m"
#define _CBLK "\033[1;30m"
#define _Cblk "\033[30m"
#define _C_blk "\033[4;30m"
#define _C_BLK "\033[4;1;30m"
#define _Bblk "\033[40m"
/*
 * ANSI attribute strings
 */
#define _A_BOLD "\033[1m"         // Leave color and underline-status as-is, set BOLD
#define _A_UNDER "\033[4m"        // Leave color and bold-status as-is, set UNDERLINE
#define _A_BOLD_UNDER "\033[1;4m" // Just an optimized version of _A_BOLD _A_UNDER

int grid[11][11];
int pieces[9][5][5] =
{
   // first four pieces are "big" and take four rows
   { { 0,1,1,0,0 },
     { 0,1,1,1,0 },
     { 1,1,1,1,1 },
     { 0,1,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,1,0,0 },
     { 0,1,1,1,0 },
     { 1,1,1,0,0 },
     { 0,1,0,0,0 },
     { 0,0,0,0,0 } },
   { { 0,1,0,0,0 },
     { 0,1,1,1,0 },
     { 0,1,1,1,1 },
     { 1,1,1,1,0 },
     { 0,0,0,0,0 } },
   { { 0,1,0,0,0 },
     { 1,1,1,1,0 },
     { 0,1,1,1,1 },
     { 0,1,0,1,0 },
     { 0,0,0,0,0 } },
   // remaining five pieces take three rows
   { { 0,1,1,1,0 },
     { 1,1,1,0,0 },
     { 1,1,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,1,1,0 },
     { 1,1,1,0,0 },
     { 1,0,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,1,0,0 },
     { 1,1,1,1,0 },
     { 1,0,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 0,1,1,1,0 },
     { 1,1,1,0,0 },
     { 1,0,1,0,0 },
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } },
   { { 1,1,0,0,0 },
     { 1,1,1,1,0 },
     #if 0
     { 0,1,1,0,0 },
     #else
     { 1,1,1,0,0 },
     #endif
     { 0,0,0,0,0 },
     { 0,0,0,0,0 } }
};

int all_pieces[9][4][5][5];

int place[9][3]; // rotation,row,col for the piece

#define BORDER 10

void init_border(void)
{
   // Set grid to zero
   memset(grid,0,sizeof(grid));
   // Border defaults to blocked
   for(int i=0;i<11;++i)
   {
      grid[0][i] = grid[i][0] = grid[10][i] = grid[i][10] = BORDER;
   }
   // Clear spots
   grid[0][2] = grid[0][4] = grid[0][8] = 0;
   grid[10][1] = grid[10][4] = grid[10][6] = grid[10][8] = 0;
   grid[3][0] = grid[5][0] = grid[8][0] = 0;
   grid[1][10] = grid[5][10] = grid[7][10] = grid[9][10] = 0;
}

void init_rotations(void)
{
   for (int piece=0; piece<9; ++piece)
   {
      for (int rotation=0; rotation<4; ++rotation )
      {
         if ( rotation == 0 )
         {
            for ( int r=0;r<5;++r )
            {
               for ( int c=0;c<5;++c )
               {
                  all_pieces[piece][rotation][r][c] = pieces[piece][r][c];
               }
            }
         }
         else
         {
            for ( int r=0;r<5;++r )
            {
               for ( int c=0;c<5;++c )
               {
                  all_pieces[piece][rotation][4-c][r] = all_pieces[piece][rotation-1][r][c];
               }
            }
         }
      }
   }
   // Now shift rotations to all be in the upper-left (no all-zero rows or all-zero columns
   for (int piece=0; piece<9; ++piece)
   {
      for (int rotation=1; rotation<4; ++rotation )
      {
         for ( int i=0; i<5; ++i )
         {
            if ( all_pieces[piece][rotation][0][i] ) goto next; // Something in the first row
         }
         for ( int r=0;r<4;++r )
         {
            for ( int c=0;c<5;++c )
            {
               all_pieces[piece][rotation][r][c] = all_pieces[piece][rotation][r+1][c];
            }
         }
         for ( int c=0;c<5;++c )
         {
            all_pieces[piece][rotation][4][c] = 0;
         }
         --rotation; continue;
      next: ;
      }
   }
   for (int piece=0; piece<9; ++piece)
   {
      for (int rotation=1; rotation<4; ++rotation )
      {
         for ( int i=0; i<5; ++i )
         {
            if ( all_pieces[piece][rotation][i][0] ) goto next2; // Something in the first column
         }
         for ( int r=0;r<5;++r )
         {
            for ( int c=0;c<4;++c )
            {
               all_pieces[piece][rotation][r][c] = all_pieces[piece][rotation][r][c+1];
            }
         }
         for ( int r=0;r<5;++r )
         {
            all_pieces[piece][rotation][r][4] = 0;
         }
         --rotation; continue;
      next2: ;
      }
   }
}

void display_grid(void)
{
   printf(_Bblk"             "_CN"\n");
   for (int row=0;row<11;++row)
   {
      printf(_Bblk" "_CN);
      for (int col=0;col<11;++col)
      {
         char c;
         c = grid[row][col] + '0';
         switch(grid[row][col]) {
            case 0: c=' '; printf(_Bwht); break;
            case 1:
               printf(_Bcyn); break;
            case 2:
            case 3:
               printf(_Bblu); break;
            case 4:
            case 5:
               printf(_Bmag); break;
            case 6:
            case 7:
               printf(_Byel); break;
            case 8:
               printf(_Bred); break;
            case 9:
               printf(_Bgrn); break;
            case BORDER: c=' '; printf(_Bblk); break;
         }
         printf("%c"_CN,c);
      }
      printf(_Bblk" "_CN"\n");
   }
   printf(_Bblk"             "_CN"\n");
   printf("\n");
}

void display_pieces(void)
{
   for (int piece=0;piece<9;++piece)
   {
      printf("Piece %d rotations\n",piece+1);
      for (int row=0;row<5;++row)
      {
         for (int rotation=0;rotation<4;++rotation)
         {
            for (int col=0;col<5;++col)
            {
               char c;
               c = all_pieces[piece][rotation][row][col] ? '1' + piece : ' ';
              
               switch(all_pieces[piece][rotation][row][col] * (piece+1)) {
                  case 0: c=' '; printf(_Bwht); break;
                  case 1:
                     printf(_Bcyn); break;
                  case 2:
                  case 3:
                     printf(_Bblu); break;
                  case 4:
                  case 5:
                     printf(_Bmag); break;
                  case 6:
                  case 7:
                     printf(_Byel); break;
                  case 8:
                     printf(_Bred); break;
                  case 9:
                     printf(_Bgrn); break;
                  default:
                     printf("invalid"); break;
               }
               printf("%c"_CN,c);
            }
            printf("      ");
         }
         printf("\n");
      }
   }
}

void remove_piece(int piece)
{
   for (int row=0;row<11;++row)
   {
      for (int col=0;col<11;++col)
      {
         if ( grid[row][col] == piece+1 )
         {
            grid[row][col] = 0;
         }
      }
   }
}

int place_piece_here(int piece,int rotation,int row,int col)
{
   for ( int r=0; r<5; ++r )
   {
      for ( int c=0; c<5; ++c )
      {
         if ( all_pieces[piece][rotation][r][c] )
         {
            if ( r+row >= 11 || c+col >=11 ) return 0; // Off the grid
            if ( grid[r+row][c+col] ) return 0; // Spot full
         }
      }
   }
   // Yes, it goes here; put it in the grid
   for ( int r=0; r<5; ++r )
   {
      for ( int c=0; c<5; ++c )
      {
         if ( all_pieces[piece][rotation][r][c] )
         {
            grid[r+row][c+col] = piece+1;
         }
      }
   }
   return 1;
}

// Place a piece if possible; return true if placed
int place_piece(int piece)
{
   for ( int rotation=place[piece][0]; rotation<4; ++rotation,place[piece][1]=0,place[piece][2]=0 )
   {
      for ( int row=place[piece][1]; row<9; ++row,place[piece][2]=0 )
      {
         for ( int col=place[piece][2]; col<9; ++col )
         {
            if ( place_piece_here(piece,rotation,row,col) )
            {
               place[piece][0]=rotation;
               place[piece][1]=row;
               place[piece][2]=col;
               return 1;
            }
         }
      }
   }
   return 0;
}

int main(int argc,char *argv[])
{
   (void)argc;(void)argv;
   init_border();
   init_rotations();
   display_pieces();
   printf("Empty board\n");
   display_grid();

   int solutions=0;
   for (int i=0; i<9; ++i )
   {
      if ( place_piece(i) )
      {
         if ( i == 8 )
         {
            ++solutions;
            printf("Solution %d found:\n",solutions);
            display_grid();
            remove_piece(i);
         }
         else
         {
            if ( i >=7 && 0 ) // debug
            {
               printf("Placed %d pieces:\n",i+1);
               display_grid();
            }
            if ( i != 8 )
            {
               for ( int j=0;j<3; ++j)
                  place[i+1][j]=0;
            }
            continue;
         }
      }
      if ( i == 0 )
      {
         if ( !solutions )
         {
            printf("Failed to solve puzzle\n");
         }
         break;
      }
//printf("Can't place piece %d; remove piece %d\n",i+1,i);
      // Advance the previous piece
      --i;
      remove_piece(i);
//      printf("piece %d was at rot,row,col %d,%d,%d\n",i+1,place[i][0],place[i][1],place[i][2]);
      ++place[i][2];
      // Go back one more as the loop will increment
      --i;
//      printf("piece %d was at rot,row,col %d,%d,%d\n",i+1,place[i][0],place[i][1],place[i][2]);
   }
   //display_grid();
   return 0;
}

Now much to my surprise, once I had debugged it, it ran quite fast.  I had guessed that it might run for hours before finding the solution, but it ran to completion in under a minute.  The biggest surprise was that it found not one, but eight solutions!  I shared my results with the creator of the puzzle.  He said he had also used a computer to verify that there was only one solution, but apparently his program had a bug.  He has made a slight change to the puzzle, so if you buy it now, it will have one unique solution.

In case you're interested in buying and playing with the puzzle yourself, I won't spoil it by posting the solution here.  It's simple enough to run the program if you want to see them.

And a note on copyright: While I hold the copyright on all my posts, including the above code, in my opinion, the ANSI color code defines are simple and obvious enough that they can hardly be considered copyrightable, so feel free to use them.