RosettaCodeData/Task/Maze-generation/AWK/maze-generation.awk

188 lines
4.8 KiB
Awk

#!/usr/bin/awk -f
# Remember: AWK is 1-based, for better or worse.
BEGIN {
# The maze dimensions.
width = 20; # Global
height = 20; # Global
resetMaze();
# Some constants.
top = 1;
bottom = 2;
left = 3;
right = 4;
# Randomize the PRNG.
randomize();
# Visit all the cells starting at a random point.
visitCell(getRandX(), getRandY());
# Show the result.
printMaze();
}
# Wander through the maze removing walls as we go.
function visitCell(x, y, dirList, dir, nx, ny, ndir, pi) {
setVisited(x, y); # This cell has been visited.
# Visit neighbors in a random order.
dirList = getRandDirList();
for (dir = 1; dir <= 4; dir++) {
# Get coordinates of a random neighbor (next in random direction list).
ndir = substr(dirList, dir, 1);
nx = getNextX(x, ndir);
ny = getNextY(y, ndir);
# Visit an unvisited neighbor, removing the separating walls.
if (wasVisited(nx, ny) == 0) {
rmWall(x, y, ndir);
rmWall(nx, ny, getOppositeDir(ndir));
visitCell(nx, ny)
}
}
}
# Display the text-mode maze.
function printMaze( x, y, r, w) {
for (y = 1; y <= height; y++) {
for (pass = 1; pass <= 2; pass++) { # Go over each row twice: top, middle
for (x = 1; x <= width; x++) {
if (pass == 1) { # top
printf("+");
printf(hasWall(x, y, top) == 1 ? "---" : " ");
if (x == width) printf("+");
}
else if (pass == 2) { # left, right
printf(hasWall(x, y, left) == 1 ? "|" : " ");
printf(" ");
if (x == width) printf(hasWall(x, y, right) == 1 ? "|" : " ");
}
}
print;
}
}
for (x = 1; x <= width; x++) printf("+---"); # bottom row
print("+"); # bottom right corner
}
# Given a direction, get its opposite.
function getOppositeDir(d) {
if (d == top) return bottom;
if (d == bottom) return top;
if (d == left) return right;
if (d == right) return left;
}
# Build a list (string) of the four directions in random order.
function getRandDirList( dirList, randDir, nx, ny, idx) {
dirList = "";
while (length(dirList) < 4) {
randDir = getRandDir();
if (!index(dirList, randDir)) {
dirList = dirList randDir;
}
}
return dirList;
}
# Get x coordinate of the neighbor in a given a direction.
function getNextX(x, dir) {
if (dir == left) x = x - 1;
if (dir == right) x = x + 1;
if (!isGoodXY(x, 1)) return -1; # Off the edge.
return x;
}
# Get y coordinate of the neighbor in a given a direction.
function getNextY(y, dir) {
if (dir == top) y = y - 1;
if (dir == bottom) y = y + 1;
if (!isGoodXY(1, y)) return -1; # Off the edge.
return y;
}
# Mark a cell as visited.
function setVisited(x, y, cell) {
cell = getCell(x, y);
if (cell == -1) return;
cell = substr(cell, 1, 4) "1"; # walls plus visited
setCell(x, y, cell);
}
# Get the visited state of a cell.
function wasVisited(x, y, cell) {
cell = getCell(x, y);
if (cell == -1) return 1; # Off edges already visited.
return substr(getCell(x,y), 5, 1);
}
# Remove a cell's wall in a given direction.
function rmWall(x, y, d, i, oldCell, newCell) {
oldCell = getCell(x, y);
if (oldCell == -1) return;
newCell = "";
for (i = 1; i <= 4; i++) { # Ugly as concat of two substrings and a constant?.
newCell = newCell (i == d ? "0" : substr(oldCell, i, 1));
}
newCell = newCell wasVisited(x, y);
setCell(x, y, newCell);
}
# Determine if a cell has a wall in a given direction.
function hasWall(x, y, d, cell) {
cell = getCell(x, y);
if (cell == -1) return 1; # Cells off edge always have all walls.
return substr(getCell(x, y), d, 1);
}
# Plunk a cell into the maze.
function setCell(x, y, cell, idx) {
if (!isGoodXY(x, y)) return;
maze[x, y] = cell
}
# Get a cell from the maze.
function getCell(x, y, idx) {
if (!isGoodXY(x, y)) return -1; # Bad cell marker.
return maze[x, y];
}
# Are the given coordinates in the maze?
function isGoodXY(x, y) {
if (x < 1 || x > width) return 0;
if (y < 1 || y > height) return 0;
return 1;
}
# Build the empty maze.
function resetMaze( x, y) {
delete maze;
for (y = 1; y <= height; y++) {
for (x = 1; x <= width; x++) {
maze[x, y] = "11110"; # walls (up, down, left, right) and visited state.
}
}
}
# Random things properly scaled.
function getRandX() {
return 1 + int(rand() * width);
}
function getRandY() {
return 1 +int(rand() * height);
}
function getRandDir() {
return 1 + int(rand() * 4);
}
function randomize() {
"echo $RANDOM" | getline t;
srand(t);
}