

/* 
 * puzzle.c:  This QNX Windows program creates a
 * moving tile puzzle, with 15 numbered tiles in a 
 * 4 X 4 enclosure.
 * 
 * It is reproduced here by permission of Quantum Software Systems, Ltd
 * of Kanata, Canada.
 */

#include <stdio.h>
#include <windows.h>


#define RECT_HEIGHT	250
#define RECT_WIDTH	360
#define PANE_BORDER	140

#define PANE_HEIGHT	((rows)*RECT_HEIGHT + 120)
#define PANE_WIDTH	((rows)*RECT_WIDTH  + 120)
#define CASE(str)	(((str)[0] << 8) | (str)[1])

EVENT_MSG	msg;

int			rows = 4, max_num;
int 		b[20][20], old[20][20];
int			mv;

main()
	{
	int	go = YES, wid, pid;

	/* connect to QWindows */
	if ( !GraphicsOpen (NULL) )
		exit ( -1 );

	SetName("Puzzle", NULL);	/* Optional */

	pid = PictureOpen ( "pict", NULL, NULL, LIGHT_GRAY, NULL, NULL, NULL );
	PictureHighlightOptions( NULL, 'N', 0, 0 );

	init();
	display();

	wid = WindowOpen ("Puzzle", PANE_HEIGHT, PANE_WIDTH,
					  "MT480-r;s", NULL, NULL, pid);

	/*
	 *	Put control button in top frame.
	 */

	WindowBarCurrent( TOP, NULL );
	SetButton( NULL, WHITE, NULL );
	DrawAt( 300, 120 );
	AttachDialog("Options", NULL, 
		"Scramble;Size|@(Easy|03;The Famous 4 x 4|04;Hard|05;Very Hard|06;You've got to be Kidding|10)^R",
		NULL, "MD", NULL, NULL); 
	DrawButton( "Options", NULL, "Ns;D", "op");
	Draw();
	WindowBarCurrent( NULL, NULL );

	load_icon( "/windows/apps/puzzle" );

	DrawStart(NULL, 0);

	while ( go )
		{
		GetEvent ( 0, &msg, sizeof(msg));

		switch ( Event ( &msg ) )
			{
			case QUIT:
				WindowClose( 0, NULL );
				go = NO;
				break;

			case TERMINATED:
				go = NO;
				break;

			case CLICK:
				if (msg.hdr.code == 'S') {
					/* game piece selected */					move(atoi(msg.hdr.key) - 1);
					mv++;
					win ();
					}

				break;

			case DIALOG:
				switch(CASE(msg.hdr.key)) {
				case '03': case '04': case '05': case '06':
                   case '07': case '10':
					rows = atoi(msg.hdr.key);
				case 'Sc':
					new_game();
				break;
				}
			}
		}

	GraphicsClose( );
	exit (0);
	}

init() {
	int	i, j, r, loop;

	max_num = rows*rows;

	for (i = 0; i < rows; i++)
		for (j = 0; j < rows; j++) {
			b[i][j] = ( i * rows + j);
			old[i][j] = -1;
			}

	i = j = rows - 1;
	srand(get_ticks());
	r = rand() % 200 + 500;

	for (loop = 0; loop < 5000; ++loop) {
		switch (rand() % 4) {
		case 0:
			if (i != 0) {
				b[i][j] = b[i - 1][j];
				b[i - 1][j] = max_num;
				--i;
				}
			break;

		case 1:
			if (i != rows - 1) {
				b[i][j] = b[i + 1][j];
				b[i + 1][j] = max_num;
				i++;
				}
			break;
		case 2:
			if (j != 0) {
				b[i][j] = b[i][j - 1];
				b[i][j - 1] = max_num;
				--j;
				}
			break;

		case 3:
			if (j != rows - 1) {
				b[i][j] = b[i][j + 1];
				b[i][j + 1] = max_num;
				j++;
				}
			break;
			}
		}
	}



display() {
	int	i, j;

	for (i = 0; i < rows; i++)
		if (memcmp(b[i], old[i], rows)) {
			for (j = 0; j < rows; j++)
				if (b[i][j] != max_num)
					draw_number(i, j, b[i][j] + 1);
			memcpy(old[i], b[i], rows);
			}
	}


draw_number( row, col, number)
int row, col, number;
	{
	char tbuf[4];

	tsprintf(tbuf, "%2d", number);
		
	SetColor ( "T", BLACK );

	DrawAt (PANE_BORDER + (row*RECT_HEIGHT) + CHARH/2,
			PANE_BORDER + (col*RECT_WIDTH ));

	DrawText( tbuf, 0, 0, 0, "Sem", tbuf);

	Draw ( );
	}




move_number( row, col, number)int row, col, number;
	{
	char buffer[7];

	tsprintf(buffer, "%2d", number);
	ShiftTo( buffer, 	PANE_BORDER + (row*RECT_HEIGHT) + CHARH/2,
						PANE_BORDER + (col*RECT_WIDTH ));
	}



move(m)
int m;
	{
	int	i1, j1, i, j, i2, j2, c;

	i1 = j1 = -1;

	for (i = 0; i1 == -1 && i < rows; i++)
		for (j = 0; j < rows; j++)
			if (b[i][j] == m) {
				i1 = i;
				j1 = j;
				break;
				}

	if (i1 != -1) {
		i2 = j2 = -1;
		for (i = 0; i < rows; i++)
			if (b[i][j1] == max_num) {
				i2 = i;
				j2 = j1;
				break;
				}
		if (i2 == -1)
			for (j = 0; j < rows; j++)
				if (b[i1][j] == max_num) {
					i2 = i1;
					j2 = j;
					break;
					}
		if (i2 == -1)
			return;
		}
	else
		return;

	/* Hold picture for smoother updates */
	PictureHold( );

	if (i1 == i2)
		if (j1 < j2)
			for (j = j2 - 1; j >= j1; --j) {
				b[i1][j + 1] = b[i1][j];
				move_number(i1, j+1, b[i1][j]+1);				}
		else
			for (j = j2; j < j1; j++) {
				b[i1][j] = b[i1][j + 1];
				move_number(i1, j, b[i1][j+1]+1);
				}
	else
		if (i1 < i2)
			for (i = i2 - 1; i >= i1; --i) {
				b[i + 1][j1] = b[i][j1];
				move_number(i+1, j1, b[i][j1]+1);
				}
		else
			for (i = i2; i < i1; i++) {
				b[i][j1] = b[i + 1][j1];
				move_number(i, j1, b[i+1][j1]+1);
				}
	b[i1][j1] = max_num;

	/* update current picture */
	PictureContinue( );
	}



win() {
	int	c = 0, i, j;
	char buf[50];

	for (i = 0; i < rows; i++)
		for (j = 0; j < rows; j++)
			if (b[i][j] < c)
				return( 0 );
			else
				c = (int) b[i][j];

	tsprintf(buf, "You got it in %d moves!", mv);
	Notice( NULL, "You Win", NULL, "W", buf);

	return( 1 );
	}


new_game() {
	RECT_AREA area;

	WindowInfo(NULL,&area,NULL,NULL,NULL,NULL);

	/* erase all elements in the picture */
	WindowHold( );
	Erase( ALL );

	area.height = PANE_HEIGHT;
	area.width  = PANE_WIDTH;
	init( );
	display( );
	mv = 0;

	WindowChange( &area, NULL, KEEP, NULL, "!" );
	WindowContinue( );
	}

