2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,4 +1,8 @@
{{wikipedia|Maze generation algorithm}}
[[File:a maze.png|300px||right|a maze]]
<br>
;Task:
Generate and show a maze, using the simple [[wp:Maze_generation_algorithm#Depth-first_search|Depth-first search]] algorithm.
<!-- BEGIN TEXT FROM WIKIPEDIA -->
@ -6,5 +10,8 @@ Generate and show a maze, using the simple [[wp:Maze_generation_algorithm#Depth-
#Mark the current cell as visited, and get a list of its neighbors. For each neighbor, starting with a randomly selected neighbor:
#:If that neighbor hasn't been visited, remove the wall between this cell and that neighbor, and then recurse with that neighbor as the current cell.
<!-- END TEXT FROM WIKIPEDIA -->
<br><br>
See also [[Maze solving]].
; Related tasks
* [[Maze solving]].
<br><br>

View file

@ -1,27 +1,28 @@
defmodule Maze do
def generate(w, h) do
:random.seed(:os.timestamp)
(for i <- 1..w, j <- 1..h, do: {i,j}) |>
Enum.each(fn{i,j} -> Process.put({:vis, i, j}, true) end)
walk(:random.uniform(w), :random.uniform(h))
print(w, h)
maze = (for i <- 1..w, j <- 1..h, into: Map.new, do: {{:vis, i, j}, true})
|> walk(:rand.uniform(w), :rand.uniform(h))
print(maze, w, h)
maze
end
defp walk(x, y) do
Process.put({:vis, x, y}, false)
Enum.each(Enum.shuffle([[x-1,y], [x,y+1], [x+1,y], [x,y-1]]), fn [i,j] ->
if Process.get({:vis, i, j}) do
if i == x, do: Process.put({:hor, x, max(y, j)}, "+ "),
else: Process.put({:ver, max(x, i), y}, " ")
walk(i, j)
defp walk(map, x, y) do
Enum.shuffle( [[x-1,y], [x,y+1], [x+1,y], [x,y-1]] )
|> Enum.reduce(Map.put(map, {:vis, x, y}, false), fn [i,j],acc ->
if acc[{:vis, i, j}] do
{k, v} = if i == x, do: {{:hor, x, max(y, j)}, "+ "},
else: {{:ver, max(x, i), y}, " "}
walk(Map.put(acc, k, v), i, j)
else
acc
end
end)
end
defp print(w, h) do
defp print(map, w, h) do
Enum.each(1..h, fn j ->
IO.puts (Enum.map(1..w, fn i -> Process.get({:hor, i, j}, "+---") end) |> Enum.join) <> "+"
IO.puts (Enum.map(1..w, fn i -> Process.get({:ver, i, j}, "| ") end) |> Enum.join) <> "|"
IO.puts Enum.map_join(1..w, fn i -> Map.get(map, {:hor, i, j}, "+---") end) <> "+"
IO.puts Enum.map_join(1..w, fn i -> Map.get(map, {:ver, i, j}, "| ") end) <> "|"
end)
IO.puts String.duplicate("+---", w) <> "+"
end

View file

@ -0,0 +1,114 @@
-module(maze).
-record(maze, {g, m, n}).
-export([generate_default/0, generate_MxN/2]).
make_maze(M, N) ->
Maze = #maze{g = digraph:new(), m = M, n = N},
lists:foreach(fun(X) -> digraph:add_vertex(Maze#maze.g, X) end, lists:seq(0, M * N - 1)),
Maze.
row_at(V, Maze) -> trunc(V / Maze#maze.n).
col_at(V, Maze) -> V - row_at(V, Maze) * Maze#maze.n.
vertex_at(Row, Col, Maze) -> Cell_Exists = cell_exists(Row, Col, Maze), if Cell_Exists -> Row * Maze#maze.n + Col; true -> -1 end.
cell_exists(Row, Col, Maze) -> (Row >= 0) and (Row < Maze#maze.m) and (Col >= 0) and (Col < Maze#maze.n).
adjacent_cells(V, Maze) -> % ordered: left, up, right, down
adjacent_cell(cell_left, V, Maze)++adjacent_cell(cell_up, V, Maze)++adjacent_cell(cell_right, V, Maze)++adjacent_cell(cell_down, V, Maze).
adjacent_cell(cell_left, V, Maze) -> case (col_at(V, Maze) == 0) of true -> []; _Else -> [V - 1] end;
adjacent_cell(cell_up, V, Maze) -> case (row_at(V, Maze) == 0) of true -> []; _Else -> [V - Maze#maze.n] end;
adjacent_cell(cell_right, V, Maze) -> case (col_at(V, Maze) == Maze#maze.n - 1) of true -> []; _Else -> [V + 1] end;
adjacent_cell(cell_down, V, Maze) -> case (row_at(V, Maze) == Maze#maze.m - 1) of true -> []; _Else -> [V + Maze#maze.n] end.
connect_all(V, Maze) ->
lists:foreach(fun(X) -> digraph:add_edge(Maze#maze.g, V, X) end, adjacent_cells(V, Maze)).
make_maze(M, N, all_connected) ->
Maze = make_maze(M, N),
lists:foreach(fun(X) -> connect_all(X, Maze) end, lists:seq(0, M * N - 1)),
Maze.
maze_parts(Maze) ->
SPR = Maze#maze.n + 1, % slots per row is #columns + 1
NPR = (Maze#maze.m * 2) + 1, % # part rows is #(rows * 2) + 1
[make_part(Maze, trunc(Index/SPR), Index - trunc(Index/SPR) * SPR) || Index <- lists:seq(0, (SPR * NPR) - 1)].
draw_part(Part) ->
case Part of
{pwall, pclosed} -> io:format("+---");
{pwall, popen} -> io:format("+ ");
{pwall, pend} -> io:format("+~n");
{phall, pclosed} -> io:format("| ");
{phall, popen} -> io:format(" ");
{phall, pend} -> io:format("|~n")
end.
has_neighbour(Maze, Row, Col, Direction) ->
V = vertex_at(Row, Col, Maze),
if
V >= 0 ->
Adjacent = adjacent_cell(Direction, V, Maze),
if
length(Adjacent) > 0 ->
Neighbours = digraph:out_neighbours(Maze#maze.g, lists:nth(1, Adjacent)),
lists:member(V, Neighbours);
true -> false
end;
true -> false
end.
make_part(Maze, DoubledRow, Col) ->
if
trunc(DoubledRow/2) * 2 == DoubledRow -> % --- (even row) making a wall above the cell
make_part(Maze, trunc(DoubledRow/2), Col, cell_up, pwall);
true -> % ---otherwise (odd row) making a hall through the cell
make_part(Maze, trunc(DoubledRow/2), Col, cell_left, phall)
end.
make_part(Maze, _, Col, _, Part_Type) when Col == Maze#maze.n -> {Part_Type, pend};
make_part(Maze, Row, Col, Direction, Part_Type) ->
Has_Neighbour = has_neighbour(Maze, Row, Col, Direction),
if
Has_Neighbour -> {Part_Type, popen};
true -> {Part_Type, pclosed}
end.
shuffle([], Acc) -> Acc;
shuffle(List, Acc) ->
Elem = lists:nth(random:uniform(length(List)), List),
shuffle(lists:delete(Elem, List), Acc++[Elem]).
processDepthFirst(Maze) ->
if
Maze#maze.m * Maze#maze.n == 0 -> [{pwall, pend}];
true ->
Visited = array:new([{size, Maze#maze.m * Maze#maze.n},{fixed,true},{default,false}]),
{_, Path} = processDepthFirst(Maze, -1, random:uniform(Maze#maze.m * Maze#maze.n) - 1, {Visited, []}),
Path
end.
processDepthFirst(Maze, Vfrom, V, VandP) ->
{Visited, Path} = VandP,
Was_Visited = array:get(V, Visited),
if
not Was_Visited ->
Walker = fun(X, Acc) -> processDepthFirst(Maze, V, X, Acc) end,
Random_Neighbours = shuffle(digraph:out_neighbours(Maze#maze.g, V), []),
lists:foldl(Walker, {array:set(V, true, Visited), Path++[{Vfrom, V}]}, Random_Neighbours);
true -> VandP
end.
open_wall(_, {-1, _}) -> ok;
open_wall(Maze, {V, V2}) ->
case (V2 > V) of true -> digraph:add_edge(Maze#maze.g, V, V2); _Else -> digraph:add_edge(Maze#maze.g, V2, V) end.
generate_MxN(M, N) ->
Maze = make_maze(M, N),
Matrix = make_maze(M, N, all_connected),
Trail = processDepthFirst(Matrix),
lists:foreach(fun(X) -> open_wall(Maze, X) end, Trail),
Parts = maze_parts(Maze),
lists:foreach(fun(X) -> draw_part(X) end, Parts).
generate_default() ->
generate_MxN(9, 9).

View file

@ -1,3 +1,6 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad
import Control.Monad.ST
import Data.Array

View file

@ -0,0 +1,61 @@
import shuffle.shuffle
class Maze_generator(val x: Int, val y: Int) {
fun generate(cx: Int, cy: Int) {
arrayOf(*DIR.values()).shuffle().forEach {
val nx = cx + it.dx
val ny = cy + it.dy
if (between(nx, x) && between(ny, y) && maze[nx][ny] == 0) {
maze[cx][cy] = maze[cx][cy] or it.bit
maze[nx][ny] = maze[nx][ny] or it.opposite!!.bit
generate(nx, ny)
}
}
}
fun display() {
for (i in 0..y - 1) {
// draw the north edge
for (j in 0..x - 1)
print(if (maze[j][i] and 1 == 0) "+---" else "+ ")
println('+')
// draw the west edge
for (j in 0..x - 1)
print(if (maze[j][i] and 8 == 0) "| " else " ")
println('|')
}
// draw the bottom line
for (j in 0..x - 1) print("+---")
println('+')
}
private enum class DIR(val bit: Int, val dx: Int, val dy: Int) {
N(1, 0, -1), S(2, 0, 1), E(4, 1, 0),W(8, -1, 0);
var opposite: DIR? = null
companion object {
init {
N.opposite = S
S.opposite = N
E.opposite = W
W.opposite = E
}
}
}
private fun between(v: Int, upper: Int) = v >= 0 && v < upper
private val maze = Array(x) { IntArray(y) }
}
fun main(args: Array<String>) {
val x = if (args.size >= 1) args[0].toInt() else 8
val y = if (args.size == 2) args[1].toInt() else 8
with(Maze_generator(x, y)) {
generate(0, 0)
display()
}
}

View file

@ -0,0 +1,73 @@
math.randomseed( os.time() )
-- Fisher-Yates shuffle from http://santos.nfshost.com/shuffling.html
function shuffle(t)
for i = 1, #t - 1 do
local r = math.random(i, #t)
t[i], t[r] = t[r], t[i]
end
end
-- builds a width-by-height grid of trues
function initialize_grid(w, h)
local a = {}
for i = 1, h do
table.insert(a, {})
for j = 1, w do
table.insert(a[i], true)
end
end
return a
end
-- average of a and b
function avg(a, b)
return (a + b) / 2
end
dirs = {
{x = 0, y = -2}, -- north
{x = 2, y = 0}, -- east
{x = -2, y = 0}, -- west
{x = 0, y = 2}, -- south
}
function make_maze(w, h)
w = w or 16
h = h or 8
local map = initialize_grid(w*2+1, h*2+1)
function walk(x, y)
map[y][x] = false
local d = { 1, 2, 3, 4 }
shuffle(d)
for i, dirnum in ipairs(d) do
local xx = x + dirs[dirnum].x
local yy = y + dirs[dirnum].y
if map[yy] and map[yy][xx] then
map[avg(y, yy)][avg(x, xx)] = false
walk(xx, yy)
end
end
end
walk(math.random(1, w)*2, math.random(1, h)*2)
local s = {}
for i = 1, h*2+1 do
for j = 1, w*2+1 do
if map[i][j] then
table.insert(s, '#')
else
table.insert(s, ' ')
end
end
table.insert(s, '\n')
end
return table.concat(s)
end
print(make_maze())

View file

@ -1,103 +1,103 @@
/*REXX program generates and displays a rectangular solvable maze (any size).*/
height=0; @.=0 /*default for all cells visited. */
parse arg rows cols seed . /*allow user to specify the maze size. */
if rows='' | rows==',' then rows=19 /*No rows given? Then use the default.*/
if cols='' | cols==',' then cols=19 /* " cols " ? " " " " */
if seed\=='' then call random ,,seed /*use a random seed for repeatability.*/
call buildRow ''copies('~',cols-1)'~' /*construct top edge of the maze. */
/* [↓] construct the maze's grid. */
do r=1 for rows; _=; __=; hp= '|'; hj=''
do c=1 for cols; _= _||hp'1'; __=__||hj'~'; hj=''; hp=''
/*REXX program generates and displays a rectangular solvable maze (of any size).*/
height=0; @.=0 /*default for all cells visited. */
parse arg rows cols seed . /*allow user to specify the maze size. */
if rows='' | rows=="," then rows=19 /*No rows given? Then use the default.*/
if cols='' | cols=="," then cols=19 /* " cols " ? " " " " */
if seed\=='' then call random ,,seed /*use a random seed for repeatability.*/
call buildRow ''copies("~┬",cols-1)'~' /*construct the top edge of the maze. */
/* [↓] construct the maze's grid. */
do r=1 for rows; _=; __=; hp= "|"; hj=''
do c=1 for cols; _= _||hp'1'; __=__||hj"~"; hj=''; hp=""
end /*c*/
call buildRow _'' /*construct the right edge of cells.*/
if r\==rows then call buildRow __'' /* " " " " " maze. */
call buildRow _'' /*construct the right edge of the cells*/
if r\==rows then call buildRow __'' /* " " " " " " maze.*/
end /*r*/
call buildRow ''copies('~',cols-1)'~' /*construct the bottom maze edge.*/
r!=random(1,rows)*2; c!=random(1,cols)*2; @.r!.c!=0 /*choose the 1st cell.*/
/* [↓] traipse through the maze. */
do forever; n=hood(r!,c!); if n==0 then if \fCell() then leave
call ?; @._r._c=0 /*get the (next) maze direction to go. */
ro=r!; co=c!; r!=_r; c!=_c /*save original maze cell coordinates. */
?.zr=?.zr%2; ?.zc=?.zc%2 /*get the maze row and cell directions.*/
rw=ro+?.zr; cw=co+?.zc /*calculate the next row and column. */
@.rw.cw=. /*mark the maze cell as being visited. */
call buildRow ''copies("~┴", cols-1)'~' /*construct the bottom edge of the maze*/
r!=random(1,rows)*2; c!=random(1,cols)*2; @.r!.c!=0 /*choose the first cell in maze*/
/* [↓] traipse through the maze. */
do forever; n=hood(r!,c!)
if n==0 then if \fCell() then leave
call ?; @._r._c=0 /*get the (next) maze direction to go. */
ro=r!; co=c!; r!=_r; c!=_c /*save original maze cell coordinates. */
?.zr=?.zr%2; ?.zc=?.zc%2 /*get the maze row and cell directions.*/
rw=ro+?.zr; cw=co+?.zc /*calculate the next row and column. */
@.rw.cw=. /*mark the maze cell as being visited. */
end /*forever*/
do r=1 for height; _= /*display the maze. */
do r=1 for height; _= /*display the maze. */
do c=1 for cols*2 + 1; _=_ || @.r.c; end /*c*/
if \(r//2) then _=translate(_, '\', .) /*trans to backslash*/
@.r=_ /*save the row in @.*/
if \(r//2) then _=translate(_, '\', .) /*trans to backslash*/
@.r=_ /*save the row in @.*/
end /*r*/
do #=1 for height; _=@.# /*display maze to the terminal. */
call makeNice /*make some cell corners prettier*/
do #=1 for height; _=@.# /*display the maze to the terminal. */
call makeNice /*make some cell corners look prettier.*/
_=changestr(1,_,111) /*──────these four ────────────────────*/
_=changestr(0,_,000) /*───────── statements are ────────────*/
_=changestr( . ,_," ") /*────────────── used for preserving ──*/
_=changestr('~',_,"───") /*────────────────── the aspect ratio. */
say translate(_, '', "═|\10") /*make it presentable for the screen. */
_=changestr(1 , _, 111) /*──────these four ────────────────────*/
_=changestr(0 , _, 000) /*───────── statements are ────────────*/
_=changestr(. , _, " ") /*────────────── used for preserving ──*/
_=changestr('~' , _, "───") /*────────────────── the aspect ratio. */
say translate(_ , '', "═|\10") /*make it presentable for the screen. */
end /*#*/
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
@: parse arg _r,_c; return @._r._c /*a fast way to reference a maze cell. */
/*────────────────────────────────────────────────────────────────────────────*/
?: do forever; ?.=0; ?=random(1,4); if ?==1 then ?.zc=-2 /*north*/
if ?==2 then ?.zr= 2 /* east*/
if ?==3 then ?.zc= 2 /*south*/
if ?==4 then ?.zr=-2 /* west*/
_r=r!+?.zr; _c=c!+?.zc; if @._r._c==1 then return
end /*forever*/
/*────────────────────────────────────────────────────────────────────────────*/
buildRow: parse arg z; height=height+1; width=length(z)
do c=1 for width; @.height.c=substr(z,c,1); end; return
/*────────────────────────────────────────────────────────────────────────────*/
fCell: do r=1 for rows; rr=r+r
do c=1 for cols; cc=c+c
if hood(rr,cc)==1 then do; r!=rr; c!=cc; @.r!.c!=0; return 1; end
end /*c*/
end /*r*/ /* [↑] r! & c! are used by invoker.*/
return 0
/*────────────────────────────────────────────────────────────────────────────*/
hood: parse arg rh,ch; return @(rh+2,ch) + @(rh-2,ch) + @(rh,ch-2) + @(rh,ch+2)
/*────────────────────────────────────────────────────────────────────────────*/
makeNice: width=length(_); old=#-1; new=#+1; old_=@.old; new_=@.new
if left(_,2) =='.' then _=translate(_, '|', "")
if right(_,2)=='.' then _=translate(_, '|', "")
/* [↓] handle the top grid row.*/
do k=1 for width while #==1; z=substr(_,k,1) /*maze top row.*/
if z\=='' then iterate
if substr(new_,k,1)=='\' then _=overlay('',_,k)
end /*k*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
@: parse arg _r,_c; return @._r._c /*a fast way to reference a maze cell. */
hood: parse arg rh,ch; return @(rh+2,ch) + @(rh-2,ch) + @(rh,ch-2) + @(rh,ch+2)
/*──────────────────────────────────────────────────────────────────────────────────────*/
?: do forever; ?.=0; ?=random(1,4); if ?==1 then ?.zc=-2 /*north*/
if ?==2 then ?.zr= 2 /* east*/
if ?==3 then ?.zc= 2 /*south*/
if ?==4 then ?.zr=-2 /* west*/
_r=r!+?.zr; _c=c!+?.zc; if @._r._c==1 then return
end /*forever*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
buildRow: parse arg z; height=height+1; width=length(z)
do c=1 for width; @.height.c=substr(z,c,1); end; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
fCell: do r=1 for rows; rr=r+r
do c=1 for cols; cc=c+c
if hood(rr, cc)==1 then do; r!=rr; c!=cc; @.r!.c!=0; return 1; end
end /*c*/
end /*r*/ /* [↑] r! and c! are used by invoker.*/
return 0
/*──────────────────────────────────────────────────────────────────────────────────────*/
makeNice: width=length(_); old=#-1; new=#+1; old_=@.old; new_=@.new
if left(_,2) =='.' then _=translate(_, "|", '')
if right(_,2)=='.' then _=translate(_, "|", '')
/* [↓] handle the top row of the grid.*/
do k=1 for width while #==1; z=substr(_,k,1) /*maze top row.*/
if z\=='' then iterate
if substr(new_,k,1)=='\' then _=overlay("",_,k)
end /*k*/
do k=1 for width while #==height; z=substr(_,k,1) /*maze bot row.*/
if z\=='' then iterate
if substr(old_,k,1)=='\' then _=overlay('',_,k)
end /*k*/
/* [↓] handle the mid grid rows*/
do k=3 to width-2 by 2 while #//2; z=substr(_,k,1) /*maze mid rows*/
if z\=='' then iterate
le=substr(_,k-1,1)
ri=substr(_,k+1,1)
up=substr(old_,k,1)
dw=substr(new_,k,1)
select
when le== . & ri== . & up=='' & dw=='' then _=overlay('|',_,k)
when le=='~' & ri=='~' & up=='\' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri=='~' & up=='\' & dw=='' then _=overlay('',_,k)
when le=='~' & ri=='~' & up=='' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri== . & up=='\' & dw=='\' then _=overlay('',_,k)
when le== . & ri=='~' & up=='\' & dw=='\' then _=overlay('',_,k)
when le== . & ri== . & up=='' & dw=='\' then _=overlay('|',_,k)
when le== . & ri== . & up=='\' & dw=='' then _=overlay('|',_,k)
when le== . & ri=='~' & up=='\' & dw=='' then _=overlay('',_,k)
when le== . & ri=='~' & up=='' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri== . & up=='\' & dw=='' then _=overlay('',_,k)
when le=='~' & ri== . & up=='' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri== . & up=='' & dw=='' then _=overlay('',_,k)
when le== . & ri=='~' & up=='' & dw=='' then _=overlay('',_,k)
otherwise nop
end /*select*/
end /*k*/
return
do k=1 for width while #==height; z=substr(_,k,1) /*maze bot row.*/
if z\=='' then iterate
if substr(old_, k, 1)=='\' then _=overlay("", _, k)
end /*k*/
/* [↓] handle the mid rows of the grid*/
do k=3 to width-2 by 2 while #//2; z=substr(_,k,1) /*maze mid rows*/
if z\=='' then iterate
le=substr(_,k-1,1)
ri=substr(_,k+1,1)
up=substr(old_,k,1)
dw=substr(new_,k,1)
select
when le== . & ri== . & up=='' & dw=="" then _=overlay('|',_,k)
when le=='~' & ri=="~" & up=='\' & dw=="\" then _=overlay('',_,k)
when le=='~' & ri=="~" & up=='\' & dw=="" then _=overlay('',_,k)
when le=='~' & ri=="~" & up=='' & dw=="\" then _=overlay('',_,k)
when le=='~' & ri== . & up=='\' & dw=="\" then _=overlay('',_,k)
when le== . & ri=="~" & up=='\' & dw=="\" then _=overlay('',_,k)
when le== . & ri== . & up=='' & dw=="\" then _=overlay('|',_,k)
when le== . & ri== . & up=='\' & dw=="" then _=overlay('|',_,k)
when le== . & ri=="~" & up=='\' & dw=="" then _=overlay('',_,k)
when le== . & ri=="~" & up=='' & dw=="\" then _=overlay('',_,k)
when le=='~' & ri== . & up=='\' & dw=="" then _=overlay('',_,k)
when le=='~' & ri== . & up=='' & dw=="\" then _=overlay('',_,k)
when le=='~' & ri== . & up=='' & dw=="" then _=overlay('',_,k)
when le== . & ri=="~" & up=='' & dw=="" then _=overlay('',_,k)
otherwise nop
end /*select*/
end /*k*/
return

View file

@ -1,57 +1,58 @@
/*REXX program generates and displays a rectangular solvable maze (any size).*/
height=0; @.=0 /*default for all cells visited. */
parse arg rows cols seed . /*allow user to specify the maze size. */
if rows='' | rows==',' then rows=19 /*No rows given? Then use the default.*/
if cols='' | cols==',' then cols=19 /* " cols " ? " " " " */
if seed\=='' then call random ,,seed /*use a random seed for repeatability.*/
call buildRow ''copies('',cols-1)'' /*build the top edge of the maze. */
/* [↓] construct the maze's grid. */
do r=1 for rows; _=; __=; hp= '|'; hj=''
do c=1 for cols; _= _||hp'1'; __=__||hj''; hj=''; hp=''
/*REXX program generates and displays a rectangular solvable maze (of any size). */
height=0; @.=0 /*default for all cells visited. */
parse arg rows cols seed . /*allow user to specify the maze size. */
if rows='' | rows=="," then rows=19 /*No rows given? Then use the default.*/
if cols='' | cols=="," then cols=19 /* " cols " ? " " " " */
if seed\=='' then call random ,,seed /*use a random seed for repeatability.*/
call buildRow ''copies("─┬", cols-1)'' /*construct the top edge of the maze.*/
/* [↓] construct the maze's grid. */
do r=1 for rows; _=; __=; hp= "|"; hj=''
do c=1 for cols; _= _||hp'1'; __=__||hj""; hj=''; hp=""
end /*c*/
call buildRow _'' /*build the right edge of cells. */
if r\==rows then call buildRow __'' /* " " " " " maze. */
call buildRow _'' /*construct the right edge of the cells*/
if r\==rows then call buildRow __'' /* " " " " " " maze.*/
end /*r*/
call buildRow ''copies('',cols-1)'' /*construct the bottom maze edge. */
r!=random(1,rows)*2; c!=random(1,cols)*2; @.r!.c!=0 /*choose the 1st cell.*/
/* [↓] traipse through the maze. */
do forever; n=hood(r!,c!); if n==0 then if \fCell() then leave
call ?; @._r._c=0 /*get the (next) maze direction to go. */
ro=r!; co=c!; r!=_r; c!=_c /*save the original cell coordinates. */
?.zr=?.zr%2; ?.zc=?.zc%2 /*get the maze row and cell directions.*/
rw=ro+?.zr; cw=co+?.zc /*calculate the next maze row and col. */
@.rw.cw=. /*mark the maze cell as being visited. */
call buildRow ''copies("─┴", cols-1)'' /*construct the bottom edge of the maze*/
r!=random(1,rows)*2; c!=random(1,cols)*2; @.r!.c!=0 /*choose the first cell.*/
/* [↓] traipse through the maze. */
do forever; n=hood(r!, c!) /*number of free maze cells. */
if n==0 then if \fCell() then leave /*if no free maze cells left, then done*/
call ?; @._r._c=0 /*get the (next) maze direction to go. */
ro=r!; co=c!; r!=_r; c!=_c /*save the original cell coordinates. */
?.zr=?.zr%2; ?.zc=?.zc%2 /*get the maze row and cell directions.*/
rw=ro+?.zr; cw=co+?.zc /*calculate the next maze row and col. */
@.rw.cw=. /*mark the maze cell as being visited. */
end /*forever*/
do r=1 for height; _= /*display the maze. */
do r=1 for height; _= /*display the maze. */
do c=1 for cols*2 + 1; _=_ || @.r.c; end /*c*/
if \(r//2) then _=translate(_, '\', .) /*trans to backslash*/
_=changestr(1,_,111) /*──────these four ────────────────────*/
_=changestr(0,_,000) /*───────── statements are ────────────*/
_=changestr( . ,_," ") /*────────────── used for preserving ──*/
_=changestr('',_,"───") /*────────────────── the aspect ratio. */
say translate(_,'',"|\10") /*make it presentable for the screen. */
if \(r//2) then _=translate(_, '\', .) /*trans to backslash*/
_=changestr(1 , _, 111) /*──────these four ────────────────────*/
_=changestr(0 , _, 000) /*───────── statements are ────────────*/
_=changestr(. , _, " ") /*────────────── used for preserving ──*/
_=changestr('', _, "───") /*────────────────── the aspect ratio. */
say translate(_, '', "|\10") /*make it presentable for the screen. */
end /*r*/
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
@: parse arg _r,_c; return @._r._c /*a fast way to reference a maze cell. */
/*────────────────────────────────────────────────────────────────────────────*/
?: do forever; ?.=0; ?=random(1,4); if ?==1 then ?.zc=-2 /*north*/
if ?==2 then ?.zr= 2 /* east*/
if ?==3 then ?.zc= 2 /*south*/
if ?==4 then ?.zr=-2 /* west*/
_r=r!+?.zr; _c=c!+?.zc; if @._r._c==1 then return
end /*forever*/
/*────────────────────────────────────────────────────────────────────────────*/
buildRow: parse arg z; height=height+1; width=length(z)
do c=1 for width; @.height.c=substr(z,c,1); end; return
/*────────────────────────────────────────────────────────────────────────────*/
fCell: do r=1 for rows; rr=r+r
do c=1 for cols; cc=c+c
if hood(rr,cc)==1 then do; r!=rr; c!=cc; @.r!.c!=0; return 1; end
end /*c*/
end /*r*/ /* [↑] r! & c! are used by invoker.*/
return 0
/*────────────────────────────────────────────────────────────────────────────*/
hood: parse arg rh,ch; return @(rh+2,ch) + @(rh-2,ch) + @(rh,ch-2) + @(rh,ch+2)
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
@: parse arg _r,_c; return @._r._c /*a fast way to reference a maze cell. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
?: do forever; ?.=0; ?=random(1,4); if ?==1 then ?.zc=-2 /*north*/
if ?==2 then ?.zr= 2 /* east*/
if ?==3 then ?.zc= 2 /*south*/
if ?==4 then ?.zr=-2 /* west*/
_r=r!+?.zr; _c=c!+?.zc; if @._r._c==1 then return
end /*forever*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
buildRow: parse arg z; height=height+1; width=length(z)
do c=1 for width; @.height.c=substr(z,c,1); end; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
fCell: do r=1 for rows; rr=r+r
do c=1 for cols; cc=c+c
if hood(rr,cc)==1 then do; r!=rr; c!=cc; @.r!.c!=0; return 1; end
end /*c*/
end /*r*/ /* [↑] r! and c! are used by invoker.*/
return 0
/*──────────────────────────────────────────────────────────────────────────────────────*/
hood: parse arg rh,ch; return @(rh+2,ch) + @(rh-2,ch) + @(rh,ch-2) + @(rh,ch+2)

View file

@ -0,0 +1,56 @@
// some useful functions
(
~grid = { 0 ! 60 } ! 60;
~at = { |coord|
var col = ~grid.at(coord[0]);
if(col.notNil) { col.at(coord[1]) }
};
~put = { |coord, value|
var col = ~grid.at(coord[0]);
if(col.notNil) { col.put(coord[1], value) }
};
~coord = ~grid.shape.rand;
~next = { |p|
var possible = [p] + [[0, 1], [1, 0], [-1, 0], [0, -1]];
possible = possible.select { |x|
var c = ~at.(x);
c.notNil and: { c == 0 }
};
possible.choose
};
~walkN = { |p, scale|
var next = ~next.(p);
if(next.notNil) {
~put.(next, 1);
Pen.lineTo(~topoint.(next, scale));
~walkN.(next, scale);
~walkN.(next, scale);
Pen.moveTo(~topoint.(p, scale));
};
};
~topoint = { |c, scale| (c + [1, 1] * scale).asPoint };
)
// do the drawing
(
var b, w;
b = Rect(100, 100, 700, 700);
w = Window("so-a-mazing", b);
w.view.background_(Color.black);
w.drawFunc = {
var p = ~grid.shape.rand;
var scale = b.width / ~grid.size * 0.98;
Pen.moveTo(~topoint.(p, scale));
~walkN.(p, scale);
Pen.width = scale / 4;
Pen.color = Color.white;
Pen.stroke;
};
w.front.refresh;
)

View file

@ -1,94 +1,90 @@
@(do
(defvar vi) ;; visited hash
(defvar pa) ;; path connectivity hash
(defvar sc) ;; count, derived from straightness fator
(defvar vi) ;; visited hash
(defvar pa) ;; path connectivity hash
(defvar sc) ;; count, derived from straightness fator
(defun scramble (list)
(let ((out ()))
(each ((item list))
(let ((r (rand (+ 1 (length out)))))
(set [out r..r] (list item))))
out))
(defun scramble (list)
(let ((out ()))
(each ((item list))
(let ((r (rand (+ 1 (length out)))))
(set [out r..r] (list item))))
out))
(defun rnd-pick (list)
(if list [list (rand (length list))]))
(defun rnd-pick (list)
(if list [list (rand (length list))]))
(defmacro while (expr . body)
^(for () (,expr) () ,*body))
(defun neigh (loc)
(let ((x (from loc))
(y (to loc)))
(list (- x 1)..y (+ x 1)..y
x..(- y 1) x..(+ y 1))))
(defun neigh (loc)
(let ((x (from loc))
(y (to loc)))
(list (- x 1)..y (+ x 1)..y
x..(- y 1) x..(+ y 1))))
(defun make-maze-impl (cu)
(let ((fr (hash :equal-based))
(q (list cu))
(c sc))
(set [fr cu] t)
(while q
(let* ((cu (first q))
(ne (rnd-pick (remove-if (orf vi fr) (neigh cu)))))
(cond (ne (set [fr ne] t)
(push ne [pa cu])
(push cu [pa ne])
(push ne q)
(cond ((<= (dec c) 0)
(set q (scramble q))
(set c sc))))
(t (set [vi cu] t)
(del [fr cu])
(pop q)))))))
(defun make-maze-impl (cu)
(let ((fr (hash :equal-based))
(q (list cu))
(c sc))
(set [fr cu] t)
(while q
(let* ((cu (first q))
(ne (rnd-pick (remove-if (orf vi fr) (neigh cu)))))
(cond (ne (set [fr ne] t)
(push ne [pa cu])
(push cu [pa ne])
(push ne q)
(cond ((<= (dec c) 0)
(set q (scramble q))
(set c sc))))
(t (set [vi cu] t)
(del [fr cu])
(pop q)))))))
(defun make-maze (w h sf)
(let ((vi (hash :equal-based))
(pa (hash :equal-based))
(sc (max 1 (trunc (* sf w h) 100))))
(each ((x (range -1 w)))
(set [vi x..-1] t)
(set [vi x..h] t))
(each ((y (range* 0 h)))
(set [vi -1..y] t)
(set [vi w..y] t))
(make-maze-impl 0..0)
pa))
(defun make-maze (w h sf)
(let ((vi (hash :equal-based))
(pa (hash :equal-based))
(sc (max 1 (trunc (* sf w h) 100))))
(each ((x (range -1 w)))
(set [vi x..-1] t)
(set [vi x..h] t))
(each ((y (range* 0 h)))
(set [vi -1..y] t)
(set [vi w..y] t))
(make-maze-impl 0..0)
pa))
(defun print-tops (pa w j)
(each ((i (range* 0 w)))
(if (memqual i..(- j 1) [pa i..j])
(put-string "+ ")
(put-string "+----")))
(put-line "+"))
(defun print-tops (pa w j)
(each ((i (range* 0 w)))
(if (memqual i..(- j 1) [pa i..j])
(put-string "+ ")
(put-string "+----")))
(put-line "+"))
(defun print-sides (pa w j)
(let ((str ""))
(each ((i (range* 0 w)))
(if (memqual (- i 1)..j [pa i..j])
(set str `@str `)
(set str `@str| `)))
(put-line `@str|\n@str|`)))
(defun print-sides (pa w j)
(let ((str ""))
(each ((i (range* 0 w)))
(if (memqual (- i 1)..j [pa i..j])
(set str `@str `)
(set str `@str| `)))
(put-line `@str|\n@str|`)))
(defun print-maze (pa w h)
(each ((j (range* 0 h)))
(print-tops pa w j)
(print-sides pa w j))
(print-tops pa w h))
(defun print-maze (pa w h)
(each ((j (range* 0 h)))
(print-tops pa w j)
(print-sides pa w j))
(print-tops pa w h))
(defun usage ()
(let ((invocation (ldiff *full-args* *args*)))
(put-line "usage: ")
(put-line `@invocation <width> <height> [<straightness>]`)
(put-line "straightness-factor is a percentage, defaulting to 15")
(exit 1)))
(defun usage ()
(let ((invocation (ldiff *full-args* *args*)))
(put-line "usage: ")
(put-line `@invocation <width> <height> [<straightness>]`)
(put-line "straightness-factor is a percentage, defaulting to 15")
(exit 1)))
(let ((args [mapcar int-str *args*])
(*random-state* (make-random-state nil)))
(if (memq nil args)
(usage))
(tree-case args
((w h s ju . nk) (usage))
((w h : (s 15)) (set w (max 1 w))
(set h (max 1 h))
(print-maze (make-maze w h s) w h))
(else (usage)))))
(let ((args [mapcar int-str *args*])
(*random-state* (make-random-state nil)))
(if (memq nil args)
(usage))
(tree-case args
((w h s ju . nk) (usage))
((w h : (s 15)) (set w (max 1 w))
(set h (max 1 h))
(print-maze (make-maze w h s) w h))
(else (usage))))