#include #include #include #include #include struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; struct RastPort tmpRPactual; struct RastPort *tmpRP = &tmpRPactual; struct BitMap tmpBM; struct TextAttr font = { (STRPTR)"topaz.font", /* Font Name */ TOPAZ_EIGHTY, /* Font Height */ FS_NORMAL, /* Font Style */ FPF_ROMFONT /* Preferences */ }; #define tmpBMxy 60L #define windowW 552L #define windowH 183L struct NewWindow nw = { 6, 12, windowW, windowH, /* EDGES, SIZE */ 0, 1, /* PENS */ CLOSEWINDOW, /* IDCMPFlags */ WINDOWDRAG | WINDOWDEPTH | /* Flags (requested window features)*/ WINDOWCLOSE | ACTIVATE | REPORTMOUSE, NULL, /* gadgets */ NULL, /* checkmark image */ NULL, /* title */ NULL, /* screen */ NULL, /* bitmap */ 0,0,0,0, /* min and max w and h */ WBENCHSCREEN, /* TYPE */ }; struct IntuiText it_GiveUp = { 0,1, /* frontpen, backpen */ JAM1, /* drawmode */ 1,1, /* leftedge, topedge */ &font, /* TextAttr * ITextFont */ (UBYTE *)" Give Up! ", /* IText */ NULL, /* NextText */ }; struct MenuItem mi_GiveUp = { (struct MenuItem *) NULL, 0,40, /* LeftEdge, TopEdge */ 21*9,10, /* Width, Height */ ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ 0, /* Mutual Exclude */ (APTR)&it_GiveUp, /* ItemFill */ NULL, /* SelectFill */ 0, /* Command */ NULL, /* Subitem */ 0, /* NextSelect */ }; struct IntuiText it_3Level = { 0,1, /* frontpen, backpen */ JAM1, /* drawmode */ 1,1, /* leftedge, topedge */ &font, /* TextAttr * ITextFont */ (UBYTE *)"New 3-Level Maze", /* IText */ NULL, /* NextText */ }; struct MenuItem mi_3Level = { (struct MenuItem *) &mi_GiveUp, 0,30, /* LeftEdge, TopEdge */ 21*9,10, /* Width, Height */ ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ 0, /* Mutual Exclude */ (APTR)&it_3Level, /* ItemFill */ NULL, /* SelectFill */ 0, /* Command */ NULL, /* Subitem */ 0, /* NextSelect */ }; struct IntuiText it_2Level = { 0,1, /* frontpen, backpen */ JAM1, /* drawmode */ 1,1, /* leftedge, topedge */ &font, /* TextAttr * ITextFont */ (UBYTE *)"New 2-Level Maze", /* IText */ NULL, /* NextText */ }; struct MenuItem mi_2Level = { (struct MenuItem *) &mi_3Level, 0,20, /* LeftEdge, TopEdge */ 21*9,10, /* Width, Height */ ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ 0, /* Mutual Exclude */ (APTR)&it_2Level, /* ItemFill */ NULL, /* SelectFill */ 0, /* Command */ NULL, /* Subitem */ 0, /* NextSelect */ }; struct IntuiText it_1Level = { 0,1, /* frontpen, backpen */ JAM1, /* drawmode */ 1,1, /* leftedge, topedge */ &font, /* TextAttr * ITextFont */ (UBYTE *)"New 1-Level Maze", /* IText */ NULL, /* NextText */ }; struct MenuItem mi_1Level = { (struct MenuItem *)&mi_2Level, 0,10, /* LeftEdge, TopEdge */ 21*9,10, /* Width, Height */ ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ 0, /* Mutual Exclude */ (APTR)&it_1Level, /* ItemFill */ NULL, /* SelectFill */ 0, /* Command */ NULL, /* Subitem */ 0, /* NextSelect */ }; struct IntuiText it_About = { 0,1, /* frontpen, backpen */ JAM1, /* drawmode */ 1,1, /* leftedge, topedge */ &font, /* TextAttr * ITextFont */ (UBYTE *)"About TML's AmigaMaze", /* IText */ NULL, /* NextText */ }; struct MenuItem mi_About = { (struct MenuItem *) &mi_1Level, 0,0, /* LeftEdge, TopEdge */ 21*9,10, /* Width, Height */ ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */ 0, /* Mutual Exclude */ (APTR)&it_About, /* ItemFill */ NULL, /* SelectFill */ 0, /* Command */ NULL, /* Subitem */ 0, /* NextSelect */ }; struct Menu menu = { (struct Menu *) NULL, /* NEXT menu */ 0, 0, 12*9, 0, /* LeftEdge, TopEdge, Width, Height */ MENUENABLED, /* Flags */ (BYTE *) "Maze Control", /* MenuName */ (struct MenuItem *)&mi_About, /* First item */ 0,0,0,0, /* JazzX,JazzY, BeatX, BeatY */ }; struct Window *window; /* BEWARE: NOWHERE has a dual nature, being used as a doesn't-go-anywhere indicator, and also as an invalid level marker!!! */ #define NOWHERE -1 #define SOUTH 0 #define EAST 1 #define WEST 2 #define NORTH 3 #define SOLVED -3 #define DIRECTIONS 4 #define MAXLEVELS 3 #define BOARDMAXX 36 #define BOARDMAXY 29 /*** the following are "inpath" stata. (see struct square) *******/ #define NOTTRAVERSED 1 #define EDGE 2 #define LASTPIECE 3 + 32 #define FIRSTPIECE 3 + 64 #define CURPIECE 3 + 96 #define TRAVERSED 3 #define MIDCMP1 CLOSEWINDOW|MOUSEMOVE|MOUSEBUTTONS|MENUPICK|MENUVERIFY #define MIDCMP2 REQCLEAR #define MIDCMP3 CLOSEWINDOW extern ULONG RangeRand(); extern int about(); extern int cycle(); typedef struct { int conlev[ DIRECTIONS ]; /** level connected to in each dir. **/ int inpath; /** Also the color this should be drawn in. **/ int compass; /** Which direction to go to get home from here **/ } square; typedef square BOARD[ BOARDMAXX+1 ][ BOARDMAXY+1 ][ MAXLEVELS ]; BOARD board; struct RastPort *rp; int StartX, StartY, StartLevel; int EndX, EndY, EndLevel; int CurX, CurY, CurLevel; int NewX, NewY, NewLevel; int MouseX, MouseY; int MinLevel, MaxLevel; int BoardMaxX = BOARDMAXX; int BoardMaxY = BOARDMAXY; int SquareXsize = 34; int SquareYsize = 18; /* We need to have some Fill patterns for various places. Here goes... */ USHORT Pat_Normal[] = { /** the normal filled pattern **/ 0xffff, /* plane 1 pattern */ 0xffff, 0xffff, /* plane 2 pattern */ 0xffff }; USHORT Pat_P1[] = { /** first dithered filled pattern **/ 0x5555, /* plane 1 pattern */ 0xaaaa, 0xffff, /* plane 2 pattern */ 0xffff }; USHORT Pat_P2[] = { /** second dithered filled pattern **/ 0xffff, /* plane 1 pattern */ 0xffff, 0x5555, /* plane 2 pattern */ 0xaaaa }; setHint(ShowHint) int ShowHint; { char *s; int oldBPen; if (ShowHint) { switch ( board[CurX][CurY][CurLevel].compass) { case NORTH : s = " HINT: Go North! "; break; case SOUTH : s = " HINT: Go South! "; break; case EAST : s = " HINT: Go East! "; break; case WEST : s = " HINT: Go West! "; break; case SOLVED: s = " CONGRATULATIONS! "; break; default : s = " Hmm. I don't know. "; break; } } else { s = " (Press Left Button for a hint.)"; } Move(rp, 20L, 180L); SetAPen(rp, 1L); oldBPen = rp -> BgPen; SetBPen(rp, 0L); SetDrMd(rp, (LONG)JAM2); Text(rp, s, 32L); SetBPen(rp, (LONG)oldBPen); } showside(ax,ay, bx,by, cx,cy, dx,dy, color) LONG ax,ay, bx,by, cx,cy, dx,dy, color; { SetAPen( tmpRP, color); /* The shape isn't allways a rect., so */ SetOPen( tmpRP, color); /* we can't use RectFill() here... */ BNDRYOFF( tmpRP); /* Boundaries look bad w/ patterns...*/ switch (color) { case CURPIECE : case TRAVERSED : SetAfPt( tmpRP, Pat_P2,-1L); break; case LASTPIECE : SetAfPt( tmpRP, Pat_P1,-1L); break; case NOTTRAVERSED : case FIRSTPIECE : default : SetAfPt( tmpRP, Pat_Normal,-1L); break; } AreaMove( tmpRP, ax,ay); AreaDraw( tmpRP, bx,by); AreaDraw( tmpRP, cx,cy); AreaDraw( tmpRP, dx,dy); AreaEnd( tmpRP); SetAPen( tmpRP, (LONG)EDGE); Move( tmpRP, ax, ay); Draw( tmpRP, bx, by); Draw( tmpRP, bx+1,by); Draw( tmpRP, ax+1,ay); Move( tmpRP, cx, cy); Draw( tmpRP, dx, dy); Draw( tmpRP, dx+1,dy); Draw( tmpRP, cx+1,cy); } putedge(ax,ay,bx,by) LONG ax,ay,bx,by; { SetAPen(tmpRP,(LONG)EDGE); Move(tmpRP,ax, ay); Draw(tmpRP,bx, by); Draw(tmpRP,bx+1,by); Draw(tmpRP,ax+1,ay); } LONG renderColor(x, y, z, x1, y1, z1) int x, y, z, x1, y1, z1; { int color; color = board[x][y][z].inpath; if (!(z == MaxLevel-1 && (color == FIRSTPIECE || color == LASTPIECE))) if ((color == NOTTRAVERSED) || (board[x1][y1][z1].inpath == NOTTRAVERSED)) color = NOTTRAVERSED; return((LONG)color); } #define XDIF 6 #define YDIF 3 render(x,y) int x, y; { LONG ax,ay, bx,by, cx,cy, dx,dy, color; int level, Bleft, Btop, deltaX, deltaY, tmpL; int left, right, bottom, top; /* clear the temporary bitmap */ SetAPen( tmpRP, 0L); SetBPen( tmpRP, 0L); RectFill( tmpRP, 0L, 0L, (LONG)SquareXsize, (LONG)SquareYsize ); Bleft = 4 + (x - 1) * SquareXsize; Btop = 11 + (y - 1) * SquareYsize; left = 0; right = SquareXsize - 1; top = 0; bottom = SquareYsize - 1; for (level=MinLevel; level= topscore) { topscore = score; StartX = x; StartY = y; StartLevel = z; } return; } /* if we got here, we are not at the end of a path. paths > 1 */ score = score + paths; for (i=0; i MinLevel) { /* can't stay on this level so go down */ nl = level - 1; link = linkable(x, y, level, nd, nx, ny, nl, od); /* if we can't go down, go up */ } if (!link && (level < MaxLevel - 1)) { nl = level + 1; /* can't go down or level, so try up */ link = linkable(x, y, level, nd, nx, ny, nl, od); } } if (link) { board[x ][y ][level].conlev[nd] = nl; board[nx][ny][nl ].conlev[od] = level; render( x, y); render(nx,ny); moves++; /* sometimes fork, putting our old coordinates in a free end slot. */ if (firstfree != LASTEND) { /* make sure there IS a free end slot */ if (RangeRand(10L) < 4) { ends[firstfree].x = x; ends[firstfree].y = y; ends[firstfree].z = level; ends[firstfree].prevdir = nd; firstfree = ends[firstfree].nextfree; } } ends[curend].x = x = nx; ends[curend].y = y = ny; ends[curend].z = level = nl; } } /* if we got this far and made no moves, we are probably on dead end and */ /* should put this ends[] on the free list. */ if (moves == 0) { ends[curend].x = FREEEND; ends[curend].nextfree = firstfree; firstfree = curend; } return(moves); } int xties, xlength; int makepath() { int i, endsfound, totalsquares; initends(); /* This sets the table of path ends to all zeros */ /* ends[] is a list of the ends of paths which we can add more paths */ /* onto. Note that they don't really have to be an end; they can be */ /* in the middle of a path as well. Not all of them are in use as */ /* an end all the time. Some are in a linked list of "free" ends, */ /* available for use if we want to add an end. Free (unused) array */ /* elements are marked by a .x == FREEEND. Ends get taken out of active */ /* service and put on the free list when the path generator "extendend()" */ /* fails to extend the path from that end. When no active ends remain, */ /* maze generation terminates. */ /* p.s.: I don't particularly care for the mazes that are generated by */ /* this method, nor am I particularly fond of the method. I would like */ /* to hear of other strategies for maze generation. */ ends[firstfree].x = 1 + RangeRand((LONG)BoardMaxX - 1); ends[firstfree].y = 1 + RangeRand((LONG)BoardMaxY - 1); ends[firstfree].z = 0; ends[firstfree].prevdir = RangeRand((LONG)DIRECTIONS); firstfree = ends[firstfree].nextfree; totalsquares = 0; do { endsfound = 0; for (i=0; i< MaxEnds; i++) { if (ends[i].x != FREEEND) { endsfound++; totalsquares += extendend(i,xties,xlength); } } } while (endsfound); return( totalsquares ); } pickends() { int x, y, z, i; /* Find then end of a path on the bottom level. */ do { StartX = 1 + RangeRand( (LONG) BoardMaxX-1L ); StartY = 1 + RangeRand( (LONG) BoardMaxY-1L ); StartLevel = MinLevel; } while (references( StartX, StartY, StartLevel) != 1); /* The reason for doing this 4 times is to make the ends as far apart as */ /* possible. If our first choice is close to the top of the tree, it */ /* can't be very far away from every leaf node. The way this works is: */ /* 1 Find a leaf node. */ /* 2 Find the leaf farthest from the one found in step 1. */ /* 3 Find the leaf farthest from the one found in step 2. ... */ /* Eventually you should end up with two good choices for the ends. */ for (i=0; i<4; i++) { for (x=1; xinpath = TRAVERSED; switch (newSq->inpath) { case TRAVERSED : case FIRSTPIECE : /* We must have backed up to get here, so...*/ oldSq->inpath = NOTTRAVERSED; break; default : ; } /* set the inpath flag of the new square in any case */ newSq->inpath = CURPIECE; /* Also, just in case we backed up to the starting square */ /* or from the ending square.... */ board[StartX][StartY][StartLevel].inpath = FIRSTPIECE; board[EndX ][EndY ][EndLevel ].inpath = LASTPIECE; /* Now draw both squares */ render(CurX,CurY); render(NewX,NewY); /* Now new becomes current */ CurX = NewX; CurY = NewY; CurLevel = NewLevel; } } int mousewatch() { static int canmove = FALSE; int x, y, newDir; x = ((MouseX) - 4)/SquareXsize + 1; y = ((MouseY) - 11)/SquareYsize + 1; x = x * SIGN(MouseX); y = y * SIGN(MouseY); if (x == CurX && y == CurY) { canmove = TRUE; } newDir = NOWHERE; if (x == CurX) { if (y == CurY + 1 && y < BoardMaxY) newDir = SOUTH; else if (y == CurY - 1 && y > 0) newDir = NORTH; } else if (y == CurY) { if (x == CurX + 1 && x < BoardMaxX) newDir = EAST; else if (x == CurX - 1 && x > 0) newDir = WEST; } if (newDir == NOWHERE ) { canmove = FALSE; return(NOWHERE); } NewLevel = board[CurX][CurY][CurLevel].conlev[newDir]; if (NewLevel == NOWHERE) { canmove = FALSE; return(NOWHERE); } NewX = x; NewY = y; return(newDir); } int trymove() /* returns TRUE if we won, FALSE if we didn't win. */ { int newDir; square *oldSq, *newSq; newDir = mousewatch(); if (newDir == NOWHERE) return(board[CurX][CurY][CurLevel].compass == SOLVED); oldSq = &board[ CurX ][ CurY ][ CurLevel]; newSq = &board[ NewX ][ NewY ][ NewLevel]; oldSq->inpath = TRAVERSED; switch (newSq->inpath) { case TRAVERSED : case FIRSTPIECE : /* We must have backed up to get here, so...*/ oldSq->inpath = NOTTRAVERSED; break; default : ; } /* set the inpath flag of the new square in any case */ newSq->inpath = CURPIECE; /* Also, just in case we backed up from the starting square */ /* or the ending square.... */ board[StartX][StartY][StartLevel].inpath = FIRSTPIECE; board[EndX ][EndY ][EndLevel ].inpath = LASTPIECE; /* Now draw both squares */ render(CurX,CurY); render(NewX,NewY); /* Now new becomes current */ CurX = NewX; CurY = NewY; CurLevel = NewLevel; /* ?did we win? */ setHint((int)(newSq->compass == SOLVED)); if (newSq->compass == SOLVED) { cycle( 3 ); return( (int)TRUE ); } return( (int)FALSE); } HandleMenu(menucode) USHORT menucode; { ModifyIDCMP( window, (ULONG) MIDCMP2 ); switch ( ITEMNUM( menucode) ) { case 0 : about() ; break; case 1 : init1( 1 ); break; case 2 : init1( 2 ); break; case 3 : init1( 3 ); break; case 4 : autosolve(); break; } ModifyIDCMP( window, (ULONG) MIDCMP1 ); } EventLoop() { struct IntuiMessage *mesg; ULONG class, code, mousemoved, closewindow, menupick; USHORT menucode; closewindow = FALSE; ModifyIDCMP( window, (ULONG) MIDCMP1 ); do { mousemoved = FALSE; menupick = FALSE; Wait( (LONG) 1 << window -> UserPort -> mp_SigBit); while ((mesg=(struct IntuiMessage *)GetMsg(window->UserPort))) { class = mesg->Class; code = mesg->Code; MouseX = mesg->MouseX; MouseY = mesg->MouseY; ReplyMsg(mesg); if (class == MOUSEMOVE) mousemoved = TRUE; if (class == CLOSEWINDOW) closewindow = TRUE; if (class == MOUSEBUTTONS && code == SELECTDOWN) setHint( (int) TRUE); if (class == MENUPICK) { menupick = TRUE; menucode = code; } } if (mousemoved) trymove(); if (menupick) HandleMenu(menucode); } while (closewindow == FALSE); } struct TextFont *textfont = NULL; openstuff() { ULONG Seconds, Micros; IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L); if (IntuitionBase == NULL) {closestuff(); exit(0);} GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L); if (GfxBase == NULL) {closestuff(); exit(0); } /* My shows OpenFont() returns a *Font. */ /* I think that is an error! */ textfont = (struct TextFont *)OpenFont( &font ); if (textfont==NULL) { closestuff(); exit(0); } if (( window=(struct Window *)OpenWindow(&nw))==NULL) { closestuff(); exit(0); } if (SetFont(window->RPort,textfont)==0) { closestuff(); exit(0); } SetWindowTitles(window,(UBYTE *)" TML's AmigaMaze ver. 1.2 ", (UBYTE *)"TML's AmigaMaze First distributed on JUMPDISK."); SetMenuStrip( window, &menu); /* This next bit is a cludge because I can't seem to get */ /* extern ULONG RangeSeed; */ /* to work. Anyone got any ideas? */ /* LATER: It turns out that the RangeSeed is not public */ /* in the Manx sources. Hope they fix this someday. */ CurrentTime(&Seconds,&Micros); Micros = Micros & (ULONG) 0x0fff; for (Seconds=0; Seconds < Micros; Seconds++) RangeRand(4L); } closestuff() { int i; for(i=0; i<2; i++) { if (tmpBM.Planes[i]) FreeRaster(tmpBM.Planes[i],tmpBMxy,tmpBMxy); } if (window) { ClearMenuStrip( window ); CloseWindow(window); } if (textfont) CloseFont(textfont); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); } main(/*argc,argv*/) /* int argc; */ /* char *argv[]; */ { UWORD areabuffer[250], areabuffer2[250]; struct TmpRas tmpras, tmprasB; struct AreaInfo areainfo, areainfo2; PLANEPTR plane,planeB; int i; /* if (argc > 1) */ /* xties = atoi(argv[1]); */ /* if (xties < 1 || xties > 100) */ xties = 30; /* if (argc > 2) */ /* xlength = atoi(argv[2]); */ /* if (xlength < 1 || xlength > 100) */ xlength = 9; /* if (argc > 3) */ /* MaxEnds = atoi(argv[3]); */ /* if (MaxEnds < 2 || MaxEnds > MAXENDS) */ MaxEnds = MAXENDS; MaxLevel = 1; MinLevel = 0; openstuff(); InitBitMap( &tmpBM, 2L, tmpBMxy, tmpBMxy ); for (i=0; i<2; i++) { if ((tmpBM.Planes[i] = (PLANEPTR)AllocRaster(tmpBMxy,tmpBMxy))==NULL) { closestuff(); exit(0); } } rp = window->RPort; if ((plane = AllocRaster(windowW,windowH))==NULL) { closestuff(); exit(0); } if ((planeB = AllocRaster(tmpBMxy,tmpBMxy))==NULL) { FreeRaster(plane, windowW,windowH); closestuff(); exit(0); } InitArea(&areainfo, areabuffer, 90L); InitArea(&areainfo2,areabuffer2,90L); InitTmpRas(&tmpras, plane, RASSIZE( windowW, windowH)); InitTmpRas(&tmprasB,planeB,RASSIZE( tmpBMxy, tmpBMxy)); rp->AreaInfo = &areainfo; rp->TmpRas = &tmpras; InitRastPort( tmpRP ); if (SetFont(tmpRP,textfont)==0) { closestuff(); exit(0); } tmpRP->BitMap = &tmpBM; tmpRP->AreaInfo = &areainfo2; tmpRP->TmpRas = &tmprasB; init1(0); /* Zero is a special case. Super simple fast easy maze so the user can get to the menus w/o waiting so long. */ ModifyIDCMP( window, (ULONG) MIDCMP2 ); about(); EventLoop(); FreeRaster(plane, windowW, windowH); FreeRaster(planeB,tmpBMxy, tmpBMxy); closestuff(); } /* Save some code space by stubbing _wb_parse() and _cli_parse(). */ void _wb_parse() { } void _cli_parse() { }