Generate a maze and display it with the raycaster
This commit is contained in:
parent
a5c2a4c825
commit
d9f69ecbd6
3 changed files with 238 additions and 40 deletions
170
mapper.fnl
170
mapper.fnl
|
@ -3,27 +3,105 @@
|
|||
; grid. Each cell has a "cell-size" of walls ("wall-width") and hallways
|
||||
; ("hall-width").
|
||||
(var cell-num 10)
|
||||
(var (hall-width wall-width) (values 2 1))
|
||||
(var (hall-width wall-width) (values 1 1))
|
||||
(var cell-size (+ hall-width wall-width))
|
||||
|
||||
; Initialize the cell table
|
||||
(var cells {})
|
||||
(for [c 1 (* cell-num cell-num)]
|
||||
(var cell [])
|
||||
(for [i 1 cell-size]
|
||||
(for [j 1 cell-size]
|
||||
(var cell-index (+ j (* (- i 1) cell-size)))
|
||||
(if (and (< i (+ hall-width 1)) (< j (+ hall-width 1)))
|
||||
(tset cell cell-index 0)
|
||||
(tset cell cell-index 1))))
|
||||
(table.insert cells cell))
|
||||
|
||||
(fn cell-row [i] (+ 1 (// (- i 1) cell-num)))
|
||||
; Helper functions for dealing with the cell list
|
||||
(fn cell-row [i] (+ 1 (math.floor (/ (- i 1) cell-num))))
|
||||
(fn map-index [i]
|
||||
(var cell-col (if (= 0 (% i cell-num)) cell-num (% i cell-num)))
|
||||
(+ (* cell-size cell-size cell-num (- (cell-row i) 1))
|
||||
(- (* cell-col cell-size) (- cell-size 1))))
|
||||
|
||||
; Meta-Cells is the data before the digits
|
||||
(var spawn-cell (math.random 1 cell-num))
|
||||
(var leave-cell (math.random (+ (* cell-num (- cell-num 1)) 1) (* cell-num cell-num)))
|
||||
(var spawn-spot
|
||||
{:x cell-size
|
||||
:y (+ (- (* spawn-cell cell-size) (- cell-size 1)) (* 2 hall-width))})
|
||||
(fn default-meta-cells []
|
||||
(var meta-cells {})
|
||||
(for [c 1 (* cell-num cell-num)]
|
||||
(table.insert meta-cells {:e false :s false :v false}))
|
||||
(tset meta-cells leave-cell :s true)
|
||||
meta-cells)
|
||||
|
||||
; Cells is the 0's and 1's
|
||||
(fn convert-cells [meta-cells leave-cell]
|
||||
(var cells {})
|
||||
(each [k v (ipairs meta-cells)]
|
||||
(var cell [])
|
||||
(var row-limit (if (. v :e) (+ hall-width wall-width) hall-width))
|
||||
(var col-limit (if (. v :s) (+ hall-width wall-width) hall-width))
|
||||
(for [i 1 cell-size]
|
||||
(for [j 1 cell-size]
|
||||
(var cell-index (+ j (* (- i 1) cell-size)))
|
||||
(if (and (< i (+ col-limit 1)) (< j (+ row-limit 1)))
|
||||
(tset cell cell-index 0)
|
||||
(tset cell cell-index 1))
|
||||
(if (and (> i hall-width) (> j hall-width))
|
||||
(tset cell cell-index 1))
|
||||
(if (= k leave-cell)
|
||||
(tset cell cell-index 0))))
|
||||
(table.insert cells cell))
|
||||
cells)
|
||||
|
||||
(fn generate-maze [meta-cells spawn-cell]
|
||||
; Maze Generation
|
||||
; 1. Push the starting cell to the stack
|
||||
; 2. Pop the top of the stack, mark that cell visited
|
||||
; 3. If there are any unvisited neighbors:
|
||||
; a. Push the current cell to the stack
|
||||
; b. Choose a random un-visited neighbor
|
||||
; c. Adjust walls accordingly
|
||||
; d. Push chosen neighbor to the stack
|
||||
(var cell-stack [spawn-cell])
|
||||
(while (> (length cell-stack) 0)
|
||||
(var current-cell (table.remove cell-stack))
|
||||
(var next-cells {})
|
||||
(tset meta-cells current-cell :v true)
|
||||
|
||||
; Check if any of the current cell's neighbors are viable
|
||||
; North -
|
||||
; if current-cell <= cell-num, then north is a map-side/barrier
|
||||
(when (> current-cell cell-num)
|
||||
(var n-cell (- current-cell cell-num))
|
||||
(if (not (. meta-cells n-cell :v)) (table.insert next-cells {:c n-cell :d 1})))
|
||||
|
||||
; South -
|
||||
; if current-cell > (- (* cell-num cell-num) cell-num),
|
||||
; then south is a map-side/barrier
|
||||
(when (< current-cell (- (* cell-num cell-num) cell-num))
|
||||
(var s-cell (+ current-cell cell-num))
|
||||
(if (not (. meta-cells s-cell :v)) (table.insert next-cells {:c s-cell :d 3})))
|
||||
|
||||
; East -
|
||||
; if current-cell % cell-num = 0, then east is a map-side/barrier
|
||||
(when (not (= 0 (% current-cell cell-num)))
|
||||
(var e-cell (+ current-cell 1))
|
||||
(if (not (. meta-cells e-cell :v)) (table.insert next-cells {:c e-cell :d 2})))
|
||||
|
||||
; West -
|
||||
; if current-cell % cell-num = 1, then west is a map-side/barrier
|
||||
(when (not (= 1 (% current-cell cell-num)))
|
||||
(var w-cell (- current-cell 1))
|
||||
(if (not (. meta-cells w-cell :v)) (table.insert next-cells {:c w-cell :d 4})))
|
||||
|
||||
; Randomly choose a viable neighbor, if possible
|
||||
(var chosen-next-cell {})
|
||||
(when (> (length next-cells) 0)
|
||||
(table.insert cell-stack current-cell)
|
||||
(set chosen-next-cell (. next-cells (math.random 1 (length next-cells))))
|
||||
; Adjust walls accordingly
|
||||
(case chosen-next-cell
|
||||
{:d 1} (tset meta-cells (. chosen-next-cell :c) :s true)
|
||||
{:d 2} (tset meta-cells current-cell :e true)
|
||||
{:d 3} (tset meta-cells current-cell :s true)
|
||||
{:d 4} (tset meta-cells (. chosen-next-cell :c) :e true))
|
||||
(table.insert cell-stack (. chosen-next-cell :c)))
|
||||
)
|
||||
meta-cells
|
||||
)
|
||||
|
||||
(fn generate_cell_map [cells]
|
||||
(var cell_map [])
|
||||
(for [c 1 (length cells)]
|
||||
|
@ -35,6 +113,48 @@
|
|||
(tset cell_map new-map-index (. cells c new-cell-index)))))
|
||||
cell_map)
|
||||
|
||||
(fn generate-north-spawn [spawn-cell]
|
||||
(var (map map-row) (values [] []))
|
||||
(for [i 1 cell-size]
|
||||
(set map-row [])
|
||||
(for [j 1 (- (* (+ 2 cell-num) cell-size) hall-width)] (table.insert map-row 1))
|
||||
(table.insert map map-row))
|
||||
(var spawn (+ (- (* spawn-cell cell-size) (- cell-size 1)) cell-size))
|
||||
(tset map cell-size spawn 0)
|
||||
(tset map cell-size (+ spawn 1) 2)
|
||||
(tset map cell-size (- spawn 1) 2)
|
||||
(tset map (- cell-size 1) spawn 3)
|
||||
map)
|
||||
|
||||
(fn generate-south-exit [cells leave-cell]
|
||||
(var (map map-row) (values [] []))
|
||||
(for [i 1 (- cell-size hall-width)]
|
||||
(set map-row [])
|
||||
(for [j 1 (- (* (+ 2 cell-num) cell-size) hall-width)] (table.insert map-row 1))
|
||||
(table.insert map map-row))
|
||||
(var escape (+ (* (- leave-cell (* cell-num (- cell-num 1))) cell-size) 1))
|
||||
(for [i 1 cell-size]
|
||||
(tset map 1 (+ escape (- i 1)) 3))
|
||||
map)
|
||||
|
||||
(fn generate_map [cells spawn-cell leave-cell]
|
||||
(var map [])
|
||||
; Generate the northern feature - spawn point
|
||||
(each [_ row (ipairs (generate-north-spawn spawn-cell))] (table.insert map row))
|
||||
; Pad the east and west sides of the map
|
||||
(for [c 1 (* cell-num cell-size)]
|
||||
(var map-row [])
|
||||
(for [i 1 cell-size] (table.insert map-row 1))
|
||||
(table.move cells
|
||||
(+ (* (- c 1) (* cell-num cell-size)) 1)
|
||||
(* c (* cell-num cell-size))
|
||||
(+ cell-size 1) map-row)
|
||||
(for [i 1 (- cell-size hall-width)] (table.insert map-row 1))
|
||||
(table.insert map map-row))
|
||||
; Generate the northern feature - spawn point
|
||||
(each [_ row (ipairs (generate-south-exit cells leave-cell))] (table.insert map row))
|
||||
map)
|
||||
|
||||
(fn print_cell_map [cells]
|
||||
(var output "")
|
||||
(for [i 1 (length cells)]
|
||||
|
@ -42,4 +162,22 @@
|
|||
(if (= 0 (% i (* cell-num cell-size))) (set output (.. output "\n"))))
|
||||
(print output))
|
||||
|
||||
(print_cell_map (generate_cell_map cells))
|
||||
(fn print_map [map]
|
||||
(for [i 1 (length map)]
|
||||
(var r "")
|
||||
(for [j 1 (length (. map i))]
|
||||
(set r (.. r " " (. map i j))))
|
||||
(print r)))
|
||||
|
||||
(fn generate []
|
||||
(var meta-maze (generate-maze (default-meta-cells) spawn-cell))
|
||||
(var data-maze (convert-cells meta-maze leave-cell))
|
||||
(var data-map (generate_cell_map data-maze))
|
||||
(var map (generate_map data-map spawn-cell leave-cell))
|
||||
(print_map map)
|
||||
(values map spawn-spot))
|
||||
|
||||
; (print (.. "SPAWN: " spawn-cell "(" (. spawn-spot :x) "," (. spawn-spot :y) ")"))
|
||||
; (print (.. "END: " leave-cell))
|
||||
; (print (.. "Escape: " (- leave-cell (* cell-num (- cell-num 1)))))
|
||||
{: generate}
|
||||
|
|
72
notes.md
72
notes.md
|
@ -118,10 +118,70 @@ Each cell is a combination of hallways and walls. For each cell, if the "row" or
|
|||
|
||||
```lisp
|
||||
(var cell [])
|
||||
(for [i 1 cell-size]
|
||||
(for [j 1 cell-size]
|
||||
(var cell-index (+ j (* (- i 1) cell-size)))
|
||||
(if (and (< i (+ hall-width 1)) (< j (+ hall-width 1)))
|
||||
(tset cell cell-index 0)
|
||||
(tset cell cell-index 1))))
|
||||
(each [k v (ipairs meta-cells)]
|
||||
(var row-limit (if (. v :e) (+ hall-width wall-width) hall-width))
|
||||
(var col-limit (if (. v :s) (+ hall-width wall-width) hall-width))
|
||||
(for [i 1 cell-size]
|
||||
(for [j 1 cell-size]
|
||||
(var cell-index (+ j (* (- i 1) cell-size)))
|
||||
(if (and (< i (+ col-limit 1)) (< j (+ row-limit 1)))
|
||||
(tset cell cell-index 0)
|
||||
(tset cell cell-index 1))
|
||||
(if (and (> i hall-width) (> j hall-width))
|
||||
(tset cell cell-index 1))))
|
||||
```
|
||||
|
||||
This becomes a bit more challenging with considering connections. It is likely a
|
||||
matter of modifying the conditional such that: when there is a horizontal
|
||||
connection, check for `i` to be less than the entire cell width (`hall-width` +
|
||||
`wall-width`); and then similar for `j` with vertical connections. However, this
|
||||
will then remove the corner wall when there are both vertical and horizontal
|
||||
connections. This could be solved by checking if both `i` and `j` are beyond
|
||||
`hall-width`, which would represent the always corner.
|
||||
|
||||
The above code should translate from a `meta-cells` list of meta-data to a
|
||||
`cells` list of 0's and 1's.
|
||||
|
||||
---
|
||||
With a bit of modification, the logic for the maze generation from above appears
|
||||
to work. There is one challenge which remains, which is updating the walls. A
|
||||
way around this is to make the data about the next cell more verbose: include
|
||||
not only the cell number, but also the direction. Then, use a case statement to
|
||||
update accordingly.
|
||||
|
||||
The last remaining tasks are to buffer the entire maze, such that there are two
|
||||
cells worth of walls around it; and to establish starting and ending squares.
|
||||
|
||||
Creating the buffer means adding two cells worth of walls to the north and west
|
||||
walls, and 1 cell worth of walls to the south and east walls. This should
|
||||
theoretically be very easy using `table.insert`, which automatically modifies
|
||||
all further table values. However, will need to be mindful when updating east
|
||||
and west. Updating north is just `(table.insert 1 1)` for `(* cell-num cell-size
|
||||
cell-size)` times twice. Similar for south, except `(table.insert 1)` for `(*
|
||||
cell-num cell-size cell-size)` once. East and west seem more challenging.
|
||||
Though, thinking about it for a moment, I could essentially migrate the
|
||||
generated array one "row" at a time into a new array, padding it on either side
|
||||
as I do so. I believe this may be the way. I can use `table.move` to accomplish
|
||||
this.
|
||||
|
||||
As part of the padding, I also need to translate the map from a single list into
|
||||
a list of lists. Again, this can be done using the `table.move` function and
|
||||
`table.insert`.
|
||||
|
||||
```lisp
|
||||
(var map [])
|
||||
(var map-row [])
|
||||
(table.move cell_map x y 1 map-row)
|
||||
(table.insert map map-row)
|
||||
```
|
||||
|
||||
---
|
||||
Remaining tasks:
|
||||
1. Finalize map generation:
|
||||
[X] Establish starting spot, and add the northern feature spawn point.
|
||||
[X] Establish ending spot, and add the southern feature finale point.
|
||||
[ ] Could do an eastern and western spot too
|
||||
[ ] Modify the map wall values to account for random wall heights.
|
||||
2. Draw floors
|
||||
3. Draw skyboxen
|
||||
4. Add monster mechanics
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
(local state (require :state))
|
||||
;(local mapper (require :mapper))
|
||||
(local mapper (require :mapper))
|
||||
(local overlay (require :overlay))
|
||||
(local pi math.pi)
|
||||
; ### Screen Size ###
|
||||
(var (screen-width screen-height) (love.window.getMode))
|
||||
|
||||
; ### Map Information ###
|
||||
; (var map (mapper.generate 15 15))
|
||||
(var map [[1 1 2 1 1 1 1 2 1 1 1 ]
|
||||
[1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
[1 0 0 0 0 0 0 0 0 0 2 ]
|
||||
[2 0 0 0 0 0 0 0 0 0 2 ]
|
||||
[1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
[1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
[1 0 0 0 0 0 0 0 3 0 1 ]
|
||||
[1 0 0 0 0 0 0 3 3 0 1 ]
|
||||
[1 1 1 1 1 1 0 0 0 0 1 ]
|
||||
[1 0 0 0 0 0 0 1 1 1 1 ]
|
||||
[1 0 1 1 1 1 1 1 1 1 1 ]
|
||||
[1 0 1 1 1 1 0 0 0 0 1 ]
|
||||
[1 0 1 1 1 1 1 1 1 0 1 ]
|
||||
[1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
[1 1 1 1 1 1 1 1 1 1 1 ]])
|
||||
(var (map spawn) (mapper.generate))
|
||||
; (var map [[1 1 2 1 1 1 1 2 1 1 1 ]
|
||||
; [1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
; [1 0 0 0 0 0 0 0 0 0 2 ]
|
||||
; [2 0 0 0 0 0 0 0 0 0 2 ]
|
||||
; [1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
; [1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
; [1 0 0 0 0 0 0 0 3 0 1 ]
|
||||
; [1 0 0 0 0 0 0 3 3 0 1 ]
|
||||
; [1 1 1 1 1 1 0 0 0 0 1 ]
|
||||
; [1 0 0 0 0 0 0 1 1 1 1 ]
|
||||
; [1 0 1 1 1 1 1 1 1 1 1 ]
|
||||
; [1 0 1 1 1 1 0 0 0 0 1 ]
|
||||
; [1 0 1 1 1 1 1 1 1 0 1 ]
|
||||
; [1 0 0 0 0 0 0 0 0 0 1 ]
|
||||
; [1 1 1 1 1 1 1 1 1 1 1 ]])
|
||||
|
||||
; ### Texture Information ###
|
||||
(var walls [])
|
||||
|
@ -46,7 +46,7 @@
|
|||
(table.insert skybox skyb))
|
||||
|
||||
; ### "Player" variables ###
|
||||
(var (posx posy) (values 4.0 4.0)) ; Initial map position
|
||||
(var (posx posy) (values (+ (. spawn :x) 0.5) (+ (. spawn :y) 0.5)))
|
||||
(var (dirx diry) (values -1.0 0.0)) ; Initial direction vector
|
||||
(var (planex planey) (values 0 0.66)) ; Camera plane
|
||||
|
||||
|
|
Loading…
Reference in a new issue