#include #include #include #include #include #include #include #include #include extern const char *__progname; #include "data.h" #define NTYPES 7 #define BOARDX 8 #define BOARDY 8 static const char *displayname = 0; static const char *geometryspec = 0; static const char *background = "#000"; static const char *bordercstr = "#fff"; static const char *borderwstr = "1"; static const char *name = 0; static const char *iconname = 0; static const char *visualstr = 0; static int synch = 0; static int argc; static char **argv; static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static Colormap wincmap; static int defcmap; static XVisualInfo visinfo; static GC wingc; static GC clipgc; static GC bitgc; static int borderwidth; static int barheight = 3; static Window topwin; static Window mainwin; static Window gamewin; static XColor bgcolour; static XColor bdcolour; static XColor barcolour; static Window mousewin; static int curtopw; static int curtoph; static Cursor nilcursor; static Window markwin; static Window barwin; static int *fadev; static int fadeva; static int fadevn; static int (*prevIOerr)(Display *); static int (*preverr)(Display *, XErrorEvent *); typedef struct pmsetup PMSETUP; typedef struct tickstate TICKSTATE; struct pmsetup { PMSETUP *link; Pixmap *img; Pixmap *mask; const unsigned char *data; char *map; } ; struct tickstate { struct timeval next; struct timeval inc; }; static Pixmap cellimg[NTYPES]; static Pixmap cellmask[NTYPES]; static Pixmap markimg; static Pixmap markmask; static int gamew; static int gameh; static int mainw; static int mainh; static int gotmotion; static int carrying; static int carrycx; static int carrycy; static int carrydcx; static int carrydcy; static int carryx; static int carryy; static Pixmap gwbg; static PMSETUP *pmsetups; static Pixmap animpm; static int chain_len; static int level_bar; static int level_bar_max; static int multiplier; static int score; static unsigned char board[BOARDX][BOARDY]; #define B_TYPE 0x07 /* must be able to store 0..NTYPES, not just 0..NTYPES-1 */ #define B_DIRTY 0x08 #define B_MARK 0x10 static void *deconst(const void *s) { void *rv; bcopy(&s,&rv,sizeof(void *)); return(rv); } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = (char **) malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryspec = av[skip]; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); background = av[skip]; continue; } if (!strcmp(*av,"-bordercolor") || !strcmp(*av,"-bd")) { WANTARG(); bordercstr = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); borderwstr = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); name = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-sync")) { synch = 1; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static Display *open_display(const char *d) { Display *rv; rv = XOpenDisplay(d); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(d)); exit(1); } return(rv); } static int ioerr(Display *d) { return((*prevIOerr)(d)); } static int err(Display *d, XErrorEvent *e) { return((*preverr)(d,e)); } static void setup_random(void) { struct timeval tv; gettimeofday(&tv,0); srandom(tv.tv_sec^(tv.tv_usec*2147)); } static int forms_line(int x, int y, int val) { return( ( (x >= 2) && ((board[x-2][y]&B_TYPE) == val) && ((board[x-1][y]&B_TYPE) == val) ) || ( (x <= BOARDX-3) && ((board[x+2][y]&B_TYPE) == val) && ((board[x+1][y]&B_TYPE) == val) ) || ( (y >= 2) && ((board[x][y-2]&B_TYPE) == val) && ((board[x][y-1]&B_TYPE) == val) ) || ( (y <= BOARDY-3) && ((board[x][y+2]&B_TYPE) == val) && ((board[x][y+1]&B_TYPE) == val) ) || ( (x >= 1) && (x <= BOARDX-2) && ((board[x-1][y]&B_TYPE) == val) && ((board[x+1][y]&B_TYPE) == val) ) || ( (y >= 1) && (y <= BOARDY-2) && ((board[x][y-1]&B_TYPE) == val) && ((board[x][y+1]&B_TYPE) == val) ) ); } static void setup_game(void) { int loc[BOARDX*BOARDY][2]; int nloc; int i; int x; int y; int type; int ntypes; for (i=(BOARDX*BOARDY)-1;i>=0;i--) { x = i % BOARDX; y = i / BOARDX; loc[i][0] = x; loc[i][1] = y; board[x][y] = NTYPES; } nloc = BOARDX * BOARDY; while (nloc > 0) { i = random() % nloc; x = loc[i][0]; y = loc[i][1]; nloc --; if (i != nloc) { loc[i][0] = loc[nloc][0]; loc[i][1] = loc[nloc][1]; } type = -1; ntypes = 0; for (i=NTYPES-1;i>=0;i--) { if (! forms_line(x,y,i)) { ntypes ++; if (! (random() % ntypes)) type = i; } } if ((type < 0) || (type >= NTYPES)) abort(); board[x][y] = type; } carrying = -1; level_bar = 0; level_bar_max = 80; multiplier = 1; score = 0; fadev = 0; fadeva = 0; fadevn = 0; } static void setup_visual(void) { XVisualInfo *xvi; int nvi; XVisualInfo template; int class; const char *classname; int best_onscr; int best; int i; if (! visualstr) { template.visualid = XVisualIDFromVisual(XDefaultVisual(disp,XScreenNumberOfScreen(scr))); xvi = XGetVisualInfo(disp,VisualIDMask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: default visual not found?!\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo found nonexistent default visual?\n",__progname); exit(1); } visinfo = xvi[0]; } else { do <"vis"> { #define FOO(c) if (!strcasecmp(visualstr,#c)) { class = c; classname = #c; } else FOO(StaticGray) FOO(StaticColor) FOO(TrueColor) FOO(GrayScale) FOO(PseudoColor) FOO(DirectColor) #undef FOO { unsigned long int id; char *cp; id = strtol(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; xvi = XGetVisualInfo(disp,VisualIDMask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: no such visual found\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } visinfo = xvi[0]; if (visinfo.screen != XDefaultScreen(disp)) { fprintf(stderr,"%s: warning: visual %s is on screen %d\n",__progname,visualstr,(int)visinfo.screen); } break <"vis">; } template.class = class; xvi = XGetVisualInfo(disp,VisualClassMask,&template,&nvi); best = -1; best_onscr = -1; for (i=0;i xvi[best_onscr].depth) || ( (xvi[i].depth == xvi[best_onscr].depth) && (xvi[i].visual == XDefaultVisual(disp,xvi[i].screen)) ) ) { best_onscr = i; } } if ( (best < 0) || (xvi[i].depth > xvi[best].depth) || ( (xvi[i].depth == xvi[best].depth) && (xvi[i].visual == XDefaultVisual(disp,xvi[i].screen)) ) ) { best = i; } } if (best < 0) { fprintf(stderr,"%s: no %s visual available\n",__progname,classname); exit(1); } if (best_onscr < 0) { fprintf(stderr,"%s: no %s visual on screen %d (try using screen %d)\n",__progname,classname,XDefaultScreen(disp),xvi[best].screen); exit(1); } else { best = best_onscr; } visinfo = xvi[best]; } while (0); } } static void setup_cmap(void) { if (visinfo.visual == XDefaultVisualOfScreen(scr)) { wincmap = XDefaultColormapOfScreen(scr); defcmap = 1; } else { wincmap = XCreateColormap(disp,rootwin,visinfo.visual,AllocNone); defcmap = 0; } } static void setup_gc(void) { Pixmap pm; pm = XCreatePixmap(disp,rootwin,1,1,depth); wingc = XCreateGC(disp,pm,0,0); clipgc = XCreateGC(disp,pm,0,0); XFreePixmap(disp,pm); pm = XCreatePixmap(disp,rootwin,1,1,1); bitgc = XCreateGC(disp,pm,0,0); XFreePixmap(disp,pm); } static int setup_xcolour(XColor *c) { while (1) { if (XAllocColor(disp,wincmap,c) == 0) { if (! defcmap) return(-1); wincmap = XCopyColormapAndFree(disp,wincmap); defcmap = 0; continue; } return(0); } } static void init_pixmap_setup(void) { pmsetups = 0; } static void add_pixmap_setup(Pixmap *img, Pixmap *mask, const unsigned char *data) { PMSETUP *ps; ps = malloc(sizeof(PMSETUP)); ps->link = pmsetups; pmsetups = ps; ps->img = img; ps->mask = mask; ps->data = data; } static void do_pixmap_setup(void) { int npm; PMSETUP *ps; char *map; int cpp; int cx; const unsigned char *cdp; PMSETUP *ps2; int cx2; const unsigned char *cdp2; XColor c; cpp = cellx * celly; npm = 0; for (ps=pmsetups;ps;ps=ps->link) npm ++; map = malloc(npm*cpp); bzero(map,npm*cpp); XSetForeground(disp,bitgc,0); for (ps=pmsetups;ps;ps=ps->link) { ps->map = map; map += cpp; *ps->img = XCreatePixmap(disp,rootwin,cellx,celly,depth); *ps->mask = XCreatePixmap(disp,rootwin,cellx,celly,1); XFillRectangle(disp,*ps->mask,bitgc,0,0,cellx,celly); } ps = pmsetups; cx = cpp; while (1) { cx --; if (cx < 0) { ps = ps->link; if (! ps) break; cx = cpp - 1; } if (ps->map[cx]) continue; cdp = &ps->data[cx*4]; if (! cdp[3]) continue; c.red = cdp[0] * 0x0101; c.green = cdp[1] * 0x0101; c.blue = cdp[2] * 0x0101; c.flags = DoRed | DoGreen | DoBlue; if (setup_xcolour(&c) < 0) { fprintf(stderr,"%s: can't allocate colourmap cell for colour %d/%d/%d\n", __progname, cdp[0], cdp[1], cdp[2] ); exit(1); } XSetForeground(disp,wingc,c.pixel); ps2 = ps; cx2 = cx; cdp2 = &ps2->data[cx2*4]; while (1) { if ( cdp2[3] && (cdp2[0] == cdp[0]) && (cdp2[1] == cdp[1]) && (cdp2[2] == cdp[2]) ) { XDrawPoint(disp,*ps2->img,wingc,cx2%cellx,cx2/cellx); ps2->map[cx2] = 1; } cx2 --; if (cx2 < 0) { ps2 = ps2->link; if (! ps2) break; cx2 = cpp - 1; cdp2 = &ps2->data[cx2*4]; } else { cdp2 -= 4; } } } free(pmsetups->map); XSetForeground(disp,bitgc,1); ps = pmsetups; cx = cpp; while (1) { cx --; if (cx < 0) { ps = ps->link; if (! ps) break; cx = cpp - 1; } if (ps->data[(cx*4)+3]) XDrawPoint(disp,*ps->mask,bitgc,cx%cellx,cx/cellx); } } static void setup_pixmaps(void) { int t; XColor c; int i; init_pixmap_setup(); for (t=NTYPES-1;t>=0;t--) add_pixmap_setup(&cellimg[t],&cellmask[t],&celldata[cellx*celly*t*4]); add_pixmap_setup(&markimg,&markmask,&markdata[0]); do_pixmap_setup(); gwbg = XCreatePixmap(disp,rootwin,gamew,gameh,depth); c.red = 0x7fff; c.green = 0x7fff; c.blue = 0x7fff; c.flags = DoRed | DoGreen | DoBlue; if (setup_xcolour(&c) < 0) { fprintf(stderr,"%s: can't allocate colourmap cell for grey\n",__progname); exit(1); } XSetForeground(disp,wingc,c.pixel); XFillRectangle(disp,gwbg,wingc,0,0,gamew,gameh); c.red = 0; c.green = 0; c.blue = 0; c.flags = DoRed | DoGreen | DoBlue; if (setup_xcolour(&c) < 0) { fprintf(stderr,"%s: can't allocate colourmap cell for black\n",__progname); exit(1); } XSetForeground(disp,wingc,c.pixel); for (i=1;i<=BOARDY;i++) XDrawLine(disp,gwbg,wingc,0,0,gamew,i*celly); for (i=BOARDX-1;i>0;i--) XDrawLine(disp,gwbg,wingc,0,0,i*cellx,gameh); c.red = 65535; c.green = 65535; c.blue = 65535; c.flags = DoRed | DoGreen | DoBlue; if (setup_xcolour(&c) < 0) { fprintf(stderr,"%s: can't allocate colourmap cell for white\n",__progname); exit(1); } XSetForeground(disp,wingc,c.pixel); for (i=1;i<=BOARDY;i++) XDrawLine(disp,gwbg,wingc,gamew,0,0,i*celly); for (i=1;i= BOARDX) x = BOARDX - 1; return(x); } static int cell_y(int y) { y /= celly; if (y < 0) y = 0; if (y >= BOARDY) y = BOARDY - 1; return(y); } static void repair_damage(void) { int i; int j; int t; for (i=BOARDX-1;i>=0;i--) for (j=BOARDY-1;j>=0;j--) { if (board[i][j] & B_DIRTY) { board[i][j] &= ~B_DIRTY; t = board[i][j] & B_TYPE; XSetClipMask(disp,clipgc,cellmask[t]); XSetClipOrigin(disp,clipgc,i*cellx,j*celly); XCopyArea(disp,cellimg[t],gamewin,clipgc,0,0,cellx,celly,i*cellx,j*celly); } } } static void redraw_bar(void) { int x; x = (level_bar * gamew) / level_bar_max; if (x > 0) { XSetForeground(disp,wingc,barcolour.pixel); XFillRectangle(disp,barwin,wingc,0,1,x,barheight-1); } if (x < gamew) { XSetForeground(disp,wingc,bgcolour.pixel); XFillRectangle(disp,barwin,wingc,x,1,gamew-x,barheight-1); } } static void doexpose(Window win, int x, int y, int w, int h, int c) { int x1; int x2; int y1; int y2; int i; int j; if (win == gamewin) { x1 = cell_x(x); x2 = cell_x(x+w-1); y1 = cell_y(y); y2 = cell_y(y+h-1); for (i=x1;i<=x2;i++) for (j=y1;j<=y2;j++) board[i][j] |= B_DIRTY; if (c == 0) repair_damage(); } else if (win == barwin) { if (c == 0) redraw_bar(); } } static TICKSTATE *ts_start(unsigned int incus) { TICKSTATE *ts; ts = malloc(sizeof(TICKSTATE)); gettimeofday(&ts->next,0); ts->inc.tv_sec = incus / 1000000; ts->inc.tv_usec = incus % 1000000; return(ts); } static void ts_await(TICKSTATE *ts) { struct timeval now; int ms; ts->next.tv_sec += ts->inc.tv_sec; ts->next.tv_usec += ts->inc.tv_usec; if (ts->next.tv_usec >= 1000000) { ts->next.tv_usec -= 1000000; ts->next.tv_sec ++; } while (1) { gettimeofday(&now,0); if ( (now.tv_sec > ts->next.tv_sec) || ( (now.tv_sec == ts->next.tv_sec) && (now.tv_usec >= ts->next.tv_usec) ) ) return; ms = ((ts->next.tv_sec - now.tv_sec) * 1000) + (ts->next.tv_usec / 1000) - (now.tv_usec / 1000); if (ms < 1) ms = 1; poll(0,0,ms); } } static void ts_done(TICKSTATE *ts) { free(ts); } static void animate_swap(int j1, int x1, int y1, int j2, int x2, int y2) { int mx; int my; int i; Pixmap img1; Pixmap mask1; Pixmap img2; Pixmap mask2; int atx1; int aty1; int atx2; int aty2; int incx; int incy; int count; int cw; int ch; TICKSTATE *ts; x1 *= cellx; y1 *= celly; x2 *= cellx; y2 *= celly; mx = (x1 < x2) ? x1 : x2; my = (y1 < y2) ? y1 : y2; img1 = cellimg[j1]; mask1 = cellmask[j1]; img2 = cellimg[j2]; mask2 = cellmask[j2]; if (x1 == x2) { incx = 0; incy = (y1 < y2) ? 1 : -1; count = celly; cw = cellx; ch = celly * 2; } else { incx = (x1 < x2) ? 1 : -1; incy = 0; count = cellx; cw = cellx * 2; ch = celly; } atx1 = x1 - mx; aty1 = y1 - my; atx2 = x2 - mx; aty2 = y2 - my; ts = ts_start(20000); for (i=count;i>0;i--) { atx1 += incx; aty1 += incy; atx2 -= incx; aty2 -= incy; XCopyArea(disp,gwbg,animpm,wingc,mx,my,cw,ch,0,0); XSetClipMask(disp,clipgc,mask2); XSetClipOrigin(disp,clipgc,atx2,aty2); XCopyArea(disp,img2,animpm,clipgc,0,0,cellx,celly,atx2,aty2); XSetClipMask(disp,clipgc,mask1); XSetClipOrigin(disp,clipgc,atx1,aty1); XCopyArea(disp,img1,animpm,clipgc,0,0,cellx,celly,atx1,aty1); XCopyArea(disp,animpm,gamewin,wingc,0,0,cw,ch,mx,my); XFlush(disp); ts_await(ts); } ts_done(ts); } static void clear_marks(void) { int i; int j; for (i=BOARDX-1;i>=0;i--) for (j=BOARDY-1;j>=0;j--) board[i][j] &= ~B_MARK; } static void add_score_for_line(int len) { void pts(int n) { int p; p = ((n - 2) + (chain_len * 3)) * multiplier; score += p; printf("%d pts (len %d chain %d mult %d) -> %d\n",p,n,chain_len,multiplier,score); chain_len ++; } while (len > 5) { pts(3); len -= 3; } switch (len) { case 3: case 4: case 5: pts(len); break; default: abort(); break; } } static void set_marks(int x, int y, int dx, int dy, int n) { for (;n>0;n--) { board[x][y] |= B_MARK; x += dx; y += dy; } } static void animate_fadeout(void) { int x; int y; int n; int i; int j; int v; int t; TICKSTATE *ts; n = 0; for (y=BOARDY-1;y>=0;y--) for (x=BOARDX-1;x>=0;x--) if (board[x][y] & B_MARK) n ++; fadevn = n * cellx * celly; if (fadevn > fadeva) { free(fadev); fadeva = fadevn; fadev = malloc(fadevn*sizeof(int)); } v = 0; for (y=BOARDY-1;y>=0;y--) for (x=BOARDX-1;x>=0;x--) { if (board[x][y] & B_MARK) { t = board[x][y] & B_TYPE; for (i=cellx-1;i>=0;i--) for (j=celly-1;j>=0;j--) { fadev[v++] = (i + (x * cellx)) + ((j + (y * celly)) * gamew); } } } if (v != fadevn) abort(); ts = ts_start(20000); n = fadevn; for (i=25;i>=0;i--) { j = (n * i) / 25; while (fadevn > j) { t = random() % fadevn; v = fadev[t]; fadevn --; if (t != fadevn) fadev[t] = fadev[fadevn]; x = v % gamew; y = v / gamew; XCopyArea(disp,gwbg,gamewin,wingc,x,y,1,1,x,y); } XFlush(disp); ts_await(ts); } ts_done(ts); } static void drop_replacements(void) { int dropdist[BOARDX][BOARDY]; int x; int y; int yf; int yt; int d; int t; int i; static XRectangle *rects = 0; static int rectsa = 0; int rectsn; TICKSTATE *ts; void addrect(int x, int y, int w, int h) { if (rectsn >= rectsa) { rects = realloc(rects,(rectsa=rectsn+8)*sizeof(XRectangle)); } rects[rectsn++] = (XRectangle){.x=x,.y=y,.width=w,.height=h}; } for (x=BOARDX-1;x>=0;x--) { yt = BOARDY - 1; for (yf=BOARDY-1;yf>=0;yf--) { if (board[x][yf] & B_MARK) continue; board[x][yt] = board[x][yf]; dropdist[x][yt] = yt - yf; yt --; } d = yt - yf; for (;yt>=0;yt--) { board[x][yt] = random() % NTYPES; dropdist[x][yt] = d; } } rectsn = 0; for <"x"> (x=BOARDX-1;x>=0;x--) { for (y=BOARDY-1;y>=0;y--) { if (dropdist[x][y] > 0) { addrect(x*cellx,0,cellx,(y+1)*celly); continue <"x">; } } } XSetClipRectangles(disp,clipgc,0,0,rects,rectsn,Unsorted); ts = ts_start(20000); for (i=25;i>=0;i--) { XCopyArea(disp,gwbg,animpm,clipgc,0,0,gamew,gameh,0,0); for <"x"> (x=BOARDX-1;x>=0;x--) { for (y=BOARDY-1;y>=0;y--) { d = dropdist[x][y]; if (d > 0) { yt = (y * celly) - ((i * d * celly) / 25); if (yt <= -celly) continue <"x">; t = board[x][y] & B_TYPE; XSetClipMask(disp,wingc,cellmask[t]); XSetClipOrigin(disp,wingc,x*cellx,yt); XCopyArea(disp,cellimg[t],animpm,wingc,0,0,cellx,celly,x*cellx,yt); } } } XCopyArea(disp,animpm,gamewin,clipgc,0,0,gamew,gameh,0,0); XFlush(disp); ts_await(ts); } XSetClipMask(disp,wingc,None); ts_done(ts); } static void mark_random(int n) { int x; int y; for (;n>0;n--) { do { x = random() % BOARDX; y = random() % BOARDY; } while (board[x][y] & B_MARK); board[x][y] |= B_MARK; } } static void mark_and_score(void) { int x; int y; int i; int any; int jcount; int lastmult; lastmult = multiplier; while (1) { clear_marks(); #define T(x,y) (board[(x)][(y)] & B_TYPE) jcount = 0; while (1) { if (multiplier != lastmult) { mark_random(16); level_bar -= 16; lastmult = multiplier; } else { any = 0; for (y=BOARDY-1;y>=0;y--) { for (x=BOARDX-3;x>=0;x--) { if ((T(x,y) == T(x+1,y)) && (T(x,y) == T(x+2,y))) { for (i=x-1;(i>=0)&&(T(i,y)==T(x,y));i--) ; add_score_for_line(x+2-i); set_marks(i+1,y,1,0,x+2-i); any = 1; } } } for (x=BOARDX-1;x>=0;x--) { for (y=BOARDY-3;y>=0;y--) { if ((T(x,y) == T(x,y+1)) && (T(x,y) == T(x,y+2))) { for (i=y-1;(i>=0)&&(T(x,i)==T(x,y));i--) ; add_score_for_line(y+2-i); set_marks(x,i+1,0,1,y+2-i); any = 1; } } } if (! any) break; } #undef T for (y=BOARDY-1;y>=0;y--) for (x=BOARDX-1;x>=0;x--) if (board[x][y] & B_MARK) jcount ++; animate_fadeout(); drop_replacements(); } if (jcount == 0) break; level_bar += jcount; if (level_bar >= level_bar_max) { multiplier ++; level_bar -= level_bar_max; level_bar_max *= 2; printf("level %d\n",multiplier); } redraw_bar(); } } static void check_exit(void) { int x; int y; #define T(x,y) (board[(x)][(y)] & B_TYPE) for (x=BOARDX-1;x>=0;x--) for (y=BOARDY-1;y>=0;y--) { if ( ( (x > 0) && (T(x,y) == T(x-1,y)) && ( ( (x > 2) && (T(x,y) == T(x-3,y)) ) || ( (x > 1) && ( ((y > 0) && (T(x,y) == T(x-2,y-1))) || ((y < BOARDY-1) && (T(x,y) == T(x-2,y+1))) ) ) || ( (x < BOARDX-2) && (T(x,y) == T(x+2,y)) ) || ( (x < BOARDX-1) && ( ((y > 0) && (T(x,y) == T(x+1,y-1))) || ((y < BOARDY-1) && (T(x,y) == T(x+1,y+1))) ) ) ) ) || ( (y > 0) && (T(x,y) == T(x,y-1)) && ( ( (y > 2) && (T(x,y) == T(x,y-3)) ) || ( (y > 1) && ( ((x > 0) && (T(x,y) == T(x-1,y-2))) || ((x < BOARDX-1) && (T(x,y) == T(x+1,y-2))) ) ) || ( (y < BOARDY-2) && (T(x,y) == T(x,y+2)) ) || ( (y < BOARDY-1) && ( ((x > 0) && (T(x,y) == T(x-1,y+1))) || ((x < BOARDX-1) && (T(x,y) == T(x+1,y+1))) ) ) ) ) || ( (x > 1) && (T(x,y) == T(x-2,y)) && ( ((y > 0) && (T(x,y) == T(x-1,y-1))) || ((y < BOARDY-1) && (T(x,y) == T(x-1,y+1))) ) ) || ( (y > 1) && (T(x,y) == T(x,y-2)) && ( ((x > 0) && (T(x,y) == T(x-1,y-1))) || ((x < BOARDX-1) && (T(x,y) == T(x+1,y-1))) ) ) ) return; } #undef T printf("No more moves\n"); XUnmapWindow(disp,gamewin); XFlush(disp); poll(0,0,1000); XMapWindow(disp,gamewin); return; } static void doswap(int x1, int y1, int x2, int y2) { int t1; int t2; t1 = board[x1][y1] & B_TYPE; t2 = board[x2][y2] & B_TYPE; animate_swap(t1,x1,y1,t2,x2,y2); board[x1][y1] = t2; board[x2][y2] = t1; if (!forms_line(x1,y1,t2) && !forms_line(x2,y2,t1)) { animate_swap(t2,x1,y1,t1,x2,y2); board[x1][y1] = t1; board[x2][y2] = t2; return; } chain_len = 0; mark_and_score(); check_exit(); } static void move(void) { Window root; Window child; int rootx; int rooty; int x; int y; unsigned int mask; int cx; int cy; int dx; int dy; if (carrying < 0) return; if (XQueryPointer(disp,gamewin,&root,&child,&rootx,&rooty,&x,&y,&mask) == False) return; if (x < 0) x = 0; else if (x >= cellx*BOARDX) x = (cellx*BOARDX) - 1; if (y < 0) y = 0; else if (y >= gameh) y = gameh - 1; cx = (carrycx * cellx) + (cellx / 2); cy = (carrycy * celly) + (celly / 2); dx = x - cx; dy = y - cy; if ((abs(carryx-cx) < cellx/3) && (abs(carryy-cy) < celly/3)) { if (abs(dx) > abs(dy)) { carryx = x; carryy = cy; } else { carryx = cx; carryy = y; } } else { if (carryx == cx) { carryy = y; } else { carryx = x; } } if (carryx < cx-((cellx*4)/5)) carryx = cx - ((cellx*4)/5); if (carryx > cx+((cellx*4)/5)) carryx = cx + ((cellx*4)/5); if (carryy < cy-((celly*4)/5)) carryy = cy - ((celly*4)/5); if (carryy > cy+((celly*4)/5)) carryy = cy + ((celly*4)/5); if (carryx < cx-(cellx/2)) carrydcx = carrycx - 1; else if (carryx > cx+(cellx/2)) carrydcx = carrycx + 1; else carrydcx = carrycx; if (carryy < cy-(celly/2)) carrydcy = carrycy - 1; else if (carryy > cy+(celly/2)) carrydcy = carrycy + 1; else carrydcy = carrycy; if (carrydcx < 0) carrydcx = 0; if (carrydcx >= BOARDX) carrydcx = BOARDX - 1; if (carrydcy < 0) carrydcy = 0; if (carrydcy >= BOARDY) carrydcy = BOARDY - 1; XMoveWindow(disp,mousewin,carryx-(cellx/2),carryy-(celly/2)); } static void buttonevent(Window win, int x, int y, int downp) { if (win != gamewin) return; if ((carrying < 0) && downp) { if ((x < 0) || (x >= gamew) || (y < 0) || (y >= gameh)) return; carrycx = x / cellx; carrycy = y / celly; carryx = (carrycx * cellx) + (cellx / 2); carryy = (carrycy * celly) + (celly / 2); carrying = board[carrycx][carrycy] & B_TYPE; move(); XMoveWindow(disp,markwin,carrycx*cellx,carrycy*celly); XSetWindowBackgroundPixmap(disp,mousewin,cellimg[carrying]); XShapeCombineMask(disp,mousewin,ShapeBounding,0,0,cellmask[carrying],ShapeSet); XClearWindow(disp,mousewin); /*XDefineCursor(disp,gamewin,nilcursor);*/ XMapWindow(disp,markwin); XMapWindow(disp,mousewin); } else if ((carrying >= 0) && !downp) { carrying = -1; XUnmapWindow(disp,mousewin); XUnmapWindow(disp,markwin); XUndefineCursor(disp,gamewin); if ((carrydcx != carrycx) || (carrydcy != carrycy)) { doswap(carrycx,carrycy,carrydcx,carrydcy); } } } static void handle_event(XEvent *e) { switch (e->type) { default: break; case ConfigureNotify: /* XConfigureEvent - xconfigure */ resize(e->xconfigure.width,e->xconfigure.height); break; case Expose: /* XExposeEvent - xexpose */ doexpose(e->xexpose.window,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); break; case MotionNotify: /* XMotionEvent - xmotion */ gotmotion ++; break; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ buttonevent(e->xbutton.window,e->xbutton.x,e->xbutton.y,1); break; case ButtonRelease: /* XButtonPressedEvent - XButtonEvent - xbutton */ buttonevent(e->xbutton.window,e->xmotion.x,e->xmotion.y,0); break; } } static void run(void) { XEvent e; gotmotion = 0; while (1) { if (XEventsQueued(disp,QueuedAfterReading) == 0) { if (gotmotion) { gotmotion = 0; move(); } } XNextEvent(disp,&e); handle_event(&e); } } static void setup_numbers(void) { if (borderwstr) borderwidth = atoi(borderwstr); gamew = cellx * BOARDX; gameh = celly * BOARDY; mainw = gamew; mainh = gameh + barheight; } static void setup_colour(const char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad color `%s'\n",__progname,str); exit(1); } if (setup_xcolour(col) < 0) { fprintf(stderr,"%s: can't allocate colourmap cell for colour `%s'\n",__progname,str); exit(1); } } static void setup_colours(void) { setup_colour(bordercstr,&bdcolour); setup_colour(background,&bgcolour); setup_colour("#fff",&barcolour); } static void setup_cursors(void) { Pixmap pm; XColor c; pm = XCreatePixmap(disp,rootwin,1,1,1); XSetForeground(disp,bitgc,0); XDrawPoint(disp,pm,bitgc,0,0); c.red = 0; c.green = 0; c.blue = 0; nilcursor = XCreatePixmapCursor(disp,pm,pm,&c,&c,0,0); XFreePixmap(disp,pm); } int main(int, char **); int main(int ac, char **av) { saveargv(ac,av); handleargs(ac,av); disp = open_display(displayname); if (synch) XSynchronize(disp,True); preverr = XSetErrorHandler(err); prevIOerr = XSetIOErrorHandler(ioerr); scr = XDefaultScreenOfDisplay(disp); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); setup_visual(); depth = visinfo.depth; rootwin = XRootWindowOfScreen(scr); setup_numbers(); setup_cmap(); setup_colours(); setup_gc(); setup_cursors(); setup_random(); setup_game(); setup_pixmaps(); setup_windows(); run(); return(0); }