|
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <malloc.h>
#include <time.h>
#include <math.h>
#include "tk.h"
#include "trackbal.h"
#define WIDTH 4
#define HEIGHT 5
#define PIECES 10
#define OFFSETX -2
#define OFFSETY -2.5
#define OFFSETZ -0.5
typedef char Config[HEIGHT][WIDTH];
struct puzzle { struct puzzle *backptr; struct puzzle *solnptr; Config pieces; struct puzzle *next; unsigned hashvalue; };
#define HASHSIZE 10691
struct puzzlelist { struct puzzle *puzzle; struct puzzlelist *next; };
static char convert[PIECES+1] = {0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 4};
static unsigned char colors[PIECES+1][3] = { { 0, 0, 0}, {255,255,127}, {255,255,127}, {255,255,127}, {255,255,127}, {255,127,255}, {255,127,255}, {255,127,255}, {255,127,255}, {255,127,127}, {255,255,255}, };
static struct puzzle *hashtable[HASHSIZE]; static struct puzzle *startPuzzle; static struct puzzlelist *puzzles; static struct puzzlelist *lastentry;
#define MOVE_SPEED 0.2
static unsigned char movingPiece; static float move_x,move_y; static float curquat[4]; static int doubleBuffer=1; static int depth=1;
static char xsize[PIECES+1] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2 }; static char ysize[PIECES+1] = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2 }; static float zsize[PIECES+1] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.6 }; static GLuint listbase;
static Config startConfig = { { 8, 10, 10, 7 }, { 8, 10, 10, 7 }, { 6, 9, 9, 5 }, { 6, 4, 3, 5 }, { 2, 0, 0, 1 } };
static Config thePuzzle = { { 8, 10, 10, 7 }, { 8, 10, 10, 7 }, { 6, 9, 9, 5 }, { 6, 4, 3, 5 }, { 2, 0, 0, 1 } };
static int xadds[4]={-1, 0, 1, 0}; static int yadds[4]={ 0,-1, 0, 1};
static long W = 400, H = 300; static GLint viewport[4];
#define srandom srand
#define random() (rand() >> 2)
unsigned hash(Config config) { int i,j,value;
value=0; for (i=0; i<HEIGHT; i++) { for (j=0; j<WIDTH; j++) { value=value+convert[config[i][j]]; value*=6; } } return(value); }
int solution(Config config) { if (config[4][1]==10 && config[4][2]==10) return(1); return(0); }
float boxcoords[][3] = { { 0.2, 0.2, 0.9 }, { 0.8, 0.2, 0.9 }, { 0.8, 0.8, 0.9 }, { 0.2, 0.8, 0.9 }, { 0.2, 0.1, 0.8 }, { 0.8, 0.1, 0.8 }, { 0.9, 0.2, 0.8 }, { 0.9, 0.8, 0.8 }, { 0.8, 0.9, 0.8 }, { 0.2, 0.9, 0.8 }, { 0.1, 0.8, 0.8 }, { 0.1, 0.2, 0.8 }, { 0.2, 0.1, 0.2 }, { 0.8, 0.1, 0.2 }, { 0.9, 0.2, 0.2 }, { 0.9, 0.8, 0.2 }, { 0.8, 0.9, 0.2 }, { 0.2, 0.9, 0.2 }, { 0.1, 0.8, 0.2 }, { 0.1, 0.2, 0.2 }, { 0.2, 0.2, 0.1 }, { 0.8, 0.2, 0.1 }, { 0.8, 0.8, 0.1 }, { 0.2, 0.8, 0.1 }, };
float boxnormals[][3] = { { 0, 0, 1 }, /* 0 */ { 0, 1, 0 }, { 1, 0, 0 }, { 0, 0,-1 }, { 0,-1, 0 }, {-1, 0, 0 }, { 0.7071, 0.7071, 0.0000}, /* 6 */ { 0.7071,-0.7071, 0.0000}, {-0.7071, 0.7071, 0.0000}, {-0.7071,-0.7071, 0.0000}, { 0.7071, 0.0000, 0.7071}, /* 10 */ { 0.7071, 0.0000,-0.7071}, {-0.7071, 0.0000, 0.7071}, {-0.7071, 0.0000,-0.7071}, { 0.0000, 0.7071, 0.7071}, /* 14 */ { 0.0000, 0.7071,-0.7071}, { 0.0000,-0.7071, 0.7071}, { 0.0000,-0.7071,-0.7071}, { 0.5774, 0.5774, 0.5774}, /* 18 */ { 0.5774, 0.5774,-0.5774}, { 0.5774,-0.5774, 0.5774}, { 0.5774,-0.5774,-0.5774}, {-0.5774, 0.5774, 0.5774}, {-0.5774, 0.5774,-0.5774}, {-0.5774,-0.5774, 0.5774}, {-0.5774,-0.5774,-0.5774}, };
int boxfaces[][4] = { { 0, 1, 2, 3 }, /* 0 */ { 9, 8, 16, 17 }, { 6, 14, 15, 7 }, { 20, 23, 22, 21 }, { 12, 13, 5, 4 }, { 19, 11, 10, 18 }, { 7, 15, 16, 8 }, /* 6 */ { 13, 14, 6, 5 }, { 18, 10, 9, 17 }, { 19, 12, 4, 11 }, { 1, 6, 7, 2 }, /* 10 */ { 14, 21, 22, 15 }, { 11, 0, 3, 10 }, { 20, 19, 18, 23 }, { 3, 2, 8, 9 }, /* 14 */ { 17, 16, 22, 23 }, { 4, 5, 1, 0 }, { 20, 21, 13, 12 }, { 2, 7, 8, -1 }, /* 18 */ { 16, 15, 22, -1 }, { 5, 6, 1, -1 }, { 13, 21, 14, -1 }, { 10, 3, 9, -1 }, { 18, 17, 23, -1 }, { 11, 4, 0, -1 }, { 20, 12, 19, -1 }, };
#define NBOXFACES (sizeof(boxfaces)/sizeof(boxfaces[0]))
/* Draw a box. Bevel as desired. */ void drawBox(int piece, float xoff, float yoff) { int xlen, ylen; int i,j,k; float x,y,z; float zlen; float *v;
xlen=xsize[piece]; ylen=ysize[piece]; zlen=zsize[piece];
glColor3ubv(colors[piece]); glBegin(GL_QUADS); for (i=0; i<18; i++) { glNormal3fv(boxnormals[i]); for (k=0; k<4; k++) { if (boxfaces[i][k] == -1) continue; v=boxcoords[boxfaces[i][k]]; x=v[0] + OFFSETX; if (v[0] > 0.5) x += xlen-1; y=v[1] + OFFSETY; if (v[1] > 0.5) y += ylen-1; z=v[2] + OFFSETZ; if (v[2] > 0.5) z += zlen-1; glVertex3f(xoff+x,yoff+y,z); } } glEnd(); glBegin(GL_TRIANGLES); for (i=18; i<NBOXFACES; i++) { glNormal3fv(boxnormals[i]); for (k=0; k<3; k++) { if (boxfaces[i][k] == -1) continue; v=boxcoords[boxfaces[i][k]]; x=v[0] + OFFSETX; if (v[0] > 0.5) x += xlen-1; y=v[1] + OFFSETY; if (v[1] > 0.5) y += ylen-1; z=v[2] + OFFSETZ; if (v[2] > 0.5) z += zlen-1; glVertex3f(xoff+x,yoff+y,z); } } glEnd(); }
float containercoords[][3] = { { -0.1, -0.1, 1.0 }, { -0.1, -0.1, -0.1 }, { 4.1, -0.1, -0.1 }, { 4.1, -0.1, 1.0 }, { 1.0, -0.1, 0.6 }, /* 4 */ { 3.0, -0.1, 0.6 }, { 1.0, -0.1, 0.0 }, { 3.0, -0.1, 0.0 }, { 1.0, 0.0, 0.0 }, /* 8 */ { 3.0, 0.0, 0.0 }, { 3.0, 0.0, 0.6 }, { 1.0, 0.0, 0.6 }, { 0.0, 0.0, 1.0 }, /* 12 */ { 4.0, 0.0, 1.0 }, { 4.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 5.0, 0.0 }, /* 16 */ { 0.0, 5.0, 1.0 }, { 4.0, 5.0, 1.0 }, { 4.0, 5.0, 0.0 }, { -0.1, 5.1, -0.1 }, /* 20 */ { 4.1, 5.1, -0.1 }, { 4.1, 5.1, 1.0 }, { -0.1, 5.1, 1.0 }, };
float containernormals[][3] = { { 0,-1, 0 }, { 0,-1, 0 }, { 0,-1, 0 }, { 0,-1, 0 }, { 0,-1, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 0, 1, 0 }, { 1, 0, 0 }, { 1, 0, 0 }, { 1, 0, 0 }, {-1, 0, 0 }, {-1, 0, 0 }, {-1, 0, 0 }, { 0, 1, 0 }, { 0, 0,-1 }, { 0, 0,-1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, { 0, 0, 1 }, };
int containerfaces[][4] = { { 1, 6, 4, 0 }, { 0, 4, 5, 3 }, { 1, 2, 7, 6 }, { 7, 2, 3, 5 }, { 16, 19, 18, 17 },
{ 23, 22, 21, 20 }, { 12, 11, 8, 15 }, { 10, 13, 14, 9 },
{ 15, 16, 17, 12 }, { 2, 21, 22, 3 }, { 6, 8, 11, 4 },
{ 1, 0, 23, 20 }, { 14, 13, 18, 19 }, { 9, 7, 5, 10 },
{ 12, 13, 10, 11 },
{ 1, 20, 21, 2 }, { 4, 11, 10, 5 },
{ 15, 8, 19, 16 }, { 19, 8, 9, 14 }, { 8, 6, 7, 9 }, { 0, 3, 13, 12 }, { 13, 3, 22, 18 }, { 18, 22, 23, 17 }, { 17, 23, 0, 12 }, };
#define NCONTFACES (sizeof(containerfaces)/sizeof(containerfaces[0]))
/* Draw the container */ void drawContainer(void) { int i,k; float *v;
/* Y is reversed here because the model has it reversed */
/* Arbitrary bright wood-like color */ glColor3ub(209, 103, 23); glBegin(GL_QUADS); for (i=0; i<NCONTFACES; i++) { v=containernormals[i]; glNormal3f(v[0], -v[1], v[2]); for (k=3; k>=0; k--) { v=containercoords[containerfaces[i][k]]; glVertex3f(v[0]+OFFSETX, -(v[1]+OFFSETY), v[2]+OFFSETZ); } } glEnd(); }
void drawAll(int withTags) { int i,j; int piece; char done[PIECES+1]; float m[4][4];
build_rotmatrix(m, curquat); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0,0,10, 0,0,0, 0,-1,0); glMultMatrixf(&(m[0][0]));
if (depth) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { glClear(GL_COLOR_BUFFER_BIT); } for (i=1; i <= PIECES; i++) { done[i] = 0; } glLoadName(0); drawContainer(); for (i=0; i<HEIGHT; i++) { for (j=0; j<WIDTH; j++) { piece = thePuzzle[i][j]; if (piece == 0) continue; if (done[piece]) continue; done[piece] = 1; glLoadName(piece); if (piece == movingPiece) { drawBox(piece, move_x, move_y); } else { drawBox(piece, j, i); } } } }
void redraw(void) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, 1.0, 0.1, 100.0);
drawAll(GL_FALSE);
if (doubleBuffer) tkSwapBuffers(); }
void solidifyChain(struct puzzle *puzzle) { int i;
i=0; while (puzzle->backptr) { i++; puzzle->backptr->solnptr = puzzle; puzzle=puzzle->backptr; } printf("%d moves to complete!\n", i); }
int addConfig(Config config, struct puzzle *back) { unsigned hashvalue; struct puzzle *newpiece; struct puzzlelist *newlistentry;
hashvalue=hash(config);
newpiece=hashtable[hashvalue % HASHSIZE]; while (newpiece != NULL) { if (newpiece->hashvalue == hashvalue) { int i,j;
for (i=0; i<WIDTH; i++) { for (j=0; j<HEIGHT; j++) { if (convert[config[j][i]] != convert[newpiece->pieces[j][i]]) goto nomatch; } } return 0; } nomatch: newpiece=newpiece->next; }
newpiece=(struct puzzle *) malloc(sizeof(struct puzzle)); newpiece->next=hashtable[hashvalue % HASHSIZE]; newpiece->hashvalue=hashvalue; memcpy(newpiece->pieces, config, HEIGHT*WIDTH); newpiece->backptr=back; newpiece->solnptr=NULL; hashtable[hashvalue % HASHSIZE]=newpiece;
newlistentry=(struct puzzlelist *) malloc(sizeof(struct puzzlelist)); newlistentry->puzzle=newpiece; newlistentry->next=NULL;
if (lastentry) { lastentry->next=newlistentry; } else { puzzles=newlistentry; } lastentry=newlistentry;
if (back == NULL) { startPuzzle = newpiece; }
if (solution(config)) { solidifyChain(newpiece); return 1; }
return 0; }
/* Checks if a space can move */ int canmove0(Config pieces, int x, int y, int dir, Config newpieces) { char piece; int xadd, yadd; int l,m;
xadd=xadds[dir]; yadd=yadds[dir];
if (x+xadd<0 || x+xadd>=WIDTH || y+yadd<0 || y+yadd>=HEIGHT) return 0; piece=pieces[y+yadd][x+xadd]; if (piece==0) return 0; memcpy(newpieces, pieces, HEIGHT*WIDTH); for (l=0; l<WIDTH; l++) { for (m=0; m<HEIGHT; m++) { if (newpieces[m][l]==piece) newpieces[m][l]=0; } } xadd= -xadd; yadd= -yadd; for (l=0; l<WIDTH; l++) { for (m=0; m<HEIGHT; m++) { if (pieces[m][l]==piece) { int newx, newy;
newx=l+xadd; newy=m+yadd; if (newx<0 || newx>=WIDTH || newy<0 || newy>=HEIGHT) return 0; if (newpieces[newy][newx] != 0) return 0; newpieces[newy][newx]=piece; } } } return 1; }
/* Checks if a piece can move */ int canmove(Config pieces, int x, int y, int dir, Config newpieces) { int xadd, yadd;
xadd=xadds[dir]; yadd=yadds[dir];
if (x+xadd<0 || x+xadd>=WIDTH || y+yadd<0 || y+yadd>=HEIGHT) return 0; if (pieces[y+yadd][x+xadd] == pieces[y][x]) { return canmove(pieces, x+xadd, y+yadd, dir, newpieces); } if (pieces[y+yadd][x+xadd] != 0) return 0; return canmove0(pieces, x+xadd, y+yadd, (dir+2) % 4, newpieces); }
int generateNewConfigs(struct puzzle *puzzle) { int i,j,k; Config pieces; Config newpieces;
memcpy(pieces, puzzle->pieces, HEIGHT*WIDTH); for (i=0; i<WIDTH; i++) { for (j=0; j<HEIGHT; j++) { if (pieces[j][i] == 0) { for (k=0; k<4; k++) { if (canmove0(pieces, i, j, k, newpieces)) { if (addConfig(newpieces, puzzle)) return 1; } } } } } return 0; }
void freeSolutions(void) { struct puzzlelist *nextpuz; struct puzzle *puzzle, *next; int i;
while (puzzles) { nextpuz = puzzles->next; free((char *) puzzles); puzzles=nextpuz; } lastentry = NULL; for (i=0; i<HASHSIZE; i++) { puzzle = hashtable[i]; hashtable[i] = NULL; while (puzzle) { next = puzzle->next; free((char *) puzzle); puzzle = next; } } startPuzzle = NULL; }
int continueSolving(void) { struct puzzle *nextpuz; int i,j; int movedPiece; int movedir; int fromx, fromy; int tox, toy;
if (startPuzzle == NULL) return 0; if (startPuzzle->solnptr == NULL) { freeSolutions(); return 0; }
nextpuz = startPuzzle->solnptr; movedPiece=0; movedir=0; for (i=0; i<HEIGHT; i++) { for (j=0; j<WIDTH; j++) { if (startPuzzle->pieces[i][j] != nextpuz->pieces[i][j]) { if (startPuzzle->pieces[i][j]) { movedPiece=startPuzzle->pieces[i][j]; fromx=j; fromy=i; if (i<HEIGHT-1 && nextpuz->pieces[i+1][j] == movedPiece) { movedir=3; } else { movedir=2; } goto found_piece; } else { movedPiece=nextpuz->pieces[i][j]; if (i<HEIGHT-1 && startPuzzle->pieces[i+1][j] == movedPiece) { fromx=j; fromy=i+1; movedir=1; } else { fromx=j+1; fromy=i; movedir=0; } goto found_piece; } } } } printf("What! No change?\n"); freeSolutions(); return 0;
found_piece: if (!movingPiece) { movingPiece = movedPiece; move_x = fromx; move_y = fromy; } move_x += xadds[movedir] * MOVE_SPEED; move_y += yadds[movedir] * MOVE_SPEED;
tox = fromx + xadds[movedir]; toy = fromy + yadds[movedir];
if (move_x > tox - MOVE_SPEED/2 && move_x < tox + MOVE_SPEED/2 && move_y > toy - MOVE_SPEED/2 && move_y < toy + MOVE_SPEED/2) { startPuzzle = nextpuz; movingPiece=0; }
memcpy(thePuzzle, startPuzzle->pieces, HEIGHT*WIDTH); return 1; }
int solvePuzzle(void) { struct puzzlelist *nextpuz; int i;
if (solution(thePuzzle)) { printf("Puzzle already solved!\n"); return 0; } addConfig(thePuzzle, NULL); i=0;
while (puzzles) { i++; if (generateNewConfigs(puzzles->puzzle)) break; nextpuz=puzzles->next; free((char *) puzzles); puzzles=nextpuz; } if (puzzles == NULL) { freeSolutions(); printf("I can't solve it! (%d positions examined)\n", i); return 1; } return 1; }
int selectPiece(int mousex, int mousey) { long hits; GLuint selectBuf[1024]; GLuint closest; GLuint dist;
glSelectBuffer(1024, selectBuf); (void) glRenderMode(GL_SELECT); glInitNames();
/* Because LoadName() won't work with no names on the stack */ glPushName(-1);
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPickMatrix(mousex, H-mousey, 4, 4, viewport); gluPerspective(45, 1.0, 0.1, 100.0);
drawAll(GL_TRUE);
hits = glRenderMode(GL_RENDER); if (hits <= 0) { return 0; }
closest=0; dist=4294967295; while (hits) { if (selectBuf[(hits-1)*4+1] < dist) { dist = selectBuf[(hits-1)*4+1]; closest = selectBuf[(hits-1)*4+3]; } hits--; } return closest; }
void nukePiece(int piece) { int i,j;
for (i=0; i<HEIGHT; i++) { for (j=0; j<WIDTH; j++) { if (thePuzzle[i][j] == piece) { thePuzzle[i][j] = 0; } } } }
void multMatrices(const GLfloat a[16], const GLfloat b[16], GLfloat r[16]) { int i, j;
for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { r[i*4+j] = a[i*4+0]*b[0*4+j] + a[i*4+1]*b[1*4+j] + a[i*4+2]*b[2*4+j] + a[i*4+3]*b[3*4+j]; } } }
void makeIdentity(GLfloat m[16]) { m[0+4*0] = 1; m[0+4*1] = 0; m[0+4*2] = 0; m[0+4*3] = 0; m[1+4*0] = 0; m[1+4*1] = 1; m[1+4*2] = 0; m[1+4*3] = 0; m[2+4*0] = 0; m[2+4*1] = 0; m[2+4*2] = 1; m[2+4*3] = 0; m[3+4*0] = 0; m[3+4*1] = 0; m[3+4*2] = 0; m[3+4*3] = 1; }
/*
** inverse = invert(src) */ int invertMatrix(const GLfloat src[16], GLfloat inverse[16]) { int i, j, k, swap; double t; GLfloat temp[4][4];
for (i=0; i<4; i++) { for (j=0; j<4; j++) { temp[i][j] = src[i*4+j]; } } makeIdentity(inverse);
for (i = 0; i < 4; i++) { /*
** Look for largest element in column */ swap = i; for (j = i + 1; j < 4; j++) { if (fabs(temp[j][i]) > fabs(temp[i][i])) { swap = j; } }
if (swap != i) { /*
** Swap rows. */ for (k = 0; k < 4; k++) { t = temp[i][k]; temp[i][k] = temp[swap][k]; temp[swap][k] = t;
t = inverse[i*4+k]; inverse[i*4+k] = inverse[swap*4+k]; inverse[swap*4+k] = t; } }
if (temp[i][i] == 0) { /*
** No non-zero pivot. The matrix is singular, which shouldn't ** happen. This means the user gave us a bad matrix. */ return 0; }
t = temp[i][i]; for (k = 0; k < 4; k++) { temp[i][k] /= t; inverse[i*4+k] /= t; } for (j = 0; j < 4; j++) { if (j != i) { t = temp[j][i]; for (k = 0; k < 4; k++) { temp[j][k] -= temp[i][k]*t; inverse[j*4+k] -= inverse[i*4+k]*t; } } } } return 1; }
/*
** This is a screwball function. What it does is the following: ** Given screen x and y coordinates, compute the corresponding object space ** x and y coordinates given that the object space z is 0.9 + OFFSETZ. ** Since the tops of (most) pieces are at z = 0.9 + OFFSETZ, we use that ** number. */ int computeCoords(int piece, int mousex, int mousey, GLfloat *selx, GLfloat *sely) { GLfloat modelMatrix[16]; GLfloat projMatrix[16]; GLfloat finalMatrix[16]; GLfloat in[4]; GLfloat a,b,c,d; GLfloat top, bot; GLfloat z; GLfloat w; GLfloat height;
if (piece == 0) return 0; height = zsize[piece] - 0.1 + OFFSETZ;
glGetFloatv(GL_PROJECTION_MATRIX, projMatrix); glGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix); multMatrices(modelMatrix, projMatrix, finalMatrix); if (!invertMatrix(finalMatrix, finalMatrix)) return 0;
in[0] = (2.0 * (mousex - viewport[0]) / viewport[2]) - 1; in[1] = (2.0 * ((H - mousey) - viewport[1]) / viewport[3]) - 1;
a = in[0] * finalMatrix[0*4+2] + in[1] * finalMatrix[1*4+2] + finalMatrix[3*4+2]; b = finalMatrix[2*4+2]; c = in[0] * finalMatrix[0*4+3] + in[1] * finalMatrix[1*4+3] + finalMatrix[3*4+3]; d = finalMatrix[2*4+3];
/*
** Ok, now we need to solve for z: ** (a + b z) / (c + d z) = height. ** ("height" is the height in object space we want to solve z for) ** ** ==> a + b z = height c + height d z ** bz - height d z = height c - a ** z = (height c - a) / (b - height d) */ top = height * c - a; bot = b - height * d; if (bot == 0.0) return 0;
z = top / bot;
/*
** Ok, no problem. ** Now we solve for x and y. We know that w = c + d z, so we compute it. */ w = c + d * z;
/*
** Now for x and y: */ *selx = (in[0] * finalMatrix[0*4+0] + in[1] * finalMatrix[1*4+0] + z * finalMatrix[2*4+0] + finalMatrix[3*4+0]) / w - OFFSETX; *sely = (in[0] * finalMatrix[0*4+1] + in[1] * finalMatrix[1*4+1] + z * finalMatrix[2*4+1] + finalMatrix[3*4+1]) / w - OFFSETY; return 1; }
static int selected; static int selectx, selecty; static float selstartx, selstarty;
void grabPiece(int piece, float selx, float sely) { int hit;
selectx=selx; selecty=sely; if (selectx < 0 || selecty < 0 || selectx >= WIDTH || selecty >= HEIGHT) { return; } hit = thePuzzle[selecty][selectx]; if (hit != piece) return; if (hit) { movingPiece=hit; while (selectx > 0 && thePuzzle[selecty][selectx-1] == movingPiece) { selectx--; } while (selecty > 0 && thePuzzle[selecty-1][selectx] == movingPiece) { selecty--; } move_x=selectx; move_y=selecty; selected=1; selstartx=selx; selstarty=sely; } else { selected=0; } }
void moveSelection(float selx, float sely) { float deltax, deltay; int dir; Config newpieces;
if (!selected) return; deltax = selx - selstartx; deltay = sely - selstarty;
if (fabs(deltax) > fabs(deltay)) { deltay = 0; if (deltax > 0) { if (deltax > 1) deltax = 1; dir = 2; } else { if (deltax < -1) deltax = -1; dir = 0; } } else { deltax = 0; if (deltay > 0) { if (deltay > 1) deltay = 1; dir = 3; } else { if (deltay < -1) deltay = -1; dir = 1; } } if (canmove(thePuzzle, selectx, selecty, dir, newpieces)) { move_x = deltax + selectx; move_y = deltay + selecty; if (deltax > 0.5) { memcpy(thePuzzle, newpieces, HEIGHT*WIDTH); selectx++; selstartx++; } else if (deltax < -0.5) { memcpy(thePuzzle, newpieces, HEIGHT*WIDTH); selectx--; selstartx--; } else if (deltay > 0.5) { memcpy(thePuzzle, newpieces, HEIGHT*WIDTH); selecty++; selstarty++; } else if (deltay < -0.5) { memcpy(thePuzzle, newpieces, HEIGHT*WIDTH); selecty--; selstarty--; } } else { if (deltay > 0 && thePuzzle[selecty][selectx] == 10 && selectx == 1 && selecty == 3) { /* Allow visual movement of solution piece outside of the box */ move_x = selectx; move_y = sely - selstarty + selecty; } else { move_x = selectx; move_y = selecty; } } }
void dropSelection(void) { if (!selected) return; movingPiece = 0; selected = 0; }
static int left_mouse, right_mouse; static int mousex, mousey; static int solving; static int spinning; static float lastquat[4]; static int sel_piece;
static void Reshape(int width, int height) {
W = width; H = height; glViewport(0, 0, W, H); glGetIntegerv(GL_VIEWPORT, viewport); }
static GLenum Key(int key, GLenum mask) { int piece; int x, y;
if (!left_mouse && !right_mouse) { switch(key) { case TK_ESCAPE: tkQuit(); case TK_d: case TK_D: if (solving) { freeSolutions(); solving=0; movingPiece=0; } tkGetMouseLoc(&x, &y); piece = selectPiece(x, y); if (piece) { nukePiece(piece); } break; case TK_S: case TK_s: if (solving) { freeSolutions(); solving=0; movingPiece=0; } else { printf("Solving...\n"); if (solvePuzzle()) { solving = 1; } } break; case TK_R: case TK_r: if (solving) { freeSolutions(); solving=0; movingPiece=0; } memcpy(thePuzzle, startConfig, HEIGHT*WIDTH); break; case TK_b: case TK_B: depth=1-depth; if (depth) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } break; default: return GL_FALSE; } } return GL_TRUE; }
static GLenum MouseUp(int mouseX, int mouseY, GLenum button) {
if (button & TK_LEFTBUTTON) { left_mouse = GL_FALSE; dropSelection(); return GL_TRUE; } else if (button & TK_RIGHTBUTTON) { right_mouse = GL_FALSE; return GL_TRUE; } return GL_FALSE; }
static GLenum MouseDown(int mouseX, int mouseY, GLenum button) { int piece; float selx, sely;
mousex = mouseX; mousey = mouseY; if (button & TK_LEFTBUTTON) { if (solving) { freeSolutions(); solving=0; movingPiece=0; } left_mouse = GL_TRUE; sel_piece = selectPiece(mousex, mousey); if (computeCoords(sel_piece, mousex, mousey, &selx, &sely)) { grabPiece(sel_piece, selx, sely); } return GL_TRUE; } else if (button & TK_RIGHTBUTTON) { right_mouse = GL_TRUE; return GL_TRUE; } return GL_FALSE; }
void animate(void) { int piece; float selx, sely; int x, y;
if (right_mouse || left_mouse) { tkGetMouseLoc(&x, &y); if (right_mouse && !left_mouse) { if (mousex != x || mousey != y) { trackball(lastquat, 2.0*(W-mousex)/W - 1.0, 2.0*mousey/H - 1.0, 2.0*(W-x)/W - 1.0, 2.0*y/H - 1.0); spinning = 1; } else { spinning = 0; } } else { computeCoords(sel_piece, x, y, &selx, &sely); moveSelection(selx, sely); } mousex = x; mousey = y; } if (spinning) { add_quats(lastquat, curquat, curquat); } redraw(); if (solving) { if (!continueSolving()) { solving = 0; } } }
void init(void) { static float lmodel_ambient[] = { 0.0, 0.0, 0.0, 0.0 }; static float lmodel_twoside[] = { GL_FALSE }; static float lmodel_local[] = { GL_FALSE }; static float light0_ambient[] = { 0.1, 0.1, 0.1, 1.0 }; static float light0_diffuse[] = { 1.0, 1.0, 1.0, 0.0 }; static float light0_position[] = { 0.8660254, 0.5, 1, 0 }; static float light0_specular[] = { 0.0, 0.0, 0.0, 0.0 }; static float bevel_mat_ambient[] = { 0.0, 0.0, 0.0, 1.0 }; static float bevel_mat_shininess[] = { 40.0 }; static float bevel_mat_specular[] = { 0.0, 0.0, 0.0, 0.0 }; static float bevel_mat_diffuse[] = { 1.0, 0.0, 0.0, 0.0 };
glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glEnable(GL_DEPTH_TEST); glClearDepth(1.0);
glClearColor(0.5, 0.5, 0.5, 0.0); glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glEnable(GL_LIGHT0);
glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, lmodel_local); glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); glEnable(GL_LIGHTING);
glMaterialfv(GL_FRONT, GL_AMBIENT, bevel_mat_ambient); glMaterialfv(GL_FRONT, GL_SHININESS, bevel_mat_shininess); glMaterialfv(GL_FRONT, GL_SPECULAR, bevel_mat_specular); glMaterialfv(GL_FRONT, GL_DIFFUSE, bevel_mat_diffuse);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glShadeModel(GL_FLAT);
trackball(curquat, 0.0, 0.0, 0.0, 0.0); srandom(time(NULL)); }
static void Usage(void) { printf("Usage: puzzle [-s]\n"); printf(" -s: Run in single buffered mode\n"); exit(-1); }
void main(long argc, char** argv) { long i;
for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { case 's': doubleBuffer = 0; break; default: Usage(); } } else { Usage(); } }
tkInitPosition(0, 0, W, H);
tkInitDisplayMode(TK_DEPTH16|TK_RGB|TK_DOUBLE|TK_DIRECT);
if (tkInitWindow("Puzzle") == GL_FALSE) { tkQuit(); }
init();
glGetIntegerv(GL_VIEWPORT, viewport);
printf("\n\n\n\n\n\n"); printf("r Reset puzzle\n"); printf("s Solve puzzle (may take a few seconds to compute)\n"); printf("d Destroy a piece - makes the puzzle easier\n"); printf("b Toggles the depth buffer on and off\n"); printf("\n"); printf("Right mouse spins the puzzle\n"); printf("Left mouse moves pieces\n");
tkExposeFunc(Reshape); tkReshapeFunc(Reshape); tkKeyDownFunc(Key); tkMouseDownFunc(MouseDown); tkMouseUpFunc(MouseUp); tkIdleFunc(animate); tkExec(); }
|