Author Topic: Maze Generators  (Read 4945 times)

0 Members and 1 Guest are viewing this topic.

Offline TerryRitchie

  • Seasoned Forum Regular
  • Posts: 495
  • Semper Fidelis
    • View Profile
Maze Generators
« on: August 22, 2018, 02:53:34 am »
In another thread I posted a maze generator I created many years ago. Here it is again:

Code: QB64: [Select]
  1. '**
  2. '** MAZEGEN by Terry Ritchie
  3. '**
  4. '** Creates random 20x16 braided mazes (or perfect mazes with modification)
  5. '**
  6. '** This code was created from pseudo-code obtained at MAZEWORKS.COM
  7. '**
  8. '** This code could be easily modified to create any size maze and cell size
  9. '**
  10. '** If you use this code in your program please credit me and MAZEWORKS.COM
  11. '**
  12. '** Code last modified 02/23/09
  13. '**
  14. '** This maze generating code was created to use in a MEGABUG (Tandy/Radio
  15. '** Shack game from 1982) clone to teach introductory programming. The
  16. '** original game used a 20x16 maze grid. This is why the maze generator has
  17. '** been hard coded to only use a 16x20 grid. A little modification would
  18. '** allow this code to generate any size maze with any size cell.
  19. '**
  20.  
  21. CONST false = 0, true = NOT false
  22. CONST totalcells = 320
  23.  
  24. TYPE cell
  25.   walls AS INTEGER
  26.   x AS INTEGER
  27.   y AS INTEGER
  28.  
  29. TYPE stack
  30.   x AS INTEGER
  31.   y AS INTEGER
  32.  
  33. DECLARE SUB CreateRandomMaze ()
  34. DECLARE SUB DrawMaze ()
  35. DECLARE SUB DrawCell (cellx AS INTEGER, celly AS INTEGER)
  36. DECLARE SUB RemoveWall (cellx AS INTEGER, celly AS INTEGER, dir AS INTEGER)
  37.  
  38. DIM SHARED maze(0 TO 19, 0 TO 15) AS cell
  39.  
  40. SCREEN 7, 0, 1, 0
  41.  
  42.   LINE (81, 37)-(239, 163), 0, BF   'faster than CLS
  43.   CreateRandomMaze
  44.   DrawMaze
  45.   PCOPY 1, 0
  46.   SLEEP 1               '**rem this line out to see how fast it is!
  47.  
  48.  
  49. SUB CreateRandomMaze
  50. '**
  51. '** Creates a random 20x16 braided (looping) maze by first creating a perfect
  52. '** maze (a maze with no loops) then visiting each dead end and randomly
  53. '** opening them to create loops.
  54. '**
  55. '** vcells stores valid next maze cell moves in binary (bit) format
  56. '** if all adjacent cell walls are turned on then that cell is saved in vcells
  57. '** as follows:
  58. '**             bit 1 (2^0) = north cell has all walls on
  59. '**             bit 2 (2^1) = east  cell has all walls on
  60. '**             bit 3 (2^2) = south cell has all walls on
  61. '**             bit 4 (2^3) = west  cell has all walls on
  62. '**
  63.   DIM stack(totalcells) AS stack            '** maze generation LIFO stack
  64.   DIM cellx, celly, counter AS INTEGER      '** general counters
  65.   DIM cell AS stack                         '** current cell being created
  66.   DIM pointer AS INTEGER                    '** pointer for use in LIFO stack
  67.   DIM vcells AS INTEGER                     '** which cells are valid moves?
  68.   DIM randomwall AS INTEGER                 '** random valid mover
  69.   DIM forward AS INTEGER                    '** movement indicator
  70.   RANDOMIZE TIMER                           '** seed random number generator
  71.   FOR cellx = 0 TO 19                       '** initialize maze grid
  72.     FOR celly = 0 TO 15
  73.       maze(cellx, celly).walls = 15         '** turn all walls on
  74.       maze(cellx, celly).x = 80 + cellx * 8 '** x coordinate upper left cell
  75.       maze(cellx, celly).y = 36 + celly * 8 '** y coordinate upper left cell
  76.     NEXT celly
  77.   NEXT cellx
  78.   cell.x = INT(RND(1) * 19)                 '** random x location
  79.   cell.y = INT(RND(1) * 15)                 '** random y location
  80.   visitedcells = 1                          '** initialize counter
  81.   pointer = 0                               '** initialize LIFO stack pointer
  82.   forward = false                           '** initialize movement indicator
  83.   WHILE visitedcells < totalcells           '** continue until all cells made
  84.     vcells = 0                              '** initialize valid move check
  85.     IF cell.y <> 0 THEN IF maze(cell.x, cell.y - 1).walls = 15 THEN vcells = vcells + 1
  86.     IF cell.x <> 19 THEN IF maze(cell.x + 1, cell.y).walls = 15 THEN vcells = vcells + 2
  87.     IF cell.y <> 15 THEN IF maze(cell.x, cell.y + 1).walls = 15 THEN vcells = vcells + 4
  88.     IF cell.x <> 0 THEN IF maze(cell.x - 1, cell.y).walls = 15 THEN vcells = vcells + 8
  89.     IF vcells <> 0 THEN                     '** at least 1 cell has all walls
  90.       DO                                    '** find a random move direction
  91.         randomwall = INT(RND(1) * 4)        '** 0=North 1=East 2=South 3=West
  92.       LOOP UNTIL vcells AND 2 ^ randomwall  '** is random direction valid?
  93.       stack(pointer).x = cell.x             '** save current cell position in
  94.       stack(pointer).y = cell.y             '** the stack
  95.       pointer = pointer + 1                 '** increment stack pointer
  96.       visitedcells = visitedcells + 1       '** increment cell counter
  97.       forward = true                        '** forward movement indicated
  98.       CALL RemoveWall(cell.x, cell.y, randomwall) '** remove random wall
  99.       SELECT CASE randomwall                '** which direction forward?
  100.         CASE 0
  101.           cell.y = cell.y - 1               '** move north
  102.         CASE 1
  103.           cell.x = cell.x + 1               '** move east
  104.         CASE 2
  105.           cell.y = cell.y + 1               '** move south
  106.         CASE 3
  107.           cell.x = cell.x - 1               '** move west
  108.       END SELECT
  109.     ELSE                                    '** no cells have all walls
  110.      
  111. '****** remark the lines below to create perfect mazes (no loops) ***********
  112. '****** the code below creates braided mazes (contains loops)     ***********
  113.  
  114.       IF forward THEN                       '** we hit a dead end!
  115.         forward = false                     '** forward movement stops here
  116.         IF INT(RND(1) * 2) = 1 THEN         '** 50% chance of wall removal
  117.           SELECT CASE randomwall            '** which wall?
  118.             CASE 0                          '** remove north wall
  119.               IF cell.y <> 0 THEN           '** unless it's a border
  120.                 CALL RemoveWall(cell.x, cell.y, randomwall)
  121.               END IF
  122.             CASE 1                          '** remove east wall
  123.               IF cell.x <> 19 THEN          '** unless it's a border
  124.                 CALL RemoveWall(cell.x, cell.y, randomwall)
  125.               END IF
  126.             CASE 2                          '** remove south wall
  127.               IF cell.y <> 15 THEN          '** unless it's a border
  128.                 CALL RemoveWall(cell.x, cell.y, randomwall)
  129.               END IF
  130.             CASE 3                          '** remove west wall
  131.               IF cell.x <> 0 THEN           '** unless it's a border
  132.                 CALL RemoveWall(cell.x, cell.y, randomwall)
  133.               END IF
  134.           END SELECT
  135.         END IF
  136.       END IF
  137.      
  138. '****** remark the lines above to create perfect mazes (no loops) ***********
  139. '****** the code above creates braided mazes (contains loops)     ***********
  140.  
  141.       pointer = pointer - 1                 '** decrement stack pointer
  142.       cell.x = stack(pointer).x             '** go back to previous cell
  143.       cell.y = stack(pointer).y             '** go back to previous cell
  144.     END IF
  145.   WEND                                      '** exit when all cells visited
  146.  
  147.  
  148. SUB DrawCell (cellx AS INTEGER, celly AS INTEGER)
  149.  
  150. '****************************************************************************
  151. '** draw cell to graphics screen                                            *
  152. '****************************************************************************
  153.  
  154.   IF maze(cellx, celly).walls AND 1 THEN LINE (maze(cellx, celly).x, maze(cellx, celly).y)-(maze(cellx, celly).x + 8, maze(cellx, celly).y), 1
  155.   IF maze(cellx, celly).walls AND 2 THEN LINE (maze(cellx, celly).x + 8, maze(cellx, celly).y)-(maze(cellx, celly).x + 8, maze(cellx, celly).y + 8), 1
  156.   IF maze(cellx, celly).walls AND 4 THEN LINE (maze(cellx, celly).x, maze(cellx, celly).y + 8)-(maze(cellx, celly).x + 8, maze(cellx, celly).y + 8), 1
  157.   IF maze(cellx, celly).walls AND 8 THEN LINE (maze(cellx, celly).x, maze(cellx, celly).y)-(maze(cellx, celly).x, maze(cellx, celly).y + 8), 1
  158.  
  159.  
  160. SUB DrawMaze
  161.  
  162. '****************************************************************************
  163. '* Draws the entire maze to the graphics screen                             *
  164. '****************************************************************************
  165.  
  166.   DIM x AS INTEGER                          '** holds x location of maze cell
  167.   DIM y AS INTEGER                          '** holds y location of maze cell
  168.  
  169.   FOR x = 0 TO 19                           '** cycle through all cells
  170.     FOR y = 0 TO 15
  171.       CALL DrawCell(x, y)                   '** draw each cell to the screen
  172.     NEXT y
  173.   NEXT x
  174.   LINE (79, 35)-(241, 165), 1, B            '** draw border around maze
  175.  
  176.  
  177. SUB RemoveWall (cellx AS INTEGER, celly AS INTEGER, dir AS INTEGER)
  178.  
  179. '****************************************************************************
  180. '* Removes the walls between to adjoining cells based on the direction of
  181. '* movement.                                                                *
  182. '****************************************************************************
  183.  
  184.   SELECT CASE dir                           '** which direction?
  185.     CASE 0                                  '** remove north/south walls
  186.       maze(cellx, celly).walls = maze(cellx, celly).walls - 1
  187.       maze(cellx, celly - 1).walls = maze(cellx, celly - 1).walls - 4
  188.     CASE 1                                  '** remove east/west walls
  189.       maze(cellx, celly).walls = maze(cellx, celly).walls - 2
  190.       maze(cellx + 1, celly).walls = maze(cellx + 1, celly).walls - 8
  191.     CASE 2                                  '** remove south/north walls
  192.       maze(cellx, celly).walls = maze(cellx, celly).walls - 4
  193.       maze(cellx, celly + 1).walls = maze(cellx, celly + 1).walls - 1
  194.     CASE 3                                  '** remove west/east walls
  195.       maze(cellx, celly).walls = maze(cellx, celly).walls - 8
  196.       maze(cellx - 1, celly).walls = maze(cellx - 1, celly).walls - 2
  197.  

However, while organizing my code snippets I found this second maze generator I created some years later. I completely forget I made this and can't even remember why any longer. It has many features for making, saving, and loading random and custom mazes.

Code: QB64: [Select]
  1. '**
  2. '** Maze Generator V1.0
  3. '**
  4. '** by Terry Ritchie - 02/06/13
  5. '**
  6. '** This code can generate random braided looping, non-looping or custom mazes by supplying a string of HEX characters.
  7. '**
  8. '** Mazes can be saved and loaded using the SAVEMAZE and LOADMAZE commands.
  9. '**
  10. '** An example of how to draw mazes is included as DRAWMAZE. The syntax for drawmaze is as follows:
  11. '**
  12. '** DRAWMAZE MazeType%, Thickness%, MazeColor~&
  13. '**
  14. '** - MazeType%  : 0 (or constant SQUARE) for a square maze, 1 (or constant ROUND) for a round maze
  15. '** - Thickness% : the wall thickness of the maze
  16. '** - MazeColor~&: the color of the maze walls
  17. '**
  18. '**   DRAWMAZE draws the maze image to the image handle MazeImage&
  19. '**
  20. '** To generate a random maze use the MAKEMAZE command as follows:
  21. '**
  22. '**                         +--------- R specifies a random maze
  23. '** MAKEMAZE "RL0A0A15"     |+-------- L specifies a looping maze (use N for non-looping maze)
  24. '** +-------------------+   || +------ 0A the next two characters in HEX specifies the horizontal width of maze, in this case 10
  25. '** | the command above |   || |
  26. '** | will produce a    |   vv v       The largest HEX value that can be used is FF, or 255, therefore the maximum width and height
  27. '** | random maze of 20 |   RL0A0A15   of the maze can't exceed 255x255 and the maximum cell size can't exceed 255.
  28. '** | cells wide by 20  |        ^ ^
  29. '** | cells high with   |        | |
  30. '** | each cell being   |        | +-- 15 the last two characters in HEX specifies the size of each cell in maze, in this case 21
  31. '** | 21 pixels in size |        +---- 0A the next two characters in HEX specifies the vertical height of maze, in this case 10
  32. '** +-------------------+
  33. '**
  34. '** To generate a maze of your own design use the MAKEMAZE command as follows:
  35. '**
  36. '** MAKEMAZE "050515<maze_string>"
  37. '** +-------------------+    +------- 05 the first two HEX digits specifies the horizontal width of the maze, in this case 5
  38. '** | the command above |    | +----- 05 the second two HEX digits specifies the vertical height of the maze, in this case 5
  39. '** | will produce a    |    | |
  40. '** | custom maze of 5  |    v v                  A sample maze string that creates a spiraling inward maze would be:
  41. '** | cells wide by 5   |   050515<maze_string>
  42. '** | cells high with   |        ^ ^                             "0505152AAAC6AAC55685553A953AAA9"
  43. '** | each cell being   |        | |
  44. '** | 21 pixels in size |        | +- <maze_string> is a string of HEX digits that describes the actual maze (see illustration below)
  45. '** +-------------------+        +--- 15 the last two HEX digits specifies the size of each pixel in the maze, in this case 21
  46. '**
  47. '** NOTE: Round mazes will look best if you keep the size of the cells at ODD numbers. The included DRAWMAZE example
  48. '**       subroutine was optimized to make sure that round mazes have a definite center line inside the maze, because these maze
  49. '**       rotuines are a spinoff of a game in progress that needs random round maze generation with defined center lines.
  50. '**
  51. '** After you have created a maze with MAKEMAZE you can use the SAVEMAZE command to save the maze to a file.  This will allow you to
  52. '** use the generated mazes in your own programs. The SAVEMAZE command is used as follows:
  53. '**
  54. '** SAVEMAZE "mymaze"
  55. '**
  56. '** There is no need to supply an extension. ".MAZ" will automagically be added to the name you gave your maze.
  57. '**
  58. '** You can load a previously saved maze using LOADMAZE as follows:
  59. '**
  60. '** LOADMAZE "mymaze", mymaze$
  61. '**
  62. '** The maze will be loaded and placed into the variable specified, in this case mymaze$
  63. '**
  64. '*****************************************************************************************************************************************
  65. '**                                                                                                                                      *
  66. '**                 The following is an illustration of the 16 cell conditions and their corresponding HEX values:                       *
  67. '**                                                                                                                                      *
  68. '*****************************************************************************************************************************************
  69. '**   |     |     Cell 1            *   |     |     Cell 2           *   |     |     Cell 3            *   |     |     Cell 4            *
  70. '** --|     |--   North door open   * --+--------   East door open   * --|     +--   North door open   * --+-----+--   South door open   *
  71. '**   |     |                       *   |                            *   |           East door open    *   |     |                       *
  72. '**   |     |                       *   |                            *   |                             *   |     |                       *
  73. '** --+-----+--                     * --+--------                    * --+--------                     * --|     |--                     *
  74. '**   |     |     HEX - 1           *   |     |     HEX - 2          *   |     |     HEX - 3           *   |     |     HEX - 4           *
  75. '*****************************************************************************************************************************************
  76. '**   |     |     Cell 5            *   |     |     Cell 6           *   |     |     Cell 7            *   |     |     Cell 8            *
  77. '** --|     |--   North door open   * --+--------   East door open   * --|     +--   North door open   * --------+--   West door open    *
  78. '**   |     |     South door open   *   |           South door open  *   |           South door open   *         |                       *
  79. '**   |     |                       *   |                            *   |           East door open    *         |                       *
  80. '** --|     |--                     * --|     +--                    * --|     +--                     * --------+--                     *
  81. '**   |     |     HEX - 5           *   |     |     HEX - 6          *   |     |     HEX - 7           *   |     |     HEX - 8           *
  82. '*****************************************************************************************************************************************
  83. '**   |     |     Cell 9            *   |     |     Cell 10          *   |     |     Cell 11           *   |     |     Cell 12           *
  84. '** --+     |--   North door open   * -----------   East door open   * --+     +--   North door open   * --+-----+--   South door open   *
  85. '**         |     West door open    *               West door open   *               East door open    *         |     West door open    *
  86. '**         |                       *                                *               West door open    *         |                       *
  87. '** --------+--                     * -----------                    * -----------                     * --+     +--                     *
  88. '**   |     |     HEX - 9           *   |     |     HEX - A          *   |     |     HEX - B           *   |     |     HEX - C           *
  89. '*****************************************************************************************************************************************
  90. '**   |     |     Cell 13           *   |     |     Cell 14          *   |     |     Cell 15           * \ |     | /   Cell 0            *
  91. '** --+     |--   North door open   * -----------   East door open   * --+     +--   North door open   * --+-----+--   No doors open     *
  92. '**         |     South door open   *               South door open  *               East door open    *   | \ / |     (nothing will be  *
  93. '**         |     West door open    *               West door open   *               South door open   *   | / \ |      drawn to the     *
  94. '** --+     |--                     * --+     +--                    * --+     +--   West door open    * --+-----+--    maze image)      *
  95. '**   |     |     HEX - D           *   |     |     HEX - E          *   |     |     HEX - F           * / |     | \   HEX - 0           *
  96. '*****************************************************************************************************************************************
  97. '**                                                                                                                                      *
  98. '** Each open door is treated as a digit in a binary nibble:  North = 2^0  East = 2^1  South = 2^2  West = 2^3                           *
  99. '**                                                                                                                                      *
  100. '** Therefore, one hexadecimal digit can store the state of all four doors of a cell. i.e. F = 2^0+2^1+2^2+2^3 = 15 = all doors open.    *
  101. '**                                                                                                                                      *
  102. '*****************************************************************************************************************************************
  103.  
  104. CONST FALSE = 0, TRUE = NOT FALSE '           boolean truth testers
  105. CONST PI = 3.1415926 '                        would you like a piece?
  106. CONST PIUP = 1.5707963 '                      radian pointing up
  107. CONST PIDOWN = 4.7123889 '                    radian pointing down
  108. CONST SQUARE = 0
  109. CONST ROUND = 1
  110.  
  111. TYPE CELL
  112.     Xpos AS INTEGER '                         the X screen coordinate of this cell
  113.     Ypos AS INTEGER '                         the Y screen coordinate of this cell
  114.     Doors AS INTEGER '                        the doors that are open in this cell (1-up, 2-right, 4-down, 8-left)
  115.  
  116. TYPE LOCATION
  117.     Hor AS INTEGER '                          horizontal X position of this cell
  118.     Ver AS INTEGER '                          vertical Y position of this cell
  119.  
  120. TYPE STACK
  121.     Xpos AS INTEGER '                         horizontal X position of cell in stack
  122.     Ypos AS INTEGER '                         vertical Y position of cell in stack
  123.  
  124. TYPE MAZE
  125.     Hcell AS INTEGER '                        number of horizontal cells in maze
  126.     Vcell AS INTEGER '                        number of vertical cells in maze
  127.     CellSize AS INTEGER '                     the size of each maze cell
  128.     Thickness AS INTEGER '                    the wall thickness of the maze
  129.     Colour AS _UNSIGNED LONG '                the maze wall color
  130.     MazeType AS INTEGER '                     the type of maze (0-square, 1-round)
  131.     Looping AS INTEGER '                      0 for non looping, 1 for looping
  132.  
  133. REDIM Cell(0, 0) AS CELL '                    an array of maze cells
  134. DIM Maze AS MAZE '                            the maze properties
  135. DIM MazeImage& '                              the maze image holder
  136. DIM MyMaze$
  137.  
  138. RANDOMIZE TIMER '                             seed the random number generator
  139.  
  140. SCREEN _NEWIMAGE(1280, 720, 32)
  141.  
  142. MAKEMAZE "0505152AAAC6AAC55685553A953AAA9" ' create a custom maze
  143. DRAWMAZE ROUND, 6, _RGB32(64, 64, 64) '      draw the custom round maze to the screen
  144. SAVEMAZE "mymaze" '                          save the maze as MYMAZE.MAZ
  145. _PUTIMAGE (0, 0), MazeImage& '               this puts maze on screen in real size
  146. LOCATE 2, 2: PRINT " A custom maze - saved to HDD"
  147. SLEEP '                                      wait for a key press
  148. MAKEMAZE "RL141429" '                        create a 20 x 20 random looping maze with a cell size of 41 pixels
  149. DRAWMAZE SQUARE, 6, _RGB32(64, 64, 64) '     draw the random square maze to the screen
  150. _PUTIMAGE (0, 0), MazeImage& '               this puts maze on screen in real size
  151. LOCATE 2, 2: PRINT " A random 20x20 square looping maze with 41 pixel sized cells (too big for screen)"
  152. SLEEP '                                      wait for a keypress
  153. MAKEMAZE "RN0A0A1F" '                        create a 10 x 10 random non-looping maze with a cell size of 31 pixels
  154. DRAWMAZE ROUND, 6, _RGB32(64, 64, 64) '      draw the random round maze to the screen
  155. _PUTIMAGE (0, 0), MazeImage& '               this puts maze on screen in real size
  156. LOCATE 2, 2: PRINT " A random 10x10 round non-looping maze with 31 pixel sized cells"
  157. SLEEP '                                      wait for a keypress
  158. LOADMAZE "mymaze", MyMaze$ '                 load previously saved maze and place maze in MyMaze$
  159. MAKEMAZE MyMaze$ '                           create the loaded maze
  160. DRAWMAZE SQUARE, 6, _RGB32(64, 64, 64) '     draw the loaded maze, this time in square style
  161. _PUTIMAGE (0, 0), MazeImage& '               this puts maze on screen in real size
  162. LOCATE 2, 2: PRINT " The first maze loaded from HDD and now displayed as a square maze"
  163. SLEEP '                                      wait for a keypress
  164. SYSTEM '                                     return to OS
  165.  
  166.  
  167. '** NOTE:    _PUTIMAGE , MazeImage& '        this will always squeeze maze onto screen
  168.  
  169. '----------------------------------------------------------------------------------------------------------------------
  170.  
  171. SUB LOADMAZE (MazeFile$, MazeString$)
  172.  
  173. '**
  174. '** Loads a maze from HDD
  175. '**
  176.  
  177. OPEN MazeFile$ + ".MAZ" FOR INPUT AS #1 '                                      open the maze file for input
  178. LINE INPUT #1, MazeString$ '                                                   get the maze string from the file
  179. CLOSE #1 '                                                                     close the file
  180.  
  181.  
  182. '----------------------------------------------------------------------------------------------------------------------
  183.  
  184. SUB SAVEMAZE (MazeFile$)
  185.  
  186. '**
  187. '** Saves a maze to HDD
  188. '**
  189.  
  190. SHARED Cell() AS CELL '  we need access to the cell array
  191. SHARED Maze AS MAZE '    we need access to the maze properties
  192.  
  193. DIM v%, h% '             generic counters to keep track of current cell
  194. DIM MazeString$ '        the string generated that represents the maze
  195.  
  196. '**
  197. '** save the number of horizontal cells, vertical cells and cell size to the maze string
  198. '** make sure that each HEX value saved is at least two digits in length by padding a zero at the
  199. '** beginning of each HEX value
  200. '**
  201. MazeString$ = RIGHT$("0" + HEX$(Maze.Hcell), 2) + RIGHT$("0" + HEX$(Maze.Vcell), 2) + RIGHT$("0" + HEX$(Maze.CellSize), 2)
  202. FOR v% = 1 TO Maze.Vcell '                                                     cycle through all vertical cells
  203.     FOR h% = 1 TO Maze.Hcell '                                                 cycle through all horizontal cells
  204.         MazeString$ = MazeString$ + HEX$(Cell(h%, v%).Doors) '                 save this cell's door conditions to maze string
  205.     NEXT h%
  206. NEXT v%
  207. OPEN MazeFile$ + ".MAZ" FOR OUTPUT AS #1 '                                     open the maze file for output
  208. PRINT #1, MazeString$ '                                                        write the maze string to the file
  209. CLOSE #1 '                                                                     close the maze file
  210.  
  211.  
  212. '----------------------------------------------------------------------------------------------------------------------
  213.  
  214. SUB DRAWMAZE (Mtype%, Thick%, Mcolor~&)
  215.  
  216. '**
  217. '** Draws the maze to the maze image holder
  218. '**
  219. '** This command is included as an example of how you can draw custom or random mazes for use in your own programs.
  220. '** See the DRAWCELL subroutine for the complete picture.
  221. '**
  222.  
  223. SHARED Cell() AS CELL '  we need access to the cell array
  224. SHARED MazeImage& '      we need access to the maze image holder
  225. SHARED Maze AS MAZE '    we need access to the maze properties
  226.  
  227. DIM OriginalDest& '      the saved destination of the calling routine
  228. DIM v%, h% '             current horizontal and vertical cell being drawn
  229.  
  230.  
  231. Maze.Thickness = Thick% '                                                      wall thickness of maze
  232. Maze.Colour = Mcolor~& '                                                       maze wall color
  233. Maze.MazeType = Mtype% '                                                       type of maze
  234. IF MazeImage& THEN _FREEIMAGE MazeImage& '                                     free previous maze image created if one exists
  235. MazeImage& = _NEWIMAGE(Maze.CellSize * (Maze.Hcell + 1.5) * 2, Maze.CellSize * (Maze.Vcell + 1.5) * 2, 32) ' calculate maze image size
  236. OriginalDest& = _DEST '                                                        save calling routine destination
  237. _DEST MazeImage& '                                                             maze image is new destination
  238. FOR v% = 1 TO Maze.Vcell '                                                     cycle through all cell vertical locations
  239.     FOR h% = 1 TO Maze.Hcell '                                                 cycle through all cell horizontal locations
  240.         DRAWCELL Cell(h%, v%).Xpos, Cell(h%, v%).Ypos, Cell(h%, v%).Doors '    draw the cell at this location
  241.     NEXT h%
  242. NEXT v%
  243. _DEST OriginalDest& '                                                          restore calling routine destination
  244.  
  245.  
  246. '----------------------------------------------------------------------------------------------------------------------
  247.  
  248. SUB MAKEMAZE (Maze$)
  249.  
  250. '**
  251. '** Creates a random braided optional-looping maze
  252. '**
  253.  
  254. SHARED Cell() AS CELL '     we need access to the cell array
  255. SHARED Maze AS MAZE '       we need access to the maze properties
  256.  
  257. DIM CurrentCell AS STACK '  the current cell we are working with
  258. DIM Pointer% '              the stack pointer
  259. DIM ValidCells% '           contains the valid adjacent cells we can move to
  260. DIM RandomDir% '            a random direction to move
  261. DIM Forward% '              TRUE if we are moving forward through the maze
  262. DIM CellX% '                generic counter used to initiate cells
  263. DIM CellY% '                generic counter used to initiate cells
  264. DIM VisitedCells% '         number of cells visited by subroutine
  265. DIM Remove% '               TRUE if it's ok to open a looping door
  266. DIM Mz$ '                   the string holding the maze
  267. DIM Custom% '               -1 (TRUE) if custom maze, 0 (FALSE) if random
  268. DIM Mzpointer% '            maze string position pointer
  269.  
  270. Mz$ = UCASE$(Maze$) '                                                          convert passed maze string to uppercase
  271. Mzpointer% = 0 '                                                               reset maze string position pointer
  272. Custom% = -1 '                                                                 assume this will be a custom maze
  273. IF LEFT$(Mz$, 1) = "R" THEN '                                                  should we create a random maze?
  274.     Custom% = 0 '                                                              yes, remove custom flag
  275.     IF MID$(Mz$, 2, 1) = "L" THEN '                                            should this be a looping maze?
  276.         Maze.Looping = 1 '                                                     yes, make it so number one
  277.     ELSE '                                                                     no
  278.         Maze.Looping = 0 '                                                     this will not be a looping maze
  279.     END IF
  280.     Mz$ = RIGHT$(Mz$, LEN(Mz$) - 2) '                                          remove the first two characters from maze string
  281. Maze.Hcell = VAL("&H" + LEFT$(Mz$, 2)) '                                       set the number of horizontal cells in maze
  282. Maze.Vcell = VAL("&H" + MID$(Mz$, 3, 2)) '                                     set the number of vertical cells in maze
  283. Maze.CellSize = VAL("&H" + MID$(Mz$, 5, 2)) '                                  set the size of each cell
  284. REDIM Cell(Maze.Hcell, Maze.Vcell) AS CELL '                                   create an array of maze cells
  285. IF Custom% THEN '                                                              are we creating a custom maze?
  286.     Mz$ = RIGHT$(Mz$, LEN(Mz$) - 6) '                                          yes, remove first six characters from maze string
  287. ELSE '                                                                         no, we are creating a random maze
  288.     DIM Stack(Maze.Hcell * Maze.Vcell) AS STACK '                              create a LIFO stack array
  289. FOR CellY% = 1 TO Maze.Hcell '                                                 cycle through all vertical cells
  290.     FOR CellX% = 1 TO Maze.Vcell '                                             cycle through all horizontal cells
  291.         Mzpointer% = Mzpointer% + 1 '                                          increment the maze string position pointer
  292.         Cell(CellX%, CellY%).Xpos = CellX% * Maze.CellSize * 2 '               upper left X location of this cell
  293.         Cell(CellX%, CellY%).Ypos = CellY% * Maze.CellSize * 2 '               upper left Y location of this cell
  294.         IF Custom% THEN '                                                      is this a custom maze?
  295.             Cell(CellX%, CellY%).Doors = VAL("&H" + MID$(Mz$, Mzpointer%, 1)) 'yes, get this cell's door conditions from maze string
  296.         ELSE '                                                                 no, this will be a random maze
  297.             Cell(CellX%, CellY%).Doors = 0 '                                   set all cell doors closed
  298.         END IF
  299.     NEXT CellX%
  300. NEXT CellY%
  301. IF Custom% THEN EXIT SUB '                                                     no need to go further if this is a custom maze
  302. '**
  303. '** Start of random maze generation algorithm *********************************
  304. '**
  305. CurrentCell.Xpos = INT(RND(1) * Maze.Hcell) + 1 '                              random horizontal position of cell to start at
  306. CurrentCell.Ypos = INT(RND(1) * Maze.Vcell) + 1 '                              random vertical position of cell to start at
  307. VisitedCells% = 1 '                                                            keep track of how many cells visited
  308. Pointer% = 0 '                                                                 initiate the stack pointer
  309. Forward% = 0 '                                                                 not moving forward through maze yet
  310. WHILE VisitedCells% < Maze.Hcell * Maze.Vcell '                                make sure we visit all cells
  311.     ValidCells% = 0 '                                                          initiate number of valid neighbor cells
  312.     IF CurrentCell.Ypos <> 1 THEN '                                            are we at top of maze?
  313.         IF Cell(CurrentCell.Xpos, CurrentCell.Ypos - 1).Doors = 0 THEN '       no, does cell above have all doors closed?
  314.             ValidCells% = ValidCells% + 1 '                                    yes, remember this cell
  315.         END IF
  316.     END IF
  317.     IF CurrentCell.Xpos <> Maze.Hcell THEN '                                   are we at right side of maze?
  318.         IF Cell(CurrentCell.Xpos + 1, CurrentCell.Ypos).Doors = 0 THEN '       no, does cell to right have all doors closed?
  319.             ValidCells% = ValidCells% + 2 '                                    yes, remember this cell
  320.         END IF
  321.     END IF
  322.     IF CurrentCell.Ypos <> Maze.Vcell THEN '                                   are we at bottom of maze?
  323.         IF Cell(CurrentCell.Xpos, CurrentCell.Ypos + 1).Doors = 0 THEN '       no, does cell below have all doors closed?
  324.             ValidCells% = ValidCells% + 4 '                                    yes, remember this cell
  325.         END IF
  326.     END IF
  327.     IF CurrentCell.Xpos <> 1 THEN '                                            are we at left side of maze?
  328.         IF Cell(CurrentCell.Xpos - 1, CurrentCell.Ypos).Doors = 0 THEN '       no, does cell to left have all doors closed?
  329.             ValidCells% = ValidCells% + 8 '                                    yes, remember this cell
  330.         END IF
  331.     END IF
  332.     IF ValidCells% <> 0 THEN '                                                 did at least one cell have all doors closed?
  333.         DO '                                                                   yes, one or more of the cells have all doors closed
  334.             RandomDir% = INT(RND(1) * 4) '                                     choose a random cell direction
  335.         LOOP UNTIL ValidCells% AND 2 ^ RandomDir% '                            continue if this is a valid move
  336.         Stack(Pointer%) = CurrentCell '                                        push our current location into the LIFO stack
  337.         Pointer% = Pointer% + 1 '                                              increment the stack pointer
  338.         VisitedCells% = VisitedCells% + 1 '                                    increment the number of cells visited so far
  339.         Forward% = -1 '                                                        we are moving forward in the maze
  340.         OPENDOORS CurrentCell.Xpos, CurrentCell.Ypos, RandomDir% '             open current and adjacent cell doors
  341.         SELECT CASE RandomDir% '                                               which direction did we move forward?
  342.             CASE 0 '                                                           up/north
  343.                 CurrentCell.Ypos = CurrentCell.Ypos - 1 '                      make this the new current cell
  344.             CASE 1 '                                                           right/east
  345.                 CurrentCell.Xpos = CurrentCell.Xpos + 1 '                      make this the new current cell
  346.             CASE 2 '                                                           down/south
  347.                 CurrentCell.Ypos = CurrentCell.Ypos + 1 '                      make this the new current cell
  348.             CASE 3 '                                                           left/west
  349.                 CurrentCell.Xpos = CurrentCell.Xpos - 1 '                      make this the new current cell
  350.         END SELECT
  351.     ELSE '                                                                     no, all adjacent cells had at least one door open
  352.         IF Forward% THEN '                                                     were we previously moving forward in the maze?
  353.             Forward% = 0 '                                                     yes, we are not any longer
  354.             IF Maze.Looping = 1 THEN '                                         should this be a looping maze?
  355.                 'IF INT(RND(1) * 2) = 1 THEN '                                 yes, flip a coin to see if a loop structure built here
  356.                 Remove% = 0 '                                                  assume no doors will be opened
  357.                 SELECT CASE RandomDir% '                                       which direction were we traveling in?
  358.                     CASE 0 '                                                   up/north
  359.                         IF CurrentCell.Ypos <> 1 THEN Remove% = -1 '           if not at top of maze upper door can be opened
  360.                     CASE 1 '                                                   right/east
  361.                         IF CurrentCell.Xpos <> Maze.Hcell THEN Remove% = -1 '  if not at right side of maze right door can be opened
  362.                     CASE 2 '                                                   down/south
  363.                         IF CurrentCell.Ypos <> Maze.Vcell THEN Remove% = -1 '  if not at bottom of maze bottom door can be opened
  364.                     CASE 3 '                                                   left/west
  365.                         IF CurrentCell.Xpos <> 1 THEN Remove% = -1 '           if not at left side of maze left door can be opened
  366.                 END SELECT
  367.                 IF Remove% THEN '                                              is it ok to open a door?
  368.                     OPENDOORS CurrentCell.Xpos, CurrentCell.Ypos, RandomDir% ' yes, open current and adjacent cell doors
  369.                 END IF
  370.                 'END IF
  371.             END IF
  372.         END IF
  373.         Pointer% = Pointer% - 1 '                                              decrement the stack pointer
  374.         CurrentCell = Stack(Pointer%) '                                        pop the previous cell position from the LIFO stack
  375.     END IF
  376. REDIM Stack(0) AS STACK '                                                      clear the memory used by the stack
  377.  
  378.  
  379. '----------------------------------------------------------------------------------------------------------------------
  380.  
  381. SUB OPENDOORS (x%, y%, Direction%)
  382.  
  383. '**
  384. '** Opens a door in the current cell and the corresponding door in the adjacent cell
  385. '**
  386.  
  387. SHARED Cell() AS CELL '   we need access to the cell array
  388.  
  389. SELECT CASE Direction% '                                                       which direction are we going?
  390.     CASE 0 '                                                                   up/north
  391.         Cell(x%, y%).Doors = Cell(x%, y%).Doors + 1 '                          open the current cell's up door
  392.         Cell(x%, y% - 1).Doors = Cell(x%, y% - 1).Doors + 4 '                  open the adjacent cell's south door
  393.     CASE 1 '                                                                   right/east
  394.         Cell(x%, y%).Doors = Cell(x%, y%).Doors + 2 '                          open the current cell's right door
  395.         Cell(x% + 1, y%).Doors = Cell(x% + 1, y%).Doors + 8 '                  open the adjacent cell's left door
  396.     CASE 2 '                                                                   down/south
  397.         Cell(x%, y%).Doors = Cell(x%, y%).Doors + 4 '                          open the current cell's down door
  398.         Cell(x%, y% + 1).Doors = Cell(x%, y% + 1).Doors + 1 '                  open the adjacent cell's up door
  399.     CASE 3 '                                                                   left/west
  400.         Cell(x%, y%).Doors = Cell(x%, y%).Doors + 8 '                          open this cell's left door
  401.         Cell(x% - 1, y%).Doors = Cell(x% - 1, y%).Doors + 2 '                  open the adjacent cell's right door
  402.  
  403.  
  404. '----------------------------------------------------------------------------------------------------------------------
  405.  
  406. SUB DRAWCELL (x%, y%, n%)
  407.  
  408. '**
  409. '** Draws a maze cell
  410. '**
  411. '** The drawing routines below can produce either a square maze (0) or a round maze (1) as defined by Maze.MazeType
  412. '** These drawing routines are included to act as an example of how you can create custom maze drawing routines for
  413. '** the mazes you generate and use in your programs.
  414. '**
  415.  
  416. SHARED Maze AS MAZE '        we need access to the maze properties
  417.  
  418. DIM xc% '                    center X coordinate of cell
  419. DIM yc% '                    center Y coordiante of cell
  420. DIM Radius! '                radius of various maze corners
  421.  
  422. xc% = x% + Maze.CellSize \ 2 '                                                 calculate center X coordinate of cell
  423. yc% = y% + Maze.CellSize \ 2 '                                                 calculate center Y coordinate of cell
  424.  
  425. SELECT CASE n% '                                                               which cell to draw?
  426.     CASE 1 '                                                                   ** up door open
  427.         IF Maze.MazeType = 0 THEN
  428.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  429.                 CIRCLES x% - 1, y% + Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  430.                 CIRCLES x% + Maze.CellSize, y% + Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  431.             NEXT Radius!
  432.             LINE (x% - 1, y% + Maze.CellSize)-(x% - Maze.Thickness, yc% - Maze.CellSize), Maze.Colour, BF
  433.             LINE (x% + Maze.CellSize, y% + Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% - Maze.CellSize), Maze.Colour, BF
  434.             LINE (x% - 1, y% + Maze.CellSize)-(x% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  435.         ELSE '                                                                 ** round maze
  436.             FOR Radius! = Maze.CellSize / 2 + 1 TO Maze.CellSize / 2 + Maze.Thickness STEP .5
  437.                 CIRCLES xc%, yc%, Radius!, Maze.Colour, PI, 0, 0
  438.             NEXT Radius!
  439.             LINE (x% - 1, yc%)-(x% - Maze.Thickness, yc% - Maze.CellSize), Maze.Colour, BF
  440.             LINE (x% + Maze.CellSize, yc%)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% - Maze.CellSize), Maze.Colour, BF
  441.         END IF
  442.     CASE 2 '                                                                   ** right door open
  443.         IF Maze.MazeType = 0 THEN
  444.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  445.                 CIRCLES x% - 1, y% - 1, Radius!, Maze.Colour, PIUP, PI, 0
  446.                 CIRCLES x% - 1, y% + Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  447.             NEXT Radius!
  448.             LINE (x% - 1, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  449.             LINE (x% - 1, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  450.             LINE (x% - 1, y% - 1)-(x% - Maze.Thickness, y% + Maze.CellSize), Maze.Colour, BF
  451.         ELSE
  452.             FOR Radius! = Maze.CellSize / 2 + 1 TO Maze.CellSize / 2 + Maze.Thickness STEP .5
  453.                 CIRCLES xc%, yc%, Radius!, Maze.Colour, PIUP, PIDOWN, 0
  454.             NEXT Radius!
  455.             LINE (xc%, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  456.             LINE (xc%, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  457.         END IF
  458.     CASE 3 '                                                                   ** up and right doors open
  459.         IF Maze.MazeType = 0 THEN
  460.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  461.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness, Radius!, Maze.Colour, PI, PIDOWN, 0
  462.                 CIRCLES x% - 1, y% + Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  463.             NEXT Radius!
  464.             LINE (x% - 1, y% + Maze.CellSize)-(x% - Maze.Thickness, yc% - Maze.CellSize), Maze.Colour, BF
  465.             LINE (x% - 1, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  466.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  467.             LINE (x% + Maze.CellSize + Maze.Thickness - 1, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  468.         ELSE
  469.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  470.                 CIRCLES xc% + Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  471.             NEXT Radius!
  472.             FOR Radius! = Maze.CellSize * 1.5 + Maze.Thickness TO Maze.CellSize * 1.5 + 1 STEP -.5
  473.                 CIRCLES xc% + Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  474.             NEXT Radius!
  475.         END IF
  476.     CASE 4 '                                                                   ** down door open
  477.         IF Maze.MazeType = 0 THEN
  478.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  479.                 CIRCLES x% - 1, y% - 1, Radius!, Maze.Colour, PIUP, PI, 0
  480.                 CIRCLES x% + Maze.CellSize, y% - 1, Radius!, Maze.Colour, 0, PIUP, 0
  481.             NEXT Radius!
  482.             LINE (x% - 1, y% - 1)-(x% - Maze.Thickness, yc% + Maze.CellSize), Maze.Colour, BF
  483.             LINE (x% + Maze.CellSize, y% - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  484.             LINE (x% - 1, y% - 1)-(x% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  485.         ELSE
  486.             FOR Radius! = Maze.CellSize / 2 + 1 TO Maze.CellSize / 2 + Maze.Thickness STEP .5
  487.                 CIRCLES xc%, yc%, Radius!, Maze.Colour, 0, PI, 0
  488.             NEXT Radius!
  489.             LINE (x% - 1, yc%)-(x% - Maze.Thickness, yc% + Maze.CellSize), Maze.Colour, BF
  490.             LINE (x% + Maze.CellSize, yc%)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  491.         END IF
  492.     CASE 5 '                                                                   **  up and down doors open
  493.         LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, yc% + Maze.CellSize), Maze.Colour, BF
  494.         LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  495.     CASE 6 '                                                                   ** down and right doors open
  496.         IF Maze.MazeType = 0 THEN
  497.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  498.                 CIRCLES x% - 1, y% - 1, Radius!, Maze.Colour, PIUP, PI, 0
  499.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, PIUP, PI, 0
  500.             NEXT Radius!
  501.             LINE (x% - 1, y% - 1)-(x% - Maze.Thickness, yc% + Maze.CellSize), Maze.Colour, BF
  502.             LINE (x% - 1, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  503.             LINE (x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  504.             LINE (x% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  505.         ELSE
  506.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  507.                 CIRCLES xc% + Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, PIUP, PI, 0
  508.             NEXT Radius!
  509.             FOR Radius! = Maze.CellSize * 1.5 + Maze.Thickness TO Maze.CellSize * 1.5 + 1 STEP -.5
  510.                 CIRCLES xc% + Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, PIUP, PI, 0
  511.             NEXT Radius!
  512.         END IF
  513.     CASE 7 '                                                                   ** up and down and right doors open
  514.         IF Maze.MazeType = 0 THEN
  515.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  516.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness, Radius!, Maze.Colour, PI, PIDOWN, 0
  517.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, PIUP, PI, 0
  518.             NEXT Radius!
  519.             LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, yc% + Maze.CellSize), Maze.Colour, BF
  520.             LINE (x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  521.             LINE (x% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  522.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  523.             LINE (xc% + Maze.CellSize, y% - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  524.         ELSE
  525.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  526.                 CIRCLES xc% + Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  527.                 CIRCLES xc% + Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, PIUP, PI, 0
  528.             NEXT Radius!
  529.             LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, yc% + Maze.CellSize), Maze.Colour, BF
  530.         END IF
  531.     CASE 8 '                                                                   ** left door open
  532.         IF Maze.MazeType = 0 THEN
  533.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  534.                 CIRCLES x% + Maze.CellSize, y% - 1, Radius!, Maze.Colour, 0, PIUP, 0
  535.                 CIRCLES x% + Maze.CellSize, y% + Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  536.             NEXT Radius!
  537.             LINE (xc% - Maze.CellSize, y% - 1)-(x% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  538.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(x% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  539.             LINE (x% + Maze.CellSize, y% - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize), Maze.Colour, BF
  540.         ELSE
  541.             FOR Radius! = Maze.CellSize / 2 + 1 TO Maze.CellSize / 2 + Maze.Thickness STEP .5
  542.                 CIRCLES xc%, yc%, Radius!, Maze.Colour, PIDOWN, PIUP, 0
  543.             NEXT Radius!
  544.             LINE (xc% - Maze.CellSize, y% - 1)-(xc%, y% - Maze.Thickness), Maze.Colour, BF
  545.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(xc%, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  546.         END IF
  547.     CASE 9 '                                                                   ** up and left doors open
  548.         IF Maze.MazeType = 0 THEN
  549.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  550.                 CIRCLES x% - Maze.Thickness, y% - Maze.Thickness, Radius!, Maze.Colour, PIDOWN, 0, 0
  551.                 CIRCLES x% + Maze.CellSize, y% + Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  552.             NEXT Radius!
  553.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(x% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  554.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize), Maze.Colour, BF
  555.             LINE (xc% - Maze.CellSize, y% - 1)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  556.             LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  557.         ELSE
  558.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  559.                 CIRCLES xc% - Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  560.             NEXT Radius!
  561.             FOR Radius! = Maze.CellSize * 1.5 + Maze.Thickness TO Maze.CellSize * 1.5 + 1 STEP -.5
  562.                 CIRCLES xc% - Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  563.             NEXT Radius!
  564.         END IF
  565.     CASE 10 '                                                                  ** left and right doors open
  566.         LINE (xc% - Maze.CellSize, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  567.         LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  568.     CASE 11 '                                                                  ** up and left and right doors open
  569.         IF Maze.MazeType = 0 THEN
  570.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  571.                 CIRCLES x% - Maze.Thickness, y% - Maze.Thickness, Radius!, Maze.Colour, PIDOWN, 0, 0
  572.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness, Radius!, Maze.Colour, PI, PIDOWN, 0
  573.             NEXT Radius!
  574.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  575.             LINE (xc% - Maze.CellSize, y% - 1)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  576.             LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  577.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  578.             LINE (xc% + Maze.CellSize, y% - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  579.         ELSE
  580.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  581.                 CIRCLES xc% - Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  582.                 CIRCLES xc% + Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  583.             NEXT Radius!
  584.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(xc% + Maze.CellSize, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  585.         END IF
  586.     CASE 12 '                                                                  ** down and left doors open
  587.         IF Maze.MazeType = 0 THEN
  588.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  589.                 CIRCLES x% + Maze.CellSize, y% - 1, Radius!, Maze.Colour, 0, PIUP, 0
  590.                 CIRCLES x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, 0, PIUP, 0
  591.             NEXT Radius!
  592.             LINE (xc% - Maze.CellSize, y% - 1)-(x% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  593.             LINE (x% + Maze.CellSize, y% - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  594.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  595.             LINE (x% - 1, yc% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  596.         ELSE
  597.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  598.                 CIRCLES xc% - Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, 0, PIUP, 0
  599.             NEXT Radius!
  600.             FOR Radius! = Maze.CellSize * 1.5 + Maze.Thickness TO Maze.CellSize * 1.5 + 1 STEP -.5
  601.                 CIRCLES xc% - Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, 0, PIUP, 0
  602.             NEXT Radius!
  603.         END IF
  604.     CASE 13 '                                                                  ** up and down and left doors open
  605.         IF Maze.MazeType = 0 THEN
  606.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  607.                 CIRCLES x% - Maze.Thickness, y% - Maze.Thickness, Radius!, Maze.Colour, PIDOWN, 0, 0
  608.                 CIRCLES x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, 0, PIUP, 0
  609.             NEXT Radius!
  610.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  611.             LINE (xc% - Maze.CellSize, y% - 1)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  612.             LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  613.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  614.             LINE (x% - 1, yc% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  615.         ELSE
  616.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  617.                 CIRCLES xc% - Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, 0, PIUP, 0
  618.                 CIRCLES xc% - Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  619.             NEXT Radius!
  620.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, yc% + Maze.CellSize), Maze.Colour, BF
  621.         END IF
  622.     CASE 14 '                                                                  ** down and left and right doors open
  623.         IF Maze.MazeType = 0 THEN
  624.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  625.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, PIUP, PI, 0
  626.                 CIRCLES x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, 0, PIUP, 0
  627.             NEXT Radius!
  628.             LINE (xc% - Maze.CellSize, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  629.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  630.             LINE (xc% + Maze.CellSize, y% + Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  631.             LINE (x% - 1, yc% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  632.             LINE (x% + Maze.CellSize, yc% + Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  633.         ELSE
  634.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  635.                 CIRCLES xc% - Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, 0, PIUP, 0
  636.                 CIRCLES xc% + Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, PIUP, PI, 0
  637.             NEXT Radius!
  638.             LINE (xc% - Maze.CellSize, y% - 1)-(xc% + Maze.CellSize, y% - Maze.Thickness), Maze.Colour, BF
  639.         END IF
  640.     CASE 15 '                                                                  ** all doors open
  641.         IF Maze.MazeType = 0 THEN
  642.             FOR Radius! = 1 TO Maze.Thickness - .5 STEP .5
  643.                 CIRCLES x% - Maze.Thickness, y% - Maze.Thickness, Radius!, Maze.Colour, PIDOWN, 0, 0
  644.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness, Radius!, Maze.Colour, PI, PIDOWN, 0
  645.                 CIRCLES x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, PIUP, PI, 0
  646.                 CIRCLES x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1, Radius!, Maze.Colour, 0, PIUP, 0
  647.             NEXT Radius!
  648.             LINE (xc% - Maze.CellSize, y% - 1)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  649.             LINE (x% - 1, yc% - Maze.CellSize)-(x% - Maze.Thickness, y% - Maze.Thickness), Maze.Colour, BF
  650.             LINE (x% + Maze.CellSize, yc% - Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  651.             LINE (xc% + Maze.CellSize, y% - 1)-(x% + Maze.CellSize + Maze.Thickness - 1, y% - Maze.Thickness), Maze.Colour, BF
  652.             LINE (xc% - Maze.CellSize, y% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  653.             LINE (xc% + Maze.CellSize, y% + Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  654.             LINE (x% - 1, yc% + Maze.CellSize)-(x% - Maze.Thickness, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  655.             LINE (x% + Maze.CellSize, yc% + Maze.CellSize)-(x% + Maze.CellSize + Maze.Thickness - 1, y% + Maze.CellSize + Maze.Thickness - 1), Maze.Colour, BF
  656.         ELSE
  657.             FOR Radius! = Maze.CellSize / 2 - Maze.Thickness TO Maze.CellSize / 2 STEP .5
  658.                 CIRCLES xc% - Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PIDOWN, 0, 0
  659.                 CIRCLES xc% - Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, 0, PIUP, 0
  660.                 CIRCLES xc% + Maze.CellSize, yc% - Maze.CellSize, Radius!, Maze.Colour, PI, PIDOWN, 0
  661.                 CIRCLES xc% + Maze.CellSize, yc% + Maze.CellSize, Radius!, Maze.Colour, PIUP, PI, 0
  662.             NEXT Radius!
  663.         END IF
  664.  
  665.  
  666. '----------------------------------------------------------------------------------------------------------------------
  667.  
  668. SUB CIRCLES (cx%, cy%, r!, c~&, s!, e!, a!)
  669.  
  670. '**
  671. '** QB64 temporary replacement CIRCLE command.
  672. '**
  673. '** The CIRCLE command in QB64 has a few bugs listed below:
  674. '**
  675. '** - radian end points are not calculate properly when creating arcs
  676. '** - center line to radian end points do not close properly due to previous bug listed
  677. '**
  678. '** This circle command replacement works very similiarly to the native CIRCLE command:
  679. '**
  680. '** SYNTAX: CIRCLES x%, y%, radius!, color~&, start_radian!, end_radian!, aspect_ratio!
  681. '**
  682. '**   x%            - center X coordinate of circle
  683. '**   y%            - center Y coordinate of circle
  684. '**   radius!       - the radius of the circle
  685. '**   color~&       - the circle's color
  686. '**   start_radian! - the radian on circle curcunference to begin drawing at
  687. '**   end_radian!   - the radian on circle circumference to end drawing at
  688. '**   aspect_ratio! - the aspect ratio of the circle
  689. '**
  690. '** NOTE: unlike the native CIRCLE command, all arguments MUST be supplied. For example,
  691. '**       with the native command this will draw a perfect circle with the default color,
  692. '**       start radian, end radian and aspect ratio:
  693. '**
  694. '**       CIRCLE (319, 239), 100
  695. '**
  696. '**       To do the same thing with this replacement command you must supply everything:
  697. '**
  698. '**       CIRCLES 319, 239, 100, _RGB32(255, 255, 255), 0, 0, 0
  699. '**
  700. '** ACKNOWLEGEMENTS: The FOR/NEXT step formula was was written by Codeguy for Unseen
  701. '**                  Machine's Visual library EllipseXS command. Specifically:
  702. '**                         MinStep! = 1 / (2 * PI535 * Radius!)
  703. '**            NOTE: The FOR/NEXT loop was replaced with a DO/LOOP by SMcNeill - 02/02/-13
  704. '**
  705. '** Includes performance tweaks made by SMcNeill on 02/02/13 - specifically removing a few redundant * -1
  706. '** statements and converting the FOR/NEXT loop to a DO loop for a ~3% increase in performance.
  707. '**
  708. '** Corrected bug in which variables being passed in were being modified and passed back - 02/02/13
  709. '**
  710.  
  711. DIM s%, e%, nx%, ny%, xr!, yr!, st!, en!, asp! '     local variables used
  712.  
  713. st! = s! '                                           copy start radian to local variable
  714. en! = e! '                                           copy end radian to local variable
  715. asp! = a! '                                          copy aspect ratio to local variable
  716. IF asp! <= 0 THEN asp! = 1 '                         keep aspect ratio between 0 and 4
  717. IF asp! > 4 THEN asp! = 4
  718. IF asp! < 1 THEN xr! = r! * asp! * 4 ELSE xr! = r! ' calculate x/y radius based on aspect ratio
  719. IF asp! > 1 THEN yr! = r! * asp! ELSE yr! = r!
  720. IF st! < 0 THEN s% = -1: st! = -st! '                remember if line needs drawn from center to start radian
  721. IF en! < 0 THEN e% = -1: en! = -en! '                remember if line needs drawn from center to end radian
  722. IF s% THEN '                                         draw line from center to start radian?
  723.     nx% = cx% + xr! * COS(st!) '                     yes, compute starting point on circle's circumference
  724.     ny% = cy% + yr! * -SIN(st!)
  725.     LINE (cx%, cy%)-(nx%, ny%), c~& '                draw line from center to radian
  726. IF en! <= st! THEN en! = en! + 6.2831852 '           come back around to proper location (draw counterclockwise)
  727. stepp! = 0.159154945806 / r!
  728. c! = st! '                                           cycle from start radian to end radian
  729.     nx% = cx% + xr! * COS(c!) '                      compute next point on circle's circumfrerence
  730.     ny% = cy% + yr! * -SIN(c!)
  731.     PSET (nx%, ny%), c~& '                           draw the point
  732.     c! = c! + stepp!
  733. LOOP UNTIL c! >= en!
  734. IF e% THEN LINE -(cx%, cy%), c~& '                   draw line from center to end radian if needed
  735.  
  736.  
  737. '----------------------------------------------------------------------------------------------------------------------
  738.  
In order to understand recursion, one must first understand recursion.