Created
July 12, 2021 16:27
-
-
Save Yepoleb/7e253d1a5ba6f37c68806c1e97e2db6e to your computer and use it in GitHub Desktop.
Labyrinth generator in nim
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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