home *** CD-ROM | disk | FTP | other *** search
/ 3D Games (Spidla) / 3dhry2.iso / Fl_Qubix / qubix.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  2003-03-17  |  24.4 KB  |  991 lines

  1. /* FLQUBIX
  2.  
  3.    VERSION:  1.0
  4.    DATE:     13.9.2000
  5.    USING:    FLTK 1.0.6
  6.    PLATFORM: WINDOWS
  7.  
  8.    A game of four-in-a-row in 3D
  9.  
  10.    Adapted for FLTK, (http://www.fltk.org) September 2000, Frank Siegert <frank@this.net>
  11.    More of my workings is available on http://www.this.net/~frank
  12.  
  13.    This code may be used freely as long as it is given away for free
  14.    
  15.    It is hereby placed under the GPL (LGPL if you remove the random generator embedded within)
  16.    
  17.    Please do not judge my professional work by the quality of this hack, this puppy was written
  18.    just for fun in about three hours out of the DisplayPostscript code from NeXTSTEP. By doing
  19.    this the code comes back from a long journey - as the original author of FLTK Bill Spitzak
  20.    has released it in his famous 'hackkit' for NeXTSTEP aeons ago (1991). The code is not written
  21.    for clarity but *hey* it works and now I do not need to boot my trusty NeXT just to play. :-)
  22.  
  23. */
  24.  
  25. /* Original comments:
  26.  
  27.    qubix.c generated from qubic.psw
  28.    by unix pswrap V1.009  Wed Apr 19 17:50:24 PDT 1989
  29.  
  30. /* This is the main file, containing all the game logic */
  31.  
  32. /* in ancient times all UI stuff was embedded within too (no AppKit!)
  33.    so do not be too upset with the suboptimal structure */
  34.  
  35. /* Original (c) 1991 by Bill Spitzak, Q&D ported to NeXTSTEP/Appkit by Frank M. Siegert, 1998 */
  36.  
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <malloc.h>
  40. #include <string.h>
  41. #include <math.h>
  42. #include <time.h>
  43.  
  44. #include "qubix.h"
  45.  
  46. typedef int flag;
  47.  
  48. /* FLTK globals */
  49.  
  50. Fl_Double_Window *mainwin;
  51. Fl_Window *infowin;
  52.  
  53. Fl_Slider *y_slider=(Fl_Slider *)0;
  54.  
  55. Fl_Slider *x_silder=(Fl_Slider *)0;
  56.  
  57. Fl_Menu_Bar *main_menu=(Fl_Menu_Bar *)0;
  58.  
  59. Fl_Menu_Item menu_main_menu[] = {
  60.  {"Info", 0,  0, 0, 64, 0, 0, 14, 0},
  61.  {"About", 0,  0, 0, 0, 0, 0, 14, 0},
  62.  {0},
  63.  {"Game", 0,  0, 0, 64, 0, 0, 14, 0},
  64.  {"New Game", 0,  0, 0, 0, 0, 0, 14, 0},
  65.  {"Flip Sides", 0,  0, 0, 0, 0, 0, 14, 0},
  66.  {"Undo Last Move", 0,  0, 0, 0, 0, 0, 14, 0},
  67.  {0},
  68.  {"System", 0,  0, 0, 64, 0, 0, 14, 0},
  69.  {"Quit", 0,  0, 0, 0, 0, 0, 14, 0},
  70.  {0},
  71.  {0}
  72. };
  73.  
  74. // taken directly from NeXTSTEP Qubix
  75.  
  76. char *remark=NULL;    /* message set by makemove */
  77.  
  78. int board[65];                /* game field */
  79. flag playerblack;            /* which side I am on */
  80. int xangle=75,yangle=75;    /* angle in range 90 */
  81. double sinx,cosx,siny,cosy;    /* sin/cos of angles */
  82.  
  83. #define viewdistance 12.0    /* units from origin of "eye" for perspective */
  84. #define MARK 1
  85. #define PLAYER 8
  86. #define COMPUTER 64
  87.  
  88. #define DISPLAYWIDTH 370
  89. #define DISPLAYHEIGHT 370
  90.  
  91. #define TRUE 1
  92. #define FALSE 0
  93.  
  94. #define MAXPATH 32
  95.  
  96. /* Mersenne Twister random stuff */
  97.  
  98. /* This library is free software; you can redistribute it and/or   */
  99. /* modify it under the terms of the GNU Library General Public     */
  100. /* License as published by the Free Software Foundation; either    */
  101. /* version 2 of the License, or (at your option) any later         */
  102. /* version.                                                        */
  103. /* This library is distributed in the hope that it will be useful, */
  104. /* but WITHOUT ANY WARRANTY; without even the implied warranty of  */
  105. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.            */
  106. /* See the GNU Library General Public License for more details.    */
  107. /* You should have received a copy of the GNU Library General      */
  108. /* Public License along with this library; if not, write to the    */
  109. /* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA   */ 
  110. /* 02111-1307  USA                                                 */
  111.  
  112. /* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.       */
  113. /* When you use this, send an email to: matumoto@math.keio.ac.jp   */
  114. /* with an appropriate reference to your work.                     */
  115.  
  116. /* REFERENCE                                                       */
  117. /* M. Matsumoto and T. Nishimura,                                  */
  118. /* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform  */
  119. /* Pseudo-Random Number Generator",                                */
  120. /* ACM Transactions on Modeling and Computer Simulation,           */
  121. /* Vol. 8, No. 1, January 1998, pp 3--30.                          */
  122.  
  123. #include<stdio.h>
  124.  
  125. /* Period parameters */  
  126. #define N 624
  127. #define M 397
  128. #define MATRIX_A 0x9908b0df   /* constant vector a */
  129. #define UPPER_MASK 0x80000000 /* most significant w-r bits */
  130. #define LOWER_MASK 0x7fffffff /* least significant r bits */
  131.  
  132. /* Tempering parameters */   
  133. #define TEMPERING_MASK_B 0x9d2c5680
  134. #define TEMPERING_MASK_C 0xefc60000
  135. #define TEMPERING_SHIFT_U(y)  (y >> 11)
  136. #define TEMPERING_SHIFT_S(y)  (y << 7)
  137. #define TEMPERING_SHIFT_T(y)  (y << 15)
  138. #define TEMPERING_SHIFT_L(y)  (y >> 18)
  139.  
  140. static unsigned long mt[N]; /* the array for the state vector  */
  141. static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
  142.  
  143. /* initializing the array with a NONZERO seed */
  144. void sgenrand(unsigned long seed) 
  145. {
  146.     /* setting initial seeds to mt[N] using         */
  147.     /* the generator Line 25 of Table 1 in          */
  148.     /* [KNUTH 1981, The Art of Computer Programming */
  149.     /*    Vol. 2 (2nd Ed.), pp102]                  */
  150.     mt[0]= seed & 0xffffffff;
  151.     for (mti=1; mti<N; mti++)
  152.         mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
  153. }
  154.  
  155. unsigned long genrand()
  156. {
  157.     unsigned long y;
  158.     static unsigned long mag01[2]={0x0, MATRIX_A};
  159.     /* mag01[x] = x * MATRIX_A  for x=0,1 */
  160.  
  161.     if (mti >= N) { /* generate N words at one time */
  162.         int kk;
  163.  
  164.         if (mti == N+1)   /* if sgenrand() has not been called, */
  165.             sgenrand(4357); /* a default initial seed is used   */
  166.  
  167.         for (kk=0;kk<N-M;kk++) {
  168.             y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
  169.             mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
  170.         }
  171.         for (;kk<N-1;kk++) {
  172.             y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
  173.             mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
  174.         }
  175.         y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
  176.         mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
  177.  
  178.         mti = 0;
  179.     }
  180.   
  181.     y = mt[mti++];
  182.     y ^= TEMPERING_SHIFT_U(y);
  183.     y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
  184.     y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
  185.     y ^= TEMPERING_SHIFT_L(y);
  186.  
  187.     return y; 
  188. }
  189.  
  190. // Compatibilty stuff
  191. // AKA poor man's DPS routines - we really only do the things the original needs omiting the rest
  192. //
  193.  
  194. struct tpspoint {
  195.     double x;
  196.     double y;
  197.     flag draw;
  198. };
  199.  
  200. struct tpspoint PSpath[MAXPATH];
  201. int nPathPtr=0;
  202.  
  203. double grayvalue=0.0;
  204. double yscale=1.0;
  205.  
  206. void PSsetgray(double value) {
  207.     int c = (value * 255);
  208.     fl_color(c,c,c);
  209.     grayvalue=value;
  210. }
  211.  
  212. void PSsetlinewidth(double value) {
  213.  
  214. }
  215.  
  216. void PSgsave() {
  217.     fl_push_matrix();
  218. }
  219.  
  220. void PSgrestore() {
  221.     fl_pop_matrix();
  222. }
  223.  
  224. void PSsetinstance(int value) {
  225.  
  226. }
  227.  
  228. void PSscale(double x, double y) {
  229.     fl_scale(x,y);
  230.     yscale = y;
  231. }
  232.  
  233.  
  234. void PStranslate(double x, double y) {
  235.     fl_translate(x,y);
  236. }
  237.  
  238. void PSshow(char *value) {
  239.  
  240. }
  241.  
  242. void PSrectfill(double x1, double y1, double x2, double y2) {
  243.  
  244. }
  245.  
  246. void PSmoveto(double x, double y) {
  247.     PSpath[nPathPtr].x = x;
  248.     PSpath[nPathPtr].y = 0.25 - y;
  249.     PSpath[nPathPtr].draw = FALSE;
  250.     nPathPtr++;
  251.     if (nPathPtr >= MAXPATH) {
  252.         fl_alert("PATH OVERFLOW in PSmoveto()");
  253.         exit(1);
  254.     }
  255. }
  256.  
  257. void PSlineto(double x, double y) {
  258.     PSpath[nPathPtr].x = x;
  259.     PSpath[nPathPtr].y = 0.25 - y;
  260.     PSpath[nPathPtr].draw = TRUE;
  261.     nPathPtr++;
  262.     if (nPathPtr >= MAXPATH) {
  263.         fl_alert("PATH OVERFLOW in PSlineto()");
  264.         exit(1);
  265.     }
  266. }
  267.  
  268. void PSstroke() {
  269.     int i;
  270.     if (nPathPtr<2) return;
  271.     fl_begin_line();
  272.     for (i=0; i<nPathPtr; i++) {
  273.         fl_vertex(PSpath[i].x, PSpath[i].y);
  274.     }
  275.     fl_end_line();
  276.     nPathPtr=0;
  277. }
  278.  
  279. void PSthickstroke() {
  280.     double x1,x2,y1,y2;
  281.     double dx,dy;
  282.     int i, steps=32;
  283.  
  284.     // this is very simple, we assume the path is only 2 elements long!
  285.     // as FLTK does not have a linewidth, we draw using circles
  286.  
  287.     if (nPathPtr<2) return;
  288.  
  289.     x1=PSpath[0].x;
  290.     x2=PSpath[1].x;
  291.     y1=PSpath[0].y;
  292.     y2=PSpath[1].y;
  293.  
  294.     // find out how much steps we need to make it look good
  295.  
  296.     steps = sqrt(((fabs(x1-x2)*fabs(x1-x2))+(fabs(y1-y2)*fabs(y1-y2)))) * 15;
  297.  
  298.     dx = (x2-x1)/steps;
  299.     dy = (y2-y1)/steps;
  300.  
  301.     // draw in yellow
  302.  
  303.     fl_color(FL_YELLOW);
  304.  
  305.     for (i=0;i<(steps);i++) {
  306.         fl_begin_polygon();
  307.         fl_circle(x1, y1, 0.15);
  308.         fl_end_polygon();
  309.         x1+=dx;
  310.         y1+=dy;
  311.     }
  312.     PSsetgray(grayvalue);
  313.     nPathPtr=0;
  314. }
  315.  
  316. #define RADIUS 0.25
  317. #define SOLARSIZE 0.2
  318.  
  319. void darkblob( void ) {
  320.     if (!nPathPtr) return;
  321.     fl_color(FL_RED);
  322.     fl_begin_polygon();
  323.     fl_circle(PSpath[nPathPtr-1].x, PSpath[nPathPtr-1].y, RADIUS);
  324.     fl_end_polygon();
  325.     fl_color(FL_BLACK);
  326.     fl_begin_line();
  327.     fl_circle(PSpath[nPathPtr-1].x, PSpath[nPathPtr-1].y, RADIUS);
  328.     fl_end_line();
  329.     PSsetgray(grayvalue);
  330.     nPathPtr=0;
  331. }
  332.  
  333. void whiteblob( void ) {
  334.     if (!nPathPtr) return;
  335.     fl_color(FL_BLUE);
  336.     fl_begin_polygon();
  337.     fl_circle(PSpath[nPathPtr-1].x, PSpath[nPathPtr-1].y, RADIUS);
  338.     fl_end_polygon();
  339.     fl_color(FL_BLACK);
  340.     fl_begin_line();
  341.     fl_circle(PSpath[nPathPtr-1].x, PSpath[nPathPtr-1].y, RADIUS);
  342.     fl_end_line();
  343.     PSsetgray(grayvalue);
  344.     nPathPtr=0;
  345. }
  346.  
  347.  
  348. // not needed, we don't do instance drawing
  349.  
  350. void clipblob( void ) {
  351.  
  352. }
  353.  
  354. // the rest is take directly from the NeXTSTEP qubix again
  355.  
  356. void moveto3(double x,double y,double z) {
  357.     double zscale;
  358.     zscale = viewdistance/(viewdistance-(siny*y+sinx*cosy*x+cosx*cosy*z));
  359.     PSmoveto((cosx*x-sinx*z)*zscale, (cosy*y-sinx*siny*x-cosx*siny*z)*zscale);
  360. /*  PSmoveto(cosx*x-sinx*siny*y+sinx*cosy*z, z*siny+y*cosy); */
  361. }
  362.  
  363. void lineto3(double x,double y,double z) {
  364.     double zscale;
  365.     zscale = viewdistance/(viewdistance-(siny*y+sinx*cosy*x+cosx*cosy*z));
  366.     PSlineto((cosx*x-sinx*z)*zscale, (cosy*y-sinx*siny*x-cosx*siny*z)*zscale);
  367. /*  PSlineto(cosx*x-sinx*siny*y+sinx*cosy*z, z*siny+y*cosy); */
  368. }
  369.  
  370. double distto(double mx,double my,double x,double y,double z) {
  371.     double zscale;
  372.     zscale = viewdistance/(viewdistance-(siny*y+sinx*cosy*x+cosx*cosy*z));
  373.     return(fabs((cosx*x-sinx*z)*zscale-mx)+
  374.        fabs((cosy*y-sinx*siny*x-cosx*siny*z)*zscale-my));
  375. }
  376.  
  377. int inwinrow(int);
  378.  
  379. static double wx,wy,wz;
  380. void winline(int i,double x,double y, double z) {
  381.     double d=0.0;
  382.     if (!inwinrow(i)) return;
  383.     if (wx) {
  384.         switch ((x!=wx)+(y!=wy)+(z!=wz)) {
  385.             case 1: d = .25; break;
  386.             case 2: d = .25*0.70710678; break;
  387.             case 3: d = .25*0.57735027; break;
  388.         }
  389.         PSsetlinewidth(0.15);
  390.         moveto3(wx+d*(x-wx),wy+d*(y-wy),wz+d*(z-wz));
  391.         lineto3(x,y,z);
  392.         PSthickstroke();
  393.         PSsetlinewidth(0.0);
  394.     }
  395.     wx = x; wy = y; wz = z;
  396. }
  397.  
  398. /* draw position x and any of the rest of the board that might obscure
  399.    it.  If x is zero, also erase the previous board.  If instance is
  400.    on, draw the new piece using instance drawing. */
  401. void drawfrom(int ifrom, int instance) {
  402.     double x,y,z,l1;
  403.     int a,b,c,i,from;
  404.     from = ifrom;
  405.     PSscale(DISPLAYWIDTH/6.0,DISPLAYHEIGHT/6.0);
  406.     PStranslate(3.3,3.3);
  407.     PSsetlinewidth(0.0);
  408.     if (!from) {
  409.         PSsetgray(.666); 
  410.         PSrectfill(-3,-3,6,6); 
  411.         PSsetgray(0.0);
  412.     }
  413.     else {
  414.         PSgsave();
  415.         a = (from-1)>>4;
  416.         b = ((from-1)>>2)&3;
  417.         c = (from-1)&3;
  418.         moveto3(-1.5+b,-1.5+a,-1.5+c);
  419.         clipblob();
  420.         if (instance) PSsetinstance(TRUE);
  421.     }
  422.     wx = 0;
  423.     for(a=0,y=-1.5; a<4; a++,y+=1.0) {
  424.         if (xangle<45) {                
  425.             for (b=0,x=-1.5; b<4; b++,x+=1.0) {
  426.                 for (c=0,l1=z=-1.5; c<4; c++,z+=1.0) {
  427.                     i = 16*a+4*b+c+1;
  428.                     if (board[i] && (!from || i==from)) {
  429.                         winline(i,x,y,z);
  430.                         if (!from && c) {
  431.                             moveto3(x,y,l1);
  432.                             lineto3(x,y,z);
  433.                             PSstroke();
  434.                         }
  435.                         from = 0;
  436.                         moveto3(x,y,z);
  437.                         if (board[i]==PLAYER)
  438.                             playerblack?darkblob():whiteblob();
  439.                         else playerblack?whiteblob():darkblob();
  440.                         l1 = z+.25;
  441.                     }
  442.                 }
  443.                 if (from) continue;
  444.                 if (l1<1.5) {
  445.                     moveto3(x,y,l1); 
  446.                     lineto3(x,y,1.5); 
  447.                     PSstroke();
  448.                 }
  449.                 if (b<3) for (c=0,z=-1.5; c<4; c++,z+=1.0) {
  450.                     moveto3(x+(board[16*a+4*b+c+1]?.25:0.0),y,z);
  451.                     lineto3(x+1.0,y,z);
  452.                     PSstroke();
  453.                 }
  454.             }
  455.         } else {
  456.             for (c=0,z=-1.5; c<4; c++,z+=1.0) {
  457.                 for (b=0,l1=x=-1.5; b<4; b++,x+=1.0) {
  458.                     i = 16*a+4*b+c+1;
  459.                     if (board[i] && (!from || i==from)) {
  460.                         winline(i,x,y,z);
  461.                         if (!from && b) {
  462.                             moveto3(l1,y,z);
  463.                             lineto3(x,y,z);
  464.                             PSstroke();
  465.                         }
  466.                         from = 0;
  467.                         moveto3(x,y,z);
  468.                         if (board[i]==PLAYER)
  469.                             playerblack?darkblob():whiteblob();
  470.                         else playerblack?whiteblob():darkblob();
  471.                         l1 = x+.25;
  472.                     }
  473.                 }
  474.                 if (from) continue;
  475.                 if (l1<1.5) {
  476.                     moveto3(l1,y,z); 
  477.                     lineto3(1.5,y,z); 
  478.                     PSstroke();
  479.                 }
  480.                 if (c<3) for (b=0,x=-1.5; b<4; b++,x+=1.0) {
  481.                     moveto3(x,y,z+(board[16*a+4*b+c+1]?.25:0.0));
  482.                     lineto3(x,y,z+1.0);
  483.                     PSstroke();
  484.                 }
  485.             }
  486.         }
  487.     } 
  488.     if (ifrom) PSgrestore();
  489. }
  490.  
  491. /*=========================== Original NS User Interface ===========================*/
  492.  
  493. void maindisplay() {
  494.     mainwin->redraw();
  495.     mainwin->flush();
  496. }
  497.  
  498. int winrow;    /* n-1 out of lines table of winning line */
  499. char moves[64];    /* undo history */
  500. int movenum;
  501. int initedfltk=0;
  502.  
  503. void newgame(void) {
  504.     memset(board,0,sizeof(board));
  505.     winrow = 0;
  506.     movenum = 0;
  507.     playerblack = 0;
  508.     remark = NULL;
  509.     if (initedfltk) maindisplay();
  510. }
  511.  
  512. void erasemarks();
  513. int makemove();
  514.  
  515. void computermove(void) {
  516.     int i = makemove(); erasemarks();
  517.     if (i) board[i] = COMPUTER;
  518.     moves[movenum++] = i;
  519.     maindisplay();
  520. }
  521.  
  522. void flipboard(void) {
  523.     int i;
  524.     for (i=1; i<65; i++)
  525.     if (board[i] == PLAYER) board[i] = COMPUTER;
  526.     else if (board[i] == COMPUTER) board[i] = PLAYER;
  527.     }
  528.  
  529. void flipsides(void) {
  530.     flipboard();
  531.     playerblack = !playerblack;
  532.     if (movenum && moves[movenum-1]==0) movenum--;
  533.     else computermove();
  534.     maindisplay();
  535. }
  536.  
  537. void help(void) {
  538.     int i;
  539.     flipboard();
  540.     i = makemove(); erasemarks();
  541.     flipboard();
  542.     if (i) {
  543.         board[i] = PLAYER;
  544.         moves[movenum++] = i;
  545.         maindisplay();
  546.         computermove();
  547.     }
  548. }
  549.  
  550. void undo(void) {
  551.     if (movenum<2) return;
  552.     board[(int)moves[--movenum]] = 0;
  553.     board[(int)moves[--movenum]] = 0;
  554.     winrow = 0;
  555.     maindisplay();
  556. }
  557.  
  558. int setAPiece(double pntx, double pnty, int for_real)
  559. {
  560.     static int needsReset=FALSE;
  561.     static int pieceReset=0;
  562.     double x,y,z,l1,mx,my,idist;
  563.     int a,b,c,i,pi;
  564.     int e;
  565.  
  566.     if (needsReset == TRUE) {
  567.         board[pieceReset] = 0;
  568.         needsReset=FALSE;
  569.     }
  570.  
  571.     if (winrow) return 1;
  572.     //printf("setAPiece %d: %f %f\n",for_real, pntx,pnty);
  573.  
  574.     pi = 0;
  575.     do {
  576.         mx = (6.0*(pntx))/DISPLAYWIDTH - 3.18; 
  577.         my = (6.0*(pnty))/DISPLAYHEIGHT - 2.72;
  578.         idist = .25; i = 0;
  579.         for(a=0,y=-1.5; a<4; a++,y+=1.0) {
  580.             for (c=0,z=-1.5; c<4; c++,z+=1.0) {
  581.                 for (b=0,x=-1.5; b<4; b++,x+=1.0) {
  582.                     if (board[16*a+4*b+c+1]) continue;
  583.                     l1 = distto(mx,my,x,y,z);
  584.                     if (l1 <= idist) {
  585.                         idist=l1;
  586.                         i = 16*a+4*b+c+1;
  587.                     }
  588.                 }
  589.             }
  590.         }
  591.         if (i!=pi) {
  592.             if (i && !for_real) {
  593.                 board[i] = PLAYER;
  594.                 maindisplay();
  595.                 needsReset=TRUE;
  596.                 pieceReset=i;
  597.             }
  598.             pi = i;
  599.         }
  600.         e = 0;
  601.     } while (e != 0);
  602.     if (!i) {
  603.         maindisplay();
  604.         return 2;
  605.     }
  606.     if (!for_real) {
  607.         maindisplay();
  608.         return 3;
  609.     }
  610.     board[i] = PLAYER; 
  611.     moves[movenum++] = i;
  612.     maindisplay();
  613.     computermove();
  614.     return 0;
  615. }
  616.  
  617.  
  618. void updateSliderPos(double xangle, double yangle)
  619. {
  620.     double x,y;
  621.     //fl_alert("%f %f",xangle, yangle);
  622.     x = (90-xangle)*(3.14159/180);
  623.     sinx = sin(x); cosx = cos(x);
  624.     y = (90-yangle)*(3.14159/180);
  625.     siny = sin(y); cosy = cos(y);
  626. }
  627.  
  628. void setupGFX() {
  629.     double x,y;
  630.  
  631.     x = (90-xangle)*(3.14159/180);
  632.     sinx = sin(x); cosx = cos(x);
  633.     y = (90-yangle)*(3.14159/180);
  634.     siny = sin(y); cosy = cos(y);
  635. }
  636.  
  637.  
  638.  
  639.  
  640. /*=============================== Game logic =============================*/
  641.  
  642. const int lines[304] = {
  643.     1,2,3,4,    8,7,6,5,    12,11,10,9,    13,14,15,16,
  644.     20,19,18,17,    21,22,23,24,    25,26,27,28,    32,31,30,29,
  645.     36,35,34,33,    37,38,39,40,    41,42,43,44,    48,47,46,45,
  646.     49,50,51,52,    56,55,54,53,    60,59,58,57,    61,62,63,64,
  647.  
  648.     1,5,9,13,    14,10,6,2,    15,11,7,3,    4,8,12,16,
  649.     29,25,21,17,    18,22,26,30,    19,23,27,31,    32,28,24,20,
  650.     45,41,37,33,    34,38,42,46,    35,39,43,47,    48,44,40,36,
  651.     49,53,57,61,    62,58,54,50,    63,59,55,51,    52,56,60,64,
  652.  
  653.     1,17,33,49,    53,37,21,5,    57,41,25,9,    13,29,45,61,
  654.     50,34,18,2,    6,22,38,54,    10,26,42,58,    62,46,30,14,
  655.     51,35,19,3,    7,23,39,55,    11,27,43,59,    63,47,31,15,
  656.     4,20,36,52,    56,40,24,8,    60,44,28,12,    16,32,48,64,
  657.  
  658.     1,6,11,16,    32,27,22,17,    48,43,38,33,    49,54,59,64,
  659.     13,10,7,4,    20,23,26,29,    36,39,42,45,    61,58,55,52,
  660.     1,21,41,61,    62,42,22,2,    63,43,23,3,    4,24,44,64,
  661.     49,37,25,13,    14,26,38,50,    15,27,39,51,    52,40,28,16,
  662.     1,18,35,52,    56,39,22,5,    60,43,26,9,    13,30,47,64,
  663.     49,34,19,4,    8,23,38,53,    12,27,42,57,    61,46,31,16,
  664.  
  665.     1,22,43,64,    49,38,27,16,    61,42,23,4,    13,26,39,52};
  666.  
  667. int nummarks;
  668.  
  669. void erasemarks() {
  670.     int *point;
  671.     for (point = &board[64]; point > board; point--)
  672.     if (*point == MARK) *point = 0;
  673.     nummarks = 0;
  674. }
  675.  
  676. int linesum(int i) {    /* add the values in a line on the board */
  677.     int sum,j;
  678.     const int *p;
  679.     if (i > 75) return(-1);
  680.     for (sum = 0, j = 0, p = lines+4*i; j < 4; j++) sum += board[*p++];
  681.     return(sum);
  682. }
  683.  
  684. void markline(int i) {
  685.     int j;
  686.     const int *p;
  687.     for (j = 0, p = lines+4*i; j < 4; j++,p++) if (!board[*p]) {
  688.     board[*p] = MARK;
  689.     nummarks++;
  690.     }
  691. }
  692.  
  693. int find(int n, int i) {    /* locate n in a line */
  694.     const int *p;
  695.     p = lines+4*i;
  696.     if (board[*(p+1)] == n) return(*(p+1));
  697.     if (board[*(p+2)] == n) return(*(p+2));
  698.     if (board[*(p+0)] == n) return(*(p+0));
  699.     if (board[*(p+3)] == n) return(*(p+3));
  700.     return(0);
  701. }
  702.  
  703. int inwinrow(int n) {
  704.     const int *p;
  705.     if (!winrow) return(FALSE);
  706.     p = lines+4*(winrow-1);
  707.     if (*p++ == n) return(TRUE);
  708.     if (*p++ == n) return(TRUE);
  709.     if (*p++ == n) return(TRUE);
  710.     if (*p == n) return(TRUE);
  711.     return(FALSE);
  712. }
  713.  
  714. int makemove() {
  715.     int i,line,move;
  716.  
  717.     for (i=0; (line=linesum(i))>=0; i++) if (line == 4*PLAYER) {
  718.     remark = "Rats.  You win.";
  719.     winrow = i+1;
  720.     return(0);
  721.     }
  722.     else if (line == 4*COMPUTER) {
  723.     remark = "I win!";
  724.     winrow = i+1;
  725.     return(0);
  726.     }
  727.  
  728.     for (i=0; (line=linesum(i))>=0; i++) if (line == 3*COMPUTER) {
  729.     remark = "I win!";
  730.     winrow = i+1;
  731.     return(find(0,i));
  732.     }
  733.  
  734.     for (i=0; (line=linesum(i))>=0; i++) if (line == 3*PLAYER) {
  735.     remark = "I'll block that.";
  736.     return(find(0,i));
  737.     }
  738.  
  739.     move = 0;
  740.  
  741.     for (i=0; (line=linesum(i))>=0; i++) {
  742.     /* computer tries to make crossed triples */
  743.     if (line == 2*COMPUTER) markline(i);
  744.     else if (line == 2*COMPUTER+MARK || line == 2*COMPUTER+2*MARK) {
  745.         remark = "Let's see you get out of this...";
  746.         move = find(MARK,i);
  747.         goto DONE;
  748.         }
  749.     }
  750.  
  751.     if (nummarks) for (i=0; (line=linesum(i))>=0; i++) {
  752.     /* computer tries to force a crossed triple */
  753.     if (line == COMPUTER+2*MARK || line == COMPUTER+3*MARK) {
  754.         remark = "You've had it now...";
  755.         move = find(MARK,i);
  756.         goto DONE;
  757.         }
  758.     else if (line == 3*MARK || line == 4*MARK) {
  759.         remark = "You have to block my triple...";
  760.         move = find(MARK,i);
  761.         goto DONE;
  762.         }
  763.     else if (line == 2*MARK) {
  764.         remark = "Look carefully or you will lose...";
  765.         move = find(0,i);
  766.         }
  767.     }
  768.  
  769.     erasemarks();
  770.     for (i=0; (line=linesum(i))>=0; i++)
  771.     if (line == 2*PLAYER) markline(i);
  772.     else if (line == 2*PLAYER+MARK || line == 2*PLAYER+2*MARK) {
  773.         remark = "You'll have to be more clever than that.";
  774.         move = find(MARK,i);
  775.         goto DONE;
  776.         }
  777.  
  778.     if (nummarks) for (i=0; (line=linesum(i))>=0; i++) {
  779.     /* try to block good players */
  780.     if (line == PLAYER+2*MARK || line == PLAYER+3*MARK) {
  781.         remark = "This should mess up your plans.";
  782.         move = find(MARK,i);
  783.         goto DONE;
  784.         }
  785.     else if (line == 2*MARK || line == 3*MARK || line == 4*MARK) {
  786.         remark = "Are you trying something?";
  787.         move = find(MARK,i);
  788.         }
  789.     else if (!move && line>COMPUTER && line<COMPUTER+4*MARK) {
  790.         remark = 0;
  791.         move = find(MARK,i);
  792.         }
  793.     }
  794.     if (move) goto DONE;
  795.  
  796.     /* random move, as long as it is in non-dead line */
  797.     erasemarks();
  798.     for (i=1; (line=linesum(i))>=0; i++)
  799.     if (!((line & 7*PLAYER) && (line & 7*COMPUTER)))
  800.         /* line does not contain both types of pieces */
  801.         markline(i);
  802.     if (nummarks) {
  803.     remark = 0;
  804.     do move = (genrand()&63)+1; while (board[move] != MARK);
  805.     }
  806.     else remark = "Tie game!";
  807.  
  808.  DONE:
  809.     erasemarks();
  810.     return(move);
  811. }
  812.  
  813. // FLTK specific stuff 
  814.  
  815. // the game board view, also the event handler
  816.  
  817. class QubixView : public Fl_Widget {
  818.   void draw() {
  819.     fl_clip(x(),y(),w(),h());
  820.     fl_color(FL_WHITE);
  821.     fl_rectf(x(),y(),w(),h());
  822.     fl_push_matrix();
  823.     drawfrom(0,0);
  824.     fl_pop_matrix();
  825.     if (remark) {
  826.         fl_font(FL_HELVETICA,12);
  827.         fl_draw(remark,(x()+3.0),(h()+y()-13.0));
  828.     }
  829.     fl_pop_clip();
  830.   }
  831.   int handle(int event) {
  832.     int x2,y2;
  833.     switch (event) {
  834.         case FL_PUSH:
  835.             /* fall throu */
  836.         case FL_DRAG:
  837.             x2 = Fl::event_x();
  838.             y2 = Fl::event_y();
  839.             setAPiece(x2-6,(h()-y2)+18, 0);
  840.             return 1;
  841.             /* not reached */
  842.             break;
  843.         case FL_RELEASE:
  844.             x2 = Fl::event_x();
  845.             y2 = Fl::event_y();
  846.             setAPiece(x2-6,(h()-y2)+18, 1);
  847.             return 1;
  848.             /* not reached */
  849.             break;
  850.     }
  851.     return 0;
  852.   }
  853. public:
  854.   QubixView(int X,int Y,int W,int H) : Fl_Widget(X,Y,W,H) {}
  855. };
  856.  
  857. /* turn the game board by using the sliders */
  858. void updatedSlider(Fl_Widget *wid, void *value) {
  859.     double xv, yv;
  860.  
  861.     xv = x_silder->value();
  862.     yv = y_slider->value();
  863.  
  864.     updateSliderPos(xv,yv);
  865.  
  866.     mainwin->redraw();
  867. }
  868.  
  869. /* handle the menu */
  870. void menu_callback(Fl_Widget* w, void*) {
  871.   char acTmp[256];
  872.   Fl_Menu_* mw = (Fl_Menu_*)w;
  873.   const Fl_Menu_Item* m = mw->mvalue();
  874.   if (!m)
  875.     sprintf(acTmp,"Fehler: NULL Menu!\n");
  876.   else if (m->shortcut())
  877.     sprintf(acTmp,"%s - %s", m->label(), fl_shortcut_label(m->shortcut()));
  878.   else
  879.     sprintf(acTmp,"%s", m->label());
  880.  
  881.   if (!strcmp("About",acTmp)) {
  882.     infowin->show();
  883.   }
  884.   if (!strcmp("New Game",acTmp)) {
  885.     newgame();
  886.   }
  887.   if (!strcmp("Flip Sides",acTmp)) {
  888.     flipsides();
  889.   }
  890.   if (!strcmp("Undo Last Move",acTmp)) {
  891.     undo();
  892.   }
  893.   if (!strcmp("Quit",acTmp)) {
  894.     fl_alert("  Thanks for playing FlQubix... Have a nice day!");
  895.     exit(0);
  896.   }
  897.  
  898. }
  899.  
  900. /* make our windows */
  901.  
  902. Fl_Window* make_infowin() {
  903.   Fl_Window* w;
  904.   { Fl_Window* o = new Fl_Window(364, 299);
  905.     w = o;
  906.     { Fl_Box* o = new Fl_Box(20, 0, 325, 95, "Qubix");
  907.       o->labelfont(11);
  908.       o->labelsize(80);
  909.     }
  910.     { Fl_Box* o = new Fl_Box(30, 105, 305, 40, "A game of 4-in-a-row in 3D");
  911.       o->labelfont(8);
  912.       o->labelsize(24);
  913.     }
  914.     { Fl_Box* o = new Fl_Box(20, 150, 335, 35, "written by Frank Siegert <frank@this.net>");
  915.       o->labelfont(8);
  916.       o->labelsize(18);
  917.     }
  918.     { Fl_Box* o = new Fl_Box(15, 190, 340, 35, "Based on Qubix by Bill Spitzak");
  919.       o->labelfont(8);
  920.       o->labelsize(18);
  921.     }
  922.     { Fl_Box* o = new Fl_Box(85, 215, 195, 25, "(c) 1991 on NeXTSTEP");
  923.       o->labelfont(8);
  924.       o->labelsize(18);
  925.     }
  926.     { Fl_Box* o = new Fl_Box(25, 260, 320, 25, "This program may be distributed freely (GPL)");
  927.       o->labelfont(8);
  928.       o->labelsize(18);
  929.     }
  930.     { Fl_Box* o = new Fl_Box(10, 75, 320, 20, "in FLTK");
  931.       o->labelfont(8);
  932.       o->labelsize(18);
  933.     }
  934.     o->end();
  935.   }
  936.   return w;
  937. }
  938.  
  939.  
  940. Fl_Double_Window* make_gamewin() {
  941.   Fl_Double_Window* w;
  942.   { Fl_Double_Window* o = new Fl_Double_Window(403, 426,"FLTK Qubix");
  943.     w = o;
  944.     y_slider = new Fl_Slider(380, 35, 20, 370);
  945.     y_slider->type(FL_VERT_NICE_SLIDER);
  946.     y_slider->minimum(0.0);
  947.     y_slider->maximum(90.0);
  948.     y_slider->value(75.0);
  949.     y_slider->callback(updatedSlider);
  950.  
  951.     // oh well... if this the way of fluid....
  952.  
  953.     { Fl_Slider* o = x_silder = new Fl_Slider(0, 405, 380, 20);
  954.       o->type(FL_HOR_NICE_SLIDER);
  955.       o->minimum(0.0);
  956.       o->maximum(90.0);
  957.       o->value(75.0);
  958.       o->callback(updatedSlider);
  959.     }
  960.     { Fl_Menu_Bar* o = main_menu = new Fl_Menu_Bar(10, 5, 170, 25);
  961.       o->menu(menu_main_menu);
  962.       o->callback(menu_callback);
  963.     }
  964.     { QubixView* o = new QubixView(10, 35, DISPLAYWIDTH, DISPLAYHEIGHT);
  965.     }
  966.     o->end();
  967.   }
  968.   return w;
  969. }
  970.  
  971. /* main routine */
  972.  
  973. int main(int argc, char **argv) {
  974.  
  975.     sgenrand(time(0));
  976.  
  977.     mainwin = make_gamewin();
  978.     infowin = make_infowin();
  979.  
  980.     setupGFX();
  981.     newgame();
  982.  
  983.     mainwin->show();
  984.  
  985.     initedfltk = 1;
  986.  
  987.     Fl::run();
  988.  
  989.     return 0;
  990. }
  991.