/*
 *      15 puzzle 
 */
  
#include        <intuition/intuition.h>
#include        <libraries/dos.h>               /* just for SIGBREAKF_CTRL_C */
#include        <proto/intuition.h>
#include        <proto/exec.h>
  
extern struct IntuitionBase     *IntuitionBase;
extern struct GfxBase           *GfxBase;
  
#define NPZ     4               /* the square puzzle is NPZ x NPZ */
#define PZ_MT   0               /* the value of the "empty" tile */
  
char    pz[NPZ][NPZ];           /* the puzzle array */
int     mtr, mtc;               /* location of the "empty" tile */
  
#define R0      11              /* origin row/col of upper left */
#define C0      4
#define RS      (IMG_HT+1)      /* row/col scaling factors (match images!!) */
#define CS      (IMG_WID+3)
#define IMG_WID 0x1f            /* from the output of 'brush' */
#define IMG_HT  0xd
  
struct Image img = {            /* shared Image struct for all "tiles" */
        0,0, IMG_WID,IMG_HT, 1, NULL, 1,0, NULL
};
  
/* the image data (in chip ram!) */
extern short    id0[], id1[],  id2[],  id3[],  id4[],  id5[],  id6[],  id7[], 
                id8[], id9[], id10[], id11[], id12[], id13[], id14[], id15[];
  
short   *imgs[] = {             /* array of pointers to the image data arrays */
        id0, id1,  id2,  id3,  id4,  id5,  id6,  id7,
        id8, id9, id10, id11, id12, id13, id14, id15
};
  
#define W_WID   141
#define W_HT    68
struct NewWindow newin = {
        100,50, W_WID,W_HT, 2,-1,
        MOUSEBUTTONS | CLOSEWINDOW | REFRESHWINDOW,
        WINDOWDEPTH | WINDOWCLOSE | WINDOWDRAG | SIMPLE_REFRESH,
        NULL, NULL, "Pz15", NULL, NULL, 0,0, 0,0, WBENCHSCREEN
};
  
struct Window   *win = NULL;
struct RastPort *rp  = NULL;
  
/* Lattice likes prototypes: */
void    putpiece(unsigned short, unsigned short, unsigned short);
void    refrow(unsigned short), refcol(unsigned short);
short   pzmove(unsigned short, unsigned short);
void    byewin(long);
  
/*
 *      Repaint a single tile.
 */
void putpiece(r, c, v)
register unsigned short r, c, v;
{
        img.ImageData = imgs[v];
        DrawImage(rp, &img, (long) c*CS+C0, (long) r*RS+R0);
}
  
/*
 *      Repaint a specified row.
 */
void refrow(r)
register unsigned short r;
{
        register unsigned short c;
  
        for ( c = 0; c < NPZ; c++ )
                putpiece(r, c, (unsigned short)pz[r][c]);
}
  
/*
 *      Repaint a specified column.
 */
void refcol(c)
register unsigned short c;
{
        register unsigned short r;
  
        for ( r = 0; r < NPZ; r++ )
                putpiece(r, c, (unsigned short) pz[r][c]);
}
  
/*
 *      Move the "empty" tile (MT) to the "moused" row and column.
 */
short pzmove(mr, mc)
unsigned short mr, mc;
{
        register unsigned short  r,  c;         /* row/col counters */
        register short  ir, ic;                 /* row/col increments */
  
        if ( mtr == mr )                /* Are we in the same row as MT? */
                ir = 0;                         /* yes, row-inc is zero */
        else
                ir = (mtr < mr) ? 1 : -1;       /* yes, row-inc is +/-1 */
  
        if ( mtc == mc )                /* Are we in the same col as MT? */
                ic = 0;                         /* yes, col-inc is zero */
        else
                ic = (mtc < mc) ? 1 : -1;       /* yes, col-inc is +/-1 */
  
        if ( ir == 0 && ic == 0 )       /* If in same row and col as MT... */
                return 0;               /* ... nothing to do */
        if ( ir != 0 && ic != 0 )       /* If not in either row or col as MT... */
                return -1;              /* ... too much to do */
  
        /* "double-loop", but really only r _or_ c gets changed */
        for ( r = mtr, c = mtc; ; r += ir, c += ic ) {  /* start at MT */
                pz[r][c] = pz[r+ir][c+ic];      /* shift pieces towards MT */
                if ( r == mr && c == mc )       /* until we hit the 'moused' position */
                        break;
        }
        pz[mr][mc] = PZ_MT;             /* mark the new empty square */
        mtr = mr;
        mtc = mc;
        if ( ir == 0 )                  /* update the display */
                refrow(mr);             /* either change a row */
        else
                refcol(mc);             /* or a column */
        return 1;                       /* say there was just enough to do */
}
  
/*
 *      Refresh the entire puzzle.
 */
void pzref()
{
        register unsigned short r;
  
        for ( r = 0; r < NPZ; r++ )     
                refrow(r);
}
  
/*
 *      Setup the puzzle array.
 */
void pzinit()
{
        register unsigned short r, c, i;
  
        i = 0;
        for ( r = 0; r < NPZ; r++ )
                for ( c = 0; c < NPZ; c++ )
                        pz[c][r] = i++;         /* scramble a little */
}
  
/*
 *      Get mouse clicks and handle them.
 */
void dopuz()
{
        register struct IntuiMessage *msg;
        register unsigned short r, c;
        unsigned long   class;
        unsigned short  code;
        long    flags, ret;
  
        flags = (1L << win->UserPort->mp_SigBit) | SIGBREAKF_CTRL_C;
  
        /* loop, get mouse clicks, call pzmove() with reduced coords */
        for ( ; ; ) {
                ret = Wait ( flags );
                while ( msg = (struct IntuiMessage *)GetMsg(win->UserPort) ) {
                        class = msg->Class;
                        code = msg->Code;
                        r = (msg->MouseY - R0) / RS;
                        c = (msg->MouseX - C0) / CS;
                        ReplyMsg((struct Message *)msg);
                        if ( class == MOUSEBUTTONS && code == SELECTDOWN ) {
                                (void)pzmove(r, c);
                        }
                        else if ( class == REFRESHWINDOW ) {
                                pzref();
                        }
                        else if ( class == CLOSEWINDOW ) {
                                return;
                        }
                }
                if ( ret & SIGBREAKF_CTRL_C ) {
                        return;
                }
        }
}
  
/*
 *      Shut the window; put back the things we borrowed.
 */
void byewin(r)
long r;
{
        if ( win )
                CloseWindow(win);
        if ( GfxBase )
                CloseLibrary(GfxBase);
        if ( IntuitionBase )
                CloseLibrary(IntuitionBase);
        _exit(r);
}
  
/*
 *      Open some libraries and a window.
 */
void mkwin()
{
        if ( (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0L)) == NULL )
                byewin(-100L);
        if ( (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0L)) == NULL )
                byewin(-101L);
        if ( (win = (struct Window *)OpenWindow(&newin)) == NULL )
                byewin(-102L);
        rp = win->RPort;
}
  
/*
 *      FINALLY, here's where it all starts.
 */
void _main()
{
        mkwin();                /* open up our window */
        pzinit();               /* initialize the puzzle */
        pzref();                /* and show it */
        dopuz();                /* play it, until we close or get a ^C */
        byewin(0L);             /* then close the window and exit */
}
  
