' ' File: txtmazea.bas ' Creation Date: Wed 12-Jul-2000 18:23:01 Jonathan D. Kirwan ' Last Modified: Thu 17-Jun-2004 00:35:37 Initial version. ' ' Copyright (C) 2000, 2004 Jonathan Dale Kirwan ' All Rights Reserved: See the file COPYRGHT for a full description. ' ' ' DESCRIPTION ' ' This module demonstrates generating and displaying a maze, in text ' mode. See ' ' http://users.easystreet.com/jkirwan/maze.htm ' ' for more information on the design. ' ' ' MODIFICATIONS ' ' No modifications. ' ' ' COPYRIGHT NOTICE ' ' Jonathan Dale Kirwan grants you a non-transferable, non-exclusive, ' royalty-free worldwide license to use, copy, modify, prepare deriva- ' tive works of and distribute this software, subject to your agreement ' that you acquire no ownership right, title, or interest in this soft- ' ware and your agreement that this software is research work which is ' provided 'as is', where Jonathan Dale Kirwan disclaims all warranties ' with regard to this software, including all implied warranties of ' merchantability and fitness of purpose. In no event shall Jonathan ' Dale Kirwan be liable for any direct, indirect, consequential or ' special damages or any damages whatsoever resulting from loss of use, ' data or profits, whether in an action of contract, negligence or ' other tortious action, arising out of or in connection with the use ' or performance of this software. DECLARE SUB GenerateMaze (MazeWidth AS INTEGER, MazeHeight AS INTEGER, WestWalls() AS INTEGER, SouthWalls() AS INTEGER) DECLARE SUB TextDrawMaze (unit AS INTEGER, MazeWidth AS INTEGER, MazeHeight AS INTEGER, WestWalls() AS INTEGER, SouthWalls() AS INTEGER) DIM answer AS STRING, idx AS INTEGER, firsttime AS INTEGER DIM unit AS INTEGER, MazeWidth AS DOUBLE, MazeHeight AS DOUBLE REDIM SouthWalls(0 TO 0) AS INTEGER, WestWalls(0 TO 0) AS INTEGER RANDOMIZE TIMER CLS DO DO PRINT "Enter the maze width and height: "; LINE INPUT answer LET answer = LTRIM$(RTRIM$(answer)) IF answer = "" THEN END END IF LET idx = INSTR(answer, ",") IF idx >= 2 THEN LET MazeWidth = VAL(LEFT$(answer, idx - 1)) LET MazeHeight = VAL(MID$(answer, idx + 1)) IF MazeWidth < 1# THEN PRINT " The width is either missing or too small." ELSEIF MazeHeight < 1# THEN PRINT " The height is either missing or too small." ELSEIF MazeWidth > 32760# THEN PRINT " The width is way too big." ELSEIF MazeHeight > 32760# THEN PRINT " The height is way too big." ELSEIF (MazeWidth + 2#) * (MazeHeight + 2#) > 32760# THEN PRINT " The maze area is way too big." ELSE EXIT DO END IF ELSE PRINT " You must enter both values, separated by a comma." END IF LOOP GenerateMaze CINT(MazeWidth), CINT(MazeHeight), WestWalls(), SouthWalls() PRINT " The maze is completed." PRINT LET firsttime = -1 DO PRINT "Enter the filename on which to write the maze: "; LINE INPUT answer LET answer = LTRIM$(RTRIM$(answer)) IF answer = "" AND NOT firsttime THEN EXIT DO ELSEIF answer = "" THEN LET answer = "SCRN:" END IF LET unit = FREEFILE OPEN answer FOR OUTPUT AS #unit TextDrawMaze unit, CINT(MazeWidth), CINT(MazeHeight), WestWalls(), SouthWalls() LET firsttime = 0 CLOSE #unit LOOP PRINT LOOP END SUB GenerateMaze (MazeWidth AS INTEGER, MazeHeight AS INTEGER, WestWalls() AS INTEGER, SouthWalls() AS INTEGER) ' ' This routine accepts a width and height for a maze and calculates a ' random maze into two arrays designed to hold the west and south walls ' of each room or cell in the maze grid. These can then be used to print ' or use the maze, as desired (such as a random labyrinth for a game.) ' DIM CurrentRoom AS INTEGER, count AS INTEGER, i AS INTEGER, j AS INTEGER DIM Exits(0 TO 3) AS INTEGER, ExitCount AS INTEGER, Selection AS INTEGER DIM PathCount AS INTEGER, UnvisitedRoomCount AS INTEGER ' This code redimensions the west and south wall arrays, as needed. ' These arrays must be redimensionable, or an error will result. ' As an important side effect I'm depending on, redimensioning ' these arrays causes their element values to be initialized to 0. LET count = (MazeWidth + 2) * (MazeHeight + 2) ERASE WestWalls ERASE SouthWalls REDIM WestWalls(0 TO count - 1) AS INTEGER REDIM SouthWalls(0 TO count - 1) AS INTEGER ' Set up our local copy of the visitation status array. Since the ' grid uses a perimeter around the maze itself, we need to mark the ' rooms in the perimeter as having been used, so that the intervening ' walls are not removed (since those walls are the maze's boundary.) DIM Visited(0 TO count - 1) AS INTEGER LET j = (MazeWidth + 2) * (MazeHeight + 1) - 1 FOR i = 0 TO MazeWidth + 2 LET Visited(i) = -1 LET Visited(i + j) = -1 NEXT i LET j = MazeWidth + MazeWidth + 3 FOR i = 1 TO MazeHeight LET Visited(j) = -1 LET Visited(j + 1) = -1 LET j = j + MazeWidth + 2 NEXT i ' Initialize the path list. LET PathCount = 0 DIM PathList(0 TO MazeWidth * MazeHeight) AS INTEGER ' Arrays are set up, the perimeter is initialized, we're ready to go. ' Compute the maze! (See the discussion on the web site for details.) LET UnvisitedRoomCount = MazeWidth * MazeHeight LET i = INT(RND * UnvisitedRoomCount) LET CurrentRoom = (1 + i \ MazeWidth) * (MazeWidth + 2) + (i MOD MazeWidth) + 1 DO WHILE UnvisitedRoomCount > 1 LET UnvisitedRoomCount = UnvisitedRoomCount - 1 LET Visited(CurrentRoom) = -1 DO LET ExitCount = 0 IF NOT Visited(CurrentRoom - MazeWidth - 2) THEN LET Exits(ExitCount) = 1 LET ExitCount = ExitCount + 1 END IF IF NOT Visited(CurrentRoom + MazeWidth + 2) THEN LET Exits(ExitCount) = 2 LET ExitCount = ExitCount + 1 END IF IF NOT Visited(CurrentRoom - 1) THEN LET Exits(ExitCount) = 3 LET ExitCount = ExitCount + 1 END IF IF NOT Visited(CurrentRoom + 1) THEN LET Exits(ExitCount) = 4 LET ExitCount = ExitCount + 1 END IF IF ExitCount >= 1 THEN EXIT DO END IF LET i = INT(RND * PathCount) LET CurrentRoom = PathList(i) LET PathCount = PathCount - 1 LET PathList(i) = PathList(PathCount) LOOP IF ExitCount > 1 THEN LET PathList(PathCount) = CurrentRoom LET PathCount = PathCount + 1 END IF LET Selection = INT(RND * ExitCount) SELECT CASE Exits(Selection) CASE 1 LET CurrentRoom = CurrentRoom - MazeWidth - 2 LET SouthWalls(CurrentRoom) = -1 CASE 2 LET SouthWalls(CurrentRoom) = -1 LET CurrentRoom = CurrentRoom + MazeWidth + 2 CASE 3 LET WestWalls(CurrentRoom) = -1 LET CurrentRoom = CurrentRoom - 1 CASE 4 LET CurrentRoom = CurrentRoom + 1 LET WestWalls(CurrentRoom) = -1 END SELECT LOOP ' Add an entrance and exit to the maze. These could be placed ' anywhere around the perimeter, if we wanted to. For now, it's ' hard-coded at the upper-left corner and the lower-right corner. LET SouthWalls(1) = -1 LET SouthWalls((MazeHeight + 1) * (MazeWidth + 2) - 2) = -1 END SUB SUB TextDrawMaze (unit AS INTEGER, MazeWidth AS INTEGER, MazeHeight AS INTEGER, WestWalls() AS INTEGER, SouthWalls() AS INTEGER) ' ' This routine accepts a width and height and the west and south walls for ' a maze and prints out the maze to the given file unit. ' DIM i AS INTEGER, j AS INTEGER, p AS INTEGER FOR j = 1 TO MazeWidth IF SouthWalls(j) THEN PRINT #unit, "+ "; ELSE PRINT #unit, "+--"; END IF NEXT j PRINT #unit, "+" LET p = 0 FOR i = 1 TO MazeHeight LET p = p + MazeWidth + 2 FOR j = 1 TO MazeWidth IF WestWalls(p + j) THEN PRINT #unit, " "; ELSE PRINT #unit, "| "; END IF NEXT j IF NOT WestWalls(p + MazeWidth + 1) THEN PRINT #unit, "|"; END IF PRINT #unit, "" FOR j = 1 TO MazeWidth IF SouthWalls(p + j) THEN PRINT #unit, "+ "; ELSE PRINT #unit, "+--"; END IF NEXT j PRINT #unit, "+" NEXT i END SUB