page%swap@Sun.COM (Bob Page) (05/09/89)
Submitted-by: utoddl@uncece.edu (Todd M. Lewis) Posting-number: Volume 89, Issue 119 Archive-name: fun/maze.1 Here's a little ditty that builds mazes and lets you solve them. This one is in C, and I even went back and made sure the comments were usually close to what the code might have done at one time. I used Manx, but conversion to other compilers should be trivial. # This is a shell archive. # Remove anything above and including the cut line. # Then run the rest of the file through 'sh'. # Unpacked files will be owned by you and have default permissions. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: SHell ARchive # Run the following text through 'sh' to create: # about.c # maze.c # stdhead.c # makefile # This is archive 1 of a 1-part kit. # This archive created: Mon May 8 16:58:06 1989 echo "extracting about.c" sed 's/^X//' << \SHAR_EOF > about.c X#include <exec/types.h> X#include <graphics/view.h> X#include <intuition/intuition.h> X#include <functions.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> X Xextern struct Window *window; Xextern ULONG RangeRand(); Xextern struct TextAttr font; X Xcycle(color) int color; X{ struct ColorMap *cm; X struct ViewPort *vp; X UWORD orig, r, g, b, ro, go, bo; X int i; X X vp = ViewPortAddress( window ); X cm = vp -> ColorMap; X X orig = GetRGB4( cm, (LONG) color ); X b = (bo = (orig ) & 15); X g = (go = (orig >> 4) & 15); X r = (ro = (orig >> 8) & 15); X X for (i=0; i<1000; i++) { X r = RangeRand(2L) * 15; X g = RangeRand(2L) * 15; X b = RangeRand(2L) * 15; X SetRGB4(vp,(LONG)color,(LONG)r,(LONG)g,(LONG)b); X } X SetRGB4(vp,(LONG)color,(LONG)ro,(LONG)go,(LONG)bo); X } X X#define reqW 544 X#define reqH 152 X#define centerX(chars) (reqW/2 - (chars)*8/2) X#define centerXright(chars) (reqW - 111 - (chars)*8/2) X#define centerXleft(chars) (111 - (chars)*8/2) X XSHORT reqborderXY[] = { 2,1, 541,1, 541,150, 2,150, 2,1 }; X Xstruct Border reqborder = { X 0,0, /* LeftEdge, TopEdge */ X 1,1, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 5, /* Count */ X reqborderXY,/* SHORT *XY */ X NULL, /* NextBorder */ X }; X XSHORT gadborderXY[] = { 2,1, 97,1, 97,28, 2,28, 2,1 }; X Xstruct Border gadborder = { X 2,1, /* LeftEdge, TopEdge */ X 1,2, /* FrontPen, BackPen */ X JAM2, /* DrawMode */ X 5, /* Count */ X gadborderXY, /* SHORT *XY */ X NULL, /* NextBorder */ X }; X Xstruct IntuiText gadtext = { X 1,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 18,11, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "Continue", /* IText */ X NULL, /* struct IntuiText *NextText */ X }; X Xstruct Gadget aboutgad = { X NULL, /* NextGadget */ X 222,117,100,30, /* LeftEdge, TopEdge, Width, Height */ X GADGHNONE, /* Flags */ X ENDGADGET | TOGGLESELECT, /* Activation Flags */ X REQGADGET | BOOLGADGET, /* GadgetType */ X (APTR)&gadborder, /* GadgetRender */ X NULL, /* SelectRender */ X &gadtext, /* GadgetText */ X NULL, /* MutualExclude */ X NULL, /* SpecialInfo */ X 0, /* GadgetID */ X NULL, /* UserData */ X }; X Xstruct IntuiText ab_txt8= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 20,102, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "Comments and suggestions are welcome.", /* IText */ X NULL, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt7= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 20,92, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "writing it. Please feel free to share it with your friends.", /* IText */ X &ab_txt8, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt6= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 20,82, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) " I hope you enjoy playing this game as much as I enjoyed", /* IText */ X &ab_txt7, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_inst3= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 20,68, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "give you a hint. Select new mazes from the menu. Good luck!", /* IText */ X &ab_txt6, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_inst2= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 20,59, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "red spot to the dark red spot. The left mouse button will", /* IText */ X &ab_inst3, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_inst1= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X 20,50, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) " Move the mouse pointer through the maze from the bright", /* IText */ X &ab_inst2, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_left2= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerXleft(19),120, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "Usenet Distribution", /* IText */ X &ab_inst1, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_left1= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerXleft(12),130, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "Spring, 1989", /* IText */ X &ab_left2, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt5= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerXright(24),140, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "utoddl@ecsvax.uncecs.edu", /* IText */ X &ab_left1, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt4= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerXright(20),130, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "utoddl@ecsvax.BITNET", /* IText */ X &ab_txt5, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt3= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerXright(21),120, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "Author: Todd M. Lewis", /* IText */ X &ab_txt4, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt2= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerX(32),35, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "The Disk Magazine for the Amiga.", /* IText */ X &ab_txt3, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt1= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerX(30),25, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "First distributed on JUMPDISK,", /* IText */ X &ab_txt2, /* struct IntuiText *NextText */ X }; Xstruct IntuiText ab_txt0= { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerX(33),15, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "Copyright (c) 1988, Todd M. Lewis", /* IText */ X &ab_txt1, /* struct IntuiText *NextText */ X }; Xstruct IntuiText firsttext = { X 3,2, /* FrontPen, BackPen */ X JAM1, /* DrawMode */ X centerX(27),6, /* LeftEdge, TopEdge */ X &font, /* TextAttr *ITextFont */ X (UBYTE *) "TML's AmigaMaze version 1.2", /* IText */ X &ab_txt0, /* struct IntuiText *NextText */ X }; X Xstruct Requester aboutreq; X Xabout() X{ struct IntuiMessage *msg; X int class; X X InitRequester( &aboutreq ); X X aboutreq.LeftEdge = 4; X aboutreq.TopEdge = 15; X aboutreq.Width = reqW; X aboutreq.Height = reqH; X aboutreq.ReqGadget = &aboutgad; X aboutreq.ReqText = &firsttext; X aboutreq.ReqBorder = &reqborder; X aboutreq.BackFill = 2; /* BLACK */ X X if (Request( &aboutreq, window )) { X do { X Wait( 1L << window->UserPort->mp_SigBit ); X while (msg = (struct IntuiMessage *)GetMsg( window->UserPort )) { X class = msg->Class; X ReplyMsg(msg); X } X } while ( class != REQCLEAR ); X } X } X SHAR_EOF echo "extracting maze.c" sed 's/^X//' << \SHAR_EOF > maze.c X#include <exec/types.h> X#include <intuition/intuition.h> X#include <functions.h> X#include <graphics/gfxmacros.h> X#include <graphics/text.h> Xstruct IntuitionBase *IntuitionBase; Xstruct GfxBase *GfxBase; X Xstruct RastPort tmpRPactual; Xstruct RastPort *tmpRP = &tmpRPactual; Xstruct BitMap tmpBM; X Xstruct TextAttr font = { X (STRPTR)"topaz.font", /* Font Name */ X TOPAZ_EIGHTY, /* Font Height */ X FS_NORMAL, /* Font Style */ X FPF_ROMFONT /* Preferences */ X }; X X#define tmpBMxy 60L X X#define windowW 552L X#define windowH 183L X Xstruct NewWindow nw = { X 6, 12, windowW, windowH, /* EDGES, SIZE */ X 0, 1, /* PENS */ X CLOSEWINDOW, /* IDCMPFlags */ X WINDOWDRAG | WINDOWDEPTH | /* Flags (requested window features)*/ X WINDOWCLOSE | ACTIVATE | REPORTMOUSE, X NULL, /* gadgets */ X NULL, /* checkmark image */ X NULL, /* title */ X NULL, /* screen */ X NULL, /* bitmap */ X 0,0,0,0, /* min and max w and h */ X WBENCHSCREEN, /* TYPE */ X }; X X Xstruct IntuiText it_GiveUp = { X 0,1, /* frontpen, backpen */ X JAM1, /* drawmode */ X 1,1, /* leftedge, topedge */ X &font, /* TextAttr * ITextFont */ X (UBYTE *)" Give Up! ", /* IText */ X NULL, /* NextText */ X }; Xstruct MenuItem mi_GiveUp = { X (struct MenuItem *) NULL, X 0,40, /* LeftEdge, TopEdge */ X 21*9,10, /* Width, Height */ X ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ X 0, /* Mutual Exclude */ X (APTR)&it_GiveUp, /* ItemFill */ X NULL, /* SelectFill */ X 0, /* Command */ X NULL, /* Subitem */ X 0, /* NextSelect */ X }; X Xstruct IntuiText it_3Level = { X 0,1, /* frontpen, backpen */ X JAM1, /* drawmode */ X 1,1, /* leftedge, topedge */ X &font, /* TextAttr * ITextFont */ X (UBYTE *)"New 3-Level Maze", /* IText */ X NULL, /* NextText */ X }; Xstruct MenuItem mi_3Level = { X (struct MenuItem *) &mi_GiveUp, X 0,30, /* LeftEdge, TopEdge */ X 21*9,10, /* Width, Height */ X ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ X 0, /* Mutual Exclude */ X (APTR)&it_3Level, /* ItemFill */ X NULL, /* SelectFill */ X 0, /* Command */ X NULL, /* Subitem */ X 0, /* NextSelect */ X }; X Xstruct IntuiText it_2Level = { X 0,1, /* frontpen, backpen */ X JAM1, /* drawmode */ X 1,1, /* leftedge, topedge */ X &font, /* TextAttr * ITextFont */ X (UBYTE *)"New 2-Level Maze", /* IText */ X NULL, /* NextText */ X }; Xstruct MenuItem mi_2Level = { X (struct MenuItem *) &mi_3Level, X 0,20, /* LeftEdge, TopEdge */ X 21*9,10, /* Width, Height */ X ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ X 0, /* Mutual Exclude */ X (APTR)&it_2Level, /* ItemFill */ X NULL, /* SelectFill */ X 0, /* Command */ X NULL, /* Subitem */ X 0, /* NextSelect */ X }; Xstruct IntuiText it_1Level = { X 0,1, /* frontpen, backpen */ X JAM1, /* drawmode */ X 1,1, /* leftedge, topedge */ X &font, /* TextAttr * ITextFont */ X (UBYTE *)"New 1-Level Maze", /* IText */ X NULL, /* NextText */ X }; Xstruct MenuItem mi_1Level = { X (struct MenuItem *)&mi_2Level, X 0,10, /* LeftEdge, TopEdge */ X 21*9,10, /* Width, Height */ X ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ X 0, /* Mutual Exclude */ X (APTR)&it_1Level, /* ItemFill */ X NULL, /* SelectFill */ X 0, /* Command */ X NULL, /* Subitem */ X 0, /* NextSelect */ X }; X Xstruct IntuiText it_About = { X 0,1, /* frontpen, backpen */ X JAM1, /* drawmode */ X 1,1, /* leftedge, topedge */ X &font, /* TextAttr * ITextFont */ X (UBYTE *)"About TML's AmigaMaze", /* IText */ X NULL, /* NextText */ X }; Xstruct MenuItem mi_About = { X (struct MenuItem *) &mi_1Level, X 0,0, /* LeftEdge, TopEdge */ X 21*9,10, /* Width, Height */ X ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ X 0, /* Mutual Exclude */ X (APTR)&it_About, /* ItemFill */ X NULL, /* SelectFill */ X 0, /* Command */ X NULL, /* Subitem */ X 0, /* NextSelect */ X }; Xstruct Menu menu = { X (struct Menu *) NULL, /* NEXT menu */ X 0, 0, 12*9, 0, /* LeftEdge, TopEdge, Width, Height */ X MENUENABLED, /* Flags */ X (BYTE *) "Maze Control", /* MenuName */ X (struct MenuItem *)&mi_About, /* First item */ X 0,0,0,0, /* JazzX,JazzY, BeatX, BeatY */ X }; X Xstruct Window *window; X X/* BEWARE: NOWHERE has a dual nature, being used as a doesn't-go-anywhere X indicator, and also as an invalid level marker!!! */ X#define NOWHERE -1 X#define SOUTH 0 X#define EAST 1 X#define WEST 2 X#define NORTH 3 X X#define SOLVED -3 X X#define DIRECTIONS 4 X#define MAXLEVELS 3 X#define BOARDMAXX 36 X#define BOARDMAXY 29 X/*** the following are "inpath" stata. (see struct square) *******/ X#define NOTTRAVERSED 1 X#define EDGE 2 X#define LASTPIECE 3 + 32 X#define FIRSTPIECE 3 + 64 X#define CURPIECE 3 + 96 X#define TRAVERSED 3 X X#define MIDCMP1 CLOSEWINDOW|MOUSEMOVE|MOUSEBUTTONS|MENUPICK|MENUVERIFY X#define MIDCMP2 REQCLEAR X#define MIDCMP3 CLOSEWINDOW X Xextern ULONG RangeRand(); Xextern int about(); Xextern int cycle(); X Xtypedef struct { X int conlev[ DIRECTIONS ]; /** level connected to in each dir. **/ X int inpath; /** Also the color this should be drawn in. **/ X int compass; /** Which direction to go to get home from here **/ X } square; X Xtypedef square BOARD[ BOARDMAXX+1 ][ BOARDMAXY+1 ][ MAXLEVELS ]; X XBOARD board; X Xstruct RastPort *rp; X Xint StartX, StartY, StartLevel; Xint EndX, EndY, EndLevel; X Xint CurX, CurY, CurLevel; Xint NewX, NewY, NewLevel; Xint MouseX, MouseY; Xint MinLevel, MaxLevel; Xint BoardMaxX = BOARDMAXX; Xint BoardMaxY = BOARDMAXY; Xint SquareXsize = 34; Xint SquareYsize = 18; X X/* We need to have some Fill patterns for various places. Here goes... */ XUSHORT Pat_Normal[] = { /** the normal filled pattern **/ X 0xffff, /* plane 1 pattern */ X 0xffff, X 0xffff, /* plane 2 pattern */ X 0xffff }; X XUSHORT Pat_P1[] = { /** first dithered filled pattern **/ X 0x5555, /* plane 1 pattern */ X 0xaaaa, X 0xffff, /* plane 2 pattern */ X 0xffff }; X XUSHORT Pat_P2[] = { /** second dithered filled pattern **/ X 0xffff, /* plane 1 pattern */ X 0xffff, X 0x5555, /* plane 2 pattern */ X 0xaaaa }; X XsetHint(ShowHint) Xint ShowHint; X{ X char *s; X int oldBPen; X X if (ShowHint) { X switch ( board[CurX][CurY][CurLevel].compass) { X case NORTH : s = " HINT: Go North! "; break; X case SOUTH : s = " HINT: Go South! "; break; X case EAST : s = " HINT: Go East! "; break; X case WEST : s = " HINT: Go West! "; break; X case SOLVED: s = " CONGRATULATIONS! "; break; X default : s = " Hmm. I don't know. "; break; X } X } X else { X s = " (Press Left Button for a hint.)"; X } X Move(rp, 20L, 180L); X SetAPen(rp, 1L); X oldBPen = rp -> BgPen; X SetBPen(rp, 0L); X SetDrMd(rp, (LONG)JAM2); X Text(rp, s, 32L); X SetBPen(rp, (LONG)oldBPen); X } X Xshowside(ax,ay, bx,by, cx,cy, dx,dy, color) XLONG ax,ay, bx,by, cx,cy, dx,dy, color; X{ X SetAPen( tmpRP, color); /* The shape isn't allways a rect., so */ X SetOPen( tmpRP, color); /* we can't use RectFill() here... */ X BNDRYOFF( tmpRP); /* Boundaries look bad w/ patterns...*/ X switch (color) { X case CURPIECE : X case TRAVERSED : SetAfPt( tmpRP, Pat_P2,-1L); break; X case LASTPIECE : SetAfPt( tmpRP, Pat_P1,-1L); break; X case NOTTRAVERSED : X case FIRSTPIECE : X default : SetAfPt( tmpRP, Pat_Normal,-1L); break; X } X X AreaMove( tmpRP, ax,ay); X AreaDraw( tmpRP, bx,by); X AreaDraw( tmpRP, cx,cy); X AreaDraw( tmpRP, dx,dy); X AreaEnd( tmpRP); X SetAPen( tmpRP, (LONG)EDGE); X Move( tmpRP, ax, ay); X Draw( tmpRP, bx, by); X Draw( tmpRP, bx+1,by); X Draw( tmpRP, ax+1,ay); X Move( tmpRP, cx, cy); X Draw( tmpRP, dx, dy); X Draw( tmpRP, dx+1,dy); X Draw( tmpRP, cx+1,cy); X } X Xputedge(ax,ay,bx,by) XLONG ax,ay,bx,by; X{ X SetAPen(tmpRP,(LONG)EDGE); X Move(tmpRP,ax, ay); X Draw(tmpRP,bx, by); X Draw(tmpRP,bx+1,by); X Draw(tmpRP,ax+1,ay); X } X XLONG renderColor(x, y, z, x1, y1, z1) Xint x, y, z, x1, y1, z1; X{ int color; X color = board[x][y][z].inpath; X if (!(z == MaxLevel-1 && (color == FIRSTPIECE || color == LASTPIECE))) X if ((color == NOTTRAVERSED) || X (board[x1][y1][z1].inpath == NOTTRAVERSED)) X color = NOTTRAVERSED; X return((LONG)color); X } X X#define XDIF 6 X#define YDIF 3 X Xrender(x,y) Xint x, y; X{ X LONG ax,ay, bx,by, cx,cy, dx,dy, color; X int level, Bleft, Btop, deltaX, deltaY, tmpL; X int left, right, bottom, top; X X /* clear the temporary bitmap */ X SetAPen( tmpRP, 0L); X SetBPen( tmpRP, 0L); X RectFill( tmpRP, 0L, 0L, (LONG)SquareXsize, (LONG)SquareYsize ); X X Bleft = 4 + (x - 1) * SquareXsize; X Btop = 11 + (y - 1) * SquareYsize; X X left = 0; X right = SquareXsize - 1; X top = 0; X bottom = SquareYsize - 1; X X for (level=MinLevel; level<MaxLevel; level++) { X if (references(x, y, level) == 0) X continue; /* This level doesn't go anywhere. */ X X deltaY = YDIF * level + 1; X deltaX = XDIF * level + 1; X X /**** Fill in the middle part first ****/ X ax = right - deltaX; ay = top + deltaY; X bx = left + deltaX; by = ay; X cx = bx; cy = bottom - deltaY; X dx = ax; dy = cy; X color = board[x][y][level].inpath; X showside(ax,ay,bx,by,cx,cy,dx,dy,color); X X /**** Do the NORTH side of this level first ****/ X ax = right-deltaX; ay = top + deltaY; X bx = ax; by = top; X cx = left +deltaX; cy = by; X dx = cx; dy = ay; X tmpL = board[x][y][level].conlev[NORTH]; X if ( tmpL == NOWHERE) { X putedge(ax,ay,dx,dy); X goto DrawWest; X } X color = renderColor(x,y,level,x,y-1,tmpL); X if (tmpL != level && level == 1) { X bx = bx - XDIF*(tmpL - 1); X cx = cx + XDIF*(tmpL - 1); X } X showside(ax,ay,bx,by,cx,cy,dx,dy,color); X X /**** Do the WEST side of this level next ****/ X DrawWest: X ax = left + deltaX; ay = top + deltaY; X bx = left; by = ay; X cx = bx; cy = bottom - deltaY; X dx = ax; dy = cy; X tmpL = board[x][y][level].conlev[WEST]; X if ( tmpL == NOWHERE) { X putedge(ax,ay,dx,dy); X goto DrawSouth; X } X color = renderColor(x,y,level,x-1,y,tmpL); X if (tmpL != level && level == 1) { X by = by + YDIF*(tmpL - 1); X cy = cy - YDIF*(tmpL - 1); X } X showside(ax,ay,bx,by,cx,cy,dx,dy,color); X X /**** Do the SOUTH side of this level next ****/ X DrawSouth: X ax = left + deltaX; ay = bottom - deltaY; X bx = ax; by = bottom; X cx = right - deltaX; cy = by; X dx = cx; dy = ay; X tmpL = board[x][y][level].conlev[SOUTH]; X if ( tmpL == NOWHERE) { X putedge(ax,ay,dx,dy); X goto DrawEast; X } X color = renderColor(x,y,level,x,y+1,tmpL); X if (tmpL != level && level == 1) { X bx = bx + XDIF*(tmpL-1); X cx = cx - XDIF*(tmpL-1); X } X showside(ax,ay,bx,by,cx,cy,dx,dy,color); X X /**** Do the EAST side of this level last ****/ X DrawEast: X ax = right - deltaX; ay = bottom - deltaY; X bx = right; by = ay; X cx = bx; cy = top + deltaY; X dx = ax; dy = cy; X tmpL = board[x][y][level].conlev[EAST]; X if ( tmpL == NOWHERE) { X putedge(ax,ay,dx,dy); X goto DrawComplete; X } X color = renderColor(x,y,level,x+1,y,tmpL); X if (tmpL != level && level == 1) { X by = by - YDIF*(tmpL - 1); X cy = cy + YDIF*(tmpL - 1); X } X showside(ax,ay,bx,by,cx,cy,dx,dy,color); X DrawComplete:; X } X /* now copy temporary bitmap into window */ X X BltBitMapRastPort(&tmpBM,0L,0L, rp,(LONG)Bleft,(LONG)Btop, X (LONG)SquareXsize,(LONG)SquareYsize, (LONG)0x0c0); X } /* end of render() */ X X X/* references() returns the number connections from a given square. */ Xint references(x, y, level) Xint x, y, level; X{ int i, refs; X refs = 0; X for (i=0; i < DIRECTIONS; i++) X if (board[x][y][level].conlev[i] != NOWHERE) X refs++; X return(refs); X } X X/* linkable() returns true if we can connect the two indicated squares. */ X/* NOTE: We don't allow connecting to the second square if it */ X/* is already part of the maze. This test if the first if stmt.*/ X/* The second test if more subtle. The problem being tested for is shown in */ X/* the following diagram. This is an edge-on view of the board: */ X/* a __ __ d */ X/* \ / */ X/* / */ X/* / */ X/* c __/ \__ b */ X/* If c and d are connected, I can't connect a to b because the paths would */ X/* intersect. The way the test is written, it will work regardless of whether */ X/* the new path is going up, down, or level. */ X Xint linkable(x1,y1,level1,dir1, x2,y2, level2, dir2) Xint x1,y1,level1,dir1, x2,y2,level2; X{ X int tmp; X if (references(x2,y2,level2)) return(0); X if (board[x1][y1][level2].conlev[dir1] == level1) return(0); X return(-1); X } X X/* Returns the other Direction, or NOWHERE if an invalid direction is passed. */ Xint otherDir(nd) Xint nd; X{ switch (nd) { X case NORTH : return(SOUTH); break; X case SOUTH : return(NORTH); break; X case EAST : return(WEST) ; break; X case WEST : return(EAST) ; break; X } X return(NOWHERE); X } X X X/* makepathlist() returns the number of directions we can go from here w/o */ X/* backtracking -- i.e., not counting the way we got here. These */ X/* "uncharted" routes are characterized by .compus==NOWHERE. The list of */ X/* directions is placed in pl[]. */ X Xint makepathlist(x,y,z,pl) Xint x, y, z, pl[]; X{ int di, le, paths; X paths = 0; X for (di=0; di<DIRECTIONS; di++) { X pl[paths] = di; X le = board[x][y][z].conlev[di]; X if (le != NOWHERE) X switch (di) { X case NORTH : if (board[x][y-1][le].compass == NOWHERE) X paths++; X break; X case SOUTH : if (board[x][y+1][le].compass == NOWHERE) X paths++; X break; X case EAST : if (board[x+1][y][le].compass == NOWHERE) X paths++; X break; X case WEST : if (board[x-1][y][le].compass == NOWHERE) X paths++; X break; X case NOWHERE : break; X } X } X return(paths); X } X Xint topscore; X X/* buildreturnmap() is a recursive routine which starts the end of a path on */ X/* our tree-structured maze and traverses up to an intersection or dead end. */ X/* When we hit a dead end, we just return, but when we hit an intersection, we */ X/* call buildreturnmap() again, once for each path going off from the */ X/* intersection, except for the one got there on. We keep a score for how far */ X/* we are from the first square. One point per square, plus 5 times the */ X/* number of paths leading off from each intersection. We keep a topscore to */ X/* find out which square is farthest (in this modified sense) from our */ X/* starting square. This helps to ensure that we get a reasonably difficult */ X/* maze to solve. */ X/* We also record the direction to the end of the maze in each square. This */ X/* information is used by the hint facility. */ X Xbuildreturnmap(x,y,z,score) Xint x, y, z, score; X{ int pathlist[DIRECTIONS], paths, i, le; X X while ( (paths = makepathlist(x,y,z,pathlist)) == 1) { X z = board[x][y][z].conlev[pathlist[0]]; X switch (pathlist[0]) { X case NORTH : y--; break; X case SOUTH : y++; break; X case EAST : x++; break; X case WEST : x--; break; X } X board[x][y][z].compass = otherDir(pathlist[0]); X /* score++; */ X } X X if (paths == 0) { /* We are at the end of a path... */ X if (score >= topscore) { X topscore = score; X StartX = x; X StartY = y; X StartLevel = z; X } X return; X } X X /* if we got here, we are not at the end of a path. paths > 1 */ X score = score + paths; X X for (i=0; i<paths; i++) { X le = board[x][y][z].conlev[pathlist[i]]; X switch (pathlist[i]) { X case NORTH : board[x][y-1][le].compass = SOUTH; X buildreturnmap(x,y-1,le,score); break; X X case SOUTH : board[x][y+1][le].compass = NORTH; X buildreturnmap(x,y+1,le,score); break; X X case EAST : board[x+1][y][le].compass = WEST; X buildreturnmap(x+1,y,le,score); break; X X case WEST : board[x-1][y][le].compass = EAST; X buildreturnmap(x-1,y,le,score); break; X } X } X } X X#define MAXENDS 120 X#define FREEEND -1 X#define LASTEND -1 Xtypedef struct { X int x,y,z; /* board coordinates. x=FREEEND means this one is free */ X int prevdir; /* The direction we went to get here. */ X /* In picking what direction to go, we are biased */ X /* toward this direction. This gives paths */ X /* with several straight sections -- keeps one path from */ X /* knotting up one part of the board. */ X int nextfree; /* next unused end, LASTEND means no more */ X } endtype; X Xendtype ends[MAXENDS]; Xint firstfree; Xint MaxEnds = MAXENDS; X Xinitends() X{ int i; X X for (i=0; i<MaxEnds; i++) { X ends[i].x = -1; X ends[i].y = -1; X ends[i].z = -1; X ends[i].nextfree = i - 1; X } X firstfree = MaxEnds - 1; X /* NOTE that ends[0].nextfree == -1 == LASTEND. If that define ever */ X /* changes, we will have to include the statement: */ X /* ends[0].nextfree = LASTEND; */ X } X X/* IF you want to play around with the parameters that effect the way X boards are built, you will want to try different combinations of X "tries", "length", and global MaxEnds. */ X Xint extendend(curend,tries,length) Xint curend, tries, length; X{ X int x, y, level; X int nd,od, nx, ny, nl, i, moves, link; X X moves = 0; X X x = ends[curend].x; X y = ends[curend].y; X level = ends[curend].z; X nd = ends[curend].prevdir; X X /* try up to i times to add a random square */ X for (i=0; i<tries && moves < length; i++) { X do { /* try not to go out of bounds */ X nd = ((RangeRand(10L) < 4) ? (int)RangeRand((LONG)DIRECTIONS) : nd); X } while ((nd == NORTH && y == 1) || X (nd == WEST && x == 1) || X (nd == SOUTH && y == BoardMaxY-1) || X (nd == EAST && x == BoardMaxX-1) ); X /* Now that we've got a direction, see that we don't already go that way */ X if (board[x][y][level].conlev[nd] != NOWHERE) X continue; X nx = x; ny = y; X switch (nd) { /* work out the new x and y */ X case NORTH : ny = y - 1; break; X case SOUTH : ny = y + 1; break; X case EAST : nx = x + 1; break; X case WEST : nx = x - 1; break; X } X od = otherDir(nd); X nl = level; X X /** get some level we can go onto from here **/ X if (!(link=linkable(x, y, level, nd, nx, ny, nl, od))) { /* stay on the same level if we can... */ X if (level > MinLevel) { /* can't stay on this level so go down */ X nl = level - 1; X link = linkable(x, y, level, nd, nx, ny, nl, od); /* if we can't go down, go up */ X } X if (!link && (level < MaxLevel - 1)) { X nl = level + 1; /* can't go down or level, so try up */ X link = linkable(x, y, level, nd, nx, ny, nl, od); X } X } X X if (link) { X board[x ][y ][level].conlev[nd] = nl; X board[nx][ny][nl ].conlev[od] = level; X render( x, y); X render(nx,ny); X moves++; X /* sometimes fork, putting our old coordinates in a free end slot. */ X if (firstfree != LASTEND) { /* make sure there IS a free end slot */ X if (RangeRand(10L) < 4) { X ends[firstfree].x = x; X ends[firstfree].y = y; X ends[firstfree].z = level; X ends[firstfree].prevdir = nd; X firstfree = ends[firstfree].nextfree; X } X } X ends[curend].x = x = nx; X ends[curend].y = y = ny; X ends[curend].z = level = nl; X } X } X /* if we got this far and made no moves, we are probably on dead end and */ X /* should put this ends[] on the free list. */ X if (moves == 0) { X ends[curend].x = FREEEND; X ends[curend].nextfree = firstfree; X firstfree = curend; X } X return(moves); X } Xint xties, xlength; Xint makepath() X{ int i, endsfound, totalsquares; X X initends(); /* This sets the table of path ends to all zeros */ X X /* ends[] is a list of the ends of paths which we can add more paths */ X /* onto. Note that they don't really have to be an end; they can be */ X /* in the middle of a path as well. Not all of them are in use as */ X /* an end all the time. Some are in a linked list of "free" ends, */ X /* available for use if we want to add an end. Free (unused) array */ X /* elements are marked by a .x == FREEEND. Ends get taken out of active */ X /* service and put on the free list when the path generator "extendend()" */ X /* fails to extend the path from that end. When no active ends remain, */ X /* maze generation terminates. */ X X /* p.s.: I don't particularly care for the mazes that are generated by */ X /* this method, nor am I particularly fond of the method. I would like */ X /* to hear of other strategies for maze generation. */ X X X ends[firstfree].x = 1 + RangeRand((LONG)BoardMaxX - 1); X ends[firstfree].y = 1 + RangeRand((LONG)BoardMaxY - 1); X ends[firstfree].z = 0; X ends[firstfree].prevdir = RangeRand((LONG)DIRECTIONS); X firstfree = ends[firstfree].nextfree; X X totalsquares = 0; X do { X endsfound = 0; X for (i=0; i< MaxEnds; i++) { X if (ends[i].x != FREEEND) { X endsfound++; X totalsquares += extendend(i,xties,xlength); X } X } X } while (endsfound); X return( totalsquares ); X } X Xpickends() X{ int x, y, z, i; X X /* Find then end of a path on the bottom level. */ X do { X StartX = 1 + RangeRand( (LONG) BoardMaxX-1L ); X StartY = 1 + RangeRand( (LONG) BoardMaxY-1L ); X StartLevel = MinLevel; X } while (references( StartX, StartY, StartLevel) != 1); X X /* The reason for doing this 4 times is to make the ends as far apart as */ X /* possible. If our first choice is close to the top of the tree, it */ X /* can't be very far away from every leaf node. The way this works is: */ X /* 1 Find a leaf node. */ X /* 2 Find the leaf farthest from the one found in step 1. */ X /* 3 Find the leaf farthest from the one found in step 2. ... */ X /* Eventually you should end up with two good choices for the ends. */ X X for (i=0; i<4; i++) { X for (x=1; x<BoardMaxX; x++) X for (y=1; y<BoardMaxY; y++) X for (z=MinLevel; z<MaxLevel; z++) X board[x][y][z].compass = NOWHERE; X X EndX = StartX; EndY = StartY; EndLevel = StartLevel; X board[ EndX ][ EndY ][ EndLevel ].compass = SOLVED; X topscore = 0; X buildreturnmap(EndX, EndY, EndLevel, 0); X } X X board[ StartX ][ StartY ][ StartLevel ].inpath = FIRSTPIECE; X board[ EndX ][ EndY ][ EndLevel ].inpath = LASTPIECE; X CurX = StartX; X CurY = StartY; X CurLevel = StartLevel; X NewX = CurX; X NewY = CurY; X NewLevel = CurLevel; X render( StartX, StartY); X render( EndX, EndY); X } X Xinit1( l ) Xint l; X{ X int i, x, y, level, dir; X static int Xsize[] = { 54, 16, 24, 34 }; /* Square widths */ X static int Xsqrs[] = { 10, 34, 22, 16 }; /* No. of squares */ X X static int Ysize[] = { 26, 10, 14, 18 }; /* Square heights */ X static int Ysqrs[] = { 6, 16, 11, 9 }; /* No. of squares */ X X /* Reset the entire board, including a strip of squares around */ X /* the edges that we never go into. */ X for(x=0; x <= BOARDMAXX; x++) X for(y=0; y <= BOARDMAXY; y++) X for(level=0; level < MAXLEVELS; level++) { X board[x][y][level].inpath = NOTTRAVERSED; X board[x][y][level].compass = NOWHERE; X for(dir=0; dir < DIRECTIONS; dir++) X board[x][y][level].conlev[dir] = NOWHERE; X } X X SetAPen( rp, 0L); X SetBPen( rp, 0L); X RectFill( rp, 4L, 11L, (LONG)windowW-4, (LONG)windowH-2); X X MaxLevel = l; /* l==0 is a special-case first-time super-easy 1-level */ X /* maze. It generates fast so we can get to the menus. */ X X BoardMaxX = Xsqrs[ MaxLevel ] + 1; X SquareXsize = Xsize[ MaxLevel ]; X BoardMaxY = Ysqrs[ MaxLevel ] + 1; X SquareYsize = Ysize[ MaxLevel ]; X X if (MaxLevel == 0) X MaxLevel = 1; X X makepath(); X X pickends(); X setHint( (int)FALSE ); X } X Xint autosolve() X{ int newDir; X square *oldSq, *newSq; X X while ( (newDir = board[CurX][CurY][CurLevel].compass) != SOLVED ) { X NewX = CurX; X NewY = CurY; X NewLevel = board[CurX][CurY][CurLevel].conlev[newDir]; X switch (newDir) { X case NORTH : NewY--; break; X case SOUTH : NewY++; break; X case EAST : NewX++; break; X case WEST : NewX--; break; X } X X oldSq = &board[ CurX ][ CurY ][ CurLevel]; X newSq = &board[ NewX ][ NewY ][ NewLevel]; X X oldSq->inpath = TRAVERSED; X X switch (newSq->inpath) { X case TRAVERSED : X case FIRSTPIECE : /* We must have backed up to get here, so...*/ X oldSq->inpath = NOTTRAVERSED; break; X default : ; X } X X /* set the inpath flag of the new square in any case */ X newSq->inpath = CURPIECE; X X /* Also, just in case we backed up to the starting square */ X /* or from the ending square.... */ X board[StartX][StartY][StartLevel].inpath = FIRSTPIECE; X board[EndX ][EndY ][EndLevel ].inpath = LASTPIECE; X X /* Now draw both squares */ X render(CurX,CurY); X render(NewX,NewY); X X /* Now new becomes current */ X CurX = NewX; X CurY = NewY; X CurLevel = NewLevel; X X } X } X X X Xint mousewatch() X{ static int canmove = FALSE; X int x, y, newDir; X X x = ((MouseX) - 4)/SquareXsize + 1; X y = ((MouseY) - 11)/SquareYsize + 1; X X x = x * SIGN(MouseX); X y = y * SIGN(MouseY); X X if (x == CurX && y == CurY) { X canmove = TRUE; X } X X newDir = NOWHERE; X if (x == CurX) { X if (y == CurY + 1 && y < BoardMaxY) X newDir = SOUTH; X else if (y == CurY - 1 && y > 0) X newDir = NORTH; X } X else X if (y == CurY) { X if (x == CurX + 1 && x < BoardMaxX) X newDir = EAST; X else if (x == CurX - 1 && x > 0) X newDir = WEST; X } X X if (newDir == NOWHERE ) { X canmove = FALSE; X return(NOWHERE); X } X X NewLevel = board[CurX][CurY][CurLevel].conlev[newDir]; X X if (NewLevel == NOWHERE) { X canmove = FALSE; X return(NOWHERE); X } X NewX = x; X NewY = y; X return(newDir); X } X Xint trymove() /* returns TRUE if we won, FALSE if we didn't win. */ X{ int newDir; X square *oldSq, *newSq; X X newDir = mousewatch(); X if (newDir == NOWHERE) X return(board[CurX][CurY][CurLevel].compass == SOLVED); X X oldSq = &board[ CurX ][ CurY ][ CurLevel]; X newSq = &board[ NewX ][ NewY ][ NewLevel]; X X oldSq->inpath = TRAVERSED; X switch (newSq->inpath) { X case TRAVERSED : X case FIRSTPIECE : /* We must have backed up to get here, so...*/ X oldSq->inpath = NOTTRAVERSED; break; X default : ; X } X X /* set the inpath flag of the new square in any case */ X newSq->inpath = CURPIECE; X X /* Also, just in case we backed up from the starting square */ X /* or the ending square.... */ X board[StartX][StartY][StartLevel].inpath = FIRSTPIECE; X board[EndX ][EndY ][EndLevel ].inpath = LASTPIECE; X X /* Now draw both squares */ X render(CurX,CurY); X render(NewX,NewY); X X /* Now new becomes current */ X CurX = NewX; X CurY = NewY; X CurLevel = NewLevel; X X /* ?did we win? */ X X setHint((int)(newSq->compass == SOLVED)); X X if (newSq->compass == SOLVED) { X cycle( 3 ); X return( (int)TRUE ); X } X return( (int)FALSE); X } X XHandleMenu(menucode) XUSHORT menucode; X{ X ModifyIDCMP( window, (ULONG) MIDCMP2 ); X X switch ( ITEMNUM( menucode) ) { X case 0 : about() ; break; X case 1 : init1( 1 ); break; X case 2 : init1( 2 ); break; X case 3 : init1( 3 ); break; X case 4 : autosolve(); break; X } X X ModifyIDCMP( window, (ULONG) MIDCMP1 ); X } X X XEventLoop() X{ X struct IntuiMessage *mesg; X ULONG class, code, mousemoved, closewindow, menupick; X USHORT menucode; X X closewindow = FALSE; X ModifyIDCMP( window, (ULONG) MIDCMP1 ); X X do { X mousemoved = FALSE; X menupick = FALSE; X X Wait( (LONG) 1 << window -> UserPort -> mp_SigBit); X X while ((mesg=(struct IntuiMessage *)GetMsg(window->UserPort))) { X class = mesg->Class; X code = mesg->Code; X MouseX = mesg->MouseX; X MouseY = mesg->MouseY; X ReplyMsg(mesg); X if (class == MOUSEMOVE) mousemoved = TRUE; X if (class == CLOSEWINDOW) closewindow = TRUE; X if (class == MOUSEBUTTONS && code == SELECTDOWN) X setHint( (int) TRUE); X if (class == MENUPICK) { X menupick = TRUE; X menucode = code; X } X } X if (mousemoved) trymove(); X if (menupick) HandleMenu(menucode); X } while (closewindow == FALSE); X } X Xstruct TextFont *textfont = NULL; X Xopenstuff() X{ ULONG Seconds, Micros; X X IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L); X if (IntuitionBase == NULL) {closestuff(); exit(0);} X X GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L); X if (GfxBase == NULL) {closestuff(); exit(0); } X X /* My <functions.h> shows OpenFont() returns a *Font. */ X /* I think that is an error! */ X X textfont = (struct TextFont *)OpenFont( &font ); X if (textfont==NULL) { closestuff(); exit(0); } X X if (( window=(struct Window *)OpenWindow(&nw))==NULL) { X closestuff(); X exit(0); X } X if (SetFont(window->RPort,textfont)==0) { closestuff(); exit(0); } X X SetWindowTitles(window,(UBYTE *)" TML's AmigaMaze ver. 1.2 ", X (UBYTE *)"TML's AmigaMaze First distributed on JUMPDISK."); X X SetMenuStrip( window, &menu); X X /* This next bit is a cludge because I can't seem to get */ X /* extern ULONG RangeSeed; */ X /* to work. Anyone got any ideas? */ X /* LATER: It turns out that the RangeSeed is not public */ X /* in the Manx sources. Hope they fix this someday. */ X CurrentTime(&Seconds,&Micros); X Micros = Micros & (ULONG) 0x0fff; X for (Seconds=0; Seconds < Micros; Seconds++) X RangeRand(4L); X } X X Xclosestuff() X{ int i; X for(i=0; i<2; i++) { X if (tmpBM.Planes[i]) X FreeRaster(tmpBM.Planes[i],tmpBMxy,tmpBMxy); X } X if (window) { X ClearMenuStrip( window ); X CloseWindow(window); X } X if (textfont) CloseFont(textfont); X if (GfxBase) CloseLibrary(GfxBase); X if (IntuitionBase) CloseLibrary(IntuitionBase); X } X Xmain(/*argc,argv*/) X/* int argc; */ X/* char *argv[]; */ X{ UWORD areabuffer[250], areabuffer2[250]; X struct TmpRas tmpras, tmprasB; X struct AreaInfo areainfo, areainfo2; X PLANEPTR plane,planeB; X int i; X X /* if (argc > 1) */ X /* xties = atoi(argv[1]); */ X /* if (xties < 1 || xties > 100) */ X xties = 30; X X /* if (argc > 2) */ X /* xlength = atoi(argv[2]); */ X /* if (xlength < 1 || xlength > 100) */ X xlength = 9; X X /* if (argc > 3) */ X /* MaxEnds = atoi(argv[3]); */ X /* if (MaxEnds < 2 || MaxEnds > MAXENDS) */ X MaxEnds = MAXENDS; X X MaxLevel = 1; X MinLevel = 0; X openstuff(); X InitBitMap( &tmpBM, 2L, tmpBMxy, tmpBMxy ); X for (i=0; i<2; i++) { X if ((tmpBM.Planes[i] = (PLANEPTR)AllocRaster(tmpBMxy,tmpBMxy))==NULL) { X closestuff(); X exit(0); X } X } X rp = window->RPort; X if ((plane = AllocRaster(windowW,windowH))==NULL) { X closestuff(); X exit(0); X } X if ((planeB = AllocRaster(tmpBMxy,tmpBMxy))==NULL) { X FreeRaster(plane, windowW,windowH); X closestuff(); X exit(0); X } X InitArea(&areainfo, areabuffer, 90L); X InitArea(&areainfo2,areabuffer2,90L); X X InitTmpRas(&tmpras, plane, RASSIZE( windowW, windowH)); X InitTmpRas(&tmprasB,planeB,RASSIZE( tmpBMxy, tmpBMxy)); X X rp->AreaInfo = &areainfo; X rp->TmpRas = &tmpras; X X InitRastPort( tmpRP ); X if (SetFont(tmpRP,textfont)==0) { closestuff(); exit(0); } X tmpRP->BitMap = &tmpBM; X tmpRP->AreaInfo = &areainfo2; X tmpRP->TmpRas = &tmprasB; X X init1(0); /* Zero is a special case. Super simple fast easy maze X so the user can get to the menus w/o waiting so long. */ X ModifyIDCMP( window, (ULONG) MIDCMP2 ); X about(); X EventLoop(); X FreeRaster(plane, windowW, windowH); X FreeRaster(planeB,tmpBMxy, tmpBMxy); X closestuff(); X } X X/* Save some code space by stubbing _wb_parse() and _cli_parse(). */ Xvoid _wb_parse() X{ } X Xvoid _cli_parse() X{ } X SHAR_EOF echo "extracting stdhead.c" sed 's/^X//' << \SHAR_EOF > stdhead.c X#include <exec/types.h> X#include <intuition/intuition.h> X#include <functions.h> X#include <graphics/gfxmacros.h> X#include <graphics/view.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> SHAR_EOF echo "extracting makefile" sed 's/^X//' << \SHAR_EOF > makefile Xstdhead.t: stdhead.c X cc +Hstdhead.t +m stdhead.c X Xabout.o: about.c stdhead.t X cc -n +Istdhead.t about.c X Xmaze.o: maze.c stdhead.t X cc -n +Istdhead.t maze.c X Xmaze: maze.o about.o X ln -g maze.o about.o -lc X X# maze.l: maze.c about.c X# lint >maze.l -iSYS1:include/ -zero -si2 SYS9:manx.c maze.c about.c X SHAR_EOF echo "End of archive 1 (of 1)" # if you want to concatenate archives, remove anything after this line exit