2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
114
Task/Maze-generation/Erlang/maze-generation-2.erl
Normal file
114
Task/Maze-generation/Erlang/maze-generation-2.erl
Normal 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).
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
|
||||
import Control.Monad
|
||||
import Control.Monad.ST
|
||||
import Data.Array
|
||||
|
|
|
|||
61
Task/Maze-generation/Kotlin/maze-generation.kotlin
Normal file
61
Task/Maze-generation/Kotlin/maze-generation.kotlin
Normal 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()
|
||||
}
|
||||
}
|
||||
73
Task/Maze-generation/Lua/maze-generation.lua
Normal file
73
Task/Maze-generation/Lua/maze-generation.lua
Normal 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())
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
)
|
||||
|
|
@ -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))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue