Listing 3

/*
 * puzzle.c:  This QNX Windows program creates a
 * moving tile puzzle, with 15 numbered tiles in a
 * 4 X 4 enclosure.
 *
 * It is reproduced here by permission of Quantum
 * Software Systems, Ltd of Kanata, Canada.
 */

#include <stdio.h>
#include <windows.h>

#define RECT_HEIGHT    250
#define RECT_WIDTH 360
#define PANE_BORDER    140

#define PANE_HEIGHT    ((rows)*RECT_HEIGHT + 120)
#define PANE_WIDTH ((rows)*RECT_WIDTH + 120)
#define CASE(str)  (((str)[0] << 8)  (str)[1])

EVENT_MSG  msg;

int           rows = 4, max_num;
int       b[20][20], old[20][20];
int           mv;

main()
   {
   int go = YES, wid, pid;

   /* connect to QWindows */
   if ( !GraphicsOpen (NULL))
       exit ( -1 );

SetName("Puzzle", NULL);    /*  Optional */

pid = PictureOpen ( "pict", NULL, NULL, LIGHT_GRAY,
                    NULL, NULL, NULL );
PictureHighlightOptions( NULL, 'N', 0, 0 );

init();
display();

wid = WindowOpen ("Puzzle", PANE_HEIGHT, PANE_WIDTH,
                  "MT480-r;s", NULL, NULL, pid);
/*
 *  Put control button in top frame.
 */

WindowBarCurrent( TOP, NULL );
SetButton( NULL, WHITE, NULL );
DrawAt( 300, 120 );
AttachDialog("Options", NULL,
    "Scramble;Size@(Easy03;The Famous 4 x 404;Hard
             05;Very Hard06;You've got to be
             Kidding 10)^R",
    NULL, "MD", NULL, NULL);
DrawButton( "Options", NULL, "Ns;D", "op");
Draw();
WindowBarCurrent( NULL, NULL );

load_icon( "/windows/apps/puzzle" );
DrawStart(NULL, 0);

while ( go )
    {
    GetEvent ( 0, &msg, sizeof(msg));

    switch ( Event ( &msg ) )
        {
        case QUIT:
            WindowClose( 0, NULL );
            go = NO;
            break;

        case TERMINATED:
            go = NO;
            break;

        case CLICK:
            if (msg.hdr.code == 'S') {
              /*  game piece selected */
              move(atoi(msg.hdr.key) - 1);
              mv++;
              win ();
              }

            break;

        case DIALOG:
            switch(CASE(msg.hdr.key)) {
            case '03': case '04': case '05': case '06':
              case '07': case '10':
              rows = atoi(msg.hdr.key);
              case 'Sc':
                 new_game();
              break;
              }
          }
       }
   GraphicsClose();
   exit (0);
   }

init() {
   int i, j, r, loop;

   max_num = rows*rows;

   for (i = 0; i < rows; i++)
       for (j = 0; j < rows; j++) {
          b[i][j] = ( i * rows + j);
          old[i][j] = -1;
          }

   i = j = rows - 1;
   srand(get_ticks());
   r = rand() % 200 + 500;

   for (loop = 0; loop < 5000; ++loop) {
       switch (rand() % 4) {
       case 0:
          if (i != 0) {
              b[i][j] = b[i - 1][j];
              b[i - 1][j] = max_num;
              --i;
              }
          break;

       case 1:
          if (i != rows - 1) {
              b[i][j] = b[i + 1][j];
              b[i + 1][j] = max_num;
              i++;
              }
          break;

       case 2:
          if (j != 0) {
              b[i][j] = b[i][j - 1];
              b[i][j - 1] = max_num;
              --j;
              }
          break;

       case 3:
          if (j != rows - 1) {
              b[i] [j] = b[i] [j + 1];
              b[i] [j + 1] = max_num;
              j++;
              }
          break;
          }
       }
   }

display() {
   int i, j;

   for (i = 0; i < rows; i++)
       if (memcmp(b[i], old[i], rows)) {
          for (j = 0; j < rows; j++)
              if (b[i][j] != max_num)
                 draw_number(i, j, b[i][j] + 1);
       memcpy(old[i], b[i], rows);
       }
   }

draw_number( row, col, number)
int row, col, number;
   {
   char tbuf[4];

   tsprintf(tbuf, "%2d", number);

   SetColor ( "T", BLACK );

   DrawAt (PANE_BORDER + (row*RECT_HEIGHT) + CHARH/2,
          PANE_BORDER + (col*RECT_WIDTH ));

   DrawText( tbuf, 0, 0, 0, "Sem", tbuf);

   Draw ();
   }

move_number( row, col, number)
int row, col, number;
   {
   char buffer[7];

   tsprintf(buffer, "%2d", number);
   ShiftTo( buffer,    PANE_BORDER + (row*RECT_HEIGHT) + CHARH/2,
                    PANE_BORDER + (col*RECT_WIDTH ));
   }


move(m)
int m;
   {
   int i1, j1, i, j, i2, j2, c;

   i1 = j1 = -1;

   for (i = 0; i1 == -1 && i < rows; i++)
       for (j = 0; j < rows; j++)
          if (b[i][j] == m) {
              i1 = i;
              j1 = j;
              break;
              }

   if (i1 != -1) {
       i2 = j2 = -1;
       for (i = 0; i < rows; i++)

          if (b[i][j1] == max_num) {
              i2 = i;
              j2 = j1;
              break;
              }

       if (i2 == -1)
          for (j = 0; j < rows; j++)
              if (b[i1][j] == max_num) {
                 i2 = i1;
                 j2 = j;
                 break;
                 }

       if (i2 == -1)
          return;
       }
   else
       return;

   /*  Hold picture for smoother updates */
   PictureHold();

   if (i1 == i2)
       if (j1 < j2)
          for (j = j2 - 1; j >= j1; --j) {
              b[i1][j + 1] = b[i1][j];
              move_number(i1, j+1, b[i1][j]+1);
              }
       else
          for (j = j2; j < j1; j++) {
              b[i1][j = b[i1][j + 1];
              move_number(i1, j, b[i1][j+1]+1);
              }

   else
       if (i1 < i2)
          for (i = i2 - 1; i >= i1; --i) {
              b[i + 1][j1] = b[i][j1];
              move_number(i+1, j1, b[i][j1]+1);
              }
       else
          for (i = i2; i < i1; i++) {
              b[i][j1] = b[i + 1][j1];
              move_number(i, j1, b[i+1][j1]+1);
              }
   b[i1][j1] = max_num;

   /* update current picture */
   PictureContinue();
   }

win() {
   int c = 0, i, j;
   char buf[50];

   for (i = 0; i < rows; i++)
       for (j = 0; j < rows; j++)
           if (b[i][j] < c)
              return( 0 );
           else
              c = (int) b[i][j];

   tsprintf(buf, "You got it in %d moves!", mv);
   Notice( NULL, "You Win", NULL, "W", buf);

   return( 1 );
   }

new_game() {
   RECT_AREA area;

   WindowInfo(NULL,&area,NULL,NULL,NULL,NULL);

   /* erase all elements in the picture */
   WindowHold();
   Erase( ALL );

   area.height = PANE_HEIGHT;
   area.width = PANE_WIDTH;

    init();
   display();
   mv = 0;

   WindowChange( &area, NULL, KEEP, NULL, "!" );
   WindowContinue();
   }