### 2.5.11 Example 2, The 15 Puzzle

This example implements the old 15-puzzle game using a collection of push buttons. All buttons have the same fixed dimension. When a button is pressed, it swaps itself with the empty square if it is adjacent to the empty square. This is done by setting a new location for the button. The layout of widgets in this program is the following.

We first create a toplevel frame `toplevel`, with a vertical orientation. It contains three children widgets; a `label` at the top; a frame `frame` in the middle for holding the game buttons; and another frame at the bottom for a `Quit` button and a `Randomize` button. For `frame`, we set its width and height both to 0 so that it will not be expanded by its parent `toplevel`, in case `toplevel` is resized. Finally, we create 15 game buttons. The location of each game buttons is encoded as an integer 4*row+col, and is saved at the IntData slots in the widget data structure.

    /***************** Example 2 ********************************/

    #include "EZ.h"                                     /* the header file     */

    static void  cleanExit(EZ_Widget *, void *);        /* quit btn callback   */
    static void  button_callback(EZ_Widget *, void *);  /* button callback     */
    static void  randomize(EZ_Widget *, void *);        /* scramble the puzzle */

    static EZ_Widget *buttons[4][4];                    /* the buttons         */
    static int width = 39, height = 33;                 /* size of the buttons */
    static int emptyx = 3, emptyy = 3;                  /* the empty slot      */

    main(int ac, char **av)
    {
      int        i,j, k;
      char       str[4];
      EZ_Widget  *toplevel, *frame, *label, *tmp, *tmp1;

      EZ_Initialize(ac,av,0);                                 /* Initialize EZWGL   */
                                                              /* the toplevel frame */
      toplevel = EZ_CreateWidget(EZ_WIDGET_FRAME,NULL,
                                 EZ_STACKING, EZ_VERTICAL,    /* vert. orientation  */
                                 0);

      label = EZ_CreateWidget(EZ_WIDGET_LABEL, toplevel,
                              EZ_LABEL_STRING, "The 15 Puzzle",
                              EZ_FOREGROUND, "blue",
                              EZ_FONT_NAME,
                                "-Adobe-Times-Bold-R-Normal--*-180-*-*-*-*-*-*",
                              0);

      frame = EZ_CreateWidget(EZ_WIDGET_FRAME, toplevel,         /* the button box  */
                              EZ_PADX,0, EZ_PADY,0,              /* no padding      */
                              EZ_BORDER_TYPE,EZ_BORDER_SUNKEN,   /* a sunken border */
                              EZ_BORDER_WIDTH, 2,                /* of width 2      */
                              EZ_WIDTH, 0, EZ_HEIGHT, 0,         /* always keep it  */
                              0);                                /* at its min size */

      tmp = EZ_CreateWidget(EZ_WIDGET_FRAME, toplevel,
                            0);                /* a frame to hold two handy buttons  */
      tmp1 = EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, tmp,         /* a quit button  */
                             EZ_LABEL_STRING, "Quit",
                             0);
      EZ_AddWidgetCallBack(tmp1, EZ_CALLBACK, cleanExit, NULL, 0);

      tmp1 = EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, tmp,    /* and a new game btn */
                             EZ_LABEL_STRING, "Scramble",
                             0);
      EZ_AddWidgetCallBack(tmp1, EZ_CALLBACK, randomize, NULL,0);

      for(i = 0; i < 4; i++)                       /* create buttons for the puzzle */
        for(j = 0; j < 4; j++)                     /* 4 rows, 4 columns,    1 btns  */
          {
            if(i + j != 6) /* ignore the last one */
              {
                k = 4 * i + j;
                sprintf(str,"%d",k+1);                            /* btn at [i,j] */
                tmp = buttons[i][j] =
                      EZ_CreateWidget(EZ_WIDGET_NORMAL_BUTTON, frame,
                                      EZ_LABEL_STRING,str,
                                      EZ_X, 3 + j * width, /* and a perminent size     */
                                      EZ_Y, 3 + i * height,
                                      EZ_WIDTH, width, EZ_HEIGHT, height,
                                      EZ_PROPAGATE, False,
                                      EZ_CALLBACK, button_callback, NULL, /* unique callback */
                                      0);
                EZ_SetWidgetIntData(tmp, k);    /* K encodes its initial location   */
    	  }
          }
      srand(getpid());
      EZ_DisplayWidget(toplevel);
      EZ_EventMainLoop();
    }

    static void button_callback(EZ_Widget *widget, void *data)
    {
      int tmp, x, y;

      tmp = EZ_GetWidgetIntData(widget);                       /* cur location  */
      x = tmp >> 2;                            /* at the yth row, the x' column */
      y = tmp & 0x3;

      if(abs(emptyx - x) + abs(emptyy - y) == 1)    /* next to the empty square */
        {                                        /*  swap with the empty square */
          int tx = emptyx, ty = emptyy;
          emptyx = x; emptyy = y;                       /* the new empty square */
          EZ_SetWidgetIntData(widget, 4 * tx + ty);     /* and my new location  */
          EZ_SetWidgetPosition(widget, 3 + ty * width, 3 + tx * height);
        }
    }

    static void randomize(EZ_Widget *widget, void *data)
    {                                          /* just make a few random moves */
      int i, x, y;
      for(i = 0; i < 256; i++)
        {
          x = (rand()>> 5) % 4;
          y = (rand()>> 5) % 4;
          if((x + y != 6)) button_callback(buttons[x][y], NULL);
        }
    }
    static void  cleanExit(EZ_Widget *w, void *data)
    {
      EZ_Shutdown();
      exit(0);
    }
    /***************** Example 2 ********************************/

Setting the width (height) of a widget has a special side effect. It prevents the widget from being stretched by its parent in the horizontal (vertical) direction. This feature is, surprisingly, very useful. Consider the following scenario. Your application's toplevel widget contains two subwidgets arranged horizontally side by side. Say, a list tree on the left and a text widget on the right. When the toplevel widget window is resized, it by default divides the extra space into two equal parts and distributes them to the two children widgets. This may not be desirable since most of the time, one wants the extra space be distributed to the text widget only. This can be achieved by setting the width of the list tree, so it will not be stretchable in the horizontal direction. Example 3 is such a program.

HTML Documentation Maintainance:`Arturo Espinosa <arturo@nuclecu.unam.mx>`