windows-nt/Source/XPSP1/NT/multimedia/opengl/test/demos/puzzle/puzzle.c
2020-09-26 16:20:57 +08:00

1253 lines
27 KiB
C

#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();
}