Skip to content

Instantly share code, notes, and snippets.

@Yepoleb
Created July 12, 2021 16:27
Show Gist options
  • Save Yepoleb/7e253d1a5ba6f37c68806c1e97e2db6e to your computer and use it in GitHub Desktop.
Save Yepoleb/7e253d1a5ba6f37c68806c1e97e2db6e to your computer and use it in GitHub Desktop.
Labyrinth generator in nim
import random
type
TileType = enum
wall, path
Direction = enum
north, east, south, west
Playfield = object
height: int
width: int
grid: seq[TileType]
Vec2 = object
y: int
x: int
const wallTile = "█"
const dieChance = 0.1
proc newVec2(y, x: int): Vec2 = Vec2(y: y, x: x)
proc `+`(a, b: Vec2): Vec2 = Vec2(y: a.y + b.y, x: a.x + b.x)
proc `+=`(a: var Vec2, b: Vec2) =
a.y += b.y
a.x += b.x
proc directionVector(dir: Direction): Vec2 =
case dir
of north: result = Vec2(y: -1, x: 0)
of east: result = Vec2(y: 0, x: 1)
of south: result = Vec2(y: 1, x: 0)
of west: result = Vec2(y: 0, x: -1)
proc stepVector(dir: Direction): Vec2 =
case dir
of north: result = Vec2(y: -2, x: 0)
of east: result = Vec2(y: 0, x: 2)
of south: result = Vec2(y: 2, x: 0)
of west: result = Vec2(y: 0, x: -2)
proc createPlayfield(height: int, width: int): Playfield =
result.height = height
result.width = width
result.grid = newSeq[TileType](width * height)
proc isOutOfBorders(pf: Playfield, pos: Vec2): bool =
pos.x < 0 or pos.y < 0 or pos.x >= pf.width or pos.y >= pf.height
proc at(pf: var Playfield, pos: Vec2): var TileType =
assert not pf.isOutOfBorders(pos)
let seqPosition = pf.width * pos.y + pos.x
result = pf.grid[seqPosition]
proc at(pf: Playfield, pos: Vec2): TileType =
assert not pf.isOutOfBorders(pos)
let seqPosition = pf.width * pos.y + pos.x
result = pf.grid[seqPosition]
proc `$`(pf: Playfield): string =
# Top border
for x in 0 ..< pf.width + 2:
result.add(wallTile)
result.add('\n')
for y in 0 ..< pf.height:
# Left border
result.add(wallTile)
for x in 0 ..< pf.width:
if pf.at(newVec2(y, x)) == wall:
result.add(wallTile)
else:
result.add(' ')
# Right border
result.add(wallTile)
result.add('\n')
# Bottom border
for x in 0 ..< pf.width + 2:
result.add(wallTile)
proc randomPos(pf: Playfield): Vec2 =
result.y = random.rand((pf.height - 1) div 2) * 2
result.x = random.rand((pf.width - 1) div 2) * 2
proc randomPath(pf: Playfield): Vec2 =
while true:
let test_pos = pf.randomPos()
if pf.at(test_pos) == path:
return test_pos
proc randomWall(pf: Playfield): Vec2 =
while true:
let test_pos = pf.randomPos()
if pf.at(test_pos) == wall:
return test_pos
proc isOccupiable(pf: Playfield, pos: Vec2): bool =
if pf.isOutOfBorders(pos):
return false
result = pf.at(pos) == wall
proc isPath(pf: Playfield, pos: Vec2): bool =
if pf.isOutOfBorders(pos):
return false
result = pf.at(pos) == path
proc moveFrom(pf: var Playfield, pos: Vec2, dir: Direction): Vec2 =
var curPos = pos
let dirVec = directionVector(dir)
pf.at(pos) = path
for step in 0 ..< 2:
curPos += dirVec
pf.at(curPos) = path
result = curPos
proc hasEmptyTiles(pf: Playfield): bool =
for y in countup(0, pf.height - 1, 2):
for x in countup(0, pf.width - 1, 2):
if pf.at(newVec2(y, x)) == wall:
return true
return false
random.randomize()
var playfield = createPlayfield(25, 55)
let startPos = playfield.randomPos()
let endPos = playfield.randomPos()
playfield.at(startPos) = path
proc genPath(pf: var Playfield, pathStart: Vec2) =
var curPos = pathStart
while true:
var possibleDir: set[Direction] = {}
for dir in Direction.low..Direction.high:
let stepVec = stepVector(dir)
if playfield.isOccupiable(curPos + stepVec):
possibleDir.incl(dir)
if len(possibleDir) == 0:
break
let moveDir = random.sample(possibleDir)
curPos = playfield.moveFrom(curPos, moveDir)
let dieValue = random.rand(1.0)
if dieValue < dieChance:
break
proc alg1(pf: var Playfield) =
while playfield.hasEmptyTiles():
let pathStart = playfield.randomPath()
playfield.genPath(pathStart)
proc genStep(pf: var Playfield, pathStart: Vec2) =
var possibleDir: set[Direction] = {}
for dir in Direction.low..Direction.high:
let stepVec = stepVector(dir)
if playfield.isPath(pathStart + stepVec):
possibleDir.incl(dir)
if len(possibleDir) == 0:
return
let moveDir = random.sample(possibleDir)
discard playfield.moveFrom(pathStart, moveDir)
proc alg2(pf: var Playfield) =
while playfield.hasEmptyTiles():
let pathStart = playfield.randomWall()
playfield.genStep(pathStart)
alg1(playfield)
echo playfield
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment