/* simple maze generator */

#include "exec/types.h"
#include "intuition/intuition.h"

#include <macros.h>

struct Library *IntuitionBase=NULL, *GfxBase=NULL;
void *OpenLibrary();

struct Window *Window=NULL, *OpenWindow();
struct Screen *Screen=NULL, *OpenScreen();
struct ViewPort *vp;							/* view port */
struct RastPort *rp;							/* rast port */

USHORT colors[8] = { 0x007,0x00C,0xFC0,0xF0F,0x407,0xFFF,0x33F,0xF00 };

struct NewScreen NewScreen = { 0,0,320,200, 3, 0,1, NULL, CUSTOMSCREEN, };

struct NewWindow back_ground =
{	0,0,320,200, 0,1,
	MENUPICK | MOUSEMOVE,
	NOCAREREFRESH | ACTIVATE | SMART_REFRESH | BORDERLESS | REPORTMOUSE,
	NULL,NULL,NULL,NULL,NULL,320,200,320,200,CUSTOMSCREEN,
};

#define DF1	ITEMTEXT | ITEMENABLED | HIGHCOMP
#define DF3	ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ

struct IntuiText
	abou_tx = { 5,1,JAM2,4,1,NULL,(UBYTE *)"By David Joiner" },
	svex_tx = { 5,1,JAM2,4,1,NULL,(UBYTE *)"Save and Exit" },
	quit_tx = { 5,1,JAM2,4,1,NULL,(UBYTE *)"Exit, No Save" };
struct MenuItem
	abou_mi = { NULL	,0,00,190,10,DF1,0,(APTR)&abou_tx       },
	svex_mi = { &abou_mi,0,10,190,10,DF3,0,(APTR)&svex_tx,0,'S' },
	quit_mi = { &svex_mi,0,20,190,10,DF3,0,(APTR)&quit_tx,0,'X' };
struct Menu menu1 =
	{	NULL, 1,0,56,10, MENUENABLED | MIDRAWN, "File", &quit_mi };

#define SCAT	goto exit_pgm;

#define	LOPEN	(1<<0)				/* has a left wall */
#define DOPEN	(1<<1)				/* has a right wall */
#define VISITED	(1<<2)				/* it's not yet visited */
#define LOOKED	(1<<3)				/* it's not yet looked at */
#define FROMUP	(1<<4)				/* this square was reached from up */
#define FROMDN	(1<<5)				/* this square was reached from down */
#define FROMLF	(1<<6)				/* this square was reached from left */
#define FROMRT	(1<<7)				/* this square was reached from right */

#define MAZE_X		99
#define MAZE_Y		99


BYTE	maze[MAZE_X][MAZE_Y];		/* 10K array */

struct unv {
	BYTE	x,y;
};

struct unv *ulist;
short	ucount;

short mainpen;

main()
{	short i,j;

	if ((IntuitionBase = OpenLibrary("intuition.library",0)) == NULL) SCAT;
	if ((GfxBase = OpenLibrary("graphics.library",0)) == NULL) SCAT;
	if ((Screen = OpenScreen(&NewScreen)) == NULL) SCAT;
	vp = &(Screen->ViewPort);
	LoadRGB4(vp,colors,8);

	if (!MakeStructArray(ulist,MAZE_X*MAZE_Y)) SCAT;

	back_ground.Screen = Screen;
	if ((Window = OpenWindow(&back_ground)) == NULL) SCAT;
	rp = Window->RPort;

	SetMenuStrip(Window,&menu1);

	SetAPen(rp,1);

	mainpen = 1;
	for (i=0; i<MAZE_X; i++)
	{	for (j=0; j<MAZE_Y; j++)
		{	maze[i][j] = 0;
			WritePixel(rp,i+i,j+j);
		}
	}
	mainpen = 2;
	ucount = 0;
	visit(0,0);
	srand(223344);
	while (ucount > 0) upick();

	showpath(MAZE_X-1,MAZE_Y-1);

	while (TRUE)
	{	ULONG class; USHORT code, length; SHORT mx, my; APTR address;
		struct IntuiMessage *message, *GetMsg();

		Wait(1 << Window->UserPort->mp_SigBit);

		while (message = GetMsg(Window->UserPort))
		{	class = message->Class;
			code = message->Code;
			address = (APTR)message->IAddress;
			mx = message->MouseX;
			my = message->MouseY;
			ReplyMsg(message);

			if (class == MENUPICK)
			{	if (code == MENUNULL) ;
				else if (MENUNUM(code)==0)
				{	switch (ITEMNUM(code)) {
					case 2: break;
					case 1: break;
					case 0: SCAT;
					}
				}
			}
		}
	}
exit_pgm:
	UnMakeStructArray(ulist,MAZE_X*MAZE_Y);
	if (Window){ ClearMenuStrip(Window); CloseWindow(Window); }
	if (Screen) CloseScreen(Screen);
	if (GfxBase) CloseLibrary(GfxBase);
	if (IntuitionBase) CloseLibrary(IntuitionBase);
}

drawmazebox(a,b) short a,b;
{	short x,y;

	x = a+a;
	y = b+b;

	SetAPen(rp,mainpen);
	WritePixel(rp,x,y);
	WritePixel(rp,x+2,y);
	WritePixel(rp,x,y+2);
	WritePixel(rp,x+2,y+2);
	if (a <= 0 || !(maze[a-1][b] & LOPEN)) SetAPen(rp,mainpen); else SetAPen(rp,0);
	WritePixel(rp,x,y+1);
	if (b <= 0 || !(maze[a][b-1] & DOPEN)) SetAPen(rp,mainpen); else SetAPen(rp,0);
	WritePixel(rp,x+1,y);
	if (!(maze[a][b] & LOPEN)) SetAPen(rp,mainpen); else SetAPen(rp,0); WritePixel(rp,x+2,y+1);
	if (!(maze[a][b] & DOPEN)) SetAPen(rp,mainpen); else SetAPen(rp,0); WritePixel(rp,x+1,y+2);
}

visit(a,b) short a,b;
{	short c = 0, d;
	loop1:
	maze[a][b] |= (c | VISITED);
	d = look(a+1,b) | look(a-1,b) | look(a,b+1) | look(a,b-1);
	drawmazebox(a,b);
	if (d)
	{	do
		{	switch (rand() & 3)
			{	case 0: if (empty(a+1,b))
						{	c = FROMLF;
							maze[a][b] |= LOPEN; a++;
							goto loop1;
						}
						break;
				case 1: if (empty(a-1,b))
						{	c = FROMRT;
							a--; maze[a][b] |= LOPEN;
							goto loop1;
						}
						break;
				case 2: if (empty(a,b+1))
						{	c = FROMUP;
							maze[a][b] |= DOPEN; b++;
							goto loop1;
						}
						break;
				case 3: if (empty(a,b-1))
						{	c = FROMDN;
							b--; maze[a][b] |= DOPEN;
							goto loop1;
						}
						break;
			}
		} while (rand() & 31);
	}
}

look(a,b) short a,b;
{	if (a >= 0 && a < MAZE_X &&
		b >= 0 && b < MAZE_Y)
	{	if (!(maze[a][b] & VISITED))
		{	if (!(maze[a][b] & LOOKED))
			{	maze[a][b] |= LOOKED;
				ulist[ucount].x = a;
				ulist[ucount].y = b;
				ucount++;
			}
			return TRUE;
		}
	}
	return FALSE;
}

check(a,b) short a,b;
{	return (a >= 0 && a < MAZE_X &&
			b >= 0 && b < MAZE_Y &&
			(maze[a][b] & VISITED));
}

empty(a,b) short a,b;
{	return (a >= 0 && a < MAZE_X &&
			b >= 0 && b < MAZE_Y &&
			!(maze[a][b] & VISITED));
}

LONG rand();

check_neighbor(a,b) short a,b;
{	short i, c;
	if (maze[a][b] & VISITED) return;
	switch (rand() & 3)
	{	case 0: if (check(a+1,b)) { c = FROMRT; maze[a  ][b  ] |= LOPEN; visit(a+1,b  ); break; }
		case 1: if (check(a-1,b)) { c = FROMLF; maze[a-1][b  ] |= LOPEN; visit(a-1,b  ); break; }
		case 2: if (check(a,b+1)) { c = FROMDN; maze[a  ][b  ] |= DOPEN; visit(a  ,b+1); break; }
		case 3: if (check(a,b-1)) { c = FROMUP; maze[a  ][b-1] |= DOPEN; visit(a  ,b-1); break; }
				if (check(a+1,b)) { c = FROMRT; maze[a  ][b  ] |= LOPEN; visit(a+1,b  ); break; }
				if (check(a-1,b)) { c = FROMLF; maze[a-1][b  ] |= LOPEN; visit(a-1,b  ); break; }
				if (check(a,b+1)) { c = FROMDN; maze[a  ][b  ] |= DOPEN; visit(a  ,b+1); break; }
	}
	maze[a][b] |= c;
	visit(a,b);
}

upick()
{	short i;
	i = rand() % ucount;
	check_neighbor(ulist[i].x,ulist[i].y);
	ucount--;
	ulist[i] = ulist[ucount];
}

showpath(a,b) short a,b;
{	short i, x,y;
	SetAPen(rp,3);
	while (a || b)
	{	x = a+a+1;
		y = b+b+1;
		WritePixel(rp,x,y);
		if (maze[a][b] & FROMUP) { WritePixel(rp,x,y-1); b--; }
		else if (maze[a][b] & FROMDN) { WritePixel(rp,x,y+1); b++; }
		else if (maze[a][b] & FROMLF) { WritePixel(rp,x-1,y); a--; }
		else if (maze[a][b] & FROMRT) { WritePixel(rp,x+1,y); a++; }
		else break;
	}
}
