Levelgen example: Mazeracer

From Bitfighter
Revision as of 00:33, 29 October 2010 by Watusimoto (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

See other examples in the Levelgen gallery.

The full level and levelgen code are included with the Bitfighter distribution.

To use the following script, copy it into a file called "mazegen.lua" in your levels folder, and add the following line to your level file:

Invalid language.

You need to specify a language like this: <source lang="html4strict">...</source>

Supported languages for syntax highlighting:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


Script mazegen.lua

or, to create a smaller maze:

Invalid language.

You need to specify a language like this: <source lang="html4strict">...</source>

Supported languages for syntax highlighting:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


Script mazegen.lua 5 5 0 0 1


-------------------------------------------------------------------------------
--
-- MazeGen script creates a random maze
-- Args: gridXSize, gridYSize, ulCornerX, ulCornerY, cellsize
--
--    gridXSize, gridYSize --> Number of cells in the maze (defaults to 10, 10)
--    ulCornerX, ulCornerY --> Coordinates of upper left corner of maze
--                        (defaults to 0, 0)
--    cellsize --> Size of each maze cell (defaults to 1)
--
-------------------------------------------------------------------------------
 
 
function isUnvisited(cell)
  return cell["N Wall"] and cell["S Wall"] and cell["E Wall"] and cell["W Wall"]
end
 
 
function getAdjacentUnvisitedCells(cell)
 
  local x = cell["x"]
  local y = cell["y"]
 
  adjCells = {}
  adjDirs = {}
 
  if(not cells[x][y]["N Edge"] and isUnvisited(cells[x][y - 1])) then
     table.insert(adjCells, cells[x][y - 1])
     table.insert(adjDirs, "N")
  end
 
  if(not cells[x][y]["S Edge"] and isUnvisited(cells[x][y + 1])) then
     table.insert(adjCells, cells[x][y + 1])
     table.insert(adjDirs, "S")
  end
 
  if(not cells[x][y]["E Edge"] and isUnvisited(cells[x + 1][y])) then
     table.insert(adjCells, cells[x + 1][y])
     table.insert(adjDirs, "E")
  end
 
  if(not cells[x][y]["W Edge"] and isUnvisited(cells[x - 1][y])) then
     table.insert(adjCells, cells[x - 1][y])
     table.insert(adjDirs, "W")
  end
end
 
 
-- Knock down wall to neighbor cell, and neighbor's wall to current cell
function knockDownWallToCell(cell, dir, adjacentCell)
  cell[dir .. " Wall"] = false
  adjacentCell[oppDir(dir) .. " Wall"] = false
end
 
 
function oppDir(dir)
  if(dir == "N") then return "S" end
  if(dir == "S") then return "N" end
  if(dir == "E") then return "W" end
  if(dir == "W") then return "E" end
end
 
 
function initGridCells(xsize, ysize)
  for x = 0, xsize + 1 do    -- Add extra cell to avoid bounds checking later
     cells[x] = {}
     for y = 0, ysize + 1 do
        cells[x][y] = {}
 
        cells[x][y]["N Wall"] = true
        cells[x][y]["S Wall"] = true
        cells[x][y]["E Wall"] = true
        cells[x][y]["W Wall"] = true
 
        cells[x][y]["x"] = x
        cells[x][y]["y"] = y
 
        cells[x][y]["N Edge"] = (y == 1)
        cells[x][y]["S Edge"] = (y == gridYSize)
        cells[x][y]["E Edge"] = (x == gridXSize)
        cells[x][y]["W Edge"] = (x == 1)
     end
  end
end
 
 
function outputMaze()
 
   -- We'll output our maze as a series of tiles, each being 2 x cellsize square
   -- Divide each tile into quadrants, as follows:
   --           X----+----+
   --           | UL | UR |    * denotes point (cenx, ceny)
   --           |    |    |    size of each cell is (gridXSize, gridXSize)
   --           +----*----+    coords of point X on upper left cell are
   --           | LL | LR |       (ulCornerX, ulCornerY)
   --           |    |    |    UL is always filled in, LR is always open
   --           +----+----+    LL is filled in when an E wall is present
   --                          UR is filled in when a N wall is present
   --  When a series of these tiles are tiled together, a maze will be drawn
   --  with an open bottom and open right side, which will be closed at the end.
   --
   --  Wall segments are drawn as 2-pt horizontal lines with the appropriate
   --  thickness to make them square.  Adjacent wall segments are partially
   --  merged into longer horizontal lines to reduce vertices and therefore
   --  level transfer size.  Note we're taking advantage of Bitfighter's vertex
   --  thinning algorithms to make our job just a bit easier.
 
   local gridsize = levelgen:getGridSize() + 1 -- Add 1 to get a tiny bit of
                                               -- overlap to make walls merge
   local wallsize = gridsize * cellsize
   local points
 
   for y = 1, gridYSize do
 
      local xpoints = { }
 
      for x = 1, gridXSize do
         local cenx = ulCornerX + (2 * x - 1) * cellsize
         local ceny = ulCornerY + (2 * y - 1) * cellsize
         local cell = cells[x][y]
 
 
         -- UL quadrant is always drawn
         if(#xpoints == 0) then        -- No line in progress
            xpoints = { Point(cenx - cellsize, ceny - cellsize * 0.5) }
         end
 
         -- UR quadrant drawn only when a N wall is present
       local endx
 
       if(cell["N Wall"]) then
         endx = cenx + cellsize
       else
         endx = cenx
         end
 
 
       if(not cell["N Wall"] or cell["E Edge"]) then
             table.insert(xpoints, Point(endx, ceny - cellsize * 0.5) )
 
          levelgen:addWall(wallsize, false, xpoints)
          xpoints = { }
       end
 
         -- LL quadrant drawn only when a W wall is present
         if(cell["W Wall"]) then
            local pts = { Point(cenx - cellsize, ceny + cellsize * 0.5),
                          Point(cenx            , ceny + cellsize * 0.5) }
            levelgen:addWall(wallsize, false, pts)
         end
 
         -- LR quadrant is always open, so do nothing
      end
 
   end
 
   -- Now add walls along entire bottom and right sides of the maze
   local pts = { Point(ulCornerX,
                     ulCornerY + 2 * gridYSize * cellsize + cellsize * 0.5),
                 Point(ulCornerX + 2 * gridXSize * cellsize + cellsize * 0.5,
                       ulCornerY + 2 * gridYSize * cellsize + cellsize * 0.5),
                 Point(ulCornerX + 2 * gridXSize * cellsize + cellsize * 0.5,
                       ulCornerY) }
   levelgen:addWall(wallsize, false, pts)
end
 
 
 
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
 
 
-- Global variables
 
-- Remember, in Bitfighter, all global variables must be
-- defined in the main block!
 
gridXSize = tonumber(arg[1])
gridYSize = tonumber(arg[2])
ulCornerX = tonumber(arg[3])
ulCornerY = tonumber(arg[4])
cellsize = tonumber(arg[5])
 
if gridXSize == nil then gridXSize = 10 end
if gridYSize == nil then gridYSize = 10 end
if ulCornerX == nil then ulCornerX = 0 end
if ulCornerY == nil then ulCornerY = 0 end
if cellsize == nil then cellsize = 1 end
 
 
cellStack = List:new()
 
cells = {}
adjCells = {}
adjDirs = {}
 
-- Initialize cells
initGridCells(gridXSize, gridYSize)
 
 
-- Get starting cell
local x = math.random(gridXSize)
local y = math.random(gridYSize)
 
local currCell = cells[x][y]
local totalCells = gridXSize * gridYSize
 
local visitedCells = 1
 
while visitedCells < totalCells do
 
  -- Find all adjacent cells with all walls intact (sets adjCells and adjDirs)
  getAdjacentUnvisitedCells(currCell)
  if(#adjCells > 0) then                   -- More than one?
     local adj = math.random(#adjCells)    -- Pick one at random
          knockDownWallToCell(currCell, adjDirs[adj], adjCells[adj])
 
          -- Push current cell location on the cellStack; we'll revisit it later
          cellStack:pushright(currCell)
 
          -- Make the randomly selected adjacent cell the new current cell
          currCell = adjCells[adj]
 
     visitedCells = visitedCells + 1
  else
     -- This cell has no neighbors, so let's grab one from the stack
     currCell = cellStack:popright()
  end
end
 
-- Output the results
outputMaze()