Maze solving: Difference between revisions
(57 intermediate revisions by 23 users not shown) | |||
Line 1: | Line 1: | ||
{{task|Games}} |
{{task|Games}} |
||
[[Category:Recursion]] |
|||
[[Category:Puzzles]] |
|||
;Task: |
|||
For a maze generated by [[Maze generation|this task]], write a function |
For a maze generated by [[Maze generation|this task]], write a function |
||
that finds (and displays) the shortest path between two cells. |
that finds (and displays) the shortest path between two cells. |
||
Note that because these mazes are generated by the [[wp:Maze_generation_algorithm#Depth-first_search|Depth-first search]] algorithm, they contain no circular paths, |
Note that because these mazes are generated by the [[wp:Maze_generation_algorithm#Depth-first_search|Depth-first search]] algorithm, they contain no circular paths, |
||
and a simple depth-first tree search can be used. |
and a simple depth-first tree search can be used. |
||
<br><br> |
|||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">F dijkstra(graph, source) |
|||
V n = graph.len |
|||
V dist = [Float.infinity] * n |
|||
V previous = [-1] * n |
|||
dist[source] = 0 |
|||
V Q = Array(0 .< n) |
|||
L !Q.empty |
|||
V u = min(Q, key' n -> @dist[n]) |
|||
Q.remove(u) |
|||
I dist[u] == Float.infinity |
|||
L.break |
|||
L(v) 0 .< n |
|||
I graph[u][v] & (v C Q) |
|||
V alt = dist[u] + graph[u][v] |
|||
I alt < dist[v] |
|||
dist[v] = alt |
|||
previous[v] = u |
|||
R previous |
|||
F display_solution(predecessor) |
|||
V cell = predecessor.len - 1 |
|||
L cell != 0 |
|||
print(cell, end' ‘<’) |
|||
cell = predecessor[cell] |
|||
print(0) |
|||
V graph = [ |
|||
[0,1,0,0,0,0], |
|||
[1,0,1,0,1,0], |
|||
[0,1,0,0,0,1], |
|||
[0,0,0,0,1,0], |
|||
[0,1,0,1,0,0], |
|||
[0,0,1,0,0,0] |
|||
] |
|||
display_solution(dijkstra(graph, 0))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
5<2<1<0 |
|||
</pre> |
|||
=={{header|Action!}}== |
|||
Action! language does not support recursion. Therefore an iterative approach with a stack has been proposed. |
|||
<syntaxhighlight lang="action!">DEFINE TOP="0" |
|||
DEFINE RIGHT="1" |
|||
DEFINE BOTTOM="2" |
|||
DEFINE LEFT="3" |
|||
DEFINE WIDTH="160" |
|||
DEFINE HEIGHT="96" |
|||
DEFINE STACK_SIZE="5000" |
|||
BYTE ARRAY stack(STACK_SIZE) |
|||
INT stackSize |
|||
PROC InitStack() |
|||
stackSize=0 |
|||
RETURN |
|||
BYTE FUNC IsEmpty() |
|||
IF stackSize=0 THEN |
|||
RETURN (1) |
|||
FI |
|||
RETURN (0) |
|||
BYTE FUNC IsFull() |
|||
IF stackSize>=STACK_SIZE THEN |
|||
RETURN (1) |
|||
FI |
|||
RETURN (0) |
|||
PROC Push(BYTE x,y) |
|||
IF IsFull() THEN Break() RETURN FI |
|||
stack(stackSize)=x stackSize==+1 |
|||
stack(stackSize)=y stackSize==+1 |
|||
RETURN |
|||
PROC Pop(BYTE POINTER x,y) |
|||
IF IsEmpty() THEN Break() RETURN FI |
|||
stackSize==-1 y^=stack(stackSize) |
|||
stackSize==-1 x^=stack(stackSize) |
|||
RETURN |
|||
PROC Push3(BYTE x,y,d) |
|||
IF IsFull() THEN Break() RETURN FI |
|||
stack(stackSize)=x stackSize==+1 |
|||
stack(stackSize)=y stackSize==+1 |
|||
stack(stackSize)=d stackSize==+1 |
|||
RETURN |
|||
PROC Pop3(BYTE POINTER x,y,d) |
|||
IF IsEmpty() THEN Break() RETURN FI |
|||
stackSize==-1 d^=stack(stackSize) |
|||
stackSize==-1 y^=stack(stackSize) |
|||
stackSize==-1 x^=stack(stackSize) |
|||
RETURN |
|||
PROC FillScreen() |
|||
BYTE POINTER ptr ;pointer to the screen memory |
|||
INT screenSize=[3840] |
|||
ptr=PeekC(88) |
|||
SetBlock(ptr,screenSize,$55) |
|||
Color=0 |
|||
Plot(0,HEIGHT-1) DrawTo(WIDTH-1,HEIGHT-1) DrawTo(WIDTH-1,0) |
|||
RETURN |
|||
PROC GetNeighbors(BYTE x,y BYTE ARRAY n BYTE POINTER count) |
|||
DEFINE WALL="1" |
|||
count^=0 |
|||
IF y>2 AND Locate(x,y-2)=WALL THEN |
|||
n(count^)=TOP count^==+1 |
|||
FI |
|||
IF x<WIDTH-3 AND Locate(x+2,y)=WALL THEN |
|||
n(count^)=RIGHT count^==+1 |
|||
FI |
|||
IF y<HEIGHT-3 AND Locate(x,y+2)=WALL THEN |
|||
n(count^)=BOTTOM count^==+1 |
|||
FI |
|||
IF x>2 AND Locate(x-2,y)=WALL THEN |
|||
n(count^)=LEFT count^==+1 |
|||
FI |
|||
RETURN |
|||
PROC DrawConnection(BYTE POINTER x,y BYTE dir) |
|||
Plot(x^,y^) |
|||
IF dir=TOP THEN |
|||
y^==-2 |
|||
ELSEIF dir=RIGHT THEN |
|||
x^==+2 |
|||
ELSEIF dir=BOTTOM THEN |
|||
y^==+2 |
|||
ELSE |
|||
x^==-2 |
|||
FI |
|||
DrawTo(x^,y^) |
|||
RETURN |
|||
PROC Maze(BYTE x,y) |
|||
BYTE ARRAY stack,neighbors |
|||
BYTE dir,nCount |
|||
FillScreen() |
|||
Color=2 |
|||
InitStack() |
|||
Push(x,y) |
|||
WHILE IsEmpty()=0 |
|||
DO |
|||
Pop(@x,@y) |
|||
GetNeighbors(x,y,neighbors,@nCount) |
|||
IF nCount>0 THEN |
|||
Push(x,y) |
|||
dir=neighbors(Rand(nCount)) |
|||
DrawConnection(@x,@y,dir) |
|||
Push(x,y) |
|||
FI |
|||
OD |
|||
RETURN |
|||
BYTE FUNC IsConnection(BYTE x,y,dir) |
|||
DEFINE WAY="2" |
|||
IF dir=TOP AND y>2 AND Locate(x,y-1)=WAY THEN |
|||
RETURN (1) |
|||
ELSEIF dir=RIGHT AND x<WIDTH-3 AND Locate(x+1,y)=WAY THEN |
|||
RETURN (1) |
|||
ELSEIF dir=BOTTOM AND y<HEIGHT-3 AND Locate(x,y+1)=WAY THEN |
|||
RETURN (1) |
|||
ELSEIF dir=LEFT AND x>2 AND Locate(x-1,y)=WAY THEN |
|||
RETURN (1) |
|||
FI |
|||
RETURN (0) |
|||
PROC Solve(BYTE x1,y1,x2,y2) |
|||
BYTE dir,x,y,lastX,lastY,back |
|||
Color=3 |
|||
Plot(x1,y1) |
|||
Plot(x2,y2) |
|||
InitStack() |
|||
Push3(x1,y1,TOP) |
|||
WHILE IsEmpty()=0 |
|||
DO |
|||
Pop3(@x,@y,@dir) |
|||
IF back THEN |
|||
Color=2 |
|||
Plot(lastX,lastY) |
|||
DrawTo(x,y) |
|||
FI |
|||
IF IsConnection(x,y,dir) THEN |
|||
Color=3 |
|||
Push3(x,y,dir+1) |
|||
DrawConnection(@x,@y,dir) |
|||
IF x=x2 AND y=y2 THEN |
|||
RETURN |
|||
FI |
|||
Push3(x,y,TOP) |
|||
back=0 |
|||
ELSEIF dir<=LEFT THEN |
|||
Push3(x,y,dir+1) |
|||
back=0 |
|||
ELSE |
|||
lastX=x |
|||
lastY=y |
|||
back=1 |
|||
FI |
|||
OD |
|||
RETURN |
|||
PROC Main() |
|||
BYTE CH=$02FC,COLOR0=$02C4,COLOR1=$02C5,COLOR2=$02C6 |
|||
BYTE x,y,x2,y2 |
|||
Graphics(7+16) |
|||
COLOR0=$0A |
|||
COLOR1=$04 |
|||
COLOR2=$A6 |
|||
x=Rand((WIDTH RSH 1)-1) LSH 1+1 |
|||
y=Rand((HEIGHT RSH 1)-1) LSH 1+1 |
|||
Maze(x,y) |
|||
x=Rand((WIDTH RSH 1)-1) LSH 1+1 |
|||
y=Rand((HEIGHT RSH 1)-1) LSH 1+1 |
|||
x2=Rand((WIDTH RSH 1)-1) LSH 1+1 |
|||
y2=Rand((HEIGHT RSH 1)-1) LSH 1+1 |
|||
Solve(x,y,x2,y2) |
|||
DO UNTIL CH#$FF OD |
|||
CH=$FF |
|||
RETURN</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Maze_solving.png Screenshot from Atari 8-bit computer] |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
Line 10: | Line 256: | ||
The maze is read from the standard input. The size of the maze is hardwired into the program (see the constants X_Size and Y_Size). |
The maze is read from the standard input. The size of the maze is hardwired into the program (see the constants X_Size and Y_Size). |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO; |
||
procedure Maze_Solver is |
procedure Maze_Solver is |
||
Line 82: | Line 328: | ||
Search(Maze, X, Y) ; -- Will output *all* Solutions. |
Search(Maze, X, Y) ; -- Will output *all* Solutions. |
||
-- If there is no output, there is no solution. |
-- If there is no output, there is no solution. |
||
end Maze_Solver;</ |
end Maze_Solver;</syntaxhighlight> |
||
{{out|Example output:}} |
{{out|Example output:}} |
||
Line 127: | Line 373: | ||
+---+---+---+---+---+---+---+---+---+---+-*-+ |
+---+---+---+---+---+---+---+---+---+---+-*-+ |
||
</pre> |
</pre> |
||
=={{header|AutoHotkey}}== |
|||
Generator and solver combined. |
|||
<syntaxhighlight lang="autohotkey">Width := 10, Height := 10 ; set grid size |
|||
SleepTime := 0 |
|||
gosub, Startup |
|||
Gui, +AlwaysOnTop |
|||
Gui, font, s12, consolas |
|||
Gui, add, edit, vEditGrid x10, % maze |
|||
Gui, add, button, xs gStartup Default, Generate maze |
|||
Gui, add, button, x+10 gSolve, Solve |
|||
Gui, show,, maze |
|||
GuiControl,, EditGrid, % maze ; show maze |
|||
return |
|||
;----------------------------------------------------------------------- |
|||
^Esc:: |
|||
GuiEscape: |
|||
GuiClose: |
|||
ExitApp |
|||
return |
|||
;----------------------------------------------------------------------- |
|||
Startup: |
|||
oMaze := [] ; initialize |
|||
Solved := false |
|||
loop, % Height |
|||
{ |
|||
row := A_Index |
|||
loop, % Width ; create oMaze[row,column] borders |
|||
col := A_Index, oMaze[row,col] := "LRTB" ; i.e. oMaze[2,5] := LRTB (add all borders) |
|||
} |
|||
Random, row, 1, % Height ; random row |
|||
Random, col, 1, % Width ; random col |
|||
grid := maze2text(oMaze) ; object to text |
|||
GuiControl,, EditGrid, % Grid ; show Grid |
|||
row := col := 1 ; reset to 1,1 |
|||
oMaze := Generate_maze(row, col, oMaze) ; generate maze starting from random row/column |
|||
oMaze[1,1] .= "X" ; start from 1,1 |
|||
maze := maze2text(oMaze) ; object to text |
|||
GuiControl,, EditGrid, % maze ; show maze |
|||
GuiControl,, EditRoute ; clear route |
|||
GuiControl, Enable, Solve |
|||
return |
|||
;----------------------------------------------------------------------- |
|||
Solve: |
|||
GuiControl, Disable, Generate maze |
|||
GuiControl, Disable, Solve |
|||
loop % oRoute.MaxIndex() |
|||
oRoute.pop() |
|||
oSolution := Solve(1, 1, oMaze) ; solve starting from 1,1 |
|||
oMaze := oSolution.1 |
|||
oRoute := oSolution.2 |
|||
Update(oMaze, oRoute) |
|||
Solved := true |
|||
GuiControl, Enable, Generate maze |
|||
return |
|||
;----------------------------------------------------------------------- |
|||
Update(oMaze, oRoute){ |
|||
global SleepTime |
|||
GuiControl,, EditGrid, % maze2text(oMaze) |
|||
Sleep, % SleepTime |
|||
} |
|||
;----------------------------------------------------------------------- |
|||
maze2text(oMaze){ |
|||
width := oMaze.1.MaxIndex() |
|||
BLK := "█" |
|||
for row, objRow in oMaze |
|||
{ |
|||
for col, val in objRow ; add ceiling |
|||
{ |
|||
ceiling := InStr(oMaze[row, col] , "x") && InStr(oMaze[row-1, col] , "x") ? "+ " BLK " " : "+ " |
|||
grid .= (InStr(val, "T") ? "+---" : ceiling) (col = Width ? "+`n" : "") |
|||
} |
|||
for col, val in objRow ; add left wall |
|||
{ |
|||
wall := SubStr(val, 0) = "X" ? BLK : " " |
|||
grid .= (InStr(val, "L") ? "| " : " ") wall " " (col = Width ? "|`n" : "") ; add left wall if needed then outer right border |
|||
} |
|||
} |
|||
Loop % Width |
|||
Grid .= "+---" ; add bottom floor |
|||
Grid .= "+" ; add right bottom corner |
|||
return RegExReplace(grid , BLK " (?=" BLK ")" , BLK BLK BLK BLK) ; fill gaps |
|||
} |
|||
;----------------------------------------------------------------------- |
|||
Generate_maze(row, col, oMaze) { |
|||
neighbors := row+1 "," col "`n" row-1 "," col "`n" row "," col+1 "`n" row "," col-1 |
|||
Sort, neighbors, random ; randomize neighbors list |
|||
Loop, parse, neighbors, `n ; for each neighbor |
|||
{ |
|||
rowX := StrSplit(A_LoopField, ",").1 ; this neighbor row |
|||
colX := StrSplit(A_LoopField, ",").2 ; this neighbor column |
|||
if !instr(oMaze[rowX,colX], "LRTB") || !oMaze[rowX, colX] ; if visited (has a missing border) or out of bounds |
|||
continue ; skip |
|||
; remove borders |
|||
if (row > rowX) ; Cell is below this neighbor |
|||
oMaze[row,col] := StrReplace(oMaze[row,col], "T") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "B") |
|||
else if (row < rowX) ; Cell is above this neighbor |
|||
oMaze[row,col] := StrReplace(oMaze[row,col], "B") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "T") |
|||
else if (col > colX) ; Cell is right of this neighbor |
|||
oMaze[row,col] := StrReplace(oMaze[row,col], "L") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "R") |
|||
else if (col < colX) ; Cell is left of this neighbor |
|||
oMaze[row,col] := StrReplace(oMaze[row,col], "R") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "L") |
|||
Generate_maze(rowX, colX, oMaze) ; recurse for this neighbor |
|||
} |
|||
return, oMaze |
|||
} |
|||
;----------------------------------------------------------------------- |
|||
Solve(row, col, oMaze){ |
|||
static oRoute := [] |
|||
oNeighbor := [], targetrow := oMaze.MaxIndex(), targetCol := oMaze.1.MaxIndex() |
|||
;~ Update(oMaze, oRoute) |
|||
oRoute.push(row ":" col) ; push current cell address to oRoute |
|||
oMaze[row, col] .= "X" ; mark it visited "X" |
|||
if (row = targetrow) && (Col = targetCol) ; if solved |
|||
return true ; return ture |
|||
; create list of Neighbors |
|||
oNeighbor[row, col] := [] |
|||
if !InStr(oMaze[row, col], "R") ; if no Right border |
|||
oNeighbor[row, col].push(row "," col+1) ; add neighbor |
|||
if !InStr(oMaze[row, col], "B") ; if no Bottom border |
|||
oNeighbor[row, col].push(row+1 "," col) ; add neighbor |
|||
if !InStr(oMaze[row, col], "T") ; if no Top border |
|||
oNeighbor[row, col].push(row-1 "," col) ; add neighbor |
|||
if !InStr(oMaze[row, col], "L") ; if no Left border |
|||
oNeighbor[row, col].push(row "," col-1) ; add neighbor |
|||
; recurese for each oNeighbor |
|||
for each, neighbor in oNeighbor[row, col] ; for each neighbor |
|||
{ |
|||
Update(oMaze, oRoute) |
|||
startrow := StrSplit(neighbor, ",").1 ; this neighbor |
|||
startCol := StrSplit(neighbor, ",").2 ; becomes starting point |
|||
if !InStr(oMaze[startrow, startCol], "X") ; if it was not visited |
|||
if Solve(startrow, startCol, oMaze) ; recurse for current neighbor |
|||
return [oMaze, oRoute] ; return solution if solved |
|||
} |
|||
oRoute.pop() ; no solution found, back track |
|||
oMaze[row, Col] := StrReplace(oMaze[row, Col], "X") ; no solution found, back track |
|||
;~ Update(oMaze, oRoute) |
|||
} |
|||
;----------------------------------------------------------------------- |
|||
#IfWinActive, maze |
|||
Right:: |
|||
Left:: |
|||
Up:: |
|||
Down:: |
|||
if Solved |
|||
return |
|||
if (A_ThisHotkey="Right") && (!InStr(oMaze[row,col], "R")) |
|||
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , col++ |
|||
if (A_ThisHotkey="Left") && (!InStr(oMaze[row,col], "L")) |
|||
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , col-- |
|||
if (A_ThisHotkey="Up") && (!InStr(oMaze[row,col], "T")) |
|||
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , row-- |
|||
if (A_ThisHotkey="Down") && (!InStr(oMaze[row,col], "B")) |
|||
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , row++ |
|||
oMaze[row, col] .= "X" |
|||
GuiControl,, EditGrid, % maze2text(oMaze) |
|||
if (col = Width) && (row = Height) |
|||
{ |
|||
Solved := true |
|||
oMaze[height, width] := StrReplace(oMaze[height, width], "X") |
|||
SleepTime := 0 |
|||
gosub, solve |
|||
return |
|||
} |
|||
return |
|||
#IfWinActive</syntaxhighlight> |
|||
Outputs:<pre>+---+---+---+---+---+---+---+---+---+---+ |
|||
| ¦¦¦¦¦ | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦¦¦¦¦ | |
|||
+---+ ¦ +---+ ¦ + ¦ +---+ ¦ +---+---+ ¦ + |
|||
| | ¦ | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | ¦ | |
|||
+ + ¦ + ¦ +---+---+---+---+ ¦ + ¦ + ¦ + |
|||
| ¦¦¦¦¦ | ¦ | ¦¦¦¦¦¦¦¦¦¦¦¦¦ | ¦ | ¦¦¦¦¦ | |
|||
+ ¦ +---+ ¦ + ¦ +---+---+ ¦ + ¦ +---+---+ |
|||
| ¦ | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | | |
|||
+ ¦ +---+---+---+ ¦ +---+---+---+ ¦ + + |
|||
| ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | |
|||
+---+ ¦ + ¦ +---+---+ +---+---+---+ ¦ + |
|||
| ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | | ¦¦¦¦¦ | | ¦ | |
|||
+ ¦ +---+---+---+ ¦ +---+ ¦ + ¦ + + ¦ + |
|||
| ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | ¦ | ¦¦¦¦¦ | |
|||
+---+ ¦ + ¦ +---+---+ ¦ +---+ ¦ + ¦ +---+ |
|||
| ¦¦¦¦¦ | ¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | ¦ | | |
|||
+ ¦ +---+ ¦ + ¦ +---+---+ ¦ +---+ ¦ + + |
|||
| ¦ | ¦¦¦¦¦ | ¦¦¦¦¦ | | ¦¦¦¦¦ | |
|||
+ ¦ +---+---+---+---+ ¦ +---+ +---+ ¦ + |
|||
| ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ | ¦ | |
|||
+---+---+---+---+---+---+---+---+---+---+</pre> |
|||
=={{header|BBC BASIC}}== |
=={{header|BBC BASIC}}== |
||
Line 132: | Line 587: | ||
[[Image:mazesolve_bbc.gif|right]] |
[[Image:mazesolve_bbc.gif|right]] |
||
Maze generation code also included. |
Maze generation code also included. |
||
< |
<syntaxhighlight lang="bbcbasic"> MazeWidth% = 11 |
||
MazeHeight% = 9 |
MazeHeight% = 9 |
||
MazeCell% = 50 |
MazeCell% = 50 |
||
Line 217: | Line 672: | ||
ENDIF |
ENDIF |
||
NEXT |
NEXT |
||
ENDPROC</ |
ENDPROC</syntaxhighlight> |
||
=={{header|C}}== |
=={{header|C}}== |
||
See [[Maze_generation#C|Maze generation]] for combined gen/solve code. |
See [[Maze_generation#C|Maze generation]] for combined gen/solve code. |
||
=={{header|C#}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="C#"> |
|||
using System; |
|||
using System.Text; |
|||
public class Maze |
|||
{ |
|||
private char[,] cells; |
|||
private char[,] hWalls; // Horizontal walls |
|||
private char[,] vWalls; // Vertical walls |
|||
private Random rand = new Random(); |
|||
public Maze(int rows, int cols) |
|||
{ |
|||
cells = new char[rows, cols]; |
|||
hWalls = new char[rows + 1, cols]; // Include walls for the bottom |
|||
vWalls = new char[rows, cols + 1]; // Include walls for the right side |
|||
// Initialize walls |
|||
for (int i = 0; i < rows; i++) |
|||
{ |
|||
for (int j = 0; j < cols; j++) |
|||
{ |
|||
hWalls[i, j] = '-'; |
|||
vWalls[i, j] = '|'; |
|||
} |
|||
} |
|||
// Set the outer walls for the bottom and right |
|||
for (int i = 0; i < cols; i++) |
|||
{ |
|||
hWalls[rows, i] = '-'; |
|||
} |
|||
for (int i = 0; i < rows; i++) |
|||
{ |
|||
vWalls[i, cols] = '|'; |
|||
} |
|||
} |
|||
public override string ToString() |
|||
{ |
|||
var builder = new StringBuilder(); |
|||
for (int i = 0; i < cells.GetLength(0); i++) |
|||
{ |
|||
// Top walls |
|||
for (int j = 0; j < cells.GetLength(1); j++) |
|||
{ |
|||
builder.Append("+"); |
|||
builder.Append(hWalls[i, j] == '-' ? "---" : " "); |
|||
} |
|||
builder.AppendLine("+"); |
|||
// Side walls and cells |
|||
for (int j = 0; j < cells.GetLength(1); j++) |
|||
{ |
|||
builder.Append(vWalls[i, j] == '|' ? "| " : " "); |
|||
char cell = cells[i, j] == '\0' ? ' ' : cells[i, j]; |
|||
builder.Append(cell + " "); |
|||
} |
|||
builder.AppendLine("|"); |
|||
} |
|||
// Bottom walls |
|||
for (int j = 0; j < cells.GetLength(1); j++) |
|||
{ |
|||
builder.Append("+---"); |
|||
} |
|||
builder.AppendLine("+"); |
|||
return builder.ToString(); |
|||
} |
|||
public void Generate() |
|||
{ |
|||
Generate(rand.Next(cells.GetLength(0)), rand.Next(cells.GetLength(1))); |
|||
} |
|||
private void Generate(int r, int c) |
|||
{ |
|||
cells[r, c] = ' '; |
|||
int[] dirs = { 0, 1, 2, 3 }; |
|||
Shuffle(dirs); |
|||
foreach (int dir in dirs) |
|||
{ |
|||
switch (dir) |
|||
{ |
|||
case 0: // Up |
|||
if (r > 0 && cells[r - 1, c] == '\0') |
|||
{ |
|||
hWalls[r, c] = ' '; |
|||
Generate(r - 1, c); |
|||
} |
|||
break; |
|||
case 1: // Down |
|||
if (r < cells.GetLength(0) - 1 && cells[r + 1, c] == '\0') |
|||
{ |
|||
hWalls[r + 1, c] = ' '; |
|||
Generate(r + 1, c); |
|||
} |
|||
break; |
|||
case 2: // Right |
|||
if (c < cells.GetLength(1) - 1 && cells[r, c + 1] == '\0') |
|||
{ |
|||
vWalls[r, c + 1] = ' '; |
|||
Generate(r, c + 1); |
|||
} |
|||
break; |
|||
case 3: // Left |
|||
if (c > 0 && cells[r, c - 1] == '\0') |
|||
{ |
|||
vWalls[r, c] = ' '; |
|||
Generate(r, c - 1); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
private void Shuffle(int[] array) |
|||
{ |
|||
for (int i = array.Length - 1; i > 0; i--) |
|||
{ |
|||
int j = rand.Next(i + 1); |
|||
int temp = array[i]; |
|||
array[i] = array[j]; |
|||
array[j] = temp; |
|||
} |
|||
} |
|||
public void Solve(int startRow, int startCol, int endRow, int endCol) |
|||
{ |
|||
if (Solve(startRow, startCol, endRow, endCol, -1)) |
|||
{ |
|||
cells[startRow, startCol] = 'S'; |
|||
cells[endRow, endCol] = 'F'; |
|||
} |
|||
} |
|||
private bool Solve(int r, int c, int endRow, int endCol, int dir) |
|||
{ |
|||
if (r == endRow && c == endCol) return true; |
|||
// Up |
|||
if (dir != 1 && r > 0 && hWalls[r, c] == ' ' && Solve(r - 1, c, endRow, endCol, 0)) |
|||
{ |
|||
cells[r, c] = '^'; |
|||
return true; |
|||
} |
|||
// Down |
|||
if (dir != 0 && r < cells.GetLength(0) - 1 && hWalls[r + 1, c] == ' ' && Solve(r + 1, c, endRow, endCol, 1)) |
|||
{ |
|||
cells[r, c] = 'v'; |
|||
return true; |
|||
} |
|||
// Right |
|||
if (dir != 3 && c < cells.GetLength(1) - 1 && vWalls[r, c + 1] == ' ' && Solve(r, c + 1, endRow, endCol, 2)) |
|||
{ |
|||
cells[r, c] = '>'; |
|||
return true; |
|||
} |
|||
// Left |
|||
if (dir != 2 && c > 0 && vWalls[r, c] == ' ' && Solve(r, c - 1, endRow, endCol, 3)) |
|||
{ |
|||
cells[r, c] = '<'; |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
var maze = new Maze(4, 7); |
|||
maze.Generate(); |
|||
Random rand = new Random(); |
|||
maze.Solve(rand.Next(4), rand.Next(7), rand.Next(4), rand.Next(7)); |
|||
Console.WriteLine(maze); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
+---+---+---+---+---+---+---+ |
|||
| | | v S | |
|||
+ +---+ +---+ + +---+ |
|||
| | | | > v | |
|||
+---+---+ + +---+---+ + |
|||
| | | | F | |
|||
+ +---+---+ + + + + |
|||
| | | |
|||
+---+---+---+---+---+---+---+ |
|||
</pre> |
|||
=={{header|C++}}== |
=={{header|C++}}== |
||
Line 226: | Line 883: | ||
[[File:maze_solving_cpp.png|300px]] |
[[File:maze_solving_cpp.png|300px]] |
||
< |
<syntaxhighlight lang="cpp"> |
||
#include <windows.h> |
#include <windows.h> |
||
#include <iostream> |
#include <iostream> |
||
Line 663: | Line 1,320: | ||
} |
} |
||
//-------------------------------------------------------------------------------------------------- |
//-------------------------------------------------------------------------------------------------- |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Clojure}}== |
|||
<syntaxhighlight lang="clojure">(ns small-projects.find-shortest-way |
|||
(:require [clojure.string :as str])) |
|||
;Misk functions |
|||
(defn cell-empty? [maze coords] |
|||
(= :empty (get-in maze coords))) |
|||
(defn wall? [maze coords] |
|||
(= :wall (get-in maze coords))) |
|||
(defn track? [maze coords] |
|||
(= :track (get-in maze coords))) |
|||
(defn get-neighbours [maze [y x cell]] |
|||
[[y (dec x)] [(inc y) x] [y (inc x)] [(dec y) x]]) |
|||
(defn get-difference [coll1 filter-coll] |
|||
(filter #(not (contains? filter-coll %)) coll1)) |
|||
(defn get-empties [maze cell] |
|||
(->> (get-neighbours maze cell) |
|||
(filter (partial cell-empty? maze)))) |
|||
(defn possible-ways [maze cell filter-coll] |
|||
(-> (get-empties maze cell) |
|||
(get-difference filter-coll))) |
|||
(defn replace-cells [maze coords v] |
|||
(if (empty? coords) |
|||
maze |
|||
(recur (assoc-in maze (first coords) v) (rest coords) v))) |
|||
;Print and parse functions |
|||
(def cell-code->str |
|||
[" " " " " " " " "· " "╵ " "╴ " "┘ " |
|||
" " " " " " " " "╶─" "└─" "──" "┴─" |
|||
" " " " " " " " "╷ " "│ " "┐ " "┤ " |
|||
" " " " " " " " "┌─" "├─" "┬─" "┼─" |
|||
" " " " " " " " "■ " "╹ " "╸ " "┛ " |
|||
" " " " " " " " "╺━" "┗━" "━━" "┻━" |
|||
" " " " " " " " "╻ " "┃ " "┓ " "┫ " |
|||
" " " " " " " " "┏━" "┣━" "┳━" "╋━" |
|||
" "]) |
|||
(defn get-cell-code [maze coords] |
|||
(let [mode (if (track? maze coords) 1 0) |
|||
check (if (zero? mode) wall? track?)] |
|||
(transduce |
|||
(comp |
|||
(map (partial check maze)) |
|||
(keep-indexed (fn [idx test] (when test idx))) |
|||
(map (partial bit-shift-left 1))) |
|||
(completing bit-or) |
|||
(bit-shift-left mode 5) |
|||
(sort (conj (get-neighbours maze coords) coords))))) |
|||
(defn code->str [cell-code] |
|||
(nth cell-code->str cell-code)) |
|||
(defn maze->str-symbols [maze] |
|||
(for [y (range (count maze))] |
|||
(for [x (range (count (nth maze y)))] |
|||
(code->str (get-cell-code maze [y x]))))) |
|||
(defn maze->str [maze] |
|||
(->> (maze->str-symbols maze) |
|||
(map str/join) |
|||
(str/join "\n"))) |
|||
(defn parse-pretty-maze [maze-str] |
|||
(->> (str/split-lines maze-str) |
|||
(map (partial take-nth 2)) |
|||
(map (partial map #(if (= \space %) :empty :wall))) |
|||
(map vec) |
|||
(vec))) |
|||
;Core |
|||
(defn find-new-border [maze border old-border] |
|||
(apply conj (map (fn [cell] |
|||
(zipmap (possible-ways maze cell (conj border old-border)) |
|||
(repeat cell))) |
|||
(keys border)))) |
|||
(defn backtrack [visited route] |
|||
(let [cur-cell (get visited (first route))] |
|||
(if (= cur-cell :start) |
|||
route |
|||
(recur visited (conj route cur-cell))))) |
|||
(defn breadth-first-search [maze start-cell end-cell] |
|||
(loop [visited {start-cell :start} |
|||
border {start-cell :start} |
|||
old-border {start-cell :start}] |
|||
(if (contains? old-border end-cell) |
|||
(backtrack visited (list end-cell)) |
|||
(recur |
|||
(conj visited border) |
|||
(find-new-border maze border old-border) |
|||
border)))) |
|||
(def maze (parse-pretty-maze maze-str)) |
|||
(def solved-maze |
|||
(replace-cells maze (breadth-first-search maze [1 1] [19 19]) :track)) |
|||
(println (maze->str solved-maze))</syntaxhighlight> |
|||
{{in}} |
|||
<pre>┌───────────┬───────┬───────┬───────────┐ |
|||
│ │ │ │ │ |
|||
│ ╶───────┘ ╷ ╵ ╷ ╵ ┌───╴ │ |
|||
│ │ │ │ │ |
|||
│ ╶───────┬───┴───┬───┴───┬───┘ ╷ │ |
|||
│ │ │ │ │ │ |
|||
├───────╴ │ ╷ ╵ ╷ │ ┌───┘ │ |
|||
│ │ │ │ │ │ │ |
|||
│ ┌───┬───┘ ├───────┤ │ ├───────┤ |
|||
│ │ │ │ │ │ │ │ |
|||
│ ╵ ╵ ╶───┴───┐ │ │ ╵ ╷ │ |
|||
│ │ │ │ │ │ |
|||
├───────────────┐ ╵ │ │ ╶───┤ │ |
|||
│ │ │ │ │ │ |
|||
│ ╶───┐ ┌───┴───╴ │ │ ┌───┘ │ |
|||
│ │ │ │ │ │ │ |
|||
├───╴ │ │ ╶───┬───┤ └───┤ ╶───┤ |
|||
│ │ │ │ │ │ │ |
|||
│ ╶───┤ └───╴ ╵ └───┐ └───╴ │ |
|||
│ │ │ │ |
|||
└───────┴───────────────────┴───────────┘ </pre> |
|||
{{out}} |
|||
<pre>┌───────────┬───────┬───────┬───────────┐ |
|||
│ ╻ │ │ │ │ |
|||
│ ┃ ╶───────┘ ╷ ╵ ╷ ╵ ┌───╴ │ |
|||
│ ┃ │ │ │ │ |
|||
│ ┃ ╶───────┬───┴───┬───┴───┬───┘ ╷ │ |
|||
│ ┗━━━━━━━┓ │ ┏━━━┓ │ ┏━━━┓ │ │ │ |
|||
├───────╴ ┃ │ ┃ ╷ ┃ ╵ ┃ ╷ ┃ │ ┌───┘ │ |
|||
│ ┏━━━━━━━┛ │ ┃ │ ┗━━━┛ │ ┃ │ │ │ |
|||
│ ┃ ┌───┬───┘ ┃ ├───────┤ ┃ │ ├───────┤ |
|||
│ ┃ │ │ ┏━━━┛ │ │ ┃ │ │ │ |
|||
│ ┃ ╵ ╵ ┃ ╶───┴───┐ │ ┃ │ ╵ ╷ │ |
|||
│ ┗━━━━━━━┛ │ │ ┃ │ │ │ |
|||
├───────────────┐ ╵ │ ┃ │ ╶───┤ │ |
|||
│ │ │ ┃ │ │ │ |
|||
│ ╶───┐ ┌───┴───╴ │ ┃ │ ┌───┘ │ |
|||
│ │ │ │ ┃ │ │ │ |
|||
├───╴ │ │ ╶───┬───┤ ┃ └───┤ ╶───┤ |
|||
│ │ │ │ │ ┗━━━┓ │ │ |
|||
│ ╶───┤ └───╴ ╵ └───┐ ┃ └───╴ │ |
|||
│ │ │ ┗━━━━━━━╸ │ |
|||
└───────┴───────────────────┴───────────┘ </pre> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
{{incorrect|D|Is output double spaced, with only two dots above the E rather than 4, see comment [[User:Petelomax|Pete Lomax]] ([[User talk:Petelomax|talk]]) 06:09, 19 March 2017 (UTC)}} |
{{incorrect|D|Is output double spaced, with only two dots above the E rather than 4, see comment [[User:Petelomax|Pete Lomax]] ([[User talk:Petelomax|talk]]) 06:09, 19 March 2017 (UTC)}} |
||
This entry reads a maze generated by http://rosettacode.org/wiki/Maze_generation#D and chooses two random start-end points. |
This entry reads a maze generated by http://rosettacode.org/wiki/Maze_generation#D and chooses two random start-end points. |
||
< |
<syntaxhighlight lang="d">import std.stdio, std.random, std.string, std.array, std.algorithm, |
||
std.file, std.conv; |
std.file, std.conv; |
||
Line 709: | Line 1,520: | ||
maze[end.y][end.x] = 'E'; |
maze[end.y][end.x] = 'E'; |
||
writefln("%-(%s\n%)", maze); |
writefln("%-(%s\n%)", maze); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
<pre>+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
||
Line 732: | Line 1,543: | ||
| . . . . . . S | | | |
| . . . . . . S | | | |
||
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+</pre> |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+</pre> |
||
=={{header|Delphi}}== |
|||
<p>Code requires source code of [[Maze generation#Delphi|Maze generation]]</p> |
|||
<syntaxhighlight lang="pascal">procedure SolveMaze(var AMaze: TMaze; const S, E: TPoint); |
|||
var |
|||
Route : TRoute; |
|||
Position : TPoint; |
|||
V : TPoint; // delta vector |
|||
begin |
|||
ClearVisited(AMaze); |
|||
Position := S; |
|||
Route := TStack<TPoint>.Create; |
|||
with Position do |
|||
try |
|||
AMaze[x, y].Visited := True; |
|||
repeat |
|||
if (y > 0) and not AMaze[x, y-1].Visited and AMaze[x, y].PassTop then V := Point(0, -1) else |
|||
if (x < mwidth-1) and not AMaze[x+1, y].Visited and AMaze[x+1, y].PassLeft then V := Point(1, 0) else |
|||
if (y < mheight-1) and not AMaze[x, y+1].Visited and AMaze[x, y+1].PassTop then V := Point(0, 1) else |
|||
if (x > 0) and not AMaze[x-1, y].Visited and AMaze[x, y].PassLeft then V := Point(-1, 0) else |
|||
begin |
|||
if Route.Count = 0 then Exit; // we are back at start so no way found |
|||
Position := Route.Pop; // step back |
|||
Continue; |
|||
end; |
|||
Route.Push(Position); // save current position to route |
|||
Offset(V); // move forward |
|||
AMaze[x, y].Visited := True; |
|||
until Position = E; // solved |
|||
ClearVisited(AMaze); |
|||
while Route.Count > 0 do // Route to Maze |
|||
with Route.Pop do |
|||
AMaze[x, y].Visited := True; |
|||
finally |
|||
Route.Free; |
|||
end; |
|||
end; |
|||
procedure Main; |
|||
var |
|||
Maze: TMaze; |
|||
S, E: TPoint; |
|||
begin |
|||
Randomize; |
|||
PrepareMaze(Maze); |
|||
S := Point(Random(mwidth), Random(mheight)); |
|||
E := Point(Random(mwidth), Random(mheight)); |
|||
SolveMaze(Maze, S, E); |
|||
Write(MazeToString(Maze, S, E)); |
|||
ReadLn; |
|||
end; |
|||
begin |
|||
Main; |
|||
end.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
|||
| | | S * * | | |
|||
+ + + +---+ + +---+---+---+ + +---+---+ +---+---+---+---+---+---+ +---+ + + |
|||
| | | | | | | | | | * | | | | | |
|||
+ + +---+ + +---+ +---+ +---+ + +---+ + +---+ +---+---+ +---+ + + + |
|||
| | | | | | | * * * | | | | | | |
|||
+ + +---+ +---+---+---+ +---+---+---+ +---+---+ +---+---+---+---+---+ +---+---+ + |
|||
| | | * * * * * | * | | | | | | | |
|||
+ +---+ +---+ +---+---+---+ +---+---+ + + +---+ + + +---+---+ + + +---+ |
|||
| | | | * * * | | * * * | * * | | | | | | | | |
|||
+ + +---+ +---+---+ + +---+---+ +---+ +---+ + +---+---+---+ +---+ + + + |
|||
| | | | * | | * * * | | | | | | |
|||
+---+---+ +---+ + + + +---+ +---+---+---+ +---+---+ + + +---+ +---+---+ + |
|||
| | | | | * | | | | | | | | | |
|||
+ + + + +---+ + +---+ +---+---+ + +---+ +---+ + +---+ +---+ +---+---+ |
|||
| | | | E * | * * | | | | | | | | | | | |
|||
+ +---+---+ +---+ +---+ + + + +---+ +---+ +---+---+ + +---+---+---+---+ + |
|||
| | | | * | * | | | | | | | |
|||
+ +---+ +---+ + +---+ +---+---+---+ +---+ +---+ + +---+---+ +---+ +---+---+ |
|||
| | | | * * | * | | | | | | | |
|||
+---+---+ + + + + + + +---+---+---+ +---+ +---+---+ + +---+ +---+---+ + |
|||
| | | | | * * | | | | | | | |
|||
+ +---+---+ + +---+---+---+---+ + + +---+---+---+ +---+---+---+ +---+---+---+ + |
|||
| | | | | | | | | | | |
|||
+---+ +---+ +---+ + +---+---+---+ +---+ +---+ +---+---+ +---+---+ +---+---+---+ |
|||
| | | | | | | | | | | | | | | |
|||
+ + + +---+ + +---+---+ + + + +---+ + + + + + + + + +---+ + |
|||
| | | | | | | | | | |
|||
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
|||
</pre> |
|||
=={{header|EasyLang}}== |
|||
[https://easylang.online/show/#cod=hVTbbqMwFHz3V4zUl1yEa1KQEmnZH0FoRcE0VsCOTJo2/fqVLxi7u9p9SfCcCz5zZpjFF0eFvCQSFQ7YYTbIHjkZDM4YnrGRyMBouSUjl5jqBhI7SPIE8tp2lzet3mUPxhi5atVhPquPX1P7xUFBCYBu5K02D4PSEKYtbgq+l8EBiAFTLRpUYB4B8IkKG4EM+RaT6iHX0CMK9eIehzo1Ko3T6bRCk7pzfGKHARkGPOOAR3xaMzXvbhiwQ07L5d9H7Sz2Zx45v4JRlhNK1DDMtbl4jRwSWY5NJrdoHBmTY+Kq5oWNqb6qOQwayDKHPvQ54AUFmpi0Ar36kDeF3N+nRwXdyl5NEAskNCrYG/V13yzkmmdU6GuR0G0utTc1jd1JK/sAGiUsgYjGtCTaVDRmKE5po4TL3sQrpx6zOk9Re+GJXv4hE6+RPKwiyZVp1iEczRv/BiGDNGL/MyR9aK2yrzt7l3je89KnmZIEJyspZ7d1N77jjZIwNFkV8ISFEH2xVHZqXDixVjBYaoZHAgcjOAt0aiSp+PdW7oUXvz/ZAqG7kdvzCy0JJW+jem1HDNba1BtbjXduNstiPYvBZ61S0fz2rmUgLcxzYiyxT+nrnSwcQb7Ff1qqYRV/YaXktXBetFAklticsYcaLHN5iF3VbD57TrXONUJHFjG1P366iYM75DfpO1Y2Jne/7KYwj7b9mhfxFFVH9Egcj8ckEoiKYPrdVi4pJ+4ezMrxQMhv Run it] |
|||
<syntaxhighlight lang=text> |
|||
size = 15 |
|||
n = 2 * size + 1 |
|||
f = 100 / (n - 0.5) |
|||
len m[] n * n |
|||
# |
|||
background 000 |
|||
proc show_maze . . |
|||
clear |
|||
for i = 1 to len m[] |
|||
if m[i] = 0 |
|||
x = (i - 1) mod n |
|||
y = (i - 1) div n |
|||
color 999 |
|||
move x * f - f / 2 y * f - f / 2 |
|||
rect f * 1.5 f * 1.5 |
|||
. |
|||
. |
|||
sleep 0.01 |
|||
. |
|||
offs[] = [ 1 n -1 (-n) ] |
|||
proc m_maze pos . . |
|||
m[pos] = 0 |
|||
show_maze |
|||
d[] = [ 1 2 3 4 ] |
|||
for i = 4 downto 1 |
|||
d = random i |
|||
dir = offs[d[d]] |
|||
d[d] = d[i] |
|||
if m[pos + dir] = 1 and m[pos + 2 * dir] = 1 |
|||
m[pos + dir] = 0 |
|||
m_maze pos + 2 * dir |
|||
. |
|||
. |
|||
. |
|||
endpos = n * n - 1 |
|||
proc make_maze . . |
|||
for i = 1 to len m[] |
|||
m[i] = 1 |
|||
. |
|||
for i = 1 to n |
|||
m[i] = 2 |
|||
m[n * i] = 2 |
|||
m[n * i - n + 1] = 2 |
|||
m[n * n - n + i] = 2 |
|||
. |
|||
h = 2 * random 15 - n + n * 2 * random 15 |
|||
m_maze h |
|||
m[endpos] = 0 |
|||
. |
|||
make_maze |
|||
show_maze |
|||
# |
|||
proc mark pos col . . |
|||
x = (pos - 1) mod n |
|||
y = (pos - 1) div n |
|||
color col |
|||
move x * f + f / 4 y * f + f / 4 |
|||
circle f / 3.5 |
|||
. |
|||
global found . |
|||
proc solve dir0 pos . . |
|||
if found = 1 |
|||
return |
|||
. |
|||
mark pos 900 |
|||
sleep 0.05 |
|||
if pos = endpos |
|||
found = 1 |
|||
return |
|||
. |
|||
of = random 4 - 1 |
|||
for h = 1 to 4 |
|||
dir = (h + of) mod1 4 |
|||
posn = pos + offs[dir] |
|||
if dir <> dir0 and m[posn] = 0 |
|||
solve (dir + 1) mod 4 + 1 posn |
|||
if found = 0 |
|||
mark posn 888 |
|||
sleep 0.08 |
|||
. |
|||
. |
|||
. |
|||
. |
|||
sleep 1 |
|||
solve 0 n + 2 |
|||
</syntaxhighlight> |
|||
=={{header|EGL}}== |
=={{header|EGL}}== |
||
< |
<syntaxhighlight lang="egl">program MazeGenAndSolve |
||
// First and last columns/rows are "dead" cells. Makes generating |
// First and last columns/rows are "dead" cells. Makes generating |
||
Line 995: | Line 1,988: | ||
end |
end |
||
end</ |
end</syntaxhighlight> |
||
{{out|Output example (for 10x10 maze)}} |
{{out|Output example (for 10x10 maze)}} |
||
<pre> |
<pre> |
||
Line 1,041: | Line 2,034: | ||
| * * | E | * * | |
| * * | E | * * | |
||
+---+---+---+---+---+---+---+---+---+---+ |
+---+---+---+---+---+---+---+---+---+---+ |
||
</pre> |
|||
=={{header|Emacs Lisp}}== |
|||
{{libheader|cl-lib}} |
|||
<syntaxhighlight lang="lisp">(require 'cl-lib) |
|||
(cl-defstruct maze rows cols data) |
|||
(defmacro maze-pt (w r c) |
|||
`(+ (* (mod ,r (maze-rows ,w)) (maze-cols ,w)) |
|||
(mod ,c (maze-cols ,w)))) |
|||
(defmacro maze-ref (w r c) |
|||
`(aref (maze-data ,w) (maze-pt ,w ,r ,c))) |
|||
(defun new-maze (rows cols) |
|||
(setq rows (1+ rows) |
|||
cols (1+ cols)) |
|||
(let ((m (make-maze :rows rows :cols cols :data (make-vector (* rows cols) nil)))) |
|||
(dotimes (r rows) |
|||
(dotimes (c cols) |
|||
(setf (maze-ref m r c) (copy-sequence '(wall ceiling))))) |
|||
(dotimes (r rows) |
|||
(maze-set m r (1- cols) 'visited)) |
|||
(dotimes (c cols) |
|||
(maze-set m (1- rows) c 'visited)) |
|||
(maze-unset m 0 0 'ceiling) ;; Maze Entrance |
|||
(maze-unset m (1- rows) (- cols 2) 'ceiling) ;; Maze Exit |
|||
m)) |
|||
(defun maze-is-set (maze r c v) |
|||
(member v (maze-ref maze r c))) |
|||
(defun maze-set (maze r c v) |
|||
(let ((cell (maze-ref maze r c))) |
|||
(when (not (member v cell)) |
|||
(setf (maze-ref maze r c) (cons v cell))))) |
|||
(defun maze-unset (maze r c v) |
|||
(setf (maze-ref maze r c) (delete v (maze-ref maze r c)))) |
|||
(defun print-maze (maze &optional marks) |
|||
(dotimes (r (1- (maze-rows maze))) |
|||
(dotimes (c (1- (maze-cols maze))) |
|||
(princ (if (maze-is-set maze r c 'ceiling) "+---" "+ "))) |
|||
(princ "+") |
|||
(terpri) |
|||
(dotimes (c (1- (maze-cols maze))) |
|||
(princ (if (maze-is-set maze r c 'wall) "|" " ")) |
|||
(princ (if (member (cons r c) marks) " * " " "))) |
|||
(princ "|") |
|||
(terpri)) |
|||
(dotimes (c (1- (maze-cols maze))) |
|||
(princ (if (maze-is-set maze (1- (maze-rows maze)) c 'ceiling) "+---" "+ "))) |
|||
(princ "+") |
|||
(terpri)) |
|||
(defun shuffle (lst) |
|||
(sort lst (lambda (a b) (= 1 (random 2))))) |
|||
(defun to-visit (maze row col) |
|||
(let (unvisited) |
|||
(dolist (p '((0 . +1) (0 . -1) (+1 . 0) (-1 . 0))) |
|||
(let ((r (+ row (car p))) |
|||
(c (+ col (cdr p)))) |
|||
(unless (maze-is-set maze r c 'visited) |
|||
(push (cons r c) unvisited)))) |
|||
unvisited)) |
|||
(defun make-passage (maze r1 c1 r2 c2) |
|||
(if (= r1 r2) |
|||
(if (< c1 c2) |
|||
(maze-unset maze r2 c2 'wall) ; right |
|||
(maze-unset maze r1 c1 'wall)) ; left |
|||
(if (< r1 r2) |
|||
(maze-unset maze r2 c2 'ceiling) ; up |
|||
(maze-unset maze r1 c1 'ceiling)))) ; down |
|||
(defun dig-maze (maze row col) |
|||
(let (backup |
|||
(run 0)) |
|||
(maze-set maze row col 'visited) |
|||
(push (cons row col) backup) |
|||
(while backup |
|||
(setq run (1+ run)) |
|||
(when (> run (/ (+ row col) 3)) |
|||
(setq run 0) |
|||
(setq backup (shuffle backup))) |
|||
(setq row (caar backup) |
|||
col (cdar backup)) |
|||
(let ((p (shuffle (to-visit maze row col)))) |
|||
(if p |
|||
(let ((r (caar p)) |
|||
(c (cdar p))) |
|||
(make-passage maze row col r c) |
|||
(maze-set maze r c 'visited) |
|||
(push (cons r c) backup)) |
|||
(pop backup) |
|||
(setq backup (shuffle backup)) |
|||
(setq run 0)))))) |
|||
(defun generate (rows cols) |
|||
(let* ((m (new-maze rows cols))) |
|||
(dig-maze m (random rows) (random cols)) |
|||
(print-maze m))) |
|||
(defun parse-ceilings (line) |
|||
(let (rtn |
|||
(i 1)) |
|||
(while (< i (length line)) |
|||
(push (eq ?- (elt line i)) rtn) |
|||
(setq i (+ i 4))) |
|||
(nreverse rtn))) |
|||
(defun parse-walls (line) |
|||
(let (rtn |
|||
(i 0)) |
|||
(while (< i (length line)) |
|||
(push (eq ?| (elt line i)) rtn) |
|||
(setq i (+ i 4))) |
|||
(nreverse rtn))) |
|||
(defun parse-maze (file-name) |
|||
(let ((rtn) |
|||
(lines (with-temp-buffer |
|||
(insert-file-contents-literally file-name) |
|||
(split-string (buffer-string) "\n" t)))) |
|||
(while lines |
|||
(push (parse-ceilings (pop lines)) rtn) |
|||
(push (parse-walls (pop lines)) rtn)) |
|||
(nreverse rtn))) |
|||
(defun read-maze (file-name) |
|||
(let* ((raw (parse-maze file-name)) |
|||
(rows (1- (/ (length raw) 2))) |
|||
(cols (length (car raw))) |
|||
(maze (new-maze rows cols))) |
|||
(dotimes (r rows) |
|||
(let ((ceilings (pop raw))) |
|||
(dotimes (c cols) |
|||
(unless (pop ceilings) |
|||
(maze-unset maze r c 'ceiling)))) |
|||
(let ((walls (pop raw))) |
|||
(dotimes (c cols) |
|||
(unless (pop walls) |
|||
(maze-unset maze r c 'wall))))) |
|||
maze)) |
|||
(defun find-exits (maze row col) |
|||
(let (exits) |
|||
(dolist (p '((0 . +1) (0 . -1) (-1 . 0) (+1 . 0))) |
|||
(let ((r (+ row (car p))) |
|||
(c (+ col (cdr p)))) |
|||
(unless |
|||
(cond |
|||
((equal p '(0 . +1)) (maze-is-set maze r c 'wall)) |
|||
((equal p '(0 . -1)) (maze-is-set maze row col 'wall)) |
|||
((equal p '(+1 . 0)) (maze-is-set maze r c 'ceiling)) |
|||
((equal p '(-1 . 0)) (maze-is-set maze row col 'ceiling))) |
|||
(push (cons r c) exits)))) |
|||
exits)) |
|||
(defun drop-visited (maze points) |
|||
(let (not-visited) |
|||
(while points |
|||
(unless (maze-is-set maze (caar points) (cdar points) 'visited) |
|||
(push (car points) not-visited)) |
|||
(pop points)) |
|||
not-visited)) |
|||
(defun solve-maze (maze) |
|||
(let (solution |
|||
(exit (cons (- (maze-rows maze) 2) (- (maze-cols maze) 2))) |
|||
(pt (cons 0 0))) |
|||
(while (not (equal pt exit)) |
|||
(maze-set maze (car pt) (cdr pt) 'visited) |
|||
(let ((exits (drop-visited maze (find-exits maze (car pt) (cdr pt))))) |
|||
(if (null exits) |
|||
(setq pt (pop solution)) |
|||
(push pt solution) |
|||
(setq pt (pop exits))))) |
|||
(push pt solution))) |
|||
(defun solve (file-name) |
|||
(let* ((maze (read-maze file-name)) |
|||
(solution (solve-maze maze))) |
|||
(print-maze maze solution))) |
|||
(solve "maze.txt")</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="height:35ex;overflow:scroll;">+ +---+---+---+---+---+---+---+---+---+ |
|||
| * * * | | | | |
|||
+---+---+ + +---+---+ +---+---+ + |
|||
| | * | | | | | | |
|||
+ + + + +---+ + + +---+ + |
|||
| | * * * * | | | |
|||
+---+---+---+---+---+ +---+---+ + + |
|||
| | | | | * * | | | | |
|||
+ +---+ + + +---+ + + + + |
|||
| | | | | * * | | |
|||
+ + + + +---+ + + +---+ + |
|||
| | | | | | * * * | |
|||
+ + + +---+---+---+ +---+---+ + |
|||
| | | | | | * | |
|||
+ + +---+---+ + + + + + + |
|||
| | | | | | * | |
|||
+ + + +---+---+---+---+---+ + + |
|||
| | | | | * | |
|||
+ +---+---+ + + +---+---+---+ + |
|||
| | | * | |
|||
+---+---+---+---+---+---+---+---+---+ + |
|||
</pre> |
</pre> |
||
=={{header|Erlang}}== |
=={{header|Erlang}}== |
||
Using the maze from [[Maze_generation]]. When the path splits each possibility gets its own process that checks if it is the correct one or a dead end. It is intentional that the receive statement in loop_stop/5 only selects successful result. |
Using the maze from [[Maze_generation]]. When the path splits each possibility gets its own process that checks if it is the correct one or a dead end. It is intentional that the receive statement in loop_stop/5 only selects successful result. |
||
<syntaxhighlight lang="erlang"> |
|||
<lang Erlang> |
|||
-module( maze_solving ). |
-module( maze_solving ). |
||
Line 1,085: | Line 2,300: | ||
{ok, Cells} -> {ok, Cells} |
{ok, Cells} -> {ok, Cells} |
||
end. |
end. |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,115: | Line 2,330: | ||
On standard input, takes a maze made up of "+", "|", and "---" (i. e. each cell is two lines high and four characters wide), such as produced by the [[Maze generation#Haskell|Haskell]] or [[Maze generation#Java|Java]] generators. |
On standard input, takes a maze made up of "+", "|", and "---" (i. e. each cell is two lines high and four characters wide), such as produced by the [[Maze generation#Haskell|Haskell]] or [[Maze generation#Java|Java]] generators. |
||
< |
<syntaxhighlight lang="frege">module MazeSolver where |
||
import frege.IO |
import frege.IO |
||
Line 1,187: | Line 2,402: | ||
brin <- BufferedReader.fromISR isrin |
brin <- BufferedReader.fromISR isrin |
||
lns <- BufferedReader.getlines brin |
lns <- BufferedReader.getlines brin |
||
printStr $ unlines $ fromMaybe ["can't solve"] $ solve lns</ |
printStr $ unlines $ fromMaybe ["can't solve"] $ solve lns</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,214: | Line 2,429: | ||
=={{header|Go}}== |
=={{header|Go}}== |
||
Generates maze, picks start and finish cells randomly, solves, prints. |
Generates maze, picks start and finish cells randomly, solves, prints. |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 1,376: | Line 2,591: | ||
rSolve(ra, ca, -1) |
rSolve(ra, ca, -1) |
||
m.c2[ra][ca] = 'S' |
m.c2[ra][ca] = 'S' |
||
}</ |
}</syntaxhighlight> |
||
{{out|Example output:}} |
{{out|Example output:}} |
||
<pre> |
<pre> |
||
Line 1,397: | Line 2,612: | ||
<!-- ugh, syntax colorer seems to not understand that single quote (prime) can be part of an identifier --> |
<!-- ugh, syntax colorer seems to not understand that single quote (prime) can be part of an identifier --> |
||
< |
<syntaxhighlight lang="haskell">#!/usr/bin/runhaskell |
||
import Data.Maybe |
import Data.Maybe (fromMaybe) |
||
-- given two points, returns the average of them |
-- given two points, returns the average of them |
||
average :: (Int, Int) -> (Int, Int) -> (Int, Int) |
average :: (Int, Int) -> (Int, Int) -> (Int, Int) |
||
average (x, y) ( |
average (x, y) (x_, y_) = ((x + x_) `div` 2, (y + y_) `div` 2) |
||
-- given a maze and a tuple of position and wall position, returns |
-- given a maze and a tuple of position and wall position, returns |
||
Line 1,421: | Line 2,636: | ||
-- given a maze and a position, draw a '*' at that position in the maze |
-- given a maze and a position, draw a '*' at that position in the maze |
||
draw :: [String] -> (Int, Int) -> [String] |
draw :: [String] -> (Int, Int) -> [String] |
||
draw maze (x,y) = |
draw maze (x, y) = |
||
let row = maze !! y |
|||
in substitute maze y $ substitute row x '*' |
|||
-- given a maze, a previous position, and a list of tuples of potential |
-- given a maze, a previous position, and a list of tuples of potential |
||
-- new positions and their wall positions, returns the solved maze, or |
-- new positions and their wall positions, returns the solved maze, or |
||
-- None if it cannot be solved |
-- None if it cannot be solved |
||
tryMoves :: |
tryMoves :: [String] |
||
-> (Int, Int) |
|||
-> [((Int, Int), (Int, Int))] |
|||
-> Maybe [String] |
|||
tryMoves _ _ [] = Nothing |
tryMoves _ _ [] = Nothing |
||
tryMoves maze prevPos ((newPos,wallPos):more) = |
tryMoves maze prevPos ((newPos, wallPos):more) = |
||
case |
case solve_ maze newPos prevPos of |
||
Nothing -> tryMoves maze prevPos more |
|||
Just maze_ -> Just $ foldl draw maze_ [newPos, wallPos] |
|||
-- given a maze, a new position, and a previous position, returns |
-- given a maze, a new position, and a previous position, returns |
||
-- the solved maze, or None if it cannot be solved |
-- the solved maze, or None if it cannot be solved |
||
-- (assumes goal is upper-left corner of maze) |
-- (assumes goal is upper-left corner of maze) |
||
solve_ :: [String] -> (Int, Int) -> (Int, Int) -> Maybe [String] |
|||
solve_ maze (2, 1) _ = Just maze |
|||
solve_ maze pos@(x, y) prevPos = |
|||
let newPositions = [(x, y - 2), (x + 4, y), (x, y + 2), (x - 4, y)] |
let newPositions = [(x, y - 2), (x + 4, y), (x, y + 2), (x - 4, y)] |
||
notPrev |
notPrev pos_ = pos_ /= prevPos |
||
newPositions_ = filter notPrev newPositions |
|||
wallPositions = map (average pos) |
wallPositions = map (average pos) newPositions_ |
||
zipped = zip |
zipped = zip newPositions_ wallPositions |
||
legalMoves = filter (notBlocked maze) zipped |
legalMoves = filter (notBlocked maze) zipped |
||
in tryMoves maze pos legalMoves |
in tryMoves maze pos legalMoves |
||
Line 1,451: | Line 2,670: | ||
-- (starts at lower right corner and goes to upper left corner) |
-- (starts at lower right corner and goes to upper left corner) |
||
solve :: [String] -> Maybe [String] |
solve :: [String] -> Maybe [String] |
||
solve maze = |
solve maze = solve_ (draw maze start) start (-1, -1) |
||
where |
|||
where startx = length (head maze) - 3 |
|||
startx = length (head maze) - 3 |
|||
starty = length maze - 2 |
|||
start = (startx, starty) |
|||
start = (startx, starty) |
|||
-- takes unsolved maze on standard input, prints solved maze on standard output |
-- takes unsolved maze on standard input, prints solved maze on standard output |
||
main = |
main = |
||
let main_ = unlines . fromMaybe ["can_t solve"] . solve . lines |
|||
in interact main_ |
|||
</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
+---+---+---+---+---+---+---+---+---+---+---+ |
+---+---+---+---+---+---+---+---+---+---+---+ |
||
Line 1,487: | Line 2,707: | ||
Replace the main with this: |
Replace the main with this: |
||
< |
<syntaxhighlight lang="icon">procedure main(A) |
||
/mh := \A[1] | 12 |
/mh := \A[1] | 12 |
||
/mw := \A[2] | 16 |
/mw := \A[2] | 16 |
||
Line 1,499: | Line 2,719: | ||
until Event() == &lpress # wait |
until Event() == &lpress # wait |
||
close(mz.window) |
close(mz.window) |
||
end</ |
end</syntaxhighlight> |
||
And include this after the Generator and Display procedures. |
And include this after the Generator and Display procedures. |
||
< |
<syntaxhighlight lang="icon">procedure Solver(r,c) |
||
static maze,h,w,rd |
static maze,h,w,rd |
||
Line 1,538: | Line 2,758: | ||
} |
} |
||
return mz |
return mz |
||
end</ |
end</syntaxhighlight> |
||
The following Unicon-only solution is a variant of the above. It shares the same |
The following Unicon-only solution is a variant of the above. It shares the same |
||
Line 1,547: | Line 2,767: | ||
cyclic paths. The shortest solution path is then marked and displayed. |
cyclic paths. The shortest solution path is then marked and displayed. |
||
< |
<syntaxhighlight lang="unicon">global showMice |
||
import Utils # To get 'Args' singleton class |
import Utils # To get 'Args' singleton class |
||
Line 1,676: | Line 2,896: | ||
procedure waitForCompletion() |
procedure waitForCompletion() |
||
critical qMiceEmpty: while *qMice > 0 do wait(qMiceEmpty) |
critical qMiceEmpty: while *qMice > 0 do wait(qMiceEmpty) |
||
end</ |
end</syntaxhighlight> |
||
=={{header|J}}== |
=={{header|J}}== |
||
Due to reports that the program failed, the generation and solver are shown together. The display verb was extended to include a dyadic definition. The complete program was tested with j 8.0.2 on linux using no profile, the command |
Due to reports that the program failed, the generation and solver are shown together. The display verb was extended to include a dyadic definition. The complete program was tested with j 8.0.2 on linux using no profile, the command |
||
$ ijconsole -jprofile |
$ ijconsole -jprofile |
||
<syntaxhighlight lang="j"> |
|||
<lang J> |
|||
maze=:4 :0 |
maze=:4 :0 |
||
assert.0<:n=.<:x*y |
assert.0<:n=.<:x*y |
||
Line 1,777: | Line 2,997: | ||
'o' cells } a NB. exercise, replace cells with a gerund to draw arrows on the path. |
'o' cells } a NB. exercise, replace cells with a gerund to draw arrows on the path. |
||
) |
) |
||
</syntaxhighlight> |
|||
</lang> |
|||
Example:<pre> |
Example:<pre> |
||
4 (display~ solve)@maze 20 |
4 (display~ solve)@maze 20 |
||
Line 1,792: | Line 3,012: | ||
=={{header|Java}}== |
=={{header|Java}}== |
||
< |
<syntaxhighlight lang="java">import java.io.*; |
||
import java.util.*; |
import java.util.*; |
||
Line 1,940: | Line 3,160: | ||
System.out.println (solvedLines[i]); |
System.out.println (solvedLines[i]); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,966: | Line 3,186: | ||
Uses code from maze generation task |
Uses code from maze generation task |
||
{{works with|Java|8}} |
{{works with|Java|8}} |
||
< |
<syntaxhighlight lang="java">import java.awt.*; |
||
import java.awt.event.*; |
import java.awt.event.*; |
||
import java.awt.geom.Path2D; |
import java.awt.geom.Path2D; |
||
Line 2,147: | Line 3,367: | ||
}); |
}); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
=={{header| |
=={{header|JavaScript}}== |
||
Animated: generating and solving.<br />To start solving, click to choose a 'start' and an 'end' points. |
|||
<br />Go [http://paulo-jorente.de/tests/mazesolver/ here] to see it in action. |
|||
<syntaxhighlight lang="javascript"> |
|||
var ctx, wid, hei, cols, rows, maze, stack = [], start = {x:-1, y:-1}, end = {x:-1, y:-1}, grid = 8; |
|||
function drawMaze() { |
|||
for( var i = 0; i < cols; i++ ) { |
|||
for( var j = 0; j < rows; j++ ) { |
|||
switch( maze[i][j] ) { |
|||
case 0: ctx.fillStyle = "black"; break; |
|||
case 1: ctx.fillStyle = "green"; break; |
|||
case 2: ctx.fillStyle = "red"; break; |
|||
case 3: ctx.fillStyle = "yellow"; break; |
|||
case 4: ctx.fillStyle = "#500000"; break; |
|||
} |
|||
ctx.fillRect( grid * i, grid * j, grid, grid ); |
|||
} |
|||
} |
|||
} |
|||
function getFNeighbours( sx, sy, a ) { |
|||
var n = []; |
|||
if( sx - 1 > 0 && maze[sx - 1][sy] == a ) { |
|||
n.push( { x:sx - 1, y:sy } ); |
|||
} |
|||
if( sx + 1 < cols - 1 && maze[sx + 1][sy] == a ) { |
|||
n.push( { x:sx + 1, y:sy } ); |
|||
} |
|||
if( sy - 1 > 0 && maze[sx][sy - 1] == a ) { |
|||
n.push( { x:sx, y:sy - 1 } ); |
|||
} |
|||
if( sy + 1 < rows - 1 && maze[sx][sy + 1] == a ) { |
|||
n.push( { x:sx, y:sy + 1 } ); |
|||
} |
|||
return n; |
|||
} |
|||
function solveMaze() { |
|||
if( start.x == end.x && start.y == end.y ) { |
|||
for( var i = 0; i < cols; i++ ) { |
|||
for( var j = 0; j < rows; j++ ) { |
|||
switch( maze[i][j] ) { |
|||
case 2: maze[i][j] = 3; break; |
|||
case 4: maze[i][j] = 0; break; |
|||
} |
|||
} |
|||
} |
|||
drawMaze(); |
|||
return; |
|||
} |
|||
var neighbours = getFNeighbours( start.x, start.y, 0 ); |
|||
if( neighbours.length ) { |
|||
stack.push( start ); |
|||
start = neighbours[0]; |
|||
maze[start.x][start.y] = 2; |
|||
} else { |
|||
maze[start.x][start.y] = 4; |
|||
start = stack.pop(); |
|||
} |
|||
drawMaze(); |
|||
requestAnimationFrame( solveMaze ); |
|||
} |
|||
function getCursorPos( event ) { |
|||
var rect = this.getBoundingClientRect(); |
|||
var x = Math.floor( ( event.clientX - rect.left ) / grid ), |
|||
y = Math.floor( ( event.clientY - rect.top ) / grid ); |
|||
if( maze[x][y] ) return; |
|||
if( start.x == -1 ) { |
|||
start = { x: x, y: y }; |
|||
} else { |
|||
end = { x: x, y: y }; |
|||
maze[start.x][start.y] = 2; |
|||
solveMaze(); |
|||
} |
|||
} |
|||
function getNeighbours( sx, sy, a ) { |
|||
var n = []; |
|||
if( sx - 1 > 0 && maze[sx - 1][sy] == a && sx - 2 > 0 && maze[sx - 2][sy] == a ) { |
|||
n.push( { x:sx - 1, y:sy } ); n.push( { x:sx - 2, y:sy } ); |
|||
} |
|||
if( sx + 1 < cols - 1 && maze[sx + 1][sy] == a && sx + 2 < cols - 1 && maze[sx + 2][sy] == a ) { |
|||
n.push( { x:sx + 1, y:sy } ); n.push( { x:sx + 2, y:sy } ); |
|||
} |
|||
if( sy - 1 > 0 && maze[sx][sy - 1] == a && sy - 2 > 0 && maze[sx][sy - 2] == a ) { |
|||
n.push( { x:sx, y:sy - 1 } ); n.push( { x:sx, y:sy - 2 } ); |
|||
} |
|||
if( sy + 1 < rows - 1 && maze[sx][sy + 1] == a && sy + 2 < rows - 1 && maze[sx][sy + 2] == a ) { |
|||
n.push( { x:sx, y:sy + 1 } ); n.push( { x:sx, y:sy + 2 } ); |
|||
} |
|||
return n; |
|||
} |
|||
function createArray( c, r ) { |
|||
var m = new Array( c ); |
|||
for( var i = 0; i < c; i++ ) { |
|||
m[i] = new Array( r ); |
|||
for( var j = 0; j < r; j++ ) { |
|||
m[i][j] = 1; |
|||
} |
|||
} |
|||
return m; |
|||
} |
|||
function createMaze() { |
|||
var neighbours = getNeighbours( start.x, start.y, 1 ), l; |
|||
if( neighbours.length < 1 ) { |
|||
if( stack.length < 1 ) { |
|||
drawMaze(); stack = []; |
|||
start.x = start.y = -1; |
|||
document.getElementById( "canvas" ).addEventListener( "mousedown", getCursorPos, false ); |
|||
return; |
|||
} |
|||
start = stack.pop(); |
|||
} else { |
|||
var i = 2 * Math.floor( Math.random() * ( neighbours.length / 2 ) ) |
|||
l = neighbours[i]; maze[l.x][l.y] = 0; |
|||
l = neighbours[i + 1]; maze[l.x][l.y] = 0; |
|||
start = l |
|||
stack.push( start ) |
|||
} |
|||
drawMaze(); |
|||
requestAnimationFrame( createMaze ); |
|||
} |
|||
function createCanvas( w, h ) { |
|||
var canvas = document.createElement( "canvas" ); |
|||
wid = w; hei = h; |
|||
canvas.width = wid; canvas.height = hei; |
|||
canvas.id = "canvas"; |
|||
ctx = canvas.getContext( "2d" ); |
|||
ctx.fillStyle = "black"; ctx.fillRect( 0, 0, wid, hei ); |
|||
document.body.appendChild( canvas ); |
|||
} |
|||
function init() { |
|||
cols = 73; rows = 53; |
|||
createCanvas( grid * cols, grid * rows ); |
|||
maze = createArray( cols, rows ); |
|||
start.x = Math.floor( Math.random() * ( cols / 2 ) ); |
|||
start.y = Math.floor( Math.random() * ( rows / 2 ) ); |
|||
if( !( start.x & 1 ) ) start.x++; if( !( start.y & 1 ) ) start.y++; |
|||
maze[start.x][start.y] = 0; |
|||
createMaze(); |
|||
} |
|||
</syntaxhighlight> |
|||
HTML to test. |
|||
<pre> |
|||
<!DOCTYPE html> |
|||
<html><head><meta charset="UTF-8"> |
|||
<title>Maze</title> |
|||
<script src="maze.js"></script></head><body onload="init()"></body></html> |
|||
</pre> |
|||
=={{header|Julia}}== |
|||
{{works with|Julia|0.6}} |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="julia">""" |
|||
+ +---+---+ |
|||
| 1 2 3 | |
|||
+---+ + + |
|||
| 4 5 | 6 |
|||
+---+---+---+ |
|||
julia> const graph = [ |
|||
0 1 0 0 0 0; |
|||
1 0 1 0 1 0; |
|||
0 1 0 0 0 1; |
|||
0 0 0 0 1 0; |
|||
0 1 0 1 0 0; |
|||
0 0 1 0 0 0] |
|||
julia> dist, path = dijkstra(graph, 1) |
|||
(Dict(4=>3,2=>1,3=>2,5=>2,6=>3,1=>0), Dict(4=>5,2=>1,3=>2,5=>2,6=>3,1=>0)) |
|||
julia> printpath(path, 6) # Display solution of the maze |
|||
1 -> 2 -> 3 -> 6 |
|||
""" |
|||
function dijkstra(graph, source::Int=1) |
|||
# ensure that the adjacency matrix is squared |
|||
@assert size(graph, 1) == size(graph, 2) |
|||
inf = typemax(Int64) |
|||
n = size(graph, 1) |
|||
Q = IntSet(1:n) # Set of unvisited nodes |
|||
dist = Dict(n => inf for n in Q) # Unknown distance function from source to v |
|||
prev = Dict(n => 0 for n in Q) # Previous node in optimal path from source |
|||
dist[source] = 0 # Distance from source to source |
|||
function _minimumdist(nodes) # Find the less distant node among nodes |
|||
kmin, vmin = nothing, inf |
|||
for (k, v) in dist |
|||
if k ∈ nodes && v ≤ vmin |
|||
kmin, vmin = k, v |
|||
end |
|||
end |
|||
return kmin |
|||
end |
|||
# Until all nodes are visited... |
|||
while !isempty(Q) |
|||
u = _minimumdist(Q) # Vertex in Q with smallest dist[] |
|||
pop!(Q, u) |
|||
if dist[u] == inf break end # All remaining vertices are inaccessible from source |
|||
for v in 1:n # Each neighbor v of u |
|||
if graph[u, v] != 0 && v ∈ Q # where v has not yet been visited |
|||
alt = dist[u] + graph[u, v] |
|||
if alt < dist[v] # Relax (u, v, a) |
|||
dist[v] = alt |
|||
prev[v] = u |
|||
end |
|||
end |
|||
end |
|||
end |
|||
return dist, prev |
|||
end |
|||
function printpath(prev::Dict, target::Int) |
|||
path = "$target" |
|||
while prev[target] != 0 |
|||
target = prev[target] |
|||
path = "$target -> " * path |
|||
end |
|||
println(path) |
|||
end |
|||
const graph = [ |
|||
0 1 0 0 0 0; |
|||
1 0 1 0 1 0; |
|||
0 1 0 0 0 1; |
|||
0 0 0 0 1 0; |
|||
0 1 0 1 0 0; |
|||
0 0 1 0 0 0] |
|||
dist, path = dijkstra(graph) |
|||
printpath(path, 6)</syntaxhighlight> |
|||
=={{header|Kotlin}}== |
|||
{{trans|Java}} |
|||
<syntaxhighlight lang="scala">// Version 1.2.31 |
|||
import java.io.File |
|||
typealias Maze = List<CharArray> |
|||
/** |
|||
* Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that |
|||
* each cell in the maze is the same size horizontally as vertically. |
|||
* (Versus the expanded version, which looks better visually.) |
|||
* Also, converts each line of the maze from a String to a |
|||
* char[], because we'll want mutability when drawing the solution later. |
|||
*/ |
|||
fun decimateHorizontally(lines: List<String>): Maze { |
|||
val width = (lines[0].length + 1) / 2 |
|||
val c = List(lines.size) { CharArray(width) } |
|||
for (i in 0 until lines.size) { |
|||
for (j in 0 until width) c[i][j] = lines[i][j * 2] |
|||
} |
|||
return c |
|||
} |
|||
/** |
|||
* Given the maze, the x and y coordinates (which must be odd), |
|||
* and the direction we came from, return true if the maze is |
|||
* solvable, and draw the solution if so. |
|||
*/ |
|||
fun solveMazeRecursively(maze: Maze, x: Int, y: Int, d: Int): Boolean { |
|||
var ok = false |
|||
var i = 0 |
|||
while (i < 4 && !ok) { |
|||
if (i != d) { |
|||
// 0 = up, 1 = right, 2 = down, 3 = left |
|||
when(i) { |
|||
0 -> if (maze[y - 1][x] == ' ') ok = solveMazeRecursively (maze, x, y - 2, 2) |
|||
1 -> if (maze[y][x + 1] == ' ') ok = solveMazeRecursively (maze, x + 2, y, 3) |
|||
2 -> if (maze[y + 1][x] == ' ') ok = solveMazeRecursively (maze, x, y + 2, 0) |
|||
3 -> if (maze[y][x - 1] == ' ') ok = solveMazeRecursively (maze, x - 2, y, 1) |
|||
else -> {} |
|||
} |
|||
} |
|||
i++ |
|||
} |
|||
// check for end condition |
|||
if (x == 1 && y == 1) ok = true |
|||
// once we have found a solution, draw it as we unwind the recursion |
|||
if (ok) { |
|||
maze[y][x] = '*' |
|||
when (d) { |
|||
0 -> maze[y - 1][x] = '*' |
|||
1 -> maze[y][x + 1] = '*' |
|||
2 -> maze[y + 1][x] = '*' |
|||
3 -> maze[y][x - 1] = '*' |
|||
else -> {} |
|||
} |
|||
} |
|||
return ok |
|||
} |
|||
/** |
|||
* Solve the maze and draw the solution. For simplicity, |
|||
* assumes the starting point is the lower right, and the |
|||
* ending point is the upper left. |
|||
*/ |
|||
fun solveMaze(maze: Maze) = |
|||
solveMazeRecursively(maze, maze[0].size - 2, maze.size - 2, -1) |
|||
/** |
|||
* Opposite of decimateHorizontally(). Adds extra characters to make |
|||
* the maze "look right", and converts each line from char[] to |
|||
* String at the same time. |
|||
*/ |
|||
fun expandHorizontally(maze: Maze): Array<String> { |
|||
val tmp = CharArray(3) |
|||
val lines = Array<String>(maze.size) { "" } |
|||
for (i in 0 until maze.size) { |
|||
val sb = StringBuilder(maze[i].size * 2) |
|||
for (j in 0 until maze[i].size) { |
|||
if (j % 2 == 0) |
|||
sb.append(maze[i][j]) |
|||
else { |
|||
for (k in 0..2) tmp[k] = maze[i][j] |
|||
if (tmp[1] == '*') { |
|||
tmp[0] = ' ' |
|||
tmp[2] = ' ' |
|||
} |
|||
sb.append(tmp) |
|||
} |
|||
} |
|||
lines[i] = sb.toString() |
|||
} |
|||
return lines |
|||
} |
|||
/** |
|||
* Accepts a maze as generated by: |
|||
* http://rosettacode.org/wiki/Maze_generation#Kotlin |
|||
* in a file whose name is specified as a command-line argument. |
|||
*/ |
|||
fun main(args: Array<String>) { |
|||
if (args.size != 1) { |
|||
println("The maze file to be read should be passed as a single command line argument.") |
|||
return |
|||
} |
|||
val f = File(args[0]) |
|||
if (!f.exists()) { |
|||
println("Sorry ${args[0]} does not exist.") |
|||
return |
|||
} |
|||
val lines = f.readLines(Charsets.US_ASCII) |
|||
val maze = decimateHorizontally(lines) |
|||
solveMaze(maze) |
|||
val solvedLines = expandHorizontally(maze) |
|||
println(solvedLines.joinToString("\n")) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
Maze (maze.txt) produced by the maze generation program: |
|||
<pre> |
|||
+---+---+---+---+---+---+---+---+ |
|||
| | | | |
|||
+---+---+ + + +---+ + + |
|||
| | | | | | | |
|||
+ + + +---+ + +---+ + |
|||
| | | | | | |
|||
+ + + + +---+---+---+ + |
|||
| | | | | | |
|||
+---+ + +---+---+---+ + + |
|||
| | | | | | |
|||
+ +---+ + +---+ + + + |
|||
| | | | | | |
|||
+ + +---+---+ +---+---+ + |
|||
| | | | | |
|||
+ +---+ + +---+ + +---+ |
|||
| | | | |
|||
+---+---+---+---+---+---+---+---+ |
|||
</pre> |
|||
Solution generated by this program when passed maze.txt - follow *'s from bottom right (start) to top left (finish): |
|||
<pre> |
|||
+---+---+---+---+---+---+---+---+ |
|||
| * * * * * | * * * * * | | |
|||
+---+---+ * + + * +---+ * + + |
|||
| | * | | * | | * * * | |
|||
+ + + * +---+ * + +---+ * + |
|||
| | | * | * * * | * | |
|||
+ + + * + * +---+---+---+ * + |
|||
| | | * | * * * * * * * | * | |
|||
+---+ + * +---+---+---+ * + * + |
|||
| | * | * * * * * | * | * | |
|||
+ +---+ * + * +---+ * + * + * + |
|||
| | * * * | * * * | * * * | * | |
|||
+ + * +---+---+ * +---+---+ * + |
|||
| * * * | * * * | * * * | * * * | |
|||
+ * +---+ * + * +---+ * + * +---+ |
|||
| * * * * * | * * * * * | * * * | |
|||
+---+---+---+---+---+---+---+---+ |
|||
</pre> |
|||
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
|||
===Graph=== |
===Graph=== |
||
Solving the maze generated in [[Maze_generation#Graph]]: |
Solving the maze generated in [[Maze_generation#Graph]]: |
||
< |
<syntaxhighlight lang="mathematica">HighlightGraph[maze, PathGraph@FindShortestPath[maze, 1, 273]]</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
[[File:MathematicaMazeGraphSolving.png]] |
[[File:MathematicaMazeGraphSolving.png]] |
||
=={{header|Nim}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="nim">import random, strutils |
|||
type |
|||
Direction {.pure.} = enum None, Up, Left, Down, Right |
|||
Maze = object |
|||
cells: seq[string] |
|||
hwalls: seq[string] |
|||
vwalls: seq[string] |
|||
#################################################################################################### |
|||
# Maze creation. |
|||
func initMaze(rows, cols: Positive): Maze = |
|||
## Initialize a maze description. |
|||
var h = repeat('-', cols) |
|||
var v = repeat("|", cols) |
|||
for i in 0..<rows: |
|||
result.cells.add newString(cols) |
|||
result.hwalls.add h |
|||
result.vwalls.add v |
|||
proc gen(maze: var Maze; r, c: Natural) = |
|||
## Generate a maze starting from point (r, c). |
|||
maze.cells[r][c] = ' ' |
|||
var dirs = [Up, Left, Down, Right] |
|||
dirs.shuffle() |
|||
for dir in dirs: |
|||
case dir |
|||
of None: |
|||
discard |
|||
of Up: |
|||
if r > 0 and maze.cells[r-1][c] == '\0': |
|||
maze.hwalls[r][c] = chr(0) |
|||
maze.gen(r-1, c) |
|||
of Left: |
|||
if c > 0 and maze.cells[r][c-1] == '\0': |
|||
maze.vwalls[r][c] = chr(0) |
|||
maze.gen(r, c-1) |
|||
of Down: |
|||
if r < maze.cells.high and maze.cells[r+1][c] == '\0': |
|||
maze.hwalls[r+1][c] = chr(0) |
|||
maze.gen(r+1, c) |
|||
of Right: |
|||
if c < maze.cells[0].high and maze.cells[r][c+1] == '\0': |
|||
maze.vwalls[r][c+1] = chr(0) |
|||
maze.gen(r, c+1) |
|||
proc gen(maze: var Maze) = |
|||
## Generate a maze, choosing a random starting point. |
|||
maze.gen(rand(maze.cells.high), rand(maze.cells[0].high)) |
|||
#################################################################################################### |
|||
# Maze solving. |
|||
proc solve(maze: var Maze; ra, ca, rz, cz: Natural) = |
|||
## Solve a maze by finding the path from point (ra, ca) to point (rz, cz). |
|||
proc rsolve(maze: var Maze; r, c: Natural; dir: Direction): bool {.discardable.} = |
|||
## Recursive solver. |
|||
if r == rz and c == cz: |
|||
maze.cells[r][c] = 'F' |
|||
return true |
|||
if dir != Down and maze.hwalls[r][c] == '\0': |
|||
if maze.rSolve(r-1, c, Up): |
|||
maze.cells[r][c] = '^' |
|||
maze.hwalls[r][c] = '^' |
|||
return true |
|||
if dir != Up and r < maze.hwalls.high and maze.hwalls[r+1][c] == '\0': |
|||
if maze.rSolve(r+1, c, Down): |
|||
maze.cells[r][c] = 'v' |
|||
maze.hwalls[r+1][c] = 'v' |
|||
return true |
|||
if dir != Left and c < maze.vwalls[0].high and maze.vwalls[r][c+1] == '\0': |
|||
if maze.rSolve(r, c+1, Right): |
|||
maze.cells[r][c] = '>' |
|||
maze.vwalls[r][c+1] = '>' |
|||
return true |
|||
if dir != Right and maze.vwalls[r][c] == '\0': |
|||
if maze.rSolve(r, c-1, Left): |
|||
maze.cells[r][c] = '<' |
|||
maze.vwalls[r][c] = '<' |
|||
return true |
|||
maze.rsolve(ra, ca, None) |
|||
maze.cells[ra][ca] = 'S' |
|||
#################################################################################################### |
|||
# Maze display. |
|||
func `$`(maze: Maze): string = |
|||
## Return the string representation fo a maze. |
|||
const |
|||
HWall = "+---" |
|||
HOpen = "+ " |
|||
VWall = "| " |
|||
VOpen = " " |
|||
RightCorner = "+\n" |
|||
RightWall = "|\n" |
|||
for r, hw in maze.hwalls: |
|||
for h in hw: |
|||
if h == '-' or r == 0: |
|||
result.add HWall |
|||
else: |
|||
result.add HOpen |
|||
if h notin {'-', '\0'}: result[^2] = h |
|||
result.add RightCorner |
|||
for c, vw in maze.vwalls[r]: |
|||
if vw == '|' or c == 0: |
|||
result.add VWall |
|||
else: |
|||
result.add VOpen |
|||
if vw notin {'|', '\0'}: result[^4] = vw |
|||
if maze.cells[r][c] != '\0': result[^2] = maze.cells[r][c] |
|||
result.add RightWall |
|||
for _ in 1..maze.hwalls[0].len: |
|||
result.add HWall |
|||
result.add RightCorner |
|||
#——————————————————————————————————————————————————————————————————————————————————————————————————— |
|||
when isMainModule: |
|||
const |
|||
Width = 8 |
|||
Height = 8 |
|||
randomize() |
|||
var maze = initMaze(Width, Height) |
|||
maze.gen() |
|||
var ra, rz = rand(Width - 1) |
|||
var ca, cz = rand(Height - 1) |
|||
while rz == ra and cz == ca: |
|||
# Make sur starting and ending points are different. |
|||
rz = rand(Width - 1) |
|||
cz = rand(Height - 1) |
|||
maze.solve(ra, ca , rz, cz) |
|||
echo maze</syntaxhighlight> |
|||
{{out}} |
|||
<pre>+---+---+---+---+---+---+---+---+ |
|||
| | > > > > > > > > > > v | |
|||
+ + ^ +---+---+---+---+ v + + |
|||
| | ^ < < | | v | | |
|||
+ +---+ ^ + +---+ + v +---+ |
|||
| | ^ | | | > > v | |
|||
+ +---+ ^ +---+---+ +---+ v + |
|||
| | ^ | | v | |
|||
+---+ + ^ + + +---+---+ v + |
|||
| | ^ | | | v | |
|||
+ +---+ ^ +---+---+ + + v + |
|||
| > > > > ^ | | | | v | |
|||
+ ^ +---+---+ + +---+ + v + |
|||
| ^ < < | F | | v | |
|||
+---+ ^ + ^ +---+---+---+---+ v + |
|||
| S | ^ < < < < < < < < < < | |
|||
+---+---+---+---+---+---+---+---+</pre> |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
This example includes maze generation code. |
This example includes maze generation code. |
||
< |
<syntaxhighlight lang="perl"> |
||
#!perl |
#!perl |
||
use strict; |
use strict; |
||
Line 2,235: | Line 4,031: | ||
print "Could not solve!\n"; |
print "Could not solve!\n"; |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>Here is the maze: |
<pre>Here is the maze: |
||
Line 2,282: | Line 4,078: | ||
+--+--+--+--+--+--+--+--+--+--+ |
+--+--+--+--+--+--+--+--+--+--+ |
||
</pre> |
</pre> |
||
=={{header|Perl 6}}== |
|||
{{works with|Rakudo|2017.02}} |
|||
(Includes maze generation code.) |
|||
<lang perl6>constant mapping = :OPEN(' '), |
|||
:N< ╵ >, |
|||
:E< ╶ >, |
|||
:NE< └ >, |
|||
:S< ╷ >, |
|||
:NS< │ >, |
|||
:ES< ┌ >, |
|||
:NES< ├ >, |
|||
:W< ╴ >, |
|||
:NW< ┘ >, |
|||
:EW< ─ >, |
|||
:NEW< ┴ >, |
|||
:SW< ┐ >, |
|||
:NSW< ┤ >, |
|||
:ESW< ┬ >, |
|||
:NESW< ┼ >, |
|||
:TODO< x >, |
|||
:TRIED< · >; |
|||
enum Sym (mapping.map: *.key); |
|||
my @ch = mapping.map: *.value; |
|||
enum Direction <DeadEnd Up Right Down Left>; |
|||
sub gen_maze ( $X, |
|||
$Y, |
|||
$start_x = (^$X).pick * 2 + 1, |
|||
$start_y = (^$Y).pick * 2 + 1 ) |
|||
{ |
|||
my @maze; |
|||
push @maze, $[ flat ES, -N, (ESW, EW) xx $X - 1, SW ]; |
|||
push @maze, $[ flat (NS, TODO) xx $X, NS ]; |
|||
for 1 ..^ $Y { |
|||
push @maze, $[ flat NES, EW, (NESW, EW) xx $X - 1, NSW ]; |
|||
push @maze, $[ flat (NS, TODO) xx $X, NS ]; |
|||
} |
|||
push @maze, $[ flat NE, (EW, NEW) xx $X - 1, -NS, NW ]; |
|||
@maze[$start_y][$start_x] = OPEN; |
|||
my @stack; |
|||
my $current = [$start_x, $start_y]; |
|||
loop { |
|||
if my $dir = pick_direction( $current ) { |
|||
@stack.push: $current; |
|||
$current = move( $dir, $current ); |
|||
} |
|||
else { |
|||
last unless @stack; |
|||
$current = @stack.pop; |
|||
} |
|||
} |
|||
return @maze; |
|||
sub pick_direction([$x,$y]) { |
|||
my @neighbors = |
|||
(Up if @maze[$y - 2][$x]), |
|||
(Down if @maze[$y + 2][$x]), |
|||
(Left if @maze[$y][$x - 2]), |
|||
(Right if @maze[$y][$x + 2]); |
|||
@neighbors.pick or DeadEnd; |
|||
} |
|||
sub move ($dir, @cur) { |
|||
my ($x,$y) = @cur; |
|||
given $dir { |
|||
when Up { @maze[--$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y--][$x+1] -= W; } |
|||
when Down { @maze[++$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y++][$x+1] -= W; } |
|||
when Left { @maze[$y][--$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x--] -= N; } |
|||
when Right { @maze[$y][++$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x++] -= N; } |
|||
} |
|||
@maze[$y][$x] = 0; |
|||
[$x,$y]; |
|||
} |
|||
} |
|||
sub display (@maze) { |
|||
for @maze -> @y { |
|||
for @y.rotor(2) -> ($w, $c) { |
|||
print @ch[abs $w]; |
|||
if $c >= 0 { print @ch[$c] x 3 } |
|||
else { print ' ', @ch[abs $c], ' ' } |
|||
} |
|||
say @ch[@y[*-1]]; |
|||
} |
|||
} |
|||
sub solve (@maze is copy, @from = [1, 1], @to = [@maze[0] - 2, @maze - 2]) { |
|||
my ($x, $y) = @from; |
|||
my ($xto, $yto) = @to; |
|||
my @stack; |
|||
sub drop-crumb($x,$y,$c) { @maze[$y][$x] = -$c } |
|||
drop-crumb($x,$y,N); |
|||
loop { |
|||
my $dir = pick_direction([$x,$y]); |
|||
if $dir { |
|||
($x, $y) = move($dir, [$x,$y]); |
|||
return @maze if $x == $xto and $y == $yto; |
|||
} |
|||
else { |
|||
@maze[$y][$x] = -TRIED; |
|||
($x,$y) = @stack.pop; |
|||
@maze[$y][$x] = -TRIED; |
|||
($x,$y) = @stack.pop; |
|||
} |
|||
} |
|||
sub pick_direction([$x,$y]) { |
|||
my @neighbors = |
|||
(Up unless @maze[$y - 1][$x]), |
|||
(Down unless @maze[$y + 1][$x]), |
|||
(Left unless @maze[$y][$x - 1]), |
|||
(Right unless @maze[$y][$x + 1]); |
|||
@neighbors.pick or DeadEnd; |
|||
} |
|||
sub move ($dir, @cur) { |
|||
my ($x,$y) = @cur; |
|||
given $dir { |
|||
when Up { for ^2 { push @stack, $[$x,$y--]; drop-crumb $x,$y,S; } } |
|||
when Down { for ^2 { push @stack, $[$x,$y++]; drop-crumb $x,$y,N; } } |
|||
when Left { for ^2 { push @stack, $[$x--,$y]; drop-crumb $x,$y,E; } } |
|||
when Right { for ^2 { push @stack, $[$x++,$y]; drop-crumb $x,$y,W; } } |
|||
} |
|||
$x,$y; |
|||
} |
|||
} |
|||
display solve gen_maze( 29, 19 );</lang> |
|||
{{out}} |
|||
<small><pre>┌ ╵ ────┬───────────────┬───────────────┬───────────────────────────────┬───────────┬───────────────┬───────────────┐ |
|||
│ ╵ · · │ ╷ ╴ ╴ ╴ ╴ │ │ ╷ ╴ ╴ · · · · · · · · · · · · │ ╷ ╴ ╴ · · │ · · · · · · · │ · · · · · · · │ |
|||
│ ╵ ╶───┘ ╷ ╶───┐ ╵ ╷ │ ╷ ╶───────┤ ╷ ╷ ╵ ╶───────┬───────┐ · ┌───┘ ╷ ╷ ╵ ╷ · │ · ╶───┬───╴ · │ · ╷ · ┌───╴ · │ |
|||
│ ╵ ╴ ╴ ╴ ╴ · · │ ╵ │ │ │ │ ╷ │ ╵ ╴ ╴ ╴ ╴ │ ╷ ╴ ╴ │ · │ ╷ ╴ ╴ │ ╵ │ · │ · · · │ · · · │ · │ · │ · · · │ |
|||
│ · ┌───────────┤ ╵ │ └───┴───────╴ │ ╷ ├───────┐ ╵ ╵ ╷ ╷ ╵ └───┘ ╷ ┌───┘ ╵ │ · │ · ╷ · │ · ┌───┴───┘ · │ · ╶───┤ |
|||
│ · │ ╶ ╶ ╶ ╶ ╷ │ ╵ │ │ ╷ │ │ ╵ ╴ ╴ │ ╵ ╴ ╴ ╴ ╴ │ ╶ ╶ ╵ │ · │ · │ · │ · │ · · · · · │ · · · │ |
|||
│ · │ ╵ ╶───┐ ╷ ╵ ╵ ├───────────────╴ │ ╷ └───╴ └───────┼───┬───────┤ ╵ ┌───┤ · ├───┘ · │ · │ · ╷ · ┌───┴───┐ · │ |
|||
│ · │ ╵ ╴ ╴ │ ╶ ╶ ╵ │ │ ╶ ╶ ╶ ╶ ╶ ╶ ╶ ╶ ╷ │ · │ ╶ ╶ ╷ │ ╵ │ · │ · │ · · · │ · │ · │ · │ · · · │ · │ |
|||
│ · └───┐ ╵ ├───────┴───────┐ ╶───────┴───────┬───────╴ ╷ │ · │ ╵ ╷ ╷ │ ╵ │ · │ · │ · ┌───┤ · │ · │ · │ · ╷ · ╵ · │ |
|||
│ · · · │ ╵ │ ╷ ╴ ╴ ╴ ╴ ╴ ╴ │ │ ╷ ╴ ╴ ╴ ╴ │ · │ ╵ │ ╷ │ ╵ │ · │ · │ · │ · │ · │ · │ · │ · │ · · · │ |
|||
├───╴ · │ ╵ │ ╷ ╶───────┐ ╵ ├───────────────┐ │ ╷ ╶───┬───┘ · │ ╵ │ ╷ ╵ ╵ │ · ╵ · ╵ · │ · ╵ · └───┘ · │ · ├───────┤ |
|||
│ · · · │ ╵ │ ╶ ╶ ╶ ╶ ╷ │ ╵ │ ╷ ╴ ╴ ╴ ╴ ╴ ╴ │ │ ╶ ╶ ╷ │ · · · │ ╵ │ ╶ ╶ ╵ │ · · · · · │ · · · · · · · │ · │ · · · │ |
|||
│ · ┌───┤ ╵ └───────╴ ╷ │ ╵ ╵ ╷ ┌───────┐ ╵ │ └───┐ ╷ └───┐ · │ ╵ ├───────┼───────┬───┴───────┬───┬───┘ · ├───╴ · │ |
|||
│ · │ · │ ╵ ╴ ╴ ╴ ╴ ╴ ╴ │ ╵ ╴ ╴ │ · · · │ ╵ │ │ ╶ ╶ ╷ │ · │ ╵ │ ╷ ╴ ╴ │ ╷ ╴ ╴ │ ╷ ╴ ╴ ╴ ╴ │ · │ · · · │ · · · │ |
|||
│ · ╵ · ├───────────────┴───────┴───┐ · │ ╵ └───────┴───┐ ╷ │ · │ ╵ ╵ ╷ ╷ ╵ ╵ ╷ ╷ ╵ │ ╷ ┌───╴ ╵ │ · │ · ╶───┤ · ╶───┤ |
|||
│ · · · │ · · · · · · · · · · · · · │ · │ ╵ ╴ ╴ ╴ ╴ · · │ ╷ │ · │ ╵ ╴ ╴ │ ╵ ╴ ╴ │ ╵ │ ╷ │ ╶ ╶ ╵ │ · │ · · · │ · · · │ |
|||
├───┐ · │ · ┌───────────╴ · ┌───┐ · │ · └───┬───╴ ╵ ┌───┘ ╷ │ · ├───────┴───────┤ ╵ ╵ ╷ │ ╵ ╶───┤ · └───┐ · │ · ╷ · │ |
|||
│ · │ · │ · │ · · · · · · · │ · │ · │ · · · │ ╶ ╶ ╵ │ ╷ ╴ ╴ │ · │ · · · · · · · │ ╵ ╴ ╴ │ ╵ ╴ ╴ │ · · · │ · │ · │ · │ |
|||
│ · │ · └───┘ · ┌───────────┘ · │ · ├───╴ · │ ╵ ┌───┘ ╷ ┌───┘ · │ · ╷ · ┌───╴ · └───────┴───┐ ╵ └───┐ · ╵ · ├───┘ · │ |
|||
│ · │ · · · · · │ · · · · · · · │ · │ · · · │ ╵ │ ╷ ╴ ╴ │ · · · │ · │ · │ · · · · · · · · · │ ╵ ╴ ╴ │ · · · │ · · · │ |
|||
│ · ├───────╴ · ├───┐ · ┌───┐ · ╵ · │ · ╶───┤ ╵ ╵ ╷ ┌───┘ · ╶───┤ · │ · └───┬───╴ · ┌───────┤ · ╷ ╵ │ · ╶───┘ · ╷ · │ |
|||
│ · │ · · · · · │ │ · │ · │ · · · │ · · · │ ╵ ╴ ╴ │ · · · · · │ · │ · · · │ · · · │ ╶ ╶ ╷ │ · │ ╵ │ · · · · · │ · │ |
|||
│ · ╵ · ┌───────┘ │ · ╵ · └───────┤ · ╷ · └───────┴───────┐ · ╵ · └───┐ · └───┐ · │ ╵ ╷ ╷ └───┘ ╵ ├───────────┤ · │ |
|||
│ · · · │ │ · · · · · · · │ · │ · · · · · · · · · │ · · · · · │ · · · │ · │ ╵ │ ╶ ╶ ╶ ╶ ╵ │ ╷ ╴ ╴ ╴ ╴ │ · │ |
|||
│ · ╶───┤ ╶───────┴───┬───────┐ · ├───┘ · ┌───────────┐ · ├───────────┴───╴ · │ · │ ╵ └───────┐ · │ ╷ ┌───╴ ╵ │ · │ |
|||
│ · · · │ │ · · · │ · │ · · · │ · · · · · │ · │ · · · · · · · · · │ · │ ╵ ╴ ╴ ╴ ╴ │ · │ ╷ │ ╶ ╶ ╵ │ · │ |
|||
├───┐ · ├───────┬───╴ │ · ╷ · │ · │ · ┌───┤ · ╶───────┤ · │ · ╶───────┬───┐ · ├───┴───────┐ ╵ └───┘ ╷ │ ╵ ┌───┴───┤ |
|||
│ · │ · │ │ │ · │ · │ · │ · │ · │ · · · · · │ · │ · · · · · │ · │ · │ │ ╵ ╴ ╴ ╴ ╴ │ ╵ │ ╷ ╴ ╴ │ |
|||
│ · │ · │ ╷ ╵ ┌───┘ · │ · ╵ · ╵ · │ · ╵ · ┌───┐ · ╵ · └───────╴ · │ · ╵ · │ ┌───╴ └───────┐ · │ ╵ ╵ ╷ ╷ ╵ │ |
|||
│ · │ · │ │ │ · · · │ · · · · · │ · · · │ · │ · · · · · · · · · │ · · · │ │ │ · │ ╵ ╴ ╴ │ ╵ │ |
|||
│ · │ · │ └───┬───┴───────┴───┬───────┤ · ┌───┘ · ├───────────────────┴───────┤ └───────────┐ └───┴───────┤ ╵ │ |
|||
│ · │ · │ │ │ │ · │ · · · │ │ │ │ ╵ │ |
|||
│ · ╵ · ├───┐ │ ╶───────┐ ╵ ╷ │ · ╵ · ╷ · │ ╶───────────────────┐ └───┬───────┐ └───────┬───┐ │ ╵ │ |
|||
│ · · · │ · │ │ │ │ │ · · · │ · │ │ │ │ │ │ │ ╵ │ |
|||
│ · ╶───┤ · │ └───────┐ ├───────┤ └───┬───┴───┼───────────┬───────┐ ├───┐ ╵ ╷ ├───────┐ │ │ │ ╵ │ |
|||
│ · · · │ · │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╵ │ |
|||
├───╴ · ╵ · └───────┐ ╵ │ ╷ └───╴ ╵ ╷ ╵ ┌───╴ ╵ ╷ ╵ │ └───────┘ ╵ ╷ ╵ │ ╵ ╵ ╵ │ |
|||
│ · · · · · · · · · │ │ │ │ │ │ │ │ │ ╵ │ |
|||
└───────────────────┴───────┴───┴───────────────┴───────┴───────────┴───────┴───────────────────┴───────┴──────── │ ┘</pre></small> |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
Combined generator and solver. |
Combined generator and solver. |
||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<lang Phix>constant w = 11, h = 8 |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
-- demo\rosetta\Maze_solving.exw |
|||
sequence wall = join(repeat("+",w+1),"---")&"\n", |
|||
-- ============================= |
|||
cell = join(repeat("|",w+1)," ? ")&"\n", |
|||
--</span> |
|||
grid = split(join(repeat(wall,h+1),cell),'\n') |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">h</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">8</span> |
|||
procedure amaze(integer x, integer y) |
|||
grid[y][x] = ' ' -- mark cell visited |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">wall</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"+"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #008000;">"---"</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span> |
|||
sequence p = shuffle({{x-4,y},{x,y+2},{x+4,y},{x,y-2}}) |
|||
<span style="color: #000000;">cell</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"|"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #008000;">" ? "</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span> |
|||
for i=1 to length(p) do |
|||
<span style="color: #000000;">grid</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wall</span><span style="color: #0000FF;">,</span><span style="color: #000000;">h</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #000000;">cell</span><span style="color: #0000FF;">),</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span> |
|||
integer {nx,ny} = p[i] |
|||
if nx>1 and nx<w*4 and ny>1 and ny<=2*h and grid[ny][nx]='?' then |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">amaze</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">)</span> |
|||
integer mx = (x+nx)/2 |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> <span style="color: #000080;font-style:italic;">-- mark cell visited</span> |
|||
grid[(y+ny)/2][mx-1..mx+1] = ' ' -- knock down wall |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">shuffle</span><span style="color: #0000FF;">({{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}})</span> |
|||
amaze(nx,ny) |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
end if |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">p</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> |
|||
end for |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">nx</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">nx</span><span style="color: #0000FF;"><</span><span style="color: #000000;">w</span><span style="color: #0000FF;">*</span><span style="color: #000000;">4</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ny</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ny</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">h</span> <span style="color: #008080;">and</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">'?'</span> <span style="color: #008080;">then</span> |
|||
end procedure |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">mx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span> |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">][</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> <span style="color: #000080;font-style:italic;">-- knock down wall</span> |
|||
integer dx,dy -- door location (in a wall!) |
|||
<span style="color: #000000;">amaze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
function solve_maze(integer x, y) |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
sequence p = {{x-4,y},{x,y+2},{x+4,y},{x,y-2}} |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
for d=1 to length(p) do |
|||
integer {nx,ny} = p[d] |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">dx</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">dy</span> <span style="color: #000080;font-style:italic;">-- door location (in a wall!)</span> |
|||
integer {wx,wy} = {(x+nx)/2,(y+ny)/2} |
|||
if grid[wy][wx]=' ' then |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">solve_maze</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">)</span> |
|||
grid[wy][wx] = "-:-:"[d] -- mark path |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}}</span> |
|||
if {wx,wy}={dx,dy} then return true end if |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">d</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
if grid[ny][nx]=' ' then |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">p</span><span style="color: #0000FF;">[</span><span style="color: #000000;">d</span><span style="color: #0000FF;">]</span> |
|||
grid[ny][nx] = 'o' -- mark cell |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">wx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">wy</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}</span> |
|||
if solve_maze(nx,ny) then return true end if |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wy</span><span style="color: #0000FF;">][</span><span style="color: #000000;">wx</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span> |
|||
grid[ny][nx] = ' ' -- unmark cell |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wy</span><span style="color: #0000FF;">][</span><span style="color: #000000;">wx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"-:-:"</span><span style="color: #0000FF;">[</span><span style="color: #000000;">d</span><span style="color: #0000FF;">]</span> <span style="color: #000080;font-style:italic;">-- mark path</span> |
|||
end if |
|||
<span style="color: #008080;">if</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">wx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">wy</span><span style="color: #0000FF;">}={</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">true</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
grid[wy][wx] = ' ' -- unmark path |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span> |
|||
end if |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'o'</span> <span style="color: #000080;font-style:italic;">-- mark cell</span> |
|||
end for |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">solve_maze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">true</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
return false |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> <span style="color: #000080;font-style:italic;">-- unmark cell</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">wy</span><span style="color: #0000FF;">][</span><span style="color: #000000;">wx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> <span style="color: #000080;font-style:italic;">-- unmark path</span> |
|||
function heads() |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
return rand(2)=1 -- toin coss 50:50 true(1)/false(0) |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
end function |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">false</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
integer {x,y} = {(rand(w)*4)-1,rand(h)*2} |
|||
amaze(x,y) |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">heads</span><span style="color: #0000FF;">()</span> |
|||
-- mark start pos |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">1</span> <span style="color: #000080;font-style:italic;">-- toin coss 50:50 true(1)/false(0)</span> |
|||
grid[y][x] = '*' |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
-- add a random door (heads=rhs/lhs, tails=top/btm) |
|||
if heads() then |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{(</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">w</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">4</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}</span> |
|||
{dy,dx} = {rand(h)*2,heads()*w*4+1} |
|||
<span style="color: #000000;">amaze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">)</span> |
|||
grid[dy][dx] = ' ' |
|||
<span style="color: #000080;font-style:italic;">-- mark start pos</span> |
|||
else |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'*'</span> |
|||
{dy,dx} = {heads()*h*2+1,rand(w)*4-1} |
|||
<span style="color: #000080;font-style:italic;">-- add a random door (heads=rhs/lhs, tails=top/btm)</span> |
|||
grid[dy][dx-1..dx+1] = ' ' |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">heads</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">then</span> |
|||
end if |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">heads</span><span style="color: #0000FF;">()*</span><span style="color: #000000;">w</span><span style="color: #0000FF;">*</span><span style="color: #000000;">4</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> |
|||
{} = solve_maze(x,y) |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">][</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> |
|||
puts(1,join(grid,'\n'))</lang> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">heads</span><span style="color: #0000FF;">()*</span><span style="color: #000000;">h</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">w</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">4</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}</span> |
|||
<span style="color: #000000;">grid</span><span style="color: #0000FF;">[</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">][</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">solve_maze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">grid</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">))</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 2,535: | Line 4,165: | ||
| | o - o - o - o | | | |
| | o - o - o - o | | | |
||
+---+---+---+---+---+---+---+---+---+---+---+ |
+---+---+---+---+---+---+---+---+---+---+---+ |
||
</pre> |
|||
=={{header|Picat}}== |
|||
<syntaxhighlight lang="picat">main => |
|||
Maze0 = ["+---+---+---+---+---+---+---+---+", |
|||
"| | | | |", |
|||
"+ + + +---+ + +---+ +", |
|||
"| | | | | |", |
|||
"+---+---+ + +---+---+ + +", |
|||
"| | | | |", |
|||
"+---+ + + +---+---+---+ +", |
|||
"| | | | |", |
|||
"+ + + +---+---+---+ + +", |
|||
"| | | | | |", |
|||
"+ + + +---+ +---+---+ +", |
|||
"| | | | | |", |
|||
"+ +---+---+ +---+---+---+ +", |
|||
"| | | |", |
|||
"+ +---+ +---+ + +---+ +", |
|||
"| | | |", |
|||
"+---+---+---+---+---+---+---+---+"], |
|||
MaxR = len(Maze0) div 2, |
|||
MaxC = len(Maze0[1]) div 4, |
|||
Maze = new_array(MaxR,MaxC), |
|||
foreach (R in 1..MaxR, C in 1..MaxC) |
|||
Maze[R,C] = 0 |
|||
end, |
|||
fill_maze(Maze0,Maze,1), |
|||
solve_maze(Maze,(1,1),(MaxR,MaxC),Cost,Plan), |
|||
OutputMaze = new_array(MaxR,MaxC), |
|||
foreach ((R,C) in Plan) |
|||
OutputMaze[R,C] = '*' |
|||
end, |
|||
output_maze(Maze0,OutputMaze,1). |
|||
fill_maze([Line1,Line2|Maze0],Maze,R) => |
|||
fill_maze_cols(Line1,Maze,R,1), |
|||
fill_maze_cols(Line2,Maze,R,1), |
|||
fill_maze(Maze0,Maze,R+1). |
|||
fill_maze(_,_,_) => true. |
|||
fill_maze_cols(['+',' ',' ',' '|Line],Maze,R,C) => |
|||
Maze[R,C] := Maze[R,C] \/ 4, % up |
|||
Maze[R-1,C] := Maze[R-1,C] \/ 8, % down |
|||
fill_maze_cols(Line,Maze,R,C+1). |
|||
fill_maze_cols([' ',' ',' ',' '|Line],Maze,R,C) => |
|||
Maze[R,C] := Maze[R,C] \/ 1, % left |
|||
Maze[R,C-1] := Maze[R,C-1] \/ 2, % right |
|||
fill_maze_cols(Line,Maze,R,C+1). |
|||
fill_maze_cols([_,_,_,_|Line],Maze,R,C) => |
|||
fill_maze_cols(Line,Maze,R,C+1). |
|||
fill_maze_cols(_,_,_,_) => true. |
|||
table (+,+,+,min,-) |
|||
solve_maze(_Maze,FPos,FPos,Cost,Plan) => |
|||
Cost = 0, |
|||
Plan = [FPos]. |
|||
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=> |
|||
Maze[R,C] /\ 1 == 1, % left |
|||
solve_maze(Maze,(R,C-1),FPos,Cost1,Plan1), |
|||
Plan = [Pos|Plan1], |
|||
Cost = Cost1+1. |
|||
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=> |
|||
Maze[R,C] /\ 2 == 2, % right |
|||
solve_maze(Maze,(R,C+1),FPos,Cost1,Plan1), |
|||
Plan = [Pos|Plan1], |
|||
Cost = Cost1+1. |
|||
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=> |
|||
Maze[R,C] /\ 4 == 4, % up |
|||
solve_maze(Maze,(R-1,C),FPos,Cost1,Plan1), |
|||
Plan = [Pos|Plan1], |
|||
Cost = Cost1+1. |
|||
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=> |
|||
Maze[R,C] /\ 8 == 8, % down |
|||
solve_maze(Maze,(R+1,C),FPos,Cost1,Plan1), |
|||
Plan = [Pos|Plan1], |
|||
Cost = Cost1+1. |
|||
output_maze([Line1,Line2|Maze0],Maze,R) => |
|||
output_maze_cols(Line1,Maze,R,1), |
|||
output_maze_cols(Line2,Maze,R,1), |
|||
output_maze(Maze0,Maze,R+1). |
|||
output_maze([Line],_,_) => println(Line). |
|||
output_maze_cols([' ',' ',' ',' '|Line],Maze,R,C) => |
|||
if Maze[R,C] == '*' then |
|||
print(" * ") |
|||
else |
|||
print(" ") |
|||
end, |
|||
output_maze_cols(Line,Maze,R,C+1). |
|||
output_maze_cols(['|',' ',' ',' '|Line],Maze,R,C) => |
|||
if Maze[R,C] == '*' then |
|||
print("| * ") |
|||
else |
|||
print("| ") |
|||
end, |
|||
output_maze_cols(Line,Maze,R,C+1). |
|||
output_maze_cols(['+',' ',' ',' '|Line],Maze,R,C) => |
|||
if Maze[R,C] == '*' && Maze[R-1,C] == '*' then |
|||
print("+ * ") |
|||
else |
|||
print("+ ") |
|||
end, |
|||
output_maze_cols(Line,Maze,R,C+1). |
|||
output_maze_cols([C1,C2,C3,C4|Line],Maze,R,C) => |
|||
printf("%c%c%c%c",C1,C3,C3,C4), |
|||
output_maze_cols(Line,Maze,R,C+1). |
|||
output_maze_cols(Line,_,_,_) => println(Line). |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
+---+---+---+---+---+---+---+---+ |
|||
| * | * * | | | |
|||
+ * + * + * +---+ + +---+ + |
|||
| * * | * | | | | |
|||
+---+---+ * + +---+---+ + + |
|||
| | * | | | |
|||
+---+ + * + +---+---+---+ + |
|||
| | * | | | |
|||
+ + + * +---+---+---+ + + |
|||
| | | * | | | |
|||
+ + + * +---+ +---+---+ + |
|||
| | | * * | | | |
|||
+ +---+---+ * +---+---+---+ + |
|||
| | * * | * * * | |
|||
+ +---+ +---+ * + * +---+ * + |
|||
| | * * | * | |
|||
+---+---+---+---+---+---+---+---+ |
|||
</pre> |
</pre> |
||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
< |
<syntaxhighlight lang="picolisp">(de shortestPath (Goal This Maze) |
||
(let (Path NIL Best NIL Dir " > ") |
(let (Path NIL Best NIL Dir " > ") |
||
(recur (This Path Dir) |
(recur (This Path Dir) |
||
Line 2,553: | Line 4,312: | ||
(=: mark NIL) ) ) ) |
(=: mark NIL) ) ) ) |
||
(disp Maze 0 |
(disp Maze 0 |
||
'((Fld) (if (asoq Fld Best) (cdr @) " ")) ) ) )</ |
'((Fld) (if (asoq Fld Best) (cdr @) " ")) ) ) )</syntaxhighlight> |
||
Using the maze produced in [[Maze generation#PicoLisp]], this finds the shortest path from the top-left cell 'a8' to the bottom-right exit 'k1': |
Using the maze produced in [[Maze generation#PicoLisp]], this finds the shortest path from the top-left cell 'a8' to the bottom-right exit 'k1': |
||
<pre>: (shortestPath 'a8 'k1 (maze 11 8)) |
<pre>: (shortestPath 'a8 'k1 (maze 11 8)) |
||
Line 2,578: | Line 4,337: | ||
Works with SWI-Prolog and XPCE. |
Works with SWI-Prolog and XPCE. |
||
< |
<syntaxhighlight lang="prolog">:- dynamic cell/2. |
||
:- dynamic maze/3. |
:- dynamic maze/3. |
||
:- dynamic path/1. |
:- dynamic path/1. |
||
Line 2,726: | Line 4,485: | ||
\+cell(L, C1). |
\+cell(L, C1). |
||
</syntaxhighlight> |
|||
</lang> |
|||
output : [[File:Prolog-Maze-solved.jpg]] |
output : [[File:Prolog-Maze-solved.jpg]] |
||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
< |
<syntaxhighlight lang="purebasic">;code from the maze generation task is place here in its entirety before the rest of the code |
||
Procedure displayMazePath(Array maze(2), List Path.POINT()) |
Procedure displayMazePath(Array maze(2), List Path.POINT()) |
||
Line 2,839: | Line 4,598: | ||
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input() |
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input() |
||
CloseConsole() |
CloseConsole() |
||
EndIf</ |
EndIf</syntaxhighlight> |
||
Using the maze produced in [[Maze generation#PureBasic]], this additional code will find and display the path between two random maze cells. A working example requires combining the two code listings by placing the 'maze generation' code at the beginning of the 'maze solving' code. |
Using the maze produced in [[Maze generation#PureBasic]], this additional code will find and display the path between two random maze cells. A working example requires combining the two code listings by placing the 'maze generation' code at the beginning of the 'maze solving' code. |
||
Line 2,861: | Line 4,620: | ||
=={{header|Python}}== |
=={{header|Python}}== |
||
<syntaxhighlight lang="python"> |
|||
<lang Python> |
|||
# python 3 |
# python 3 |
||
Line 2,912: | Line 4,671: | ||
cell = predecessor[cell] |
cell = predecessor[cell] |
||
print(0) |
print(0) |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
Following function returns a path between two cells in a maze which is created by the <tt>build-maze</tt> function (See [[Maze_generation#Racket|Maze generation]]). |
Following function returns a path between two cells in a maze which is created by the <tt>build-maze</tt> function (See [[Maze_generation#Racket|Maze generation]]). |
||
< |
<syntaxhighlight lang="racket"> |
||
;; Returns a path connecting two given cells in the maze |
;; Returns a path connecting two given cells in the maze |
||
;; find-path :: Maze Cell Cell -> (Listof Cell) |
;; find-path :: Maze Cell Cell -> (Listof Cell) |
||
Line 2,934: | Line 4,693: | ||
(append-map (next-turn (list p1)) |
(append-map (next-turn (list p1)) |
||
(alternatives p1 (list p1))))) |
(alternatives p1 (list p1))))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
Reading a maze from a file |
Reading a maze from a file |
||
< |
<syntaxhighlight lang="racket"> |
||
;; Reads the maze from the textual form |
;; Reads the maze from the textual form |
||
;; read-maze :: File-path -> Maze |
;; read-maze :: File-path -> Maze |
||
Line 2,956: | Line 4,715: | ||
1)) |
1)) |
||
(maze N M tbl)))) |
(maze N M tbl)))) |
||
</syntaxhighlight> |
|||
</lang> |
|||
Printing out a maze with a path between two given cells |
Printing out a maze with a path between two given cells |
||
< |
<syntaxhighlight lang="racket"> |
||
;; Shows a maze with a path connecting two given cells |
;; Shows a maze with a path connecting two given cells |
||
(define (show-path m p1 p2) |
(define (show-path m p1 p2) |
||
Line 2,982: | Line 4,741: | ||
(displayln "+")) |
(displayln "+")) |
||
(newline)) |
(newline)) |
||
</syntaxhighlight> |
|||
</lang> |
|||
Example: |
Example: |
||
Line 3,021: | Line 4,780: | ||
| * * * * * | | * * * * | |
| * * * * * | | * * * * | |
||
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
||
</pre> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2017.02}} |
|||
(Includes maze generation code.) |
|||
<syntaxhighlight lang="raku" line>constant mapping = :OPEN(' '), |
|||
:N< ╵ >, |
|||
:E< ╶ >, |
|||
:NE< └ >, |
|||
:S< ╷ >, |
|||
:NS< │ >, |
|||
:ES< ┌ >, |
|||
:NES< ├ >, |
|||
:W< ╴ >, |
|||
:NW< ┘ >, |
|||
:EW< ─ >, |
|||
:NEW< ┴ >, |
|||
:SW< ┐ >, |
|||
:NSW< ┤ >, |
|||
:ESW< ┬ >, |
|||
:NESW< ┼ >, |
|||
:TODO< x >, |
|||
:TRIED< · >; |
|||
enum Sym (mapping.map: *.key); |
|||
my @ch = mapping.map: *.value; |
|||
enum Direction <DeadEnd Up Right Down Left>; |
|||
sub gen_maze ( $X, |
|||
$Y, |
|||
$start_x = (^$X).pick * 2 + 1, |
|||
$start_y = (^$Y).pick * 2 + 1 ) |
|||
{ |
|||
my @maze; |
|||
push @maze, $[ flat ES, -N, (ESW, EW) xx $X - 1, SW ]; |
|||
push @maze, $[ flat (NS, TODO) xx $X, NS ]; |
|||
for 1 ..^ $Y { |
|||
push @maze, $[ flat NES, EW, (NESW, EW) xx $X - 1, NSW ]; |
|||
push @maze, $[ flat (NS, TODO) xx $X, NS ]; |
|||
} |
|||
push @maze, $[ flat NE, (EW, NEW) xx $X - 1, -NS, NW ]; |
|||
@maze[$start_y][$start_x] = OPEN; |
|||
my @stack; |
|||
my $current = [$start_x, $start_y]; |
|||
loop { |
|||
if my $dir = pick_direction( $current ) { |
|||
@stack.push: $current; |
|||
$current = move( $dir, $current ); |
|||
} |
|||
else { |
|||
last unless @stack; |
|||
$current = @stack.pop; |
|||
} |
|||
} |
|||
return @maze; |
|||
sub pick_direction([$x,$y]) { |
|||
my @neighbors = |
|||
(Up if @maze[$y - 2][$x]), |
|||
(Down if @maze[$y + 2][$x]), |
|||
(Left if @maze[$y][$x - 2]), |
|||
(Right if @maze[$y][$x + 2]); |
|||
@neighbors.pick or DeadEnd; |
|||
} |
|||
sub move ($dir, @cur) { |
|||
my ($x,$y) = @cur; |
|||
given $dir { |
|||
when Up { @maze[--$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y--][$x+1] -= W; } |
|||
when Down { @maze[++$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y++][$x+1] -= W; } |
|||
when Left { @maze[$y][--$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x--] -= N; } |
|||
when Right { @maze[$y][++$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x++] -= N; } |
|||
} |
|||
@maze[$y][$x] = 0; |
|||
[$x,$y]; |
|||
} |
|||
} |
|||
sub display (@maze) { |
|||
for @maze -> @y { |
|||
for @y.rotor(2) -> ($w, $c) { |
|||
print @ch[abs $w]; |
|||
if $c >= 0 { print @ch[$c] x 3 } |
|||
else { print ' ', @ch[abs $c], ' ' } |
|||
} |
|||
say @ch[@y[*-1]]; |
|||
} |
|||
} |
|||
sub solve (@maze is copy, @from = [1, 1], @to = [@maze[0] - 2, @maze - 2]) { |
|||
my ($x, $y) = @from; |
|||
my ($xto, $yto) = @to; |
|||
my @stack; |
|||
sub drop-crumb($x,$y,$c) { @maze[$y][$x] = -$c } |
|||
drop-crumb($x,$y,N); |
|||
loop { |
|||
my $dir = pick_direction([$x,$y]); |
|||
if $dir { |
|||
($x, $y) = move($dir, [$x,$y]); |
|||
return @maze if $x == $xto and $y == $yto; |
|||
} |
|||
else { |
|||
@maze[$y][$x] = -TRIED; |
|||
($x,$y) = @stack.pop; |
|||
@maze[$y][$x] = -TRIED; |
|||
($x,$y) = @stack.pop; |
|||
} |
|||
} |
|||
sub pick_direction([$x,$y]) { |
|||
my @neighbors = |
|||
(Up unless @maze[$y - 1][$x]), |
|||
(Down unless @maze[$y + 1][$x]), |
|||
(Left unless @maze[$y][$x - 1]), |
|||
(Right unless @maze[$y][$x + 1]); |
|||
@neighbors.pick or DeadEnd; |
|||
} |
|||
sub move ($dir, @cur) { |
|||
my ($x,$y) = @cur; |
|||
given $dir { |
|||
when Up { for ^2 { push @stack, $[$x,$y--]; drop-crumb $x,$y,S; } } |
|||
when Down { for ^2 { push @stack, $[$x,$y++]; drop-crumb $x,$y,N; } } |
|||
when Left { for ^2 { push @stack, $[$x--,$y]; drop-crumb $x,$y,E; } } |
|||
when Right { for ^2 { push @stack, $[$x++,$y]; drop-crumb $x,$y,W; } } |
|||
} |
|||
$x,$y; |
|||
} |
|||
} |
|||
display solve gen_maze( 29, 19 );</syntaxhighlight> |
|||
{{out}} |
|||
<small><pre>┌ ╵ ────┬───────────────┬───────────────┬───────────────────────────────┬───────────┬───────────────┬───────────────┐ |
|||
│ ╵ · · │ ╷ ╴ ╴ ╴ ╴ │ │ ╷ ╴ ╴ · · · · · · · · · · · · │ ╷ ╴ ╴ · · │ · · · · · · · │ · · · · · · · │ |
|||
│ ╵ ╶───┘ ╷ ╶───┐ ╵ ╷ │ ╷ ╶───────┤ ╷ ╷ ╵ ╶───────┬───────┐ · ┌───┘ ╷ ╷ ╵ ╷ · │ · ╶───┬───╴ · │ · ╷ · ┌───╴ · │ |
|||
│ ╵ ╴ ╴ ╴ ╴ · · │ ╵ │ │ │ │ ╷ │ ╵ ╴ ╴ ╴ ╴ │ ╷ ╴ ╴ │ · │ ╷ ╴ ╴ │ ╵ │ · │ · · · │ · · · │ · │ · │ · · · │ |
|||
│ · ┌───────────┤ ╵ │ └───┴───────╴ │ ╷ ├───────┐ ╵ ╵ ╷ ╷ ╵ └───┘ ╷ ┌───┘ ╵ │ · │ · ╷ · │ · ┌───┴───┘ · │ · ╶───┤ |
|||
│ · │ ╶ ╶ ╶ ╶ ╷ │ ╵ │ │ ╷ │ │ ╵ ╴ ╴ │ ╵ ╴ ╴ ╴ ╴ │ ╶ ╶ ╵ │ · │ · │ · │ · │ · · · · · │ · · · │ |
|||
│ · │ ╵ ╶───┐ ╷ ╵ ╵ ├───────────────╴ │ ╷ └───╴ └───────┼───┬───────┤ ╵ ┌───┤ · ├───┘ · │ · │ · ╷ · ┌───┴───┐ · │ |
|||
│ · │ ╵ ╴ ╴ │ ╶ ╶ ╵ │ │ ╶ ╶ ╶ ╶ ╶ ╶ ╶ ╶ ╷ │ · │ ╶ ╶ ╷ │ ╵ │ · │ · │ · · · │ · │ · │ · │ · · · │ · │ |
|||
│ · └───┐ ╵ ├───────┴───────┐ ╶───────┴───────┬───────╴ ╷ │ · │ ╵ ╷ ╷ │ ╵ │ · │ · │ · ┌───┤ · │ · │ · │ · ╷ · ╵ · │ |
|||
│ · · · │ ╵ │ ╷ ╴ ╴ ╴ ╴ ╴ ╴ │ │ ╷ ╴ ╴ ╴ ╴ │ · │ ╵ │ ╷ │ ╵ │ · │ · │ · │ · │ · │ · │ · │ · │ · · · │ |
|||
├───╴ · │ ╵ │ ╷ ╶───────┐ ╵ ├───────────────┐ │ ╷ ╶───┬───┘ · │ ╵ │ ╷ ╵ ╵ │ · ╵ · ╵ · │ · ╵ · └───┘ · │ · ├───────┤ |
|||
│ · · · │ ╵ │ ╶ ╶ ╶ ╶ ╷ │ ╵ │ ╷ ╴ ╴ ╴ ╴ ╴ ╴ │ │ ╶ ╶ ╷ │ · · · │ ╵ │ ╶ ╶ ╵ │ · · · · · │ · · · · · · · │ · │ · · · │ |
|||
│ · ┌───┤ ╵ └───────╴ ╷ │ ╵ ╵ ╷ ┌───────┐ ╵ │ └───┐ ╷ └───┐ · │ ╵ ├───────┼───────┬───┴───────┬───┬───┘ · ├───╴ · │ |
|||
│ · │ · │ ╵ ╴ ╴ ╴ ╴ ╴ ╴ │ ╵ ╴ ╴ │ · · · │ ╵ │ │ ╶ ╶ ╷ │ · │ ╵ │ ╷ ╴ ╴ │ ╷ ╴ ╴ │ ╷ ╴ ╴ ╴ ╴ │ · │ · · · │ · · · │ |
|||
│ · ╵ · ├───────────────┴───────┴───┐ · │ ╵ └───────┴───┐ ╷ │ · │ ╵ ╵ ╷ ╷ ╵ ╵ ╷ ╷ ╵ │ ╷ ┌───╴ ╵ │ · │ · ╶───┤ · ╶───┤ |
|||
│ · · · │ · · · · · · · · · · · · · │ · │ ╵ ╴ ╴ ╴ ╴ · · │ ╷ │ · │ ╵ ╴ ╴ │ ╵ ╴ ╴ │ ╵ │ ╷ │ ╶ ╶ ╵ │ · │ · · · │ · · · │ |
|||
├───┐ · │ · ┌───────────╴ · ┌───┐ · │ · └───┬───╴ ╵ ┌───┘ ╷ │ · ├───────┴───────┤ ╵ ╵ ╷ │ ╵ ╶───┤ · └───┐ · │ · ╷ · │ |
|||
│ · │ · │ · │ · · · · · · · │ · │ · │ · · · │ ╶ ╶ ╵ │ ╷ ╴ ╴ │ · │ · · · · · · · │ ╵ ╴ ╴ │ ╵ ╴ ╴ │ · · · │ · │ · │ · │ |
|||
│ · │ · └───┘ · ┌───────────┘ · │ · ├───╴ · │ ╵ ┌───┘ ╷ ┌───┘ · │ · ╷ · ┌───╴ · └───────┴───┐ ╵ └───┐ · ╵ · ├───┘ · │ |
|||
│ · │ · · · · · │ · · · · · · · │ · │ · · · │ ╵ │ ╷ ╴ ╴ │ · · · │ · │ · │ · · · · · · · · · │ ╵ ╴ ╴ │ · · · │ · · · │ |
|||
│ · ├───────╴ · ├───┐ · ┌───┐ · ╵ · │ · ╶───┤ ╵ ╵ ╷ ┌───┘ · ╶───┤ · │ · └───┬───╴ · ┌───────┤ · ╷ ╵ │ · ╶───┘ · ╷ · │ |
|||
│ · │ · · · · · │ │ · │ · │ · · · │ · · · │ ╵ ╴ ╴ │ · · · · · │ · │ · · · │ · · · │ ╶ ╶ ╷ │ · │ ╵ │ · · · · · │ · │ |
|||
│ · ╵ · ┌───────┘ │ · ╵ · └───────┤ · ╷ · └───────┴───────┐ · ╵ · └───┐ · └───┐ · │ ╵ ╷ ╷ └───┘ ╵ ├───────────┤ · │ |
|||
│ · · · │ │ · · · · · · · │ · │ · · · · · · · · · │ · · · · · │ · · · │ · │ ╵ │ ╶ ╶ ╶ ╶ ╵ │ ╷ ╴ ╴ ╴ ╴ │ · │ |
|||
│ · ╶───┤ ╶───────┴───┬───────┐ · ├───┘ · ┌───────────┐ · ├───────────┴───╴ · │ · │ ╵ └───────┐ · │ ╷ ┌───╴ ╵ │ · │ |
|||
│ · · · │ │ · · · │ · │ · · · │ · · · · · │ · │ · · · · · · · · · │ · │ ╵ ╴ ╴ ╴ ╴ │ · │ ╷ │ ╶ ╶ ╵ │ · │ |
|||
├───┐ · ├───────┬───╴ │ · ╷ · │ · │ · ┌───┤ · ╶───────┤ · │ · ╶───────┬───┐ · ├───┴───────┐ ╵ └───┘ ╷ │ ╵ ┌───┴───┤ |
|||
│ · │ · │ │ │ · │ · │ · │ · │ · │ · · · · · │ · │ · · · · · │ · │ · │ │ ╵ ╴ ╴ ╴ ╴ │ ╵ │ ╷ ╴ ╴ │ |
|||
│ · │ · │ ╷ ╵ ┌───┘ · │ · ╵ · ╵ · │ · ╵ · ┌───┐ · ╵ · └───────╴ · │ · ╵ · │ ┌───╴ └───────┐ · │ ╵ ╵ ╷ ╷ ╵ │ |
|||
│ · │ · │ │ │ · · · │ · · · · · │ · · · │ · │ · · · · · · · · · │ · · · │ │ │ · │ ╵ ╴ ╴ │ ╵ │ |
|||
│ · │ · │ └───┬───┴───────┴───┬───────┤ · ┌───┘ · ├───────────────────┴───────┤ └───────────┐ └───┴───────┤ ╵ │ |
|||
│ · │ · │ │ │ │ · │ · · · │ │ │ │ ╵ │ |
|||
│ · ╵ · ├───┐ │ ╶───────┐ ╵ ╷ │ · ╵ · ╷ · │ ╶───────────────────┐ └───┬───────┐ └───────┬───┐ │ ╵ │ |
|||
│ · · · │ · │ │ │ │ │ · · · │ · │ │ │ │ │ │ │ ╵ │ |
|||
│ · ╶───┤ · │ └───────┐ ├───────┤ └───┬───┴───┼───────────┬───────┐ ├───┐ ╵ ╷ ├───────┐ │ │ │ ╵ │ |
|||
│ · · · │ · │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╵ │ |
|||
├───╴ · ╵ · └───────┐ ╵ │ ╷ └───╴ ╵ ╷ ╵ ┌───╴ ╵ ╷ ╵ │ └───────┘ ╵ ╷ ╵ │ ╵ ╵ ╵ │ |
|||
│ · · · · · · · · · │ │ │ │ │ │ │ │ │ ╵ │ |
|||
└───────────────────┴───────┴───┴───────────────┴───────┴───────────┴───────┴───────────────────┴───────┴──────── │ ┘</pre></small> |
|||
=={{header|Red}}== |
|||
(imports maze generation code, see http://rosettacode.org/wiki/Maze_generation#Red) |
|||
<syntaxhighlight lang="red">Red ["Maze solver"] |
|||
do %mazegen.red |
|||
print [ |
|||
"start:" start: random size - 1x1 |
|||
"end:" end: random size - 1x1 |
|||
] |
|||
isnew?: function [pos] [not find visited pos] |
|||
open?: function [pos d] [ |
|||
o: pos/y * size/x + pos/x + 1 |
|||
0 = pick walls/:o d |
|||
] |
|||
expand: function [pos][ |
|||
either any [ |
|||
all [pos/x > 0 isnew? p: pos - 1x0 open? p 1] |
|||
all [pos/x < (size/x - 1) isnew? p: pos + 1x0 open? pos 1] |
|||
all [pos/y > 0 isnew? p: pos - 0x1 open? p 2] |
|||
all [pos/y < (size/y - 1) isnew? p: pos + 0x1 open? pos 2] |
|||
][append visited p insert path p][remove path] |
|||
path/1 |
|||
] |
|||
path: reduce [start] |
|||
visited: [] |
|||
until [end = expand path/1] |
|||
print reverse path</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Maze width: 15 |
|||
Maze height: 15 |
|||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
|||
| | | | _ |_ | _ _ _| |
|||
| _|_| |_ _| | | _| |_ | | |
|||
| |_ _| _ _| | _ _ _|_ _| | |
|||
|_ |_ | | _ _|_ _| _ | | |
|||
| _| _| | | _ _ _|_ _|_| |
|||
|_ _ _| _ _| |_| _ _ |_| | |
|||
| _| _|_ _ _ | | _|_ _ _| | |
|||
| | _| _ _ |_ |_ _ | | | |
|||
| _| _| _|_ |_ _ _|_ _| | |
|||
|_ | | _ | _| _ _ |_| | |
|||
| |_ _| | |_ _ _| | | _| | |
|||
| | _ _| |_ _ _ | |_ _|_ |_| |
|||
| |_ | |_ |_ _ _|_ _ _ |_ | |
|||
| _| | |_ | | | _| | |
|||
|_ _ _ _|_ _|_ _|_ _|_ _ _ _|_| |
|||
start: 2x4 end: 12x3 |
|||
2x4 3x4 3x3 2x3 2x2 3x2 3x1 3x0 4x0 4x1 5x1 5x0 6x0 7x0 7x1 7x2 7x3 6x3 5x3 5x4 5x5 4x5 3x5 3x6 2x6 2x7 1x7 1x8 0x8 0x9 1x9 1x10 2x10 2x9 2x8 3x8 3x7 4x7 5x7 6x7 6x8 7x8 7x9 6x9 6x10 7x10 8x10 8x9 9x9 10x9 11x9 11x10 11x11 10x11 10x10 9x10 9x11 9x12 10x12 11x12 12x12 12x13 11x13 11x14 12x14 13x14 13x13 14x13 14x12 13x12 13x11 12x11 12x10 13x10 13x9 14x9 14x8 14x7 14x6 14x5 13x5 13x6 12x6 11x6 11x5 10x5 9x5 8x5 8x6 8x7 7x7 7x6 6x6 6x5 6x4 7x4 8x4 9x4 10x4 10x3 11x3 12x3 |
|||
</pre> |
</pre> |
||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
This solution extends the [[Maze generation#Ruby|maze generator script]]. To get a working script, copy & paste both parts into one file. |
This solution extends the [[Maze generation#Ruby|maze generator script]]. To get a working script, copy & paste both parts into one file. |
||
< |
<syntaxhighlight lang="ruby">class Maze |
||
# Solve via breadth-first algorithm. |
# Solve via breadth-first algorithm. |
||
# Each queue entry is a path, that is list of coordinates with the |
# Each queue entry is a path, that is list of coordinates with the |
||
Line 3,099: | Line 5,082: | ||
maze = Maze.new 20, 10 |
maze = Maze.new 20, 10 |
||
maze.solve |
maze.solve |
||
maze.print</ |
maze.print</syntaxhighlight> |
||
Example output: |
Example output: |
||
Line 3,124: | Line 5,107: | ||
| * * | * * | | | |
| * * | * * | | | |
||
+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
||
</pre> |
|||
=={{header|Rust}}== |
|||
This solution reuses the [[Maze generation#Rust|maze generator Rust code]]. The modified and new parts are marked with the label "MAZE SOLVING".Uses the [https://crates.io/crates/rand rand] library. |
|||
<syntaxhighlight lang="rust">use rand::{thread_rng, Rng, rngs::ThreadRng}; |
|||
const WIDTH: usize = 16; |
|||
const HEIGHT: usize = 16; |
|||
#[derive(Clone, Copy, PartialEq)] |
|||
struct Cell { |
|||
col: usize, |
|||
row: usize, |
|||
} |
|||
impl Cell { |
|||
fn from(col: usize, row: usize) -> Cell { |
|||
Cell {col, row} |
|||
} |
|||
} |
|||
struct Maze { |
|||
cells: [[bool; HEIGHT]; WIDTH], //cell visited/non visited |
|||
walls_h: [[bool; WIDTH]; HEIGHT + 1], //horizontal walls existing/removed |
|||
walls_v: [[bool; WIDTH + 1]; HEIGHT], //vertical walls existing/removed |
|||
thread_rng: ThreadRng, //Random numbers generator |
|||
} |
|||
impl Maze { |
|||
///Inits the maze, with all the cells unvisited and all the walls active |
|||
fn new() -> Maze { |
|||
Maze { |
|||
cells: [[true; HEIGHT]; WIDTH], |
|||
walls_h: [[true; WIDTH]; HEIGHT + 1], |
|||
walls_v: [[true; WIDTH + 1]; HEIGHT], |
|||
thread_rng: thread_rng(), |
|||
} |
|||
} |
|||
///Randomly chooses the starting cell |
|||
fn first(&mut self) -> Cell { |
|||
Cell::from(self.thread_rng.gen_range(0, WIDTH), self.thread_rng.gen_range(0, HEIGHT)) |
|||
} |
|||
///Opens the enter and exit doors |
|||
fn open_doors(&mut self) { |
|||
let from_top: bool = self.thread_rng.gen(); |
|||
let limit = if from_top { WIDTH } else { HEIGHT }; |
|||
let door = self.thread_rng.gen_range(0, limit); |
|||
let exit = self.thread_rng.gen_range(0, limit); |
|||
if from_top { |
|||
self.walls_h[0][door] = false; |
|||
self.walls_h[HEIGHT][exit] = false; |
|||
} else { |
|||
self.walls_v[door][0] = false; |
|||
self.walls_v[exit][WIDTH] = false; |
|||
} |
|||
} |
|||
///Removes a wall between the two Cell arguments |
|||
fn remove_wall(&mut self, cell1: &Cell, cell2: &Cell) { |
|||
if cell1.row == cell2.row { |
|||
self.walls_v[cell1.row][if cell1.col > cell2.col { cell1.col } else { cell2.col }] = false; |
|||
} else { |
|||
self.walls_h[if cell1.row > cell2.row { cell1.row } else { cell2.row }][cell1.col] = false; |
|||
}; |
|||
} |
|||
///Returns a random non-visited neighbor of the Cell passed as argument |
|||
fn neighbor(&mut self, cell: &Cell) -> Option<Cell> { |
|||
self.cells[cell.col][cell.row] = false; |
|||
let mut neighbors = Vec::new(); |
|||
if cell.col > 0 && self.cells[cell.col - 1][cell.row] { neighbors.push(Cell::from(cell.col - 1, cell.row)); } |
|||
if cell.row > 0 && self.cells[cell.col][cell.row - 1] { neighbors.push(Cell::from(cell.col, cell.row - 1)); } |
|||
if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] { neighbors.push(Cell::from(cell.col + 1, cell.row)); } |
|||
if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] { neighbors.push(Cell::from(cell.col, cell.row + 1)); } |
|||
if neighbors.is_empty() { |
|||
None |
|||
} else { |
|||
let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap(); |
|||
self.remove_wall(cell, next); |
|||
Some(*next) |
|||
} |
|||
} |
|||
///Builds the maze (runs the Depth-first search algorithm) |
|||
fn build(&mut self) { |
|||
let mut cell_stack: Vec<Cell> = Vec::new(); |
|||
let mut next = self.first(); |
|||
loop { |
|||
while let Some(cell) = self.neighbor(&next) { |
|||
cell_stack.push(cell); |
|||
next = cell; |
|||
} |
|||
match cell_stack.pop() { |
|||
Some(cell) => next = cell, |
|||
None => break, |
|||
} |
|||
} |
|||
} |
|||
///MAZE SOLVING: Find the starting cell of the solution |
|||
fn solution_first(&self) -> Option<Cell> { |
|||
for (i, wall) in self.walls_h[0].iter().enumerate() { |
|||
if !wall { |
|||
return Some(Cell::from(i, 0)); |
|||
} |
|||
} |
|||
for (i, wall) in self.walls_v.iter().enumerate() { |
|||
if !wall[0] { |
|||
return Some(Cell::from(0, i)); |
|||
} |
|||
} |
|||
None |
|||
} |
|||
///MAZE SOLVING: Find the last cell of the solution |
|||
fn solution_last(&self) -> Option<Cell> { |
|||
for (i, wall) in self.walls_h[HEIGHT].iter().enumerate() { |
|||
if !wall { |
|||
return Some(Cell::from(i, HEIGHT - 1)); |
|||
} |
|||
} |
|||
for (i, wall) in self.walls_v.iter().enumerate() { |
|||
if !wall[WIDTH] { |
|||
return Some(Cell::from(WIDTH - 1, i)); |
|||
} |
|||
} |
|||
None |
|||
} |
|||
///MAZE SOLVING: Get the next candidate cell |
|||
fn solution_next(&mut self, cell: &Cell) -> Option<Cell> { |
|||
self.cells[cell.col][cell.row] = false; |
|||
let mut neighbors = Vec::new(); |
|||
if cell.col > 0 && self.cells[cell.col - 1][cell.row] && !self.walls_v[cell.row][cell.col] { neighbors.push(Cell::from(cell.col - 1, cell.row)); } |
|||
if cell.row > 0 && self.cells[cell.col][cell.row - 1] && !self.walls_h[cell.row][cell.col] { neighbors.push(Cell::from(cell.col, cell.row - 1)); } |
|||
if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] && !self.walls_v[cell.row][cell.col + 1] { neighbors.push(Cell::from(cell.col + 1, cell.row)); } |
|||
if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] && !self.walls_h[cell.row + 1][cell.col] { neighbors.push(Cell::from(cell.col, cell.row + 1)); } |
|||
if neighbors.is_empty() { |
|||
None |
|||
} else { |
|||
let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap(); |
|||
Some(*next) |
|||
} |
|||
} |
|||
///MAZE SOLVING: solve the maze |
|||
///Uses self.cells to store the solution cells (true) |
|||
fn solve(&mut self) { |
|||
self.cells = [[true; HEIGHT]; WIDTH]; |
|||
let mut solution: Vec<Cell> = Vec::new(); |
|||
let mut next = self.solution_first().unwrap(); |
|||
solution.push(next); |
|||
let last = self.solution_last().unwrap(); |
|||
'main: loop { |
|||
while let Some(cell) = self.solution_next(&next) { |
|||
solution.push(cell); |
|||
if cell == last { |
|||
break 'main; |
|||
} |
|||
next = cell; |
|||
} |
|||
solution.pop().unwrap(); |
|||
next = *solution.last().unwrap(); |
|||
} |
|||
self.cells = [[false; HEIGHT]; WIDTH]; |
|||
for cell in solution { |
|||
self.cells[cell.col][cell.row] = true; |
|||
} |
|||
} |
|||
///MAZE SOLVING: Ask if cell is part of the solution (cells[col][row] == true) |
|||
fn is_solution(&self, col: usize, row: usize) -> bool { |
|||
self.cells[col][row] |
|||
} |
|||
///Displays a wall |
|||
///MAZE SOLVING: Leave space for printing '*' if cell is part of the solution |
|||
/// (only when painting vertical walls) |
|||
/// |
|||
// fn paint_wall(h_wall: bool, active: bool) { |
|||
// if h_wall { |
|||
// print!("{}", if active { "+---" } else { "+ " }); |
|||
// } else { |
|||
// print!("{}", if active { "| " } else { " " }); |
|||
// } |
|||
// } |
|||
fn paint_wall(h_wall: bool, active: bool, with_solution: bool) { |
|||
if h_wall { |
|||
print!("{}", if active { "+---" } else { "+ " }); |
|||
} else { |
|||
print!("{}{}", if active { "|" } else { " " }, if with_solution { "" } else { " " }); |
|||
} |
|||
} |
|||
///MAZE SOLVING: Paint * if cell is part of the solution |
|||
fn paint_solution(is_part: bool) { |
|||
print!("{}", if is_part { " * " } else {" "}); |
|||
} |
|||
///Displays a final wall for a row |
|||
fn paint_close_wall(h_wall: bool) { |
|||
if h_wall { println!("+") } else { println!() } |
|||
} |
|||
///Displays a whole row of walls |
|||
///MAZE SOLVING: Displays a whole row of walls and, optionally, the included solution cells. |
|||
// fn paint_row(&self, h_walls: bool, index: usize) { |
|||
// let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() }; |
|||
// for &wall in iter { |
|||
// Maze::paint_wall(h_walls, wall); |
|||
// } |
|||
// Maze::paint_close_wall(h_walls); |
|||
// } |
|||
fn paint_row(&self, h_walls: bool, index: usize, with_solution: bool) { |
|||
let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() }; |
|||
for (col, &wall) in iter.enumerate() { |
|||
Maze::paint_wall(h_walls, wall, with_solution); |
|||
if !h_walls && with_solution && col < WIDTH { |
|||
Maze::paint_solution(self.is_solution(col, index)); |
|||
} |
|||
} |
|||
Maze::paint_close_wall(h_walls); |
|||
} |
|||
///Paints the maze |
|||
///MAZE SOLVING: Displaying the solution is an option |
|||
// fn paint(&self) { |
|||
// for i in 0 .. HEIGHT { |
|||
// self.paint_row(true, i); |
|||
// self.paint_row(false, i); |
|||
// } |
|||
// self.paint_row(true, HEIGHT); |
|||
// } |
|||
fn paint(&self, with_solution: bool) { |
|||
for i in 0 .. HEIGHT { |
|||
self.paint_row(true, i, with_solution); |
|||
self.paint_row(false, i, with_solution); |
|||
} |
|||
self.paint_row(true, HEIGHT, with_solution); |
|||
} |
|||
} |
|||
fn main() { |
|||
let mut maze = Maze::new(); |
|||
maze.build(); |
|||
maze.open_doors(); |
|||
println!("The maze:"); |
|||
maze.paint(false); |
|||
maze.solve(); |
|||
println!("The maze, solved:"); |
|||
maze.paint(true); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>The maze: |
|||
+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+ |
|||
| | | | | |
|||
+ +---+ + + +---+ + +---+---+---+---+---+ + + + |
|||
| | | | | | | | | |
|||
+---+---+---+---+ + +---+ + +---+---+ + +---+---+ + |
|||
| | | | | | | | | |
|||
+ +---+---+ + +---+ + + + + + + +---+---+---+ |
|||
| | | | | | | | | | |
|||
+ +---+ + + + +---+---+---+---+ + +---+---+---+ + |
|||
| | | | | | | | | | | |
|||
+---+ +---+ +---+---+ + +---+ + +---+---+ + + + |
|||
| | | | | | | | | | | |
|||
+ +---+---+---+---+---+ + + + +---+ + + + + + |
|||
| | | | | | | | | | |
|||
+ +---+ + +---+ +---+---+ + + +---+ + +---+---+ |
|||
| | | | | | | | | | | | |
|||
+ + + + + + + +---+---+---+---+ + +---+ + + |
|||
| | | | | | | | | | |
|||
+---+ +---+---+ +---+---+ +---+---+ +---+---+ + + + |
|||
| | | | | | | |
|||
+ + +---+---+---+ +---+ +---+---+---+ + +---+---+---+ |
|||
| | | | | | | |
|||
+ +---+---+---+ +---+ +---+---+ + +---+---+ +---+ + |
|||
| | | | | | | | |
|||
+ + + +---+---+ +---+---+ +---+---+---+---+---+ + + |
|||
| | | | | | | | | |
|||
+ + +---+ +---+---+ +---+---+ + +---+---+ + +---+ |
|||
| | | | | | | | | |
|||
+ +---+ +---+---+---+---+ +---+ +---+ + +---+---+ + |
|||
| | | | | | | | |
|||
+---+ + + + +---+ +---+ +---+ +---+---+---+---+ + |
|||
| | | | | |
|||
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
|||
The maze, solved: |
|||
+---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+ |
|||
| | | * * * * * * * * | | |
|||
+ +---+ + + +---+ + +---+---+---+---+---+ + + + |
|||
| | | * * | | | | | | |
|||
+---+---+---+---+ + +---+ + +---+---+ + +---+---+ + |
|||
| * * * * | | * * | | | | | | |
|||
+ +---+---+ + +---+ + + + + + + +---+---+---+ |
|||
| * * * | * | | * * | | | | | | |
|||
+ +---+ + + + +---+---+---+---+ + +---+---+---+ + |
|||
| | * * | * | | * * | | | | | | |
|||
+---+ +---+ +---+---+ + +---+ + +---+---+ + + + |
|||
| * * | * * * * | | | | | | | | | |
|||
+ +---+---+---+---+---+ + + + +---+ + + + + + |
|||
| * | | | | | | | | | |
|||
+ +---+ + +---+ +---+---+ + + +---+ + +---+---+ |
|||
| * | | | | | | | | | | | |
|||
+ + + + + + + +---+---+---+---+ + +---+ + + |
|||
| * * | | | | | | | | | |
|||
+---+ +---+---+ +---+---+ +---+---+ +---+---+ + + + |
|||
| | * | | | | | |
|||
+ + +---+---+---+ +---+ +---+---+---+ + +---+---+---+ |
|||
| | * * * * | | * * | | * * * | |
|||
+ +---+---+---+ +---+ +---+---+ + +---+---+ +---+ + |
|||
| * * | * * | | * * | * * * * | | * | |
|||
+ + + +---+---+ +---+---+ +---+---+---+---+---+ + + |
|||
| * | * | | * * * | * * * | | * * * * | * * | |
|||
+ + +---+ +---+---+ +---+---+ + +---+---+ + +---+ |
|||
| * | * * | * * * * | | * * | | * * | | |
|||
+ +---+ +---+---+---+---+ +---+ +---+ + +---+---+ + |
|||
| * * | * | * * | * * * | | * * | | |
|||
+---+ + + + +---+ +---+ +---+ +---+---+---+---+ + |
|||
| * * | * * | * * * | * * * | |
|||
+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+</pre> |
|||
=={{header|Swift}}== |
|||
{{works with|Swift|3}} |
|||
The following requires the use of the implementation for [[Maze generation#Swift|generation task]]. Assumes there will be no circular paths through the maze as the problem suggests. |
|||
<pre> |
|||
enum errors: Error { |
|||
//Occurs when a start or end position is given that is outside of bounds |
|||
case IndexOutsideMazeBoundary |
|||
} |
|||
///Used to solve any maze generated by the swift implementation of maze generator at: |
|||
///https://www.rosettacode.org/wiki/Maze_generation#Swift |
|||
class MazeSolver { |
|||
///your starting index position in the maze |
|||
var startPos: (Int, Int) |
|||
/// the end index position you are trying to reach within the maze |
|||
var endPos: (Int, Int) |
|||
/// a maze of [[INT]] where each value is between 1 and 14. |
|||
/// each number representing a |
|||
var maze: [[Int]] |
|||
var visitedSpaces: [(Int, Int)] |
|||
init(maze: [[Int]]) { |
|||
self.startPos = (0, 0) |
|||
self.endPos = (0, 0) |
|||
/// a maze that consists of a array of ints from 1-14 organized to |
|||
/// create a maze like structure. |
|||
/// Each number represents where the walls and paths will be |
|||
/// ex. 2 only has a path south and 12 has paths E or W |
|||
self.maze = maze |
|||
/// spaces each branch has visited to stop from searching previously explored |
|||
/// areas of the maze |
|||
self.visitedSpaces = [] |
|||
} |
|||
/// determine if the user has previously visited the given location within the maze |
|||
func visited(position: (Int, Int)) -> Bool { |
|||
return visitedSpaces.contains(where: {$0 == position}) |
|||
} |
|||
/// used to translate the mazes number scheme to a list of directions available |
|||
/// for each number. DirectionDiff for N would be (0, -1) for example |
|||
func getDirections(positionValue: Int) -> [(Int, Int)] { |
|||
switch positionValue { |
|||
case 1: |
|||
return [Direction.north.diff] |
|||
case 2: |
|||
return [Direction.south.diff] |
|||
case 3: |
|||
return [Direction.north.diff, Direction.south.diff] |
|||
case 4: |
|||
return [Direction.east.diff] |
|||
case 5: |
|||
return [Direction.north.diff, Direction.east.diff] |
|||
case 6: |
|||
return [Direction.south.diff, Direction.east.diff] |
|||
case 7: |
|||
return [Direction.north.diff, Direction.east.diff, Direction.south.diff] |
|||
case 8: |
|||
return [Direction.west.diff] |
|||
case 9: |
|||
return [Direction.north.diff, Direction.west.diff] |
|||
case 10: |
|||
return [Direction.west.diff, Direction.south.diff] |
|||
case 11: |
|||
return [Direction.north.diff, Direction.south.diff, Direction.west.diff] |
|||
case 12: |
|||
return [Direction.west.diff, Direction.east.diff] |
|||
case 13: |
|||
return [Direction.north.diff, Direction.east.diff, Direction.west.diff] |
|||
case 14: |
|||
return [Direction.east.diff, Direction.south.diff, Direction.west.diff] |
|||
default: |
|||
return [] |
|||
} |
|||
} |
|||
/// calculate and return all paths branching from the current position that haven't been explored yet |
|||
func availablePaths(currentPosition: (Int, Int), lastPos: (Int, Int)) -> [(Int, Int)] { |
|||
/// all available paths to be returned |
|||
var paths: [(Int, Int)] = [] |
|||
/// the maze contents at the given position (ie the maze number) |
|||
let positionValue = maze[currentPosition.0][ currentPosition.1] |
|||
/// used to build the new path positions and check them before they |
|||
/// are added to paths |
|||
var workingPos: (Int, Int) |
|||
// is the maze at a dead end and not our first position |
|||
if ([1, 2, 4, 8].contains(positionValue) && currentPosition != lastPos) { |
|||
return [] |
|||
} |
|||
/// the directions available based on the maze contents of our |
|||
/// current position |
|||
let directions: [(Int, Int)] = getDirections(positionValue: positionValue) |
|||
/// build the paths |
|||
for pos in directions { |
|||
workingPos = (currentPosition.0 + pos.0, currentPosition.1 + pos.1) |
|||
if (currentPosition == lastPos || (workingPos != lastPos && !visited(position: workingPos))) { |
|||
paths.append(workingPos) |
|||
} |
|||
} |
|||
return paths |
|||
} |
|||
/// prints a model of the maze with |
|||
/// O representing the start point |
|||
/// X representing the end point |
|||
/// * representing traveled space |
|||
/// NOTE: starting pos and end pos represent indexes and thus start at 0 |
|||
func solveAndDisplay(startPos: (Int, Int), endPos: (Int, Int)) throws { |
|||
if (startPos.0 >= maze.count || startPos.1 >= maze[0].count) { |
|||
throw errors.IndexOutsideMazeBoundary |
|||
} |
|||
/// the position you are beginning at in the maze |
|||
self.startPos = startPos |
|||
/// the position you wish to get to within the maze |
|||
self.endPos = endPos |
|||
/// spaces each branch has visited to stop from searching previously explored |
|||
/// areas of the maze |
|||
self.visitedSpaces = [] |
|||
self.displaySolvedMaze(finalPath: solveMaze(startpos: startPos, pathSoFar: [])!) |
|||
} |
|||
/// recursively solves our maze |
|||
private func solveMaze(startpos: (Int, Int), pathSoFar: [(Int, Int)]) -> [(Int, Int)]? { |
|||
/// marks our current position in the maze |
|||
var currentPos: (Int, Int) = startpos |
|||
/// saves the spaces visited on this current branch |
|||
var currVisitedSpaces : [(Int, Int)] = [startpos] |
|||
/// the final path (or the path so far if the end pos has not been reached) |
|||
/// from the start position to the end position |
|||
var finalPath: [(Int, Int)] = pathSoFar |
|||
/// all possible paths from our current position that have not been explored |
|||
var possiblePaths: [(Int, Int)] = availablePaths(currentPosition: startpos, lastPos: pathSoFar.last ?? startpos) |
|||
// move through the maze until you come to a position that has been explored, the end position, or a dead end (the |
|||
// possible paths array is empty) |
|||
while (!possiblePaths.isEmpty) { |
|||
// found the final path |
|||
guard (currentPos != self.endPos) else { |
|||
finalPath.append(contentsOf: currVisitedSpaces) |
|||
return finalPath |
|||
} |
|||
// multiple valid paths from current position found |
|||
// recursively call new branches for each valid path |
|||
if (possiblePaths.count > 1) { |
|||
visitedSpaces.append(contentsOf: currVisitedSpaces) |
|||
finalPath.append(contentsOf: currVisitedSpaces) |
|||
// for each valid path recursively call each path |
|||
// and if it contains the final path |
|||
// ie it found the endpoint, return that path |
|||
for pos in possiblePaths { |
|||
let path = solveMaze(startpos: pos, pathSoFar: finalPath) |
|||
if (path != nil) { |
|||
return path |
|||
} |
|||
} |
|||
// otherwise this branch and it's sub-branches |
|||
// are dead-ends so kill this branch |
|||
return nil |
|||
} |
|||
// continue moving along the only path available |
|||
else { |
|||
// update current path to our only available next |
|||
// position |
|||
currentPos = possiblePaths[0] |
|||
// calculate the available paths from our new |
|||
// current position |
|||
possiblePaths = availablePaths(currentPosition: currentPos, lastPos: currVisitedSpaces.last ?? currentPos) |
|||
// update the visited spaces for this branch |
|||
currVisitedSpaces.append(currentPos) |
|||
} |
|||
} |
|||
// if our new current position is the endpos return our final path |
|||
if (currentPos == self.endPos) { |
|||
finalPath.append(contentsOf: currVisitedSpaces) |
|||
return finalPath |
|||
} |
|||
else { |
|||
return nil |
|||
} |
|||
} |
|||
// Reference: https://www.rosettacode.org/wiki/Maze_generation#Swift |
|||
// adapts the display method used in the swift implementation of the maze generator we used for this app to display the maze |
|||
// solved |
|||
func displaySolvedMaze(finalPath: [(Int, Int)]) { |
|||
/// all cells are 3 wide in the x axis |
|||
let cellWidth = 3 |
|||
for j in 0..<y { |
|||
// Draw top edge |
|||
// if mark corner with +, add either (cell width) spaces or - to the topedge var dependin on if it is a wall or not |
|||
var topEdge = "" |
|||
for i in 0..<x { |
|||
topEdge += "+" |
|||
topEdge += String(repeating: (maze[i][j] & Direction.north.rawValue) == 0 ? "-" : " ", count: cellWidth) |
|||
} |
|||
topEdge += "+" |
|||
print(topEdge) |
|||
// Draw left edge |
|||
//through the center of the cell if is a wall add | if not a space |
|||
// then if it is travelled add " * " otherwise just 3 spaces then |
|||
//cap it with a | at the end for the right most wall |
|||
var leftEdge = "" |
|||
for i in 0..<x { |
|||
leftEdge += (maze[i][j] & Direction.west.rawValue) == 0 ? "|" : " " |
|||
if (finalPath.first! == (i, j)) { |
|||
leftEdge += " O " |
|||
} |
|||
else if (finalPath.last! == (i, j)) { |
|||
leftEdge += " X " |
|||
} |
|||
else { |
|||
if (finalPath.contains(where: {$0 == (i, j)})) { |
|||
leftEdge += " * " |
|||
} |
|||
else { |
|||
leftEdge += String(repeating: " ", count: cellWidth) |
|||
} |
|||
} |
|||
} |
|||
leftEdge += "|" |
|||
print(leftEdge) |
|||
} |
|||
// Draw bottom edge |
|||
// adds + on corners and _ everywhere else |
|||
var bottomEdge = "" |
|||
for _ in 0..<x { |
|||
bottomEdge += "+" |
|||
bottomEdge += String(repeating: "-", count: cellWidth) |
|||
} |
|||
bottomEdge += "+" |
|||
print(bottomEdge) |
|||
} |
|||
} |
|||
</pre> |
|||
<pre> |
|||
example implementation using the aforementioned maze generator code for the swift implementation on rosettaCode |
|||
let x = 20 |
|||
let y = 10 |
|||
let maze = MazeGenerator(x, y) |
|||
var solver: MazeSolver = MazeSolver(maze: maze.maze) |
|||
do { |
|||
// making sure the start and end pos are within the maze boundary |
|||
try solver.solveAndDisplay(startPos: (0, 0), endPos: (5, 5)) |
|||
} |
|||
catch errors.IndexOutsideMazeBoundary { |
|||
print("[ERROR] given start or end point outside of bounds") |
|||
} |
|||
</pre> |
|||
{{out}}<pre> |
|||
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
|||
| O * | | * * | * * | * * * * * * * * * | | |
|||
+---+ + +---+ + + + + +---+---+---+---+---+---+---+ + +---+ + |
|||
| * * | | * | * * | * | * | | * * | * * * * | | | |
|||
+ +---+---+ + +---+---+ + +---+ + + + +---+---+---+---+ + + |
|||
| * | | * | | * * | * * * | * | * * | | | |
|||
+ +---+ + + + + +---+---+---+ + +---+---+---+ +---+---+---+ + |
|||
| * | | | * * | * * | * * | * * * * | | | |
|||
+ + +---+ +---+ +---+ + + +---+---+---+---+ + +---+ +---+ + |
|||
| * | | | * * | * | | * | * * * | * * | | | | | |
|||
+ + + +---+ +---+ + +---+ + +---+ + +---+ + + + +---+ |
|||
| * | | | | X * | * * | * | * | * * | | | | | | |
|||
+ + +---+ +---+ +---+---+ + + +---+---+---+ + + +---+---+ + |
|||
| * | | | | | * | * | * * * | | | | |
|||
+ +---+ +---+ +---+---+ + + +---+---+ + +---+---+---+ + + + |
|||
| * * | | * * | * * * | | | | | |
|||
+---+ + +---+---+---+---+---+---+---+ +---+---+ + +---+ + +---+ + |
|||
| | * | | * * * * | * * | * * | | | | | | | |
|||
+ + +---+---+ +---+---+ + + +---+ + + + + +---+---+---+ + |
|||
| * * * * | * * | * * * | | | |
|||
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ |
|||
</pre> |
</pre> |
||
Line 3,129: | Line 5,728: | ||
{{works with|Tcl|8.6}} |
{{works with|Tcl|8.6}} |
||
This script assumes that the contents of the [[Maze generation#Tcl|generation task]] have already been <code>source</code>d. Note that the algorithm implemented here does not assume that the maze is free of circuits, and in the case that there are multiple routes, it finds the shortest one because it is a breadth-first search (by virtue of the <tt>queue</tt> variable being used as a queue). |
This script assumes that the contents of the [[Maze generation#Tcl|generation task]] have already been <code>source</code>d. Note that the algorithm implemented here does not assume that the maze is free of circuits, and in the case that there are multiple routes, it finds the shortest one because it is a breadth-first search (by virtue of the <tt>queue</tt> variable being used as a queue). |
||
< |
<syntaxhighlight lang="tcl">oo::define maze { |
||
method solve {} { |
method solve {} { |
||
### Initialization of visited matrix and location/path queue |
### Initialization of visited matrix and location/path queue |
||
Line 3,184: | Line 5,783: | ||
m solve |
m solve |
||
# Print it out |
# Print it out |
||
puts [m view]</ |
puts [m view]</syntaxhighlight> |
||
Example output: |
Example output: |
||
<pre> |
<pre> |
||
Line 3,204: | Line 5,803: | ||
| > > > ^ | | > |
| > > > ^ | | > |
||
+---+---+---+---+---+---+---+---+---+---+---+ |
+---+---+---+---+---+---+---+---+---+---+---+ |
||
</pre> |
|||
=={{header|Uiua}}== |
|||
Just add the following lines to the end of [https://rosettacode.org/wiki/Maze_generation#Uiua the generator]. It shows you the maze that has been solved with the path highlighted, the list of moves, and the total path length. |
|||
<syntaxhighlight lang="uiua"> |
|||
# You can delete from here to SOLVE THE MAZE |
|||
# if you're appending this to the maze generator code. |
|||
# Experimental! |
|||
["+-+-+-+-+-+-+" |
|||
"|. . .|. . .|" |
|||
"+-+-+ + + + +" |
|||
"|.|. .|.|.|.|" |
|||
"+ + +-+-+ + +" |
|||
"|.|. . . .|.|" |
|||
"+ +-+-+-+-+ +" |
|||
"|. . . . . .|" |
|||
"+-+-+-+-+-+-+"] |
|||
H ← 4 |
|||
W ← 6 |
|||
Nfour ← +⊙¤[¯2_0 2_0 0_2 0_¯2] # Gives N4 |
|||
InBounds ← ▽⊸≡(↧⊃(/↧≥1_1|/↧< +1 × 2 H_W)) |
|||
GetWall ← -:⊡1:÷2/-. |
|||
# S O L V E T H E M A Z E # |
|||
. |
|||
Start ← 1_1 |
|||
End ← -Start ×2 H_W |
|||
Heur ← /+⌵-End # Manhattan distance. |
|||
# (pos grid) -> 1-4 next steps, in bounds, without walls in the way. |
|||
Ns ← ≡⊡1▽:⟜(≡(=@ ⊡)⊙¤≡GetWall)≡⊟¤:InBounds Nfour. |
|||
astar(Ns|Heur|≍End) Start # Solve (costs = 1 => djikstra) |
|||
$"_ moves" ⊙⟜(⍜(⊡|+33)):°□⊢ |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
╭─ |
|||
╷ "+-+-+-+-+-+-+" |
|||
"|O O O|. O O|" |
|||
"+-+-+ + + + +" |
|||
"|.|O O|.|O|O|" |
|||
"+ + +-+-+ + +" |
|||
"|.|O O O O|O|" |
|||
"+ +-+-+-+-+ +" |
|||
"|. . . . . O|" |
|||
"+-+-+-+-+-+-+" |
|||
╯ |
|||
╭─ |
|||
╷ 1 1 |
|||
1 3 |
|||
1 5 |
|||
3 5 |
|||
3 3 |
|||
5 3 |
|||
5 5 |
|||
5 7 |
|||
5 9 |
|||
3 9 |
|||
1 9 |
|||
1 11 |
|||
3 11 |
|||
5 11 |
|||
7 11 |
|||
╯ |
|||
"14 moves" |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Kotlin}} |
|||
{{libheader|Wren-ioutil}} |
|||
<syntaxhighlight lang="wren">import "os" for Process |
|||
import "./ioutil" for File, FileUtil |
|||
/* |
|||
* Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that |
|||
* each cell in the maze is the same size horizontally as vertically. |
|||
* (Versus the expanded version, which looks better visually.) |
|||
* Also, converts each line of the maze from a string to a |
|||
* character list, because we'll want mutability when drawing the solution later. |
|||
*/ |
|||
var decimateHorizontally = Fn.new { |lines| |
|||
var width = ((lines[0].count + 1)/2).floor |
|||
var c = List.filled(lines.count, null) |
|||
for (i in 0...lines.count) { |
|||
c[i] = List.filled(width, null) |
|||
for (j in 0...width) c[i][j] = lines[i][j*2] |
|||
} |
|||
return c |
|||
} |
|||
/* |
|||
* Given the maze, the x and y coordinates (which must be odd), |
|||
* and the direction we came from, return true if the maze is |
|||
* solvable, and draw the solution if so. |
|||
*/ |
|||
var solveMazeRecursively // recursive function |
|||
solveMazeRecursively = Fn.new { |maze, x, y, d| |
|||
var ok = false |
|||
var i = 0 |
|||
while (i < 4 && !ok) { |
|||
if (i != d) { |
|||
// 0 = up, 1 = right, 2 = down, 3 = left |
|||
if (i == 0) { |
|||
if (maze[y - 1][x] == " ") ok = solveMazeRecursively.call(maze, x, y - 2, 2) |
|||
} else if (i == 1) { |
|||
if (maze[y][x + 1] == " ") ok = solveMazeRecursively.call(maze, x + 2, y, 3) |
|||
} else if (i == 2) { |
|||
if (maze[y + 1][x] == " ") ok = solveMazeRecursively.call(maze, x, y + 2, 0) |
|||
} else if (i == 3) { |
|||
if (maze[y][x - 1] == " ") ok = solveMazeRecursively.call(maze, x - 2, y, 1) |
|||
} |
|||
} |
|||
i = i + 1 |
|||
} |
|||
// check for end condition |
|||
if (x == 1 && y == 1) ok = true |
|||
// once we have found a solution, draw it as we unwind the recursion |
|||
if (ok) { |
|||
maze[y][x] = "*" |
|||
if (d == 0) { |
|||
maze[y - 1][x] = "*" |
|||
} else if (d == 1) { |
|||
maze[y][x + 1] = "*" |
|||
} else if (d == 2) { |
|||
maze[y + 1][x] = "*" |
|||
} else if (d == 3) { |
|||
maze[y][x - 1] = "*" |
|||
} |
|||
} |
|||
return ok |
|||
} |
|||
/* |
|||
* Solve the maze and draw the solution. For simplicity, |
|||
* assumes the starting point is the lower right, and the |
|||
* ending point is the upper left. |
|||
*/ |
|||
var solveMaze = Fn.new { |maze| |
|||
solveMazeRecursively.call(maze, maze[0].count - 2, maze.count - 2, -1) |
|||
} |
|||
/* |
|||
* Opposite of decimateHorizontally(). Adds extra characters to make |
|||
* the maze "look right", and converts each line from a char list to a |
|||
* string at the same time. |
|||
*/ |
|||
var expandHorizontally = Fn.new { |maze| |
|||
var tmp = List.filled(3, " ") |
|||
var lines = List.filled(maze.count, null) |
|||
for (i in 0...maze.count) { |
|||
var sb = "" |
|||
for (j in 0...maze[i].count) { |
|||
if (j % 2 == 0) { |
|||
sb = sb + maze[i][j] |
|||
} else { |
|||
for (k in 0..2) tmp[k] = maze[i][j] |
|||
if (tmp[1] == "*") { |
|||
tmp[0] = " " |
|||
tmp[2] = " " |
|||
} |
|||
sb = sb + tmp.join() |
|||
} |
|||
} |
|||
lines[i] = sb |
|||
} |
|||
return lines |
|||
} |
|||
/* |
|||
* Accepts a maze as generated by: |
|||
* http://rosettacode.org/wiki/Maze_generation#Wren |
|||
* in a file whose name is specified as a command-line argument. |
|||
*/ |
|||
var args = Process.arguments |
|||
if (args.count != 1) { |
|||
System.print("The maze file to be read should be passed as a single command line argument.") |
|||
return |
|||
} |
|||
if (!File.exists(args[0])) { |
|||
System.print("Sorry %(args[0]) does not exist.") |
|||
return |
|||
} |
|||
var lines = FileUtil.readLines(args[0]).where { |l| l != "" }.toList |
|||
var maze = decimateHorizontally.call(lines) |
|||
solveMaze.call(maze) |
|||
var solvedLines = expandHorizontally.call(maze) |
|||
System.print(solvedLines.join("\n"))</syntaxhighlight> |
|||
{{out}} |
|||
Using the same maze.txt as the Kotlin example: |
|||
<pre> |
|||
+---+---+---+---+---+---+---+---+ |
|||
| * * * * * | * * * * * | | |
|||
+---+---+ * + + * +---+ * + + |
|||
| | * | | * | | * * * | |
|||
+ + + * +---+ * + +---+ * + |
|||
| | | * | * * * | * | |
|||
+ + + * + * +---+---+---+ * + |
|||
| | | * | * * * * * * * | * | |
|||
+---+ + * +---+---+---+ * + * + |
|||
| | * | * * * * * | * | * | |
|||
+ +---+ * + * +---+ * + * + * + |
|||
| | * * * | * * * | * * * | * | |
|||
+ + * +---+---+ * +---+---+ * + |
|||
| * * * | * * * | * * * | * * * | |
|||
+ * +---+ * + * +---+ * + * +---+ |
|||
| * * * * * | * * * * * | * * * | |
|||
+---+---+---+---+---+---+---+---+ |
|||
</pre> |
</pre> |
||
Line 3,209: | Line 6,018: | ||
This entry parses a maze generated by http://rosettacode.org/wiki/Maze_generation#zkl and chooses random start/end points. |
This entry parses a maze generated by http://rosettacode.org/wiki/Maze_generation#zkl and chooses random start/end points. |
||
Parsing the maze and switching between row major (h,w) and row minor (x,y) makes for some pretty vile code. |
Parsing the maze and switching between row major (h,w) and row minor (x,y) makes for some pretty vile code. |
||
< |
<syntaxhighlight lang="zkl">ver,hor:=make_maze(); // see above link for this code |
||
fcn munge(a,b){ String(a[0,2],b,a[3,*]) } // "+---" --> "+-*-" |
fcn munge(a,b){ String(a[0,2],b,a[3,*]) } // "+---" --> "+-*-" |
||
Line 3,251: | Line 6,060: | ||
ver[endy][endx] =munge(ver[endy][endx],"E"); |
ver[endy][endx] =munge(ver[endy][endx],"E"); |
||
foreach a,b in (hor.zip(ver)) { println(a.concat(),"\n",b.concat()) } |
foreach a,b in (hor.zip(ver)) { println(a.concat(),"\n",b.concat()) } |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
Latest revision as of 20:02, 22 July 2024
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
For a maze generated by this task, write a function that finds (and displays) the shortest path between two cells.
Note that because these mazes are generated by the Depth-first search algorithm, they contain no circular paths,
and a simple depth-first tree search can be used.
11l
F dijkstra(graph, source)
V n = graph.len
V dist = [Float.infinity] * n
V previous = [-1] * n
dist[source] = 0
V Q = Array(0 .< n)
L !Q.empty
V u = min(Q, key' n -> @dist[n])
Q.remove(u)
I dist[u] == Float.infinity
L.break
L(v) 0 .< n
I graph[u][v] & (v C Q)
V alt = dist[u] + graph[u][v]
I alt < dist[v]
dist[v] = alt
previous[v] = u
R previous
F display_solution(predecessor)
V cell = predecessor.len - 1
L cell != 0
print(cell, end' ‘<’)
cell = predecessor[cell]
print(0)
V graph = [
[0,1,0,0,0,0],
[1,0,1,0,1,0],
[0,1,0,0,0,1],
[0,0,0,0,1,0],
[0,1,0,1,0,0],
[0,0,1,0,0,0]
]
display_solution(dijkstra(graph, 0))
- Output:
5<2<1<0
Action!
Action! language does not support recursion. Therefore an iterative approach with a stack has been proposed.
DEFINE TOP="0"
DEFINE RIGHT="1"
DEFINE BOTTOM="2"
DEFINE LEFT="3"
DEFINE WIDTH="160"
DEFINE HEIGHT="96"
DEFINE STACK_SIZE="5000"
BYTE ARRAY stack(STACK_SIZE)
INT stackSize
PROC InitStack()
stackSize=0
RETURN
BYTE FUNC IsEmpty()
IF stackSize=0 THEN
RETURN (1)
FI
RETURN (0)
BYTE FUNC IsFull()
IF stackSize>=STACK_SIZE THEN
RETURN (1)
FI
RETURN (0)
PROC Push(BYTE x,y)
IF IsFull() THEN Break() RETURN FI
stack(stackSize)=x stackSize==+1
stack(stackSize)=y stackSize==+1
RETURN
PROC Pop(BYTE POINTER x,y)
IF IsEmpty() THEN Break() RETURN FI
stackSize==-1 y^=stack(stackSize)
stackSize==-1 x^=stack(stackSize)
RETURN
PROC Push3(BYTE x,y,d)
IF IsFull() THEN Break() RETURN FI
stack(stackSize)=x stackSize==+1
stack(stackSize)=y stackSize==+1
stack(stackSize)=d stackSize==+1
RETURN
PROC Pop3(BYTE POINTER x,y,d)
IF IsEmpty() THEN Break() RETURN FI
stackSize==-1 d^=stack(stackSize)
stackSize==-1 y^=stack(stackSize)
stackSize==-1 x^=stack(stackSize)
RETURN
PROC FillScreen()
BYTE POINTER ptr ;pointer to the screen memory
INT screenSize=[3840]
ptr=PeekC(88)
SetBlock(ptr,screenSize,$55)
Color=0
Plot(0,HEIGHT-1) DrawTo(WIDTH-1,HEIGHT-1) DrawTo(WIDTH-1,0)
RETURN
PROC GetNeighbors(BYTE x,y BYTE ARRAY n BYTE POINTER count)
DEFINE WALL="1"
count^=0
IF y>2 AND Locate(x,y-2)=WALL THEN
n(count^)=TOP count^==+1
FI
IF x<WIDTH-3 AND Locate(x+2,y)=WALL THEN
n(count^)=RIGHT count^==+1
FI
IF y<HEIGHT-3 AND Locate(x,y+2)=WALL THEN
n(count^)=BOTTOM count^==+1
FI
IF x>2 AND Locate(x-2,y)=WALL THEN
n(count^)=LEFT count^==+1
FI
RETURN
PROC DrawConnection(BYTE POINTER x,y BYTE dir)
Plot(x^,y^)
IF dir=TOP THEN
y^==-2
ELSEIF dir=RIGHT THEN
x^==+2
ELSEIF dir=BOTTOM THEN
y^==+2
ELSE
x^==-2
FI
DrawTo(x^,y^)
RETURN
PROC Maze(BYTE x,y)
BYTE ARRAY stack,neighbors
BYTE dir,nCount
FillScreen()
Color=2
InitStack()
Push(x,y)
WHILE IsEmpty()=0
DO
Pop(@x,@y)
GetNeighbors(x,y,neighbors,@nCount)
IF nCount>0 THEN
Push(x,y)
dir=neighbors(Rand(nCount))
DrawConnection(@x,@y,dir)
Push(x,y)
FI
OD
RETURN
BYTE FUNC IsConnection(BYTE x,y,dir)
DEFINE WAY="2"
IF dir=TOP AND y>2 AND Locate(x,y-1)=WAY THEN
RETURN (1)
ELSEIF dir=RIGHT AND x<WIDTH-3 AND Locate(x+1,y)=WAY THEN
RETURN (1)
ELSEIF dir=BOTTOM AND y<HEIGHT-3 AND Locate(x,y+1)=WAY THEN
RETURN (1)
ELSEIF dir=LEFT AND x>2 AND Locate(x-1,y)=WAY THEN
RETURN (1)
FI
RETURN (0)
PROC Solve(BYTE x1,y1,x2,y2)
BYTE dir,x,y,lastX,lastY,back
Color=3
Plot(x1,y1)
Plot(x2,y2)
InitStack()
Push3(x1,y1,TOP)
WHILE IsEmpty()=0
DO
Pop3(@x,@y,@dir)
IF back THEN
Color=2
Plot(lastX,lastY)
DrawTo(x,y)
FI
IF IsConnection(x,y,dir) THEN
Color=3
Push3(x,y,dir+1)
DrawConnection(@x,@y,dir)
IF x=x2 AND y=y2 THEN
RETURN
FI
Push3(x,y,TOP)
back=0
ELSEIF dir<=LEFT THEN
Push3(x,y,dir+1)
back=0
ELSE
lastX=x
lastY=y
back=1
FI
OD
RETURN
PROC Main()
BYTE CH=$02FC,COLOR0=$02C4,COLOR1=$02C5,COLOR2=$02C6
BYTE x,y,x2,y2
Graphics(7+16)
COLOR0=$0A
COLOR1=$04
COLOR2=$A6
x=Rand((WIDTH RSH 1)-1) LSH 1+1
y=Rand((HEIGHT RSH 1)-1) LSH 1+1
Maze(x,y)
x=Rand((WIDTH RSH 1)-1) LSH 1+1
y=Rand((HEIGHT RSH 1)-1) LSH 1+1
x2=Rand((WIDTH RSH 1)-1) LSH 1+1
y2=Rand((HEIGHT RSH 1)-1) LSH 1+1
Solve(x,y,x2,y2)
DO UNTIL CH#$FF OD
CH=$FF
RETURN
- Output:
Screenshot from Atari 8-bit computer
Ada
The maze is read from the standard input. The size of the maze is hardwired into the program (see the constants X_Size and Y_Size).
with Ada.Text_IO;
procedure Maze_Solver is
X_Size: constant Natural := 45;
Y_Size: constant Natural := 17;
subtype X_Range is Natural range 1 .. X_Size;
subtype Y_Range is Natural range 1 .. Y_Size;
East: constant X_Range := 2;
South: constant Y_Range := 1;
X_Start: constant X_Range := 3; -- start at the upper left
Y_Start: constant Y_Range := 1;
X_Finish: constant X_Range := X_Size-East; -- go to the lower right
Y_Finish: constant Y_Range := Y_Size;
type Maze_Type is array (Y_Range) of String(X_Range);
function Solved(X: X_Range; Y: Y_Range) return Boolean is
begin
return (X = X_Finish) and (Y = Y_Finish);
end Solved;
procedure Output_Maze(M: Maze_Type; Message: String := "") is
begin
if Message /= "" then
Ada.Text_IO.Put_Line(Message);
end if;
for I in M'Range loop
Ada.Text_IO.Put_Line(M(I));
end loop;
end Output_Maze;
procedure Search(M: in out Maze_Type; X: X_Range; Y:Y_Range) is
begin
M(Y)(X) := '*';
if Solved(X, Y) then
Output_Maze(M, "Solution found!");
else
if Integer(Y)-South >= 1 and then M(Y-South)(X) = ' ' then
Search(M, X, Y-South);
end if;
if Integer(Y)+South <= Y_Size and then M(Y+South)(X) = ' ' then
Search(M, X, Y+South);
end if;
if Integer(X)-East >= 1 and then M(Y)(X-East) = ' ' then
Search(M, X-East, Y);
end if;
if Integer(Y)+East <= Y_Size and then M(Y)(X+East) = ' ' then
Search(M, X+East, Y);
end if;
end if;
M(Y)(X) := ' ';
end Search;
Maze: Maze_Type;
X: X_Range := X_Start;
Y: Y_Range := Y_Start;
begin
for I in 1 .. Y_Size loop
Maze(I) := Ada.Text_IO.Get_Line;
end loop;
Maze(Y_Start)(X_Start) := ' '; -- Start from
Maze(Y_Finish)(X_Finish) := ' '; -- Go_To
Output_Maze(Maze, "The Maze:");
Ada.Text_IO.New_Line;
Search(Maze, X, Y) ; -- Will output *all* Solutions.
-- If there is no output, there is no solution.
end Maze_Solver;
- Example output
- :
(using a maze generated by the Ada implementation of the maze generation task as the input):
> ./maze_solver < maze.txt The Maze: +- -+---+---+---+---+---+---+---+---+---+---+ | | | + + +---+---+---+---+---+---+---+ + + | | | | | | + +---+---+ +---+ + +---+---+---+ + | | | | | | | +---+ +---+---+ +---+ + + +---+ + | | | | | | | + +---+ +---+---+ +---+ + + +---+ | | | | | | +---+---+ + +---+---+---+---+ +---+ + | | | | | | + + +---+---+ +---+ + +---+---+ + | | | | | + +---+ +---+---+---+---+---+---+---+ + | | | +---+---+---+---+---+---+---+---+---+---+- -+ Solution found! +-*-+---+---+---+---+---+---+---+---+---+---+ | * * * * * * * * * * * * * * * * * * * | | + + +---+---+---+---+---+---+---+ * + + | | | | * * * * * * * | | + +---+---+ +---+ + * +---+---+---+ + | | | | * | | | +---+ +---+---+ +---+ * + + +---+ + | | | * * * | | | | + +---+ +---+---+ * +---+ + + +---+ | | | * * * * * | | | +---+---+ + * +---+---+---+---+ +---+ + | | * * * | * * * | | | + + +---+---+ * +---+ * + * +---+---+ + | | | * * * * * | * * * * * * * | + +---+ +---+---+---+---+---+---+---+ * + | | * | +---+---+---+---+---+---+---+---+---+---+-*-+
AutoHotkey
Generator and solver combined.
Width := 10, Height := 10 ; set grid size
SleepTime := 0
gosub, Startup
Gui, +AlwaysOnTop
Gui, font, s12, consolas
Gui, add, edit, vEditGrid x10, % maze
Gui, add, button, xs gStartup Default, Generate maze
Gui, add, button, x+10 gSolve, Solve
Gui, show,, maze
GuiControl,, EditGrid, % maze ; show maze
return
;-----------------------------------------------------------------------
^Esc::
GuiEscape:
GuiClose:
ExitApp
return
;-----------------------------------------------------------------------
Startup:
oMaze := [] ; initialize
Solved := false
loop, % Height
{
row := A_Index
loop, % Width ; create oMaze[row,column] borders
col := A_Index, oMaze[row,col] := "LRTB" ; i.e. oMaze[2,5] := LRTB (add all borders)
}
Random, row, 1, % Height ; random row
Random, col, 1, % Width ; random col
grid := maze2text(oMaze) ; object to text
GuiControl,, EditGrid, % Grid ; show Grid
row := col := 1 ; reset to 1,1
oMaze := Generate_maze(row, col, oMaze) ; generate maze starting from random row/column
oMaze[1,1] .= "X" ; start from 1,1
maze := maze2text(oMaze) ; object to text
GuiControl,, EditGrid, % maze ; show maze
GuiControl,, EditRoute ; clear route
GuiControl, Enable, Solve
return
;-----------------------------------------------------------------------
Solve:
GuiControl, Disable, Generate maze
GuiControl, Disable, Solve
loop % oRoute.MaxIndex()
oRoute.pop()
oSolution := Solve(1, 1, oMaze) ; solve starting from 1,1
oMaze := oSolution.1
oRoute := oSolution.2
Update(oMaze, oRoute)
Solved := true
GuiControl, Enable, Generate maze
return
;-----------------------------------------------------------------------
Update(oMaze, oRoute){
global SleepTime
GuiControl,, EditGrid, % maze2text(oMaze)
Sleep, % SleepTime
}
;-----------------------------------------------------------------------
maze2text(oMaze){
width := oMaze.1.MaxIndex()
BLK := "█"
for row, objRow in oMaze
{
for col, val in objRow ; add ceiling
{
ceiling := InStr(oMaze[row, col] , "x") && InStr(oMaze[row-1, col] , "x") ? "+ " BLK " " : "+ "
grid .= (InStr(val, "T") ? "+---" : ceiling) (col = Width ? "+`n" : "")
}
for col, val in objRow ; add left wall
{
wall := SubStr(val, 0) = "X" ? BLK : " "
grid .= (InStr(val, "L") ? "| " : " ") wall " " (col = Width ? "|`n" : "") ; add left wall if needed then outer right border
}
}
Loop % Width
Grid .= "+---" ; add bottom floor
Grid .= "+" ; add right bottom corner
return RegExReplace(grid , BLK " (?=" BLK ")" , BLK BLK BLK BLK) ; fill gaps
}
;-----------------------------------------------------------------------
Generate_maze(row, col, oMaze) {
neighbors := row+1 "," col "`n" row-1 "," col "`n" row "," col+1 "`n" row "," col-1
Sort, neighbors, random ; randomize neighbors list
Loop, parse, neighbors, `n ; for each neighbor
{
rowX := StrSplit(A_LoopField, ",").1 ; this neighbor row
colX := StrSplit(A_LoopField, ",").2 ; this neighbor column
if !instr(oMaze[rowX,colX], "LRTB") || !oMaze[rowX, colX] ; if visited (has a missing border) or out of bounds
continue ; skip
; remove borders
if (row > rowX) ; Cell is below this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "T") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "B")
else if (row < rowX) ; Cell is above this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "B") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "T")
else if (col > colX) ; Cell is right of this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "L") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "R")
else if (col < colX) ; Cell is left of this neighbor
oMaze[row,col] := StrReplace(oMaze[row,col], "R") , oMaze[rowX,colX] := StrReplace(oMaze[rowX,colX], "L")
Generate_maze(rowX, colX, oMaze) ; recurse for this neighbor
}
return, oMaze
}
;-----------------------------------------------------------------------
Solve(row, col, oMaze){
static oRoute := []
oNeighbor := [], targetrow := oMaze.MaxIndex(), targetCol := oMaze.1.MaxIndex()
;~ Update(oMaze, oRoute)
oRoute.push(row ":" col) ; push current cell address to oRoute
oMaze[row, col] .= "X" ; mark it visited "X"
if (row = targetrow) && (Col = targetCol) ; if solved
return true ; return ture
; create list of Neighbors
oNeighbor[row, col] := []
if !InStr(oMaze[row, col], "R") ; if no Right border
oNeighbor[row, col].push(row "," col+1) ; add neighbor
if !InStr(oMaze[row, col], "B") ; if no Bottom border
oNeighbor[row, col].push(row+1 "," col) ; add neighbor
if !InStr(oMaze[row, col], "T") ; if no Top border
oNeighbor[row, col].push(row-1 "," col) ; add neighbor
if !InStr(oMaze[row, col], "L") ; if no Left border
oNeighbor[row, col].push(row "," col-1) ; add neighbor
; recurese for each oNeighbor
for each, neighbor in oNeighbor[row, col] ; for each neighbor
{
Update(oMaze, oRoute)
startrow := StrSplit(neighbor, ",").1 ; this neighbor
startCol := StrSplit(neighbor, ",").2 ; becomes starting point
if !InStr(oMaze[startrow, startCol], "X") ; if it was not visited
if Solve(startrow, startCol, oMaze) ; recurse for current neighbor
return [oMaze, oRoute] ; return solution if solved
}
oRoute.pop() ; no solution found, back track
oMaze[row, Col] := StrReplace(oMaze[row, Col], "X") ; no solution found, back track
;~ Update(oMaze, oRoute)
}
;-----------------------------------------------------------------------
#IfWinActive, maze
Right::
Left::
Up::
Down::
if Solved
return
if (A_ThisHotkey="Right") && (!InStr(oMaze[row,col], "R"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , col++
if (A_ThisHotkey="Left") && (!InStr(oMaze[row,col], "L"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , col--
if (A_ThisHotkey="Up") && (!InStr(oMaze[row,col], "T"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , row--
if (A_ThisHotkey="Down") && (!InStr(oMaze[row,col], "B"))
oMaze[row, col] := StrReplace(oMaze[row, col], "X") , row++
oMaze[row, col] .= "X"
GuiControl,, EditGrid, % maze2text(oMaze)
if (col = Width) && (row = Height)
{
Solved := true
oMaze[height, width] := StrReplace(oMaze[height, width], "X")
SleepTime := 0
gosub, solve
return
}
return
#IfWinActive
Outputs:
+---+---+---+---+---+---+---+---+---+---+ | ¦¦¦¦¦ | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦¦¦¦¦ | +---+ ¦ +---+ ¦ + ¦ +---+ ¦ +---+---+ ¦ + | | ¦ | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | ¦ | + + ¦ + ¦ +---+---+---+---+ ¦ + ¦ + ¦ + | ¦¦¦¦¦ | ¦ | ¦¦¦¦¦¦¦¦¦¦¦¦¦ | ¦ | ¦¦¦¦¦ | + ¦ +---+ ¦ + ¦ +---+---+ ¦ + ¦ +---+---+ | ¦ | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | | + ¦ +---+---+---+ ¦ +---+---+---+ ¦ + + | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | +---+ ¦ + ¦ +---+---+ +---+---+---+ ¦ + | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | | ¦¦¦¦¦ | | ¦ | + ¦ +---+---+---+ ¦ +---+ ¦ + ¦ + + ¦ + | ¦¦¦¦¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | ¦ | ¦¦¦¦¦ | +---+ ¦ + ¦ +---+---+ ¦ +---+ ¦ + ¦ +---+ | ¦¦¦¦¦ | ¦ | ¦¦¦¦¦¦¦¦¦ | ¦¦¦¦¦ | ¦ | | + ¦ +---+ ¦ + ¦ +---+---+ ¦ +---+ ¦ + + | ¦ | ¦¦¦¦¦ | ¦¦¦¦¦ | | ¦¦¦¦¦ | + ¦ +---+---+---+---+ ¦ +---+ +---+ ¦ + | ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ | ¦ | +---+---+---+---+---+---+---+---+---+---+
BBC BASIC
![](http://static.miraheze.org/rosettacodewiki/9/90/Mazesolve_bbc.gif)
Maze generation code also included.
MazeWidth% = 11
MazeHeight% = 9
MazeCell% = 50
VDU 23,22,MazeWidth%*MazeCell%/2+3;MazeHeight%*MazeCell%/2+3;8,16,16,128
VDU 23,23,3;0;0;0; : REM Line thickness
OFF
PROCgeneratemaze(Maze&(), MazeWidth%, MazeHeight%, MazeCell%)
PROCsolvemaze(Path{()}, Maze&(), 0, MazeHeight%-1, MazeWidth%-1, 0, MazeCell%)
END
DEF PROCsolvemaze(RETURN s{()}, m&(), x%, y%, dstx%, dsty%, s%)
LOCAL h%, i%, n%, p%, q%, w%
w% = DIM(m&(),1)
h% = DIM(m&(),2)
DIM s{(w%*h%) x%,y%}
GCOL 3,14
m&(x%,y%) OR= &80
REPEAT
FOR i% = 0 TO 3
CASE i% OF
WHEN 0: p% = x%-1 : q% = y%
WHEN 1: p% = x%+1 : q% = y%
WHEN 2: p% = x% : q% = y%-1
WHEN 3: p% = x% : q% = y%+1
ENDCASE
IF p% >= 0 IF p% < w% IF q% >= 0 IF q% < h% IF m&(p%,q%) < &80 THEN
IF p% > x% IF m&(p%,q%) AND 1 EXIT FOR
IF q% > y% IF m&(p%,q%) AND 2 EXIT FOR
IF x% > p% IF m&(x%,y%) AND 1 EXIT FOR
IF y% > q% IF m&(x%,y%) AND 2 EXIT FOR
ENDIF
NEXT
IF i% < 4 THEN
m&(p%,q%) OR= &80
s{(n%)}.x% = x%
s{(n%)}.y% = y%
n% += 1
ELSE
IF n% > 0 THEN
n% -= 1
p% = s{(n%)}.x%
q% = s{(n%)}.y%
ENDIF
ENDIF
LINE (x%+0.5)*s%,(y%+0.5)*s%,(p%+0.5)*s%,(q%+0.5)*s%
x% = p%
y% = q%
UNTIL x%=dstx% AND y%=dsty%
s{(n%)}.x% = x%
s{(n%)}.y% = y%
ENDPROC
DEF PROCgeneratemaze(RETURN m&(), w%, h%, s%)
LOCAL x%, y%
DIM m&(w%, h%)
FOR y% = 0 TO h%
LINE 0,y%*s%,w%*s%,y%*s%
NEXT
FOR x% = 0 TO w%
LINE x%*s%,0,x%*s%,h%*s%
NEXT
GCOL 15
PROCcell(m&(), RND(w%)-1, y% = RND(h%)-1, w%, h%, s%)
ENDPROC
DEF PROCcell(m&(), x%, y%, w%, h%, s%)
LOCAL i%, p%, q%, r%
m&(x%,y%) OR= &40 : REM Mark visited
r% = RND(4)
FOR i% = r% TO r%+3
CASE i% MOD 4 OF
WHEN 0: p% = x%-1 : q% = y%
WHEN 1: p% = x%+1 : q% = y%
WHEN 2: p% = x% : q% = y%-1
WHEN 3: p% = x% : q% = y%+1
ENDCASE
IF p% >= 0 IF p% < w% IF q% >= 0 IF q% < h% IF m&(p%,q%) < &40 THEN
IF p% > x% m&(p%,q%) OR= 1 : LINE p%*s%,y%*s%+4,p%*s%,(y%+1)*s%-4
IF q% > y% m&(p%,q%) OR= 2 : LINE x%*s%+4,q%*s%,(x%+1)*s%-4,q%*s%
IF x% > p% m&(x%,y%) OR= 1 : LINE x%*s%,y%*s%+4,x%*s%,(y%+1)*s%-4
IF y% > q% m&(x%,y%) OR= 2 : LINE x%*s%+4,y%*s%,(x%+1)*s%-4,y%*s%
PROCcell(m&(), p%, q%, w%, h%, s%)
ENDIF
NEXT
ENDPROC
C
See Maze generation for combined gen/solve code.
C#
using System;
using System.Text;
public class Maze
{
private char[,] cells;
private char[,] hWalls; // Horizontal walls
private char[,] vWalls; // Vertical walls
private Random rand = new Random();
public Maze(int rows, int cols)
{
cells = new char[rows, cols];
hWalls = new char[rows + 1, cols]; // Include walls for the bottom
vWalls = new char[rows, cols + 1]; // Include walls for the right side
// Initialize walls
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
hWalls[i, j] = '-';
vWalls[i, j] = '|';
}
}
// Set the outer walls for the bottom and right
for (int i = 0; i < cols; i++)
{
hWalls[rows, i] = '-';
}
for (int i = 0; i < rows; i++)
{
vWalls[i, cols] = '|';
}
}
public override string ToString()
{
var builder = new StringBuilder();
for (int i = 0; i < cells.GetLength(0); i++)
{
// Top walls
for (int j = 0; j < cells.GetLength(1); j++)
{
builder.Append("+");
builder.Append(hWalls[i, j] == '-' ? "---" : " ");
}
builder.AppendLine("+");
// Side walls and cells
for (int j = 0; j < cells.GetLength(1); j++)
{
builder.Append(vWalls[i, j] == '|' ? "| " : " ");
char cell = cells[i, j] == '\0' ? ' ' : cells[i, j];
builder.Append(cell + " ");
}
builder.AppendLine("|");
}
// Bottom walls
for (int j = 0; j < cells.GetLength(1); j++)
{
builder.Append("+---");
}
builder.AppendLine("+");
return builder.ToString();
}
public void Generate()
{
Generate(rand.Next(cells.GetLength(0)), rand.Next(cells.GetLength(1)));
}
private void Generate(int r, int c)
{
cells[r, c] = ' ';
int[] dirs = { 0, 1, 2, 3 };
Shuffle(dirs);
foreach (int dir in dirs)
{
switch (dir)
{
case 0: // Up
if (r > 0 && cells[r - 1, c] == '\0')
{
hWalls[r, c] = ' ';
Generate(r - 1, c);
}
break;
case 1: // Down
if (r < cells.GetLength(0) - 1 && cells[r + 1, c] == '\0')
{
hWalls[r + 1, c] = ' ';
Generate(r + 1, c);
}
break;
case 2: // Right
if (c < cells.GetLength(1) - 1 && cells[r, c + 1] == '\0')
{
vWalls[r, c + 1] = ' ';
Generate(r, c + 1);
}
break;
case 3: // Left
if (c > 0 && cells[r, c - 1] == '\0')
{
vWalls[r, c] = ' ';
Generate(r, c - 1);
}
break;
}
}
}
private void Shuffle(int[] array)
{
for (int i = array.Length - 1; i > 0; i--)
{
int j = rand.Next(i + 1);
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
public void Solve(int startRow, int startCol, int endRow, int endCol)
{
if (Solve(startRow, startCol, endRow, endCol, -1))
{
cells[startRow, startCol] = 'S';
cells[endRow, endCol] = 'F';
}
}
private bool Solve(int r, int c, int endRow, int endCol, int dir)
{
if (r == endRow && c == endCol) return true;
// Up
if (dir != 1 && r > 0 && hWalls[r, c] == ' ' && Solve(r - 1, c, endRow, endCol, 0))
{
cells[r, c] = '^';
return true;
}
// Down
if (dir != 0 && r < cells.GetLength(0) - 1 && hWalls[r + 1, c] == ' ' && Solve(r + 1, c, endRow, endCol, 1))
{
cells[r, c] = 'v';
return true;
}
// Right
if (dir != 3 && c < cells.GetLength(1) - 1 && vWalls[r, c + 1] == ' ' && Solve(r, c + 1, endRow, endCol, 2))
{
cells[r, c] = '>';
return true;
}
// Left
if (dir != 2 && c > 0 && vWalls[r, c] == ' ' && Solve(r, c - 1, endRow, endCol, 3))
{
cells[r, c] = '<';
return true;
}
return false;
}
}
class Program
{
static void Main(string[] args)
{
var maze = new Maze(4, 7);
maze.Generate();
Random rand = new Random();
maze.Solve(rand.Next(4), rand.Next(7), rand.Next(4), rand.Next(7));
Console.WriteLine(maze);
}
}
- Output:
+---+---+---+---+---+---+---+ | | | v S | + +---+ +---+ + +---+ | | | | > v | +---+---+ + +---+---+ + | | | | F | + +---+---+ + + + + | | | +---+---+---+---+---+---+---+
C++
Generator and solver combined. The generator is the same found in Maze generation
#include <windows.h>
#include <iostream>
#include <string>
//--------------------------------------------------------------------------------------------------
using namespace std;
//--------------------------------------------------------------------------------------------------
const int BMP_SIZE = 512, CELL_SIZE = 8;
//--------------------------------------------------------------------------------------------------
enum directions { NONE, NOR = 1, EAS = 2, SOU = 4, WES = 8 };
//--------------------------------------------------------------------------------------------------
class myBitmap
{
public:
myBitmap() : pen( NULL ) {}
~myBitmap()
{
DeleteObject( pen );
DeleteDC( hdc );
DeleteObject( bmp );
}
bool create( int w, int h )
{
BITMAPINFO bi;
ZeroMemory( &bi, sizeof( bi ) );
bi.bmiHeader.biSize = sizeof( bi.bmiHeader );
bi.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = -h;
HDC dc = GetDC( GetConsoleWindow() );
bmp = CreateDIBSection( dc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0 );
if( !bmp ) return false;
hdc = CreateCompatibleDC( dc );
SelectObject( hdc, bmp );
ReleaseDC( GetConsoleWindow(), dc );
width = w; height = h;
return true;
}
void clear()
{
ZeroMemory( pBits, width * height * sizeof( DWORD ) );
}
void setPenColor( DWORD clr )
{
if( pen ) DeleteObject( pen );
pen = CreatePen( PS_SOLID, 1, clr );
SelectObject( hdc, pen );
}
void saveBitmap( string path )
{
BITMAPFILEHEADER fileheader;
BITMAPINFO infoheader;
BITMAP bitmap;
DWORD wb;
GetObject( bmp, sizeof( bitmap ), &bitmap );
DWORD* dwpBits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];
ZeroMemory( dwpBits, bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD ) );
ZeroMemory( &infoheader, sizeof( BITMAPINFO ) );
ZeroMemory( &fileheader, sizeof( BITMAPFILEHEADER ) );
infoheader.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
infoheader.bmiHeader.biCompression = BI_RGB;
infoheader.bmiHeader.biPlanes = 1;
infoheader.bmiHeader.biSize = sizeof( infoheader.bmiHeader );
infoheader.bmiHeader.biHeight = bitmap.bmHeight;
infoheader.bmiHeader.biWidth = bitmap.bmWidth;
infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD );
fileheader.bfType = 0x4D42;
fileheader.bfOffBits = sizeof( infoheader.bmiHeader ) + sizeof( BITMAPFILEHEADER );
fileheader.bfSize = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;
GetDIBits( hdc, bmp, 0, height, ( LPVOID )dwpBits, &infoheader, DIB_RGB_COLORS );
HANDLE file = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
WriteFile( file, &fileheader, sizeof( BITMAPFILEHEADER ), &wb, NULL );
WriteFile( file, &infoheader.bmiHeader, sizeof( infoheader.bmiHeader ), &wb, NULL );
WriteFile( file, dwpBits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, NULL );
CloseHandle( file );
delete [] dwpBits;
}
HDC getDC() const { return hdc; }
int getWidth() const { return width; }
int getHeight() const { return height; }
private:
HBITMAP bmp;
HDC hdc;
HPEN pen;
void *pBits;
int width, height;
};
//--------------------------------------------------------------------------------------------------
class mazeGenerator
{
public:
mazeGenerator()
{
_world = 0;
_bmp.create( BMP_SIZE, BMP_SIZE );
_bmp.setPenColor( RGB( 0, 255, 0 ) );
}
~mazeGenerator() { killArray(); }
BYTE* getMaze() const { return _world; }
void create( int side )
{
_s = side;
generate();
}
private:
void generate()
{
killArray();
_world = new BYTE[_s * _s];
ZeroMemory( _world, _s * _s );
_ptX = rand() % _s; _ptY = rand() % _s;
carve();
}
void carve()
{
while( true )
{
int d = getDirection();
if( d < NOR ) return;
switch( d )
{
case NOR:
_world[_ptX + _s * _ptY] |= NOR; _ptY--;
_world[_ptX + _s * _ptY] = SOU | SOU << 4;
break;
case EAS:
_world[_ptX + _s * _ptY] |= EAS; _ptX++;
_world[_ptX + _s * _ptY] = WES | WES << 4;
break;
case SOU:
_world[_ptX + _s * _ptY] |= SOU; _ptY++;
_world[_ptX + _s * _ptY] = NOR | NOR << 4;
break;
case WES:
_world[_ptX + _s * _ptY] |= WES; _ptX--;
_world[_ptX + _s * _ptY] = EAS | EAS << 4;
}
}
}
int getDirection()
{
int d = 1 << rand() % 4;
while( true )
{
for( int x = 0; x < 4; x++ )
{
if( testDir( d ) ) return d;
d <<= 1;
if( d > 8 ) d = 1;
}
d = ( _world[_ptX + _s * _ptY] & 0xf0 ) >> 4;
if( !d ) return -1;
switch( d )
{
case NOR: _ptY--; break;
case EAS: _ptX++; break;
case SOU: _ptY++; break;
case WES: _ptX--; break;
}
d = 1 << rand() % 4;
}
}
bool testDir( int d )
{
switch( d )
{
case NOR: return ( _ptY - 1 > -1 && !_world[_ptX + _s * ( _ptY - 1 )] );
case EAS: return ( _ptX + 1 < _s && !_world[_ptX + 1 + _s * _ptY] );
case SOU: return ( _ptY + 1 < _s && !_world[_ptX + _s * ( _ptY + 1 )] );
case WES: return ( _ptX - 1 > -1 && !_world[_ptX - 1 + _s * _ptY] );
}
return false;
}
void killArray() { if( _world ) delete [] _world; }
BYTE* _world;
int _s, _ptX, _ptY;
myBitmap _bmp;
};
//--------------------------------------------------------------------------------------------------
class mazeSolver
{
public:
mazeSolver()
{
_bmp.create( BMP_SIZE, BMP_SIZE );
_pts = 0;
}
~mazeSolver() { killPoints(); }
void solveIt( BYTE* maze, int size, int sX, int sY, int eX, int eY )
{
_lastDir = NONE;
_world = maze; _s = size; _sx = sX; _sy = sY; _ex = eX; _ey = eY;
for( int y = 0; y < _s; y++ )
for( int x = 0; x < _s; x++ )
_world[x + _s * y] &= 0x0f;
_world[_sx + _s * _sy] |= NOR << 4;
killPoints();
_pts = new BYTE[_s * _s];
ZeroMemory( _pts, _s * _s );
findTheWay();
_sx = sX; _sy = sY;
display();
}
private:
int invert( int d )
{
switch( d )
{
case NOR: return SOU;
case SOU: return NOR;
case WES: return EAS;
case EAS: return WES;
}
return NONE;
}
void updatePosition( int d )
{
switch( d )
{
case NOR: _sy--; break;
case EAS: _sx++; break;
case SOU: _sy++; break;
case WES: _sx--;
}
}
void findTheWay()
{
while( true )
{
int d = getDirection();
if( d < NOR ) return;
_lastDir = invert( d );
_world[_sx + _s * _sy] |= d;
_pts[_sx + _s * _sy] = d;
updatePosition( d );
if( _sx == _ex && _sy == _ey ) return;
_world[_sx + _s * _sy] |= _lastDir << 4;
}
}
int getDirection()
{
int d = 1 << rand() % 4;
while( true )
{
for( int x = 0; x < 4; x++ )
{
if( testDirection( d ) ) return d;
d <<= 1;
if( d > 8 ) d = 1;
}
d = ( _world[_sx + _s * _sy] & 0xf0 ) >> 4;
if( !d ) return -1;
_pts[_sx + _s * _sy] = 0;
updatePosition( d );
_lastDir = invert( d );
d = 1 << rand() % 4;
}
}
bool testDirection( int d )
{
if( d == _lastDir || !( _world[_sx + _s * _sy] & d ) ) return false;
switch( d )
{
case NOR:
return _sy - 1 > -1 && !( _world[_sx + _s * ( _sy - 1 )] & 0xf0 );
case EAS:
return _sx + 1 < _s && !( _world[_sx + 1 + _s * _sy] & 0xf0 );
case SOU:
return _sy + 1 < _s && !( _world[_sx + _s * ( _sy + 1 )] & 0xf0 );
case WES:
return _sx - 1 > -1 && !( _world[_sx - 1 + _s * _sy] & 0xf0 );
}
return false;
}
void display()
{
_bmp.setPenColor( RGB( 0, 255, 0 ) );
_bmp.clear();
HDC dc = _bmp.getDC();
for( int y = 0; y < _s; y++ )
{
int yy = y * _s;
for( int x = 0; x < _s; x++ )
{
BYTE b = _world[x + yy];
int nx = x * CELL_SIZE,
ny = y * CELL_SIZE;
if( !( b & NOR ) )
{
MoveToEx( dc, nx, ny, NULL );
LineTo( dc, nx + CELL_SIZE + 1, ny );
}
if( !( b & EAS ) )
{
MoveToEx( dc, nx + CELL_SIZE, ny, NULL );
LineTo( dc, nx + CELL_SIZE, ny + CELL_SIZE + 1 );
}
if( !( b & SOU ) )
{
MoveToEx( dc, nx, ny + CELL_SIZE, NULL );
LineTo( dc, nx + CELL_SIZE + 1, ny + CELL_SIZE );
}
if( !( b & WES ) )
{
MoveToEx( dc, nx, ny, NULL );
LineTo( dc, nx, ny + CELL_SIZE + 1 );
}
}
}
drawEndPoints( dc );
_bmp.setPenColor( RGB( 255, 0, 0 ) );
for( int y = 0; y < _s; y++ )
{
int yy = y * _s;
for( int x = 0; x < _s; x++ )
{
BYTE d = _pts[x + yy];
if( !d ) continue;
int nx = x * CELL_SIZE + 4,
ny = y * CELL_SIZE + 4;
MoveToEx( dc, nx, ny, NULL );
switch( d )
{
case NOR: LineTo( dc, nx, ny - CELL_SIZE - 1 ); break;
case EAS: LineTo( dc, nx + CELL_SIZE + 1, ny ); break;
case SOU: LineTo( dc, nx, ny + CELL_SIZE + 1 ); break;
case WES: LineTo( dc, nx - CELL_SIZE - 1, ny ); break;
}
}
}
_bmp.saveBitmap( "f:\\rc\\maze_s.bmp" );
BitBlt( GetDC( GetConsoleWindow() ), 10, 60, BMP_SIZE, BMP_SIZE, _bmp.getDC(), 0, 0, SRCCOPY );
}
void drawEndPoints( HDC dc )
{
RECT rc;
int x = 1 + _sx * CELL_SIZE, y = 1 + _sy * CELL_SIZE;
SetRect( &rc, x, y, x + CELL_SIZE - 1, y + CELL_SIZE - 1 );
FillRect( dc, &rc, ( HBRUSH )GetStockObject( WHITE_BRUSH ) );
x = 1 + _ex * CELL_SIZE, y = 1 + _ey * CELL_SIZE;
SetRect( &rc, x, y, x + CELL_SIZE - 1, y + CELL_SIZE - 1 );
FillRect( dc, &rc, ( HBRUSH )GetStockObject( WHITE_BRUSH ) );
}
void killPoints() { if( _pts ) delete [] _pts; }
BYTE* _world, *_pts;
int _s, _sx, _sy, _ex, _ey, _lastDir;
myBitmap _bmp;
};
//--------------------------------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
ShowWindow( GetConsoleWindow(), SW_MAXIMIZE );
srand( GetTickCount() );
mazeGenerator mg;
mazeSolver ms;
int s;
while( true )
{
cout << "Enter the maze size, an odd number bigger than 2 ( 0 to QUIT ): "; cin >> s;
if( !s ) return 0;
if( !( s & 1 ) ) s++;
if( s >= 3 )
{
mg.create( s );
int sx, sy, ex, ey;
while( true )
{
sx = rand() % s; sy = rand() % s;
ex = rand() % s; ey = rand() % s;
if( ex != sx || ey != sy ) break;
}
ms.solveIt( mg.getMaze(), s, sx, sy, ex, ey );
cout << endl;
}
system( "pause" );
system( "cls" );
}
return 0;
}
//--------------------------------------------------------------------------------------------------
Clojure
(ns small-projects.find-shortest-way
(:require [clojure.string :as str]))
;Misk functions
(defn cell-empty? [maze coords]
(= :empty (get-in maze coords)))
(defn wall? [maze coords]
(= :wall (get-in maze coords)))
(defn track? [maze coords]
(= :track (get-in maze coords)))
(defn get-neighbours [maze [y x cell]]
[[y (dec x)] [(inc y) x] [y (inc x)] [(dec y) x]])
(defn get-difference [coll1 filter-coll]
(filter #(not (contains? filter-coll %)) coll1))
(defn get-empties [maze cell]
(->> (get-neighbours maze cell)
(filter (partial cell-empty? maze))))
(defn possible-ways [maze cell filter-coll]
(-> (get-empties maze cell)
(get-difference filter-coll)))
(defn replace-cells [maze coords v]
(if (empty? coords)
maze
(recur (assoc-in maze (first coords) v) (rest coords) v)))
;Print and parse functions
(def cell-code->str
[" " " " " " " " "· " "╵ " "╴ " "┘ "
" " " " " " " " "╶─" "└─" "──" "┴─"
" " " " " " " " "╷ " "│ " "┐ " "┤ "
" " " " " " " " "┌─" "├─" "┬─" "┼─"
" " " " " " " " "■ " "╹ " "╸ " "┛ "
" " " " " " " " "╺━" "┗━" "━━" "┻━"
" " " " " " " " "╻ " "┃ " "┓ " "┫ "
" " " " " " " " "┏━" "┣━" "┳━" "╋━"
" "])
(defn get-cell-code [maze coords]
(let [mode (if (track? maze coords) 1 0)
check (if (zero? mode) wall? track?)]
(transduce
(comp
(map (partial check maze))
(keep-indexed (fn [idx test] (when test idx)))
(map (partial bit-shift-left 1)))
(completing bit-or)
(bit-shift-left mode 5)
(sort (conj (get-neighbours maze coords) coords)))))
(defn code->str [cell-code]
(nth cell-code->str cell-code))
(defn maze->str-symbols [maze]
(for [y (range (count maze))]
(for [x (range (count (nth maze y)))]
(code->str (get-cell-code maze [y x])))))
(defn maze->str [maze]
(->> (maze->str-symbols maze)
(map str/join)
(str/join "\n")))
(defn parse-pretty-maze [maze-str]
(->> (str/split-lines maze-str)
(map (partial take-nth 2))
(map (partial map #(if (= \space %) :empty :wall)))
(map vec)
(vec)))
;Core
(defn find-new-border [maze border old-border]
(apply conj (map (fn [cell]
(zipmap (possible-ways maze cell (conj border old-border))
(repeat cell)))
(keys border))))
(defn backtrack [visited route]
(let [cur-cell (get visited (first route))]
(if (= cur-cell :start)
route
(recur visited (conj route cur-cell)))))
(defn breadth-first-search [maze start-cell end-cell]
(loop [visited {start-cell :start}
border {start-cell :start}
old-border {start-cell :start}]
(if (contains? old-border end-cell)
(backtrack visited (list end-cell))
(recur
(conj visited border)
(find-new-border maze border old-border)
border))))
(def maze (parse-pretty-maze maze-str))
(def solved-maze
(replace-cells maze (breadth-first-search maze [1 1] [19 19]) :track))
(println (maze->str solved-maze))
- Input:
┌───────────┬───────┬───────┬───────────┐ │ │ │ │ │ │ ╶───────┘ ╷ ╵ ╷ ╵ ┌───╴ │ │ │ │ │ │ │ ╶───────┬───┴───┬───┴───┬───┘ ╷ │ │ │ │ │ │ │ ├───────╴ │ ╷ ╵ ╷ │ ┌───┘ │ │ │ │ │ │ │ │ │ ┌───┬───┘ ├───────┤ │ ├───────┤ │ │ │ │ │ │ │ │ │ ╵ ╵ ╶───┴───┐ │ │ ╵ ╷ │ │ │ │ │ │ │ ├───────────────┐ ╵ │ │ ╶───┤ │ │ │ │ │ │ │ │ ╶───┐ ┌───┴───╴ │ │ ┌───┘ │ │ │ │ │ │ │ │ ├───╴ │ │ ╶───┬───┤ └───┤ ╶───┤ │ │ │ │ │ │ │ │ ╶───┤ └───╴ ╵ └───┐ └───╴ │ │ │ │ │ └───────┴───────────────────┴───────────┘
- Output:
┌───────────┬───────┬───────┬───────────┐ │ ╻ │ │ │ │ │ ┃ ╶───────┘ ╷ ╵ ╷ ╵ ┌───╴ │ │ ┃ │ │ │ │ │ ┃ ╶───────┬───┴───┬───┴───┬───┘ ╷ │ │ ┗━━━━━━━┓ │ ┏━━━┓ │ ┏━━━┓ │ │ │ ├───────╴ ┃ │ ┃ ╷ ┃ ╵ ┃ ╷ ┃ │ ┌───┘ │ │ ┏━━━━━━━┛ │ ┃ │ ┗━━━┛ │ ┃ │ │ │ │ ┃ ┌───┬───┘ ┃ ├───────┤ ┃ │ ├───────┤ │ ┃ │ │ ┏━━━┛ │ │ ┃ │ │ │ │ ┃ ╵ ╵ ┃ ╶───┴───┐ │ ┃ │ ╵ ╷ │ │ ┗━━━━━━━┛ │ │ ┃ │ │ │ ├───────────────┐ ╵ │ ┃ │ ╶───┤ │ │ │ │ ┃ │ │ │ │ ╶───┐ ┌───┴───╴ │ ┃ │ ┌───┘ │ │ │ │ │ ┃ │ │ │ ├───╴ │ │ ╶───┬───┤ ┃ └───┤ ╶───┤ │ │ │ │ │ ┗━━━┓ │ │ │ ╶───┤ └───╴ ╵ └───┐ ┃ └───╴ │ │ │ │ ┗━━━━━━━╸ │ └───────┴───────────────────┴───────────┘
D
This entry reads a maze generated by http://rosettacode.org/wiki/Maze_generation#D and chooses two random start-end points.
import std.stdio, std.random, std.string, std.array, std.algorithm,
std.file, std.conv;
enum int cx = 4, cy = 2; // Cell size x and y.
enum int cx2 = cx / 2, cy2 = cy / 2;
enum pathSymbol = '.';
struct V2 { int x, y; }
bool solveMaze(char[][] maze, in V2 s, in V2 end) pure nothrow @safe @nogc {
if (s == end)
return true;
foreach (immutable d; [V2(0, -cy), V2(+cx, 0), V2(0, +cy), V2(-cx, 0)])
if (maze[s.y + (d.y / 2)][s.x + (d.x / 2)] == ' ' &&
maze[s.y + d.y][s.x + d.x] == ' ') {
//Would this help?
// maze[s.y + (d.y / 2)][s.x + (d.x / 2)] = pathSymbol;
maze[s.y + d.y][s.x + d.x] = pathSymbol;
if (solveMaze(maze, V2(s.x + d.x, s.y + d.y), end))
return true;
maze[s.y + d.y][s.x + d.x] = ' ';
}
return false;
}
void main() {
auto maze = "maze.txt".File.byLine.map!(r => r.chomp.dup).array;
immutable h = (maze.length.signed - 1) / cy;
assert (h > 0);
immutable w = (maze[0].length.signed - 1) / cx;
immutable start = V2(cx2 + cx * uniform(0, w), cy2 + cy * uniform(0, h));
immutable end = V2(cx2 + cx * uniform(0, w), cy2 + cy * uniform(0, h));
maze[start.y][start.x] = pathSymbol;
if (!solveMaze(maze, start, end))
return "No solution path found.".writeln;
maze[start.y][start.x] = 'S';
maze[end.y][end.x] = 'E';
writefln("%-(%s\n%)", maze);
}
- Output:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | | | . . . . . . . . . | + + +---+---+ + +---+ + . +---+---+---+ . + + | | | | . . . | | . | | +---+---+---+---+---+---+---+ +---+ . +---+ + . +---+ | | | | . . . . . | E | + +---+---+---+ + + +---+ +---+---+ . +---+---+ | | . . . | | | | | . . . | | +---+ + . + . + + +---+---+---+ + +---+ . + + | | . | . | | . . . | | . . . | | + +---+ . + . +---+---+ . + . +---+---+---+ . +---+ + | . . . . . | . | . . . . . | . . . . . . . | . . . . . | + . +---+---+ . + . +---+---+---+---+---+ . +---+---+ . + | . . . | | . . . | | | . | . | +---+ . + +---+---+ + +---+ + + . +---+---+ . + | | . . . . . . . | | | | . | . . . . . | + +---+---+---+ . + +---+ +---+ + . + . +---+---+ | | . . . . . . . | | | | | . . . | | + + . +---+---+---+---+ +---+ + +---+---+ + + | . . . . . . S | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Delphi
Code requires source code of Maze generation
procedure SolveMaze(var AMaze: TMaze; const S, E: TPoint);
var
Route : TRoute;
Position : TPoint;
V : TPoint; // delta vector
begin
ClearVisited(AMaze);
Position := S;
Route := TStack<TPoint>.Create;
with Position do
try
AMaze[x, y].Visited := True;
repeat
if (y > 0) and not AMaze[x, y-1].Visited and AMaze[x, y].PassTop then V := Point(0, -1) else
if (x < mwidth-1) and not AMaze[x+1, y].Visited and AMaze[x+1, y].PassLeft then V := Point(1, 0) else
if (y < mheight-1) and not AMaze[x, y+1].Visited and AMaze[x, y+1].PassTop then V := Point(0, 1) else
if (x > 0) and not AMaze[x-1, y].Visited and AMaze[x, y].PassLeft then V := Point(-1, 0) else
begin
if Route.Count = 0 then Exit; // we are back at start so no way found
Position := Route.Pop; // step back
Continue;
end;
Route.Push(Position); // save current position to route
Offset(V); // move forward
AMaze[x, y].Visited := True;
until Position = E; // solved
ClearVisited(AMaze);
while Route.Count > 0 do // Route to Maze
with Route.Pop do
AMaze[x, y].Visited := True;
finally
Route.Free;
end;
end;
procedure Main;
var
Maze: TMaze;
S, E: TPoint;
begin
Randomize;
PrepareMaze(Maze);
S := Point(Random(mwidth), Random(mheight));
E := Point(Random(mwidth), Random(mheight));
SolveMaze(Maze, S, E);
Write(MazeToString(Maze, S, E));
ReadLn;
end;
begin
Main;
end.
- Output:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | | S * * | | + + + +---+ + +---+---+---+ + +---+---+ +---+---+---+---+---+---+ +---+ + + | | | | | | | | | | * | | | | | + + +---+ + +---+ +---+ +---+ + +---+ + +---+ +---+---+ +---+ + + + | | | | | | | * * * | | | | | | + + +---+ +---+---+---+ +---+---+---+ +---+---+ +---+---+---+---+---+ +---+---+ + | | | * * * * * | * | | | | | | | + +---+ +---+ +---+---+---+ +---+---+ + + +---+ + + +---+---+ + + +---+ | | | | * * * | | * * * | * * | | | | | | | | + + +---+ +---+---+ + +---+---+ +---+ +---+ + +---+---+---+ +---+ + + + | | | | * | | * * * | | | | | | +---+---+ +---+ + + + +---+ +---+---+---+ +---+---+ + + +---+ +---+---+ + | | | | | * | | | | | | | | | + + + + +---+ + +---+ +---+---+ + +---+ +---+ + +---+ +---+ +---+---+ | | | | E * | * * | | | | | | | | | | | + +---+---+ +---+ +---+ + + + +---+ +---+ +---+---+ + +---+---+---+---+ + | | | | * | * | | | | | | | + +---+ +---+ + +---+ +---+---+---+ +---+ +---+ + +---+---+ +---+ +---+---+ | | | | * * | * | | | | | | | +---+---+ + + + + + + +---+---+---+ +---+ +---+---+ + +---+ +---+---+ + | | | | | * * | | | | | | | + +---+---+ + +---+---+---+---+ + + +---+---+---+ +---+---+---+ +---+---+---+ + | | | | | | | | | | | +---+ +---+ +---+ + +---+---+---+ +---+ +---+ +---+---+ +---+---+ +---+---+---+ | | | | | | | | | | | | | | | + + + +---+ + +---+---+ + + + +---+ + + + + + + + + +---+ + | | | | | | | | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
EasyLang
size = 15
n = 2 * size + 1
f = 100 / (n - 0.5)
len m[] n * n
#
background 000
proc show_maze . .
clear
for i = 1 to len m[]
if m[i] = 0
x = (i - 1) mod n
y = (i - 1) div n
color 999
move x * f - f / 2 y * f - f / 2
rect f * 1.5 f * 1.5
.
.
sleep 0.01
.
offs[] = [ 1 n -1 (-n) ]
proc m_maze pos . .
m[pos] = 0
show_maze
d[] = [ 1 2 3 4 ]
for i = 4 downto 1
d = random i
dir = offs[d[d]]
d[d] = d[i]
if m[pos + dir] = 1 and m[pos + 2 * dir] = 1
m[pos + dir] = 0
m_maze pos + 2 * dir
.
.
.
endpos = n * n - 1
proc make_maze . .
for i = 1 to len m[]
m[i] = 1
.
for i = 1 to n
m[i] = 2
m[n * i] = 2
m[n * i - n + 1] = 2
m[n * n - n + i] = 2
.
h = 2 * random 15 - n + n * 2 * random 15
m_maze h
m[endpos] = 0
.
make_maze
show_maze
#
proc mark pos col . .
x = (pos - 1) mod n
y = (pos - 1) div n
color col
move x * f + f / 4 y * f + f / 4
circle f / 3.5
.
global found .
proc solve dir0 pos . .
if found = 1
return
.
mark pos 900
sleep 0.05
if pos = endpos
found = 1
return
.
of = random 4 - 1
for h = 1 to 4
dir = (h + of) mod1 4
posn = pos + offs[dir]
if dir <> dir0 and m[posn] = 0
solve (dir + 1) mod 4 + 1 posn
if found = 0
mark posn 888
sleep 0.08
.
.
.
.
sleep 1
solve 0 n + 2
EGL
program MazeGenAndSolve
// First and last columns/rows are "dead" cells. Makes generating
// a maze with border walls much easier. Therefore, a visible
// 20x20 maze has a maze size of 22.
mazeSize int = 22;
south boolean[][];
west boolean[][];
visited boolean[][];
// Solution variables
solution Dictionary;
done boolean;
startingRow, startingCol, endingRow, endingCol int;
function main()
initMaze();
generateMaze();
drawMaze(false); // Draw maze without solution
solveMaze();
drawMaze(true); // Draw maze with solution
end
private function initMaze()
visited = createBooleanArray(mazeSize, mazeSize, false);
// Initialize border cells as already visited
for(col int from 1 to mazeSize)
visited[col][1] = true;
visited[col][mazeSize] = true;
end
for(row int from 1 to mazeSize)
visited[1][row] = true;
visited[mazeSize][row] = true;
end
// Initialize all walls as present
south = createBooleanArray(mazeSize, mazeSize, true);
west = createBooleanArray(mazeSize, mazeSize, true);
end
private function createBooleanArray(col int in, row int in, initialState boolean in) returns(boolean[][])
newArray boolean[][] = new boolean[0][0];
for(i int from 1 to col)
innerArray boolean[] = new boolean[0];
for(j int from 1 to row)
innerArray.appendElement(initialState);
end
newArray.appendElement(innerArray);
end
return(newArray);
end
private function createIntegerArray(col int in, row int in, initialValue int in) returns(int[][])
newArray int[][] = new int[0][0];
for(i int from 1 to col)
innerArray int[] = new int[0];
for(j int from 1 to row)
innerArray.appendElement(initialValue);
end
newArray.appendElement(innerArray);
end
return(newArray);
end
private function generate(col int in, row int in)
// Mark cell as visited
visited[col][row] = true;
// Keep going as long as there is an unvisited neighbor
while(!visited[col][row + 1] || !visited[col + 1][row] ||
!visited[col][row - 1] || !visited[col - 1][row])
while(true)
r float = MathLib.random(); // Choose a random direction
case
when(r < 0.25 && !visited[col][row + 1]) // Go south
south[col][row] = false; // South wall down
generate(col, row + 1);
exit while;
when(r >= 0.25 && r < 0.50 && !visited[col + 1][row]) // Go east
west[col + 1][row] = false; // West wall of neighbor to the east down
generate(col + 1, row);
exit while;
when(r >= 0.5 && r < 0.75 && !visited[col][row - 1]) // Go north
south[col][row - 1] = false; // South wall of neighbor to the north down
generate(col, row - 1);
exit while;
when(r >= 0.75 && r < 1.00 && !visited[col - 1][row]) // Go west
west[col][row] = false; // West wall down
generate(col - 1, row);
exit while;
end
end
end
end
private function generateMaze()
// Pick random start position (within the visible maze space)
randomStartCol int = MathLib.floor((MathLib.random() *(mazeSize - 2)) + 2);
randomStartRow int = MathLib.floor((MathLib.random() *(mazeSize - 2)) + 2);
generate(randomStartCol, randomStartRow);
end
private function drawMaze(solve boolean in)
line string;
// Iterate over wall arrays (skipping dead border cells as required).
// Construct a row at a time and output to console.
for(row int from 1 to mazeSize - 1)
if(row > 1)
line = "";
for(col int from 2 to mazeSize)
if(west[col][row])
line ::= cellTest(col, row, solve);
else
line ::= cellTest(col, row, solve);
end
end
Syslib.writeStdout(line);
end
line = "";
for(col int from 2 to mazeSize - 1)
if(south[col][row])
line ::= "+---";
else
line ::= "+ ";
end
end
line ::= "+";
SysLib.writeStdout(line);
end
end
private function cellTest(col int in, row int in, solve boolean in) returns(string)
wall string;
// Determine cell wall structure. If in solve mode, show start, end and
// solution markers.
if(!solve)
if(west[col][row])
wall = "| ";
else
wall = " ";
end
else
if(west[col][row])
case
when(col == startingCol and row == startingRow)
wall = "| S ";
when(col == endingCol and row == endingRow)
wall = "| E ";
when(solution.containsKey("x=" + col + "y=" + row))
wall = "| * ";
otherwise
wall = "| ";
end
else
case
when(col == startingCol and row == startingRow)
wall = " S ";
when(col == endingCol and row == endingRow)
wall = " E ";
when(solution.containsKey("x=" + col + "y=" + row))
wall = " * ";
otherwise
wall = " ";
end
end
end
return(wall);
end
private function solve(col int in, row int in)
if(col == 1 || row == 1 || col == mazeSize || row == mazeSize)
return;
end
if(done || visited[col][row])
return;
end
visited[col][row] = true;
solution["x=" + col + "y=" + row] = true;
// Reached the end point
if(col == endingCol && row == endingRow)
done = true;
end
if(!south[col][row]) // Go South
solve(col, row + 1);
end
if(!west[col + 1][row]) // Go East
solve(col + 1, row);
end
if(!south[col][row - 1]) // Go North
solve(col, row - 1);
end
if(!west[col][row]) // Go West
solve(col - 1, row);
end
if(done)
return;
end
solution.removeElement("x=" + col + "y=" + row);
end
private function solveMaze()
for(col int from 1 to mazeSize)
for(row int from 1 to mazeSize)
visited[col][row] = false;
end
end
solution = new Dictionary(false, OrderingKind.byInsertion);
done = false;
// Pick random start position on first visible row
startingCol = MathLib.floor((MathLib.random() *(mazeSize - 2)) + 2);
startingRow = 2;
// Pick random end position on last visible row
endingCol = MathLib.floor((MathLib.random() *(mazeSize - 2)) + 2);
endingRow = mazeSize - 1;
solve(startingCol, startingRow);
end
end
- Output example (for 10x10 maze):
+---+---+---+---+---+---+---+---+---+---+ | | | | | + +---+ +---+---+ + + +---+ + | | | | | | | | +---+ + +---+ +---+---+ + + + | | | | | | | + +---+---+ + + + +---+---+ + | | | | | | | + + + + + +---+---+---+ + + | | | | | | | + +---+---+---+ + +---+ +---+ + | | | | | | +---+ +---+ +---+ + +---+ + + | | | | | | | + + + +---+ +---+---+---+---+ + | | | | | | | + + + + +---+---+---+---+ + + | | | | | | | | + + + + +---+---+ + + + + | | | | +---+---+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+ | | * * S | | | + +---+ +---+---+ + + +---+ + | | * | | | | | | +---+ + +---+ +---+---+ + + + | | * * | * * | | | | + +---+---+ + + + +---+---+ + | | * * | * | * | * * * * | | + + + + + +---+---+---+ + + | * * | * * | * | | * * | | + +---+---+---+ + +---+ +---+ + | * * | * * | | * * | | +---+ +---+ +---+ + +---+ + + | | * | * * | * * * | | | + + + +---+ +---+---+---+---+ + | | * | * | | * * * * * | | + + + + +---+---+---+---+ + + | | * | * | | * * | * | | + + + + +---+---+ + + + + | * * | E | * * | +---+---+---+---+---+---+---+---+---+---+
Emacs Lisp
(require 'cl-lib)
(cl-defstruct maze rows cols data)
(defmacro maze-pt (w r c)
`(+ (* (mod ,r (maze-rows ,w)) (maze-cols ,w))
(mod ,c (maze-cols ,w))))
(defmacro maze-ref (w r c)
`(aref (maze-data ,w) (maze-pt ,w ,r ,c)))
(defun new-maze (rows cols)
(setq rows (1+ rows)
cols (1+ cols))
(let ((m (make-maze :rows rows :cols cols :data (make-vector (* rows cols) nil))))
(dotimes (r rows)
(dotimes (c cols)
(setf (maze-ref m r c) (copy-sequence '(wall ceiling)))))
(dotimes (r rows)
(maze-set m r (1- cols) 'visited))
(dotimes (c cols)
(maze-set m (1- rows) c 'visited))
(maze-unset m 0 0 'ceiling) ;; Maze Entrance
(maze-unset m (1- rows) (- cols 2) 'ceiling) ;; Maze Exit
m))
(defun maze-is-set (maze r c v)
(member v (maze-ref maze r c)))
(defun maze-set (maze r c v)
(let ((cell (maze-ref maze r c)))
(when (not (member v cell))
(setf (maze-ref maze r c) (cons v cell)))))
(defun maze-unset (maze r c v)
(setf (maze-ref maze r c) (delete v (maze-ref maze r c))))
(defun print-maze (maze &optional marks)
(dotimes (r (1- (maze-rows maze)))
(dotimes (c (1- (maze-cols maze)))
(princ (if (maze-is-set maze r c 'ceiling) "+---" "+ ")))
(princ "+")
(terpri)
(dotimes (c (1- (maze-cols maze)))
(princ (if (maze-is-set maze r c 'wall) "|" " "))
(princ (if (member (cons r c) marks) " * " " ")))
(princ "|")
(terpri))
(dotimes (c (1- (maze-cols maze)))
(princ (if (maze-is-set maze (1- (maze-rows maze)) c 'ceiling) "+---" "+ ")))
(princ "+")
(terpri))
(defun shuffle (lst)
(sort lst (lambda (a b) (= 1 (random 2)))))
(defun to-visit (maze row col)
(let (unvisited)
(dolist (p '((0 . +1) (0 . -1) (+1 . 0) (-1 . 0)))
(let ((r (+ row (car p)))
(c (+ col (cdr p))))
(unless (maze-is-set maze r c 'visited)
(push (cons r c) unvisited))))
unvisited))
(defun make-passage (maze r1 c1 r2 c2)
(if (= r1 r2)
(if (< c1 c2)
(maze-unset maze r2 c2 'wall) ; right
(maze-unset maze r1 c1 'wall)) ; left
(if (< r1 r2)
(maze-unset maze r2 c2 'ceiling) ; up
(maze-unset maze r1 c1 'ceiling)))) ; down
(defun dig-maze (maze row col)
(let (backup
(run 0))
(maze-set maze row col 'visited)
(push (cons row col) backup)
(while backup
(setq run (1+ run))
(when (> run (/ (+ row col) 3))
(setq run 0)
(setq backup (shuffle backup)))
(setq row (caar backup)
col (cdar backup))
(let ((p (shuffle (to-visit maze row col))))
(if p
(let ((r (caar p))
(c (cdar p)))
(make-passage maze row col r c)
(maze-set maze r c 'visited)
(push (cons r c) backup))
(pop backup)
(setq backup (shuffle backup))
(setq run 0))))))
(defun generate (rows cols)
(let* ((m (new-maze rows cols)))
(dig-maze m (random rows) (random cols))
(print-maze m)))
(defun parse-ceilings (line)
(let (rtn
(i 1))
(while (< i (length line))
(push (eq ?- (elt line i)) rtn)
(setq i (+ i 4)))
(nreverse rtn)))
(defun parse-walls (line)
(let (rtn
(i 0))
(while (< i (length line))
(push (eq ?| (elt line i)) rtn)
(setq i (+ i 4)))
(nreverse rtn)))
(defun parse-maze (file-name)
(let ((rtn)
(lines (with-temp-buffer
(insert-file-contents-literally file-name)
(split-string (buffer-string) "\n" t))))
(while lines
(push (parse-ceilings (pop lines)) rtn)
(push (parse-walls (pop lines)) rtn))
(nreverse rtn)))
(defun read-maze (file-name)
(let* ((raw (parse-maze file-name))
(rows (1- (/ (length raw) 2)))
(cols (length (car raw)))
(maze (new-maze rows cols)))
(dotimes (r rows)
(let ((ceilings (pop raw)))
(dotimes (c cols)
(unless (pop ceilings)
(maze-unset maze r c 'ceiling))))
(let ((walls (pop raw)))
(dotimes (c cols)
(unless (pop walls)
(maze-unset maze r c 'wall)))))
maze))
(defun find-exits (maze row col)
(let (exits)
(dolist (p '((0 . +1) (0 . -1) (-1 . 0) (+1 . 0)))
(let ((r (+ row (car p)))
(c (+ col (cdr p))))
(unless
(cond
((equal p '(0 . +1)) (maze-is-set maze r c 'wall))
((equal p '(0 . -1)) (maze-is-set maze row col 'wall))
((equal p '(+1 . 0)) (maze-is-set maze r c 'ceiling))
((equal p '(-1 . 0)) (maze-is-set maze row col 'ceiling)))
(push (cons r c) exits))))
exits))
(defun drop-visited (maze points)
(let (not-visited)
(while points
(unless (maze-is-set maze (caar points) (cdar points) 'visited)
(push (car points) not-visited))
(pop points))
not-visited))
(defun solve-maze (maze)
(let (solution
(exit (cons (- (maze-rows maze) 2) (- (maze-cols maze) 2)))
(pt (cons 0 0)))
(while (not (equal pt exit))
(maze-set maze (car pt) (cdr pt) 'visited)
(let ((exits (drop-visited maze (find-exits maze (car pt) (cdr pt)))))
(if (null exits)
(setq pt (pop solution))
(push pt solution)
(setq pt (pop exits)))))
(push pt solution)))
(defun solve (file-name)
(let* ((maze (read-maze file-name))
(solution (solve-maze maze)))
(print-maze maze solution)))
(solve "maze.txt")
- Output:
+ +---+---+---+---+---+---+---+---+---+ | * * * | | | | +---+---+ + +---+---+ +---+---+ + | | * | | | | | | + + + + +---+ + + +---+ + | | * * * * | | | +---+---+---+---+---+ +---+---+ + + | | | | | * * | | | | + +---+ + + +---+ + + + + | | | | | * * | | + + + + +---+ + + +---+ + | | | | | | * * * | + + + +---+---+---+ +---+---+ + | | | | | | * | + + +---+---+ + + + + + + | | | | | | * | + + + +---+---+---+---+---+ + + | | | | | * | + +---+---+ + + +---+---+---+ + | | | * | +---+---+---+---+---+---+---+---+---+ +
Erlang
Using the maze from Maze_generation. When the path splits each possibility gets its own process that checks if it is the correct one or a dead end. It is intentional that the receive statement in loop_stop/5 only selects successful result.
-module( maze_solving ).
-export( [task/0] ).
cells( {Start_x, Start_y}, {Stop_x, Stop_y}, Maze ) ->
Start_pid = maze:cell_pid( Start_x, Start_y, Maze ),
Stop_pid = maze:cell_pid( Stop_x, Stop_y, Maze ),
{ok, Cells} = loop( Start_pid, Stop_pid, maze:cell_accessible_neighbours(Start_pid), [Start_pid] ),
Cells.
task() ->
Max_x = 16,
Max_y = 8,
Maze = maze:generation( Max_x, Max_y ),
Start_x = random:uniform( Max_x ),
Start_y = random:uniform( Max_y ),
Stop_x = random:uniform( Max_x ),
Stop_y = random:uniform( Max_y ),
Cells = cells( {Start_x, Start_y}, {Stop_x, Stop_y}, Maze ),
[maze:cell_content_set(X, ".") || X <- Cells],
maze:cell_content_set( maze:cell_pid(Start_x, Start_y, Maze), "S" ),
maze:cell_content_set( maze:cell_pid(Stop_x, Stop_y, Maze), "G" ),
maze:display( Maze ),
maze:stop( Maze ).
loop( _Start, _Stop, [], _Acc) -> {error, dead_end};
loop( _Start, Stop, [Stop], Acc ) -> {ok, lists:reverse( [Stop | Acc] )};
loop( Start, Stop, [Next], [Previous | _T]=Acc ) -> loop( Start, Stop, lists:delete(Previous, maze:cell_accessible_neighbours(Next)), [Next | Acc] );
loop( Start, Stop, Nexts, Acc ) -> loop_stop( lists:member(Stop, Nexts), Start, Stop, Nexts, Acc ).
loop_stop( true, _Start, Stop, _Nexts, Acc ) -> {ok, lists:reverse( [Stop | Acc] )};
loop_stop( false, Start, Stop, Nexts, Acc ) ->
My_pid = erlang:self(),
[erlang:spawn( fun() -> My_pid ! loop( Start, Stop, [X], Acc ) end ) || X <- Nexts],
receive
{ok, Cells} -> {ok, Cells}
end.
- Output:
8> maze_solving:task(). +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | . . . . | . . . . | . . . . | | +---+ +---+---+ + +---+---+ + +---+---+ + +---+ + | . . | | . . | | . . | | . . | | + +---+ + +---+---+ +---+---+---+ + +---+ +---+ + | . | | | | | . | | | + +---+ +---+---+ + + +---+---+ +---+---+ + +---+ | . . | | | | | | | . . | . | | + + + + + +---+ + +---+---+---+ + + +---+ + | | . | | | S . | . . | | +---+ +---+---+ +---+---+---+ +---+---+---+ +---+---+ + | . . | . . | | . . | | | | | + + + + +---+ + + + + +---+ + + +---+---+ | . | | . | . | | . | . | | G . | | | | + +---+ + +---+---+ + +---+---+ + +---+---+---+ + | . . . | . . . . | . . . . | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Frege
On standard input, takes a maze made up of "+", "|", and "---" (i. e. each cell is two lines high and four characters wide), such as produced by the Haskell or Java generators.
module MazeSolver where
import frege.IO
import Data.Maybe
-- given two points, returns the average of them
average :: (Int, Int) -> (Int, Int) -> (Int, Int)
average (x, y) (x', y') = ((x + x') `div` 2, (y + y') `div` 2)
-- given a maze and a tuple of position and wall position, returns
-- true if the wall position is not blocked (first position is unused)
notBlocked :: [String] -> ((Int, Int), (Int, Int)) -> Bool
notBlocked maze (_, (x, y)) = (' ' == String.charAt (maze !! y) x)
-- given a list, a position, and an element, returns a new list
-- with the new element substituted at the position
substitute :: [a] -> Int -> a -> [a]
substitute orig pos el =
let (before, after) = splitAt pos orig
in before ++ [el] ++ tail after
-- like above, but for strings, since Frege strings are not
-- lists of characters
substituteString :: String -> Int -> String -> String
substituteString orig pos el =
let before = substr orig 0 pos
after = strtail orig (pos + 1)
in before ++ el ++ after
-- given a maze and a position, draw a '*' at that position in the maze
draw :: [String] -> (Int, Int) -> [String]
draw maze (x,y) = substitute maze y $ substituteString row x "*"
where row = maze !! y
-- given a maze, a previous position, and a list of tuples of potential
-- new positions and their wall positions, returns the solved maze, or
-- None if it cannot be solved
tryMoves :: [String] -> (Int, Int) -> [((Int, Int), (Int, Int))] -> Maybe [String]
tryMoves _ _ [] = Nothing
tryMoves maze prevPos ((newPos,wallPos):more) =
case solve' maze newPos prevPos
of Nothing -> tryMoves maze prevPos more
Just maze' -> Just $ foldl draw maze' [newPos, wallPos]
-- given a maze, a new position, and a previous position, returns
-- the solved maze, or None if it cannot be solved
-- (assumes goal is upper-left corner of maze)
solve' :: [String] -> (Int, Int) -> (Int, Int) -> Maybe [String]
solve' maze (2, 1) _ = Just maze
solve' maze (x, y) prevPos =
let newPositions = [(x, y - 2), (x + 4, y), (x, y + 2), (x - 4, y)]
notPrev pos' = pos' /= prevPos
newPositions' = filter notPrev newPositions
wallPositions = map (average (x,y)) newPositions'
zipped = zip newPositions' wallPositions
legalMoves = filter (notBlocked maze) zipped
in tryMoves maze (x,y) legalMoves
-- given a maze, returns a solved maze, or None if it cannot be solved
-- (starts at lower right corner and goes to upper left corner)
solve :: [String] -> Maybe [String]
solve maze = solve' (draw maze start) start (-1, -1)
where startx = (length $ head maze) - 3
starty = (length maze) - 2
start = (startx, starty)
-- takes unsolved maze on standard input, prints solved maze on standard output
main _ = do
isin <- stdin
isrin <- InputStreamReader.new isin
brin <- BufferedReader.fromISR isrin
lns <- BufferedReader.getlines brin
printStr $ unlines $ fromMaybe ["can't solve"] $ solve lns
- Output:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | * | * * * * * | | | | | | + * +---+---+ * +---+ * +---+ + + + +---+---+---+ + + + + + | * | * * * * * | | * | | | | * * * * * | | | | + * + * +---+---+ + * + +---+ +---+---+ * +---+ * + +---+---+---+ + | * * * | | | * | | | * * * * * | * * * | | * * * * * | | +---+---+ + + + * + + +---+ * +---+---+ * +---+---+ * +---+ * + + | | | * | | | * | | * * * | * * * * * | * * * | | + + +---+---+---+ * + +---+ + * + + * +---+ * +---+---+ * +---+ + | | | * * * * * * * | | * | * * * * * | | * * * * * | + + + * +---+---+---+---+---+---+ * +---+---+---+---+ + +---+---+ * + | | | * * * | * * * * * | * * * | * * * | * * * | | | * | + +---+---+ * + * +---+ * + * + * +---+ * + * + * +---+ +---+ + + * + | | | * | * | * * * | * | * * * | * * * | * * * | | | | | * | + + + + * + * + * +---+ * +---+ * +---+---+---+ * + + + +---+ * + | | * * * | * * * * * | * * * * * * * * * | | * | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ runtime 0.249 wallclock seconds.
Go
Generates maze, picks start and finish cells randomly, solves, prints.
package main
import (
"bytes"
"fmt"
"math/rand"
"time"
)
type maze struct {
c2 [][]byte // cells by row
h2 [][]byte // horizontal walls by row (ignore first row)
v2 [][]byte // vertical walls by row (ignore first of each column)
}
func newMaze(rows, cols int) *maze {
c := make([]byte, rows*cols) // all cells
h := bytes.Repeat([]byte{'-'}, rows*cols) // all horizontal walls
v := bytes.Repeat([]byte{'|'}, rows*cols) // all vertical walls
c2 := make([][]byte, rows) // cells by row
h2 := make([][]byte, rows) // horizontal walls by row
v2 := make([][]byte, rows) // vertical walls by row
for i := range h2 {
c2[i] = c[i*cols : (i+1)*cols]
h2[i] = h[i*cols : (i+1)*cols]
v2[i] = v[i*cols : (i+1)*cols]
}
return &maze{c2, h2, v2}
}
func (m *maze) String() string {
hWall := []byte("+---")
hOpen := []byte("+ ")
vWall := []byte("| ")
vOpen := []byte(" ")
rightCorner := []byte("+\n")
rightWall := []byte("|\n")
var b []byte
for r, hw := range m.h2 {
for _, h := range hw {
if h == '-' || r == 0 {
b = append(b, hWall...)
} else {
b = append(b, hOpen...)
if h != '-' && h != 0 {
b[len(b)-2] = h
}
}
}
b = append(b, rightCorner...)
for c, vw := range m.v2[r] {
if vw == '|' || c == 0 {
b = append(b, vWall...)
} else {
b = append(b, vOpen...)
if vw != '|' && vw != 0 {
b[len(b)-4] = vw
}
}
if m.c2[r][c] != 0 {
b[len(b)-2] = m.c2[r][c]
}
}
b = append(b, rightWall...)
}
for _ = range m.h2[0] {
b = append(b, hWall...)
}
b = append(b, rightCorner...)
return string(b)
}
func (m *maze) gen() {
m.g2(rand.Intn(len(m.c2)), rand.Intn(len(m.c2[0])))
}
const (
up = iota
dn
rt
lf
)
func (m *maze) g2(r, c int) {
m.c2[r][c] = ' '
for _, dir := range rand.Perm(4) {
switch dir {
case up:
if r > 0 && m.c2[r-1][c] == 0 {
m.h2[r][c] = 0
m.g2(r-1, c)
}
case lf:
if c > 0 && m.c2[r][c-1] == 0 {
m.v2[r][c] = 0
m.g2(r, c-1)
}
case dn:
if r < len(m.c2)-1 && m.c2[r+1][c] == 0 {
m.h2[r+1][c] = 0
m.g2(r+1, c)
}
case rt:
if c < len(m.c2[0])-1 && m.c2[r][c+1] == 0 {
m.v2[r][c+1] = 0
m.g2(r, c+1)
}
}
}
}
func main() {
rand.Seed(time.Now().UnixNano())
const height = 4
const width = 7
m := newMaze(height, width)
m.gen()
m.solve(
rand.Intn(height), rand.Intn(width),
rand.Intn(height), rand.Intn(width))
fmt.Print(m)
}
func (m *maze) solve(ra, ca, rz, cz int) {
var rSolve func(ra, ca, dir int) bool
rSolve = func(r, c, dir int) bool {
if r == rz && c == cz {
m.c2[r][c] = 'F'
return true
}
if dir != dn && m.h2[r][c] == 0 {
if rSolve(r-1, c, up) {
m.c2[r][c] = '^'
m.h2[r][c] = '^'
return true
}
}
if dir != up && r+1 < len(m.h2) && m.h2[r+1][c] == 0 {
if rSolve(r+1, c, dn) {
m.c2[r][c] = 'v'
m.h2[r+1][c] = 'v'
return true
}
}
if dir != lf && c+1 < len(m.v2[0]) && m.v2[r][c+1] == 0 {
if rSolve(r, c+1, rt) {
m.c2[r][c] = '>'
m.v2[r][c+1] = '>'
return true
}
}
if dir != rt && m.v2[r][c] == 0 {
if rSolve(r, c-1, lf) {
m.c2[r][c] = '<'
m.v2[r][c] = '<'
return true
}
}
return false
}
rSolve(ra, ca, -1)
m.c2[ra][ca] = 'S'
}
- Example output
- :
+---+---+---+---+---+---+---+ | | v < < < < < < | + +---+ + v + +---+ ^ + | | F < < | | ^ | +---+---+---+---+ +---+ ^ + | | | ^ | + +---+---+ +---+ + ^ + | | | S | +---+---+---+---+---+---+---+
Haskell
On standard input, takes a maze made up of "+", "|", and "---" (i. e. each cell is two lines high and four characters wide), such as produced by the Haskell or Java generators.
#!/usr/bin/runhaskell
import Data.Maybe (fromMaybe)
-- given two points, returns the average of them
average :: (Int, Int) -> (Int, Int) -> (Int, Int)
average (x, y) (x_, y_) = ((x + x_) `div` 2, (y + y_) `div` 2)
-- given a maze and a tuple of position and wall position, returns
-- true if the wall position is not blocked (first position is unused)
notBlocked :: [String] -> ((Int, Int), (Int, Int)) -> Bool
notBlocked maze (_, (x, y)) = ' ' == (maze !! y) !! x
-- given a list, a position, and an element, returns a new list
-- with the new element substituted at the position
-- (it seems such a function should exist in the standard library;
-- I must be missing it)
substitute :: [a] -> Int -> a -> [a]
substitute orig pos el =
let (before, after) = splitAt pos orig
in before ++ [el] ++ tail after
-- given a maze and a position, draw a '*' at that position in the maze
draw :: [String] -> (Int, Int) -> [String]
draw maze (x, y) =
let row = maze !! y
in substitute maze y $ substitute row x '*'
-- given a maze, a previous position, and a list of tuples of potential
-- new positions and their wall positions, returns the solved maze, or
-- None if it cannot be solved
tryMoves :: [String]
-> (Int, Int)
-> [((Int, Int), (Int, Int))]
-> Maybe [String]
tryMoves _ _ [] = Nothing
tryMoves maze prevPos ((newPos, wallPos):more) =
case solve_ maze newPos prevPos of
Nothing -> tryMoves maze prevPos more
Just maze_ -> Just $ foldl draw maze_ [newPos, wallPos]
-- given a maze, a new position, and a previous position, returns
-- the solved maze, or None if it cannot be solved
-- (assumes goal is upper-left corner of maze)
solve_ :: [String] -> (Int, Int) -> (Int, Int) -> Maybe [String]
solve_ maze (2, 1) _ = Just maze
solve_ maze pos@(x, y) prevPos =
let newPositions = [(x, y - 2), (x + 4, y), (x, y + 2), (x - 4, y)]
notPrev pos_ = pos_ /= prevPos
newPositions_ = filter notPrev newPositions
wallPositions = map (average pos) newPositions_
zipped = zip newPositions_ wallPositions
legalMoves = filter (notBlocked maze) zipped
in tryMoves maze pos legalMoves
-- given a maze, returns a solved maze, or None if it cannot be solved
-- (starts at lower right corner and goes to upper left corner)
solve :: [String] -> Maybe [String]
solve maze = solve_ (draw maze start) start (-1, -1)
where
startx = length (head maze) - 3
starty = length maze - 2
start = (startx, starty)
-- takes unsolved maze on standard input, prints solved maze on standard output
main =
let main_ = unlines . fromMaybe ["can_t solve"] . solve . lines
in interact main_
- Output:
+---+---+---+---+---+---+---+---+---+---+---+ | * | | | | + * + +---+ + +---+ +---+ +---+ + | * | | | | | | | + * +---+ + +---+ +---+ +---+---+ + | * | | | | | | + * +---+---+---+ +---+ + + + + + | * * * * * * * | | | | | +---+---+---+ * +---+ +---+---+---+ + + | | * * * | | | | | + +---+ +---+ * + + +---+ + +---+ | | | * * * | | | | | +---+---+ + * +---+---+---+ + +---+ + | | * * * * * | | | | + +---+---+---+---+ * +---+ +---+---+ + | * * * * * * * * * * * | +---+---+---+---+---+---+---+---+---+---+---+
Icon and Unicon
The following code works with the solution from Maze Generation.
![](http://static.miraheze.org/rosettacodewiki/thumb/8/83/Maze-solved-unicon-20x20-1321199667.gif/300px-Maze-solved-unicon-20x20-1321199667.gif)
Replace the main with this:
And include this after the Generator and Display procedures.
The following Unicon-only solution is a variant of the above. It shares the same maze generation code and maze display code with the above but spawns threads to parallelize the searching. The algorithm runs each path to a dead end, a target, or a length greater than the current shortest path to a target and works if there are multiple target cells, multiple paths to those targets, or cyclic paths. The shortest solution path is then marked and displayed.
global showMice
import Utils # To get 'Args' singleton class
procedure main(A)
Args(A)
if \Args().get("help","yes") then helpMesg()
showMice := Args().get("showmice","yes") # Show movements of all mice
mh := Args().get("rows") | 32 # Maze height (rows)
mw := Args().get("cols") | 48 # Maze width (columns)
mz := DisplayMaze(GenerateMaze(mh,mw)) # Build and show maze
QMouse(mz.maze,findStart(mz.maze),&null,0) # Start first quantum mouse
waitForCompletion() # block until all quantum mice have finished
# Mark the best path into the maze and display it.
if showPath(mz.maze) then DisplayMazeSolution(mz) else write("No path found!")
end
procedure helpMesg()
write(&errout,"Usage: qSolve [--showmice] [--cols=C] [--rows=R]")
write(&errout,"\twhere:")
write(&errout,"\t\t--showmice # displays all mice paths as they search")
write(&errout,"\t\t--cols=C # sets maze width to C (default 16) columns")
write(&errout,"\t\t--rows=R # sets maze height to R (default 12) rows")
stop()
end
# A "Quantum-mouse" for traversing mazes. Each mouse lives for just one cell, but
# can spawn other mice to search from adjoining cells.
global qMice, bestMouse, bestMouseLock, region, qMiceEmpty
record Position(r,c)
# Must match values used in maze generation!
$define FINISH 64 # exit
$define START 32 # entrance
$define PATH 128
$define SEEN 16 # bread crumbs for generator
$define NORTH 8 # sides ...
$define EAST 4
$define SOUTH 2
$define WEST 1
$define EMPTY 0 # like new
class QMouse(maze, loc, parent, len, val)
method getLoc(); return loc; end
method getParent(); return \parent; end
method getLen(); return len; end
method atEnd(); return EMPTY ~= iand(val, FINISH); end
method goNorth(); if EMPTY ~= iand(val,NORTH) then return visit(loc.r-1, loc.c); end
method goSouth(); if EMPTY ~= iand(val,SOUTH) then return visit(loc.r+1, loc.c); end
method goEast(); if EMPTY ~= iand(val,EAST) then return visit(loc.r, loc.c+1); end
method goWest(); if EMPTY ~= iand(val,WEST) then return visit(loc.r, loc.c-1); end
method visit(r,c)
critical region[r,c]: if EMPTY = iand(maze[r,c],SEEN) then {
if /bestMouse | (len <= bestMouse.getLen()) then { # Keep going?
mark(maze, r,c)
unlock(region[r,c])
return Position(r,c)
}
}
end
initially (m, l, p, n)
initial { # Construct critical region mutexes and completion condvar
qMice := mutex(set())
qMiceEmpty := condvar()
bestMouseLock := mutex()
region := list(*m) # Minimize critical region size
every r := 1 to *m do region[r] := list(*m[1])
every !!region := mutex()
}
maze := m
loc := l
parent := p
len := n+1
val := maze[loc.r,loc.c] | fail # Fail if outside maze
insert(qMice, self)
thread {
if atEnd() then {
critical bestMouseLock:
if /bestMouse | (len < bestMouse.getLen()) then bestMouse := self
}
else { # Spawn more mice to look for finish
QMouse(maze, goNorth(), self, len)
QMouse(maze, goSouth(), self, len)
QMouse(maze, goEast(), self, len)
QMouse(maze, goWest(), self, len)
}
delete(qMice, self)
if *qMice=0 then signal(qMiceEmpty)
}
end
procedure mark(maze, r,c)
ior(maze[r,c],SEEN)
if \showMice then markCell(r,c,"grey",5)
return Position(r,c)
end
procedure clearMaze(maze) # Clear out dregs from maze creation
every r := 1 to *maze & c := 1 to *maze[1] do # remove breadcrumbs
maze[r,c] := iand(maze[r,c],NORTH+EAST+SOUTH+WEST+START+FINISH)
end
procedure findStart(maze) # Anywhere in maze
clearMaze(maze) # Remove breadcrumbs
every r := 1 to *maze & c := 1 to *maze[1] do # Locate START cell
if EMPTY ~= iand(maze[r,c],START) then return mark(maze, r,c)
end
procedure showPath(maze)
if path := \bestMouse then { # Mark it in maze
repeat {
loc := path.getLoc()
maze[loc.r,loc.c] +:= PATH
path := \path.getParent() | break
}
return
}
end
procedure waitForCompletion()
critical qMiceEmpty: while *qMice > 0 do wait(qMiceEmpty)
end
J
Due to reports that the program failed, the generation and solver are shown together. The display verb was extended to include a dyadic definition. The complete program was tested with j 8.0.2 on linux using no profile, the command $ ijconsole -jprofile
maze=:4 :0
assert.0<:n=.<:x*y
horiz=. 0$~x,y-1
verti=. 0$~(x-1),y
path=.,:here=. ?x,y
unvisited=.0 (<here+1)} 0,0,~|:0,0,~1$~y,x
while.n do.
neighbors=. here+"1 (,-)=0 1
neighbors=. neighbors #~ (<"1 neighbors+1) {unvisited
if.#neighbors do.
n=.n-1
next=. ({~ ?@#) neighbors
unvisited=.0 (<next+1)} unvisited
if.{.next=here
do. horiz=.1 (<-:here+next-0 1)} horiz
else. verti=. 1 (<-:here+next-1 0)} verti end.
path=.path,here=.next
else.
here=.{:path
path=.}:path
end.
end.
horiz;verti
)
NB. source Dijkstra_equal_weights graph
NB.
NB. + +---+---+
NB. | 0 1 2 | (sample cell numbers)
NB. +---+ + +
NB. | 3 4 | 5
NB. +---+---+---+
NB.
NB. graph =: 1;0 2 4;1 5;4;1 3;2
NB. The graph is a vector of boxed vectors of neighbors.
Dijkstra_equal_weights =: 4 : 0
dist =. previous =. #&_ n =. # graph =. y [ source =. x
dist =. 0 source } dist
Q =. 0
while. #Q do.
u =. {.Q
Q =. }.Q
if. _ = u{dist do. break. end.
for_v. >u{graph do.
if. -. v e. previous do.
alt =. >: u { dist
if. alt < v { dist do.
dist =. alt v } dist
previous =. u v } previous
if. v e. Q do.
echo 'belch'
else.
Q =. Q,v
end.
end.
end.
end.
end.
dist;previous
)
path =: 3 : 0
p =. <:#y
while. _ > {:p do.
p =. p,y{~{:p
end.
|.}:p
)
solve=:3 :0
NB. convert walls to graph
shape =. }.@$@:>
ew =. (,.&0 ,: 0&,.)@>@{. NB. east west doors
ns =. (, &0 ,: 0&, )@>@{:
cell_offsets =. 1 _1 1 _1 * 2 # 1 , {:@shape
cell_numbers =. i.@shape
neighbors =. (cell_numbers +"_ _1 cell_offsets *"_1 (ew , ns))y
graph =. (|:@(,/"_1) <@-."1 0 ,@i.@shape)neighbors NB. list of boxed neighbors
NB. solve it
path , > {: 0 Dijkstra_equal_weights graph
)
display=:3 :0
size=. >.&$&>/y
text=. (}:1 3$~2*1+{:size)#"1":size$<' '
'hdoor vdoor'=. 2 4&*&.>&.> (#&,{@;&i./@$)&.> y
' ' (a:-.~0 1;0 2; 0 3;(2 1-~$text);(1 4&+&.> hdoor),,vdoor+&.>"0/2 1;2 2;2 3)} text
:
a=. display y
size=. >.&$&>/y
columns=. {: size
cells =. <"1(1 2&p.@<.@(%&columns) ,. 2 4&p.@(columns&|))x
'o' cells } a NB. exercise, replace cells with a gerund to draw arrows on the path.
)
Example:
4 (display~ solve)@maze 20 + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | o o o | o o o o | o o o o o o o o | | +---+---+ + +---+---+ +---+ +---+---+ +---+---+---+ + + +---+ + | o o | o | o | | o o o | | | o | | | | + + + + + + +---+---+---+ + +---+---+ + + +---+ +---+ + | o | o o | o | | | | | | | | o o | | o o | + +---+---+ + + + +---+ +---+---+---+ + +---+---+ +---+ + + | o o o o | | | | o o o | o +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Java
import java.io.*;
import java.util.*;
public class MazeSolver
{
/**
* Reads a file into an array of strings, one per line.
*/
private static String[] readLines (InputStream f) throws IOException
{
BufferedReader r =
new BufferedReader (new InputStreamReader (f, "US-ASCII"));
ArrayList<String> lines = new ArrayList<String>();
String line;
while ((line = r.readLine()) != null)
lines.add (line);
return lines.toArray(new String[0]);
}
/**
* Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that
* each cell in the maze is the same size horizontally as vertically.
* (Versus the expanded version, which looks better visually.)
* Also, converts each line of the maze from a String to a
* char[], because we'll want mutability when drawing the solution later.
*/
private static char[][] decimateHorizontally (String[] lines)
{
final int width = (lines[0].length() + 1) / 2;
char[][] c = new char[lines.length][width];
for (int i = 0 ; i < lines.length ; i++)
for (int j = 0 ; j < width ; j++)
c[i][j] = lines[i].charAt (j * 2);
return c;
}
/**
* Given the maze, the x and y coordinates (which must be odd),
* and the direction we came from, return true if the maze is
* solvable, and draw the solution if so.
*/
private static boolean solveMazeRecursively (char[][] maze,
int x, int y, int d)
{
boolean ok = false;
for (int i = 0 ; i < 4 && !ok ; i++)
if (i != d)
switch (i)
{
// 0 = up, 1 = right, 2 = down, 3 = left
case 0:
if (maze[y-1][x] == ' ')
ok = solveMazeRecursively (maze, x, y - 2, 2);
break;
case 1:
if (maze[y][x+1] == ' ')
ok = solveMazeRecursively (maze, x + 2, y, 3);
break;
case 2:
if (maze[y+1][x] == ' ')
ok = solveMazeRecursively (maze, x, y + 2, 0);
break;
case 3:
if (maze[y][x-1] == ' ')
ok = solveMazeRecursively (maze, x - 2, y, 1);
break;
}
// check for end condition
if (x == 1 && y == 1)
ok = true;
// once we have found a solution, draw it as we unwind the recursion
if (ok)
{
maze[y][x] = '*';
switch (d)
{
case 0:
maze[y-1][x] = '*';
break;
case 1:
maze[y][x+1] = '*';
break;
case 2:
maze[y+1][x] = '*';
break;
case 3:
maze[y][x-1] = '*';
break;
}
}
return ok;
}
/**
* Solve the maze and draw the solution. For simplicity,
* assumes the starting point is the lower right, and the
* ending point is the upper left.
*/
private static void solveMaze (char[][] maze)
{
solveMazeRecursively (maze, maze[0].length - 2, maze.length - 2, -1);
}
/**
* Opposite of decimateHorizontally(). Adds extra characters to make
* the maze "look right", and converts each line from char[] to
* String at the same time.
*/
private static String[] expandHorizontally (char[][] maze)
{
char[] tmp = new char[3];
String[] lines = new String[maze.length];
for (int i = 0 ; i < maze.length ; i++)
{
StringBuilder sb = new StringBuilder(maze[i].length * 2);
for (int j = 0 ; j < maze[i].length ; j++)
if (j % 2 == 0)
sb.append (maze[i][j]);
else
{
tmp[0] = tmp[1] = tmp[2] = maze[i][j];
if (tmp[1] == '*')
tmp[0] = tmp[2] = ' ';
sb.append (tmp);
}
lines[i] = sb.toString();
}
return lines;
}
/**
* Accepts a maze as generated by:
* http://rosettacode.org/wiki/Maze_generation#Java
* in a file whose name is specified as a command-line argument,
* or on standard input if no argument is specified.
*/
public static void main (String[] args) throws IOException
{
InputStream f = (args.length > 0
? new FileInputStream (args[0])
: System.in);
String[] lines = readLines (f);
char[][] maze = decimateHorizontally (lines);
solveMaze (maze);
String[] solvedLines = expandHorizontally (maze);
for (int i = 0 ; i < solvedLines.length ; i++)
System.out.println (solvedLines[i]);
}
}
- Output:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | * | | | * * * * * | | + * + + +---+ +---+---+ + +---+ * +---+ * +---+ +---+ | * | | | | * * * | | | * * * | * * * | | + * + +---+ + + * + * + +---+ +---+ * +---+ * +---+ + | * | | | * | * | | * * * | | * | | + * +---+---+ +---+ * + * +---+---+ + * +---+ + * + + + | * | * * * | | * | * | * * * | | * | | * | | | + * + * + * +---+ + * + * + * + * +---+ * +---+ + * + +---+ | * * * | * | | * | * * * | * * * * * | | * | | +---+---+ * +---+---+ * +---+---+---+---+---+ +---+ * +---+ + | | * * * | * * * | | | * * * | | + +---+---+ * + * +---+ +---+---+ + +---+ +---+ * + + | * * * * * * * | * | | | | | | | * * * | + * +---+---+---+ * + + +---+ +---+ + + +---+---+ * + | * * * * * * * * * | | | * | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Animated version
![](http://static.miraheze.org/rosettacodewiki/thumb/e/ea/Maze_java.png/300px-Maze_java.png)
Uses code from maze generation task
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.util.*;
import javax.swing.*;
public class MazeGenerator extends JPanel {
enum Dir {
N(1, 0, -1), S(2, 0, 1), E(4, 1, 0), W(8, -1, 0);
final int bit;
final int dx;
final int dy;
Dir opposite;
// use the static initializer to resolve forward references
static {
N.opposite = S;
S.opposite = N;
E.opposite = W;
W.opposite = E;
}
Dir(int bit, int dx, int dy) {
this.bit = bit;
this.dx = dx;
this.dy = dy;
}
};
final int nCols;
final int nRows;
final int cellSize = 25;
final int margin = 25;
final int[][] maze;
LinkedList<Integer> solution;
public MazeGenerator(int size) {
setPreferredSize(new Dimension(650, 650));
setBackground(Color.white);
nCols = size;
nRows = size;
maze = new int[nRows][nCols];
solution = new LinkedList<>();
generateMaze(0, 0);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
new Thread(() -> {
solve(0);
}).start();
}
});
}
@Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(5));
g.setColor(Color.black);
// draw maze
for (int r = 0; r < nRows; r++) {
for (int c = 0; c < nCols; c++) {
int x = margin + c * cellSize;
int y = margin + r * cellSize;
if ((maze[r][c] & 1) == 0) // N
g.drawLine(x, y, x + cellSize, y);
if ((maze[r][c] & 2) == 0) // S
g.drawLine(x, y + cellSize, x + cellSize, y + cellSize);
if ((maze[r][c] & 4) == 0) // E
g.drawLine(x + cellSize, y, x + cellSize, y + cellSize);
if ((maze[r][c] & 8) == 0) // W
g.drawLine(x, y, x, y + cellSize);
}
}
// draw pathfinding animation
int offset = margin + cellSize / 2;
Path2D path = new Path2D.Float();
path.moveTo(offset, offset);
for (int pos : solution) {
int x = pos % nCols * cellSize + offset;
int y = pos / nCols * cellSize + offset;
path.lineTo(x, y);
}
g.setColor(Color.orange);
g.draw(path);
g.setColor(Color.blue);
g.fillOval(offset - 5, offset - 5, 10, 10);
g.setColor(Color.green);
int x = offset + (nCols - 1) * cellSize;
int y = offset + (nRows - 1) * cellSize;
g.fillOval(x - 5, y - 5, 10, 10);
}
void generateMaze(int r, int c) {
Dir[] dirs = Dir.values();
Collections.shuffle(Arrays.asList(dirs));
for (Dir dir : dirs) {
int nc = c + dir.dx;
int nr = r + dir.dy;
if (withinBounds(nr, nc) && maze[nr][nc] == 0) {
maze[r][c] |= dir.bit;
maze[nr][nc] |= dir.opposite.bit;
generateMaze(nr, nc);
}
}
}
boolean withinBounds(int r, int c) {
return c >= 0 && c < nCols && r >= 0 && r < nRows;
}
boolean solve(int pos) {
if (pos == nCols * nRows - 1)
return true;
int c = pos % nCols;
int r = pos / nCols;
for (Dir dir : Dir.values()) {
int nc = c + dir.dx;
int nr = r + dir.dy;
if (withinBounds(nr, nc) && (maze[r][c] & dir.bit) != 0
&& (maze[nr][nc] & 16) == 0) {
int newPos = nr * nCols + nc;
solution.add(newPos);
maze[nr][nc] |= 16;
animate();
if (solve(newPos))
return true;
animate();
solution.removeLast();
maze[nr][nc] &= ~16;
}
}
return false;
}
void animate() {
try {
Thread.sleep(50L);
} catch (InterruptedException ignored) {
}
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Maze Generator");
f.setResizable(false);
f.add(new MazeGenerator(24), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
JavaScript
Animated: generating and solving.
To start solving, click to choose a 'start' and an 'end' points.
Go here to see it in action.
var ctx, wid, hei, cols, rows, maze, stack = [], start = {x:-1, y:-1}, end = {x:-1, y:-1}, grid = 8;
function drawMaze() {
for( var i = 0; i < cols; i++ ) {
for( var j = 0; j < rows; j++ ) {
switch( maze[i][j] ) {
case 0: ctx.fillStyle = "black"; break;
case 1: ctx.fillStyle = "green"; break;
case 2: ctx.fillStyle = "red"; break;
case 3: ctx.fillStyle = "yellow"; break;
case 4: ctx.fillStyle = "#500000"; break;
}
ctx.fillRect( grid * i, grid * j, grid, grid );
}
}
}
function getFNeighbours( sx, sy, a ) {
var n = [];
if( sx - 1 > 0 && maze[sx - 1][sy] == a ) {
n.push( { x:sx - 1, y:sy } );
}
if( sx + 1 < cols - 1 && maze[sx + 1][sy] == a ) {
n.push( { x:sx + 1, y:sy } );
}
if( sy - 1 > 0 && maze[sx][sy - 1] == a ) {
n.push( { x:sx, y:sy - 1 } );
}
if( sy + 1 < rows - 1 && maze[sx][sy + 1] == a ) {
n.push( { x:sx, y:sy + 1 } );
}
return n;
}
function solveMaze() {
if( start.x == end.x && start.y == end.y ) {
for( var i = 0; i < cols; i++ ) {
for( var j = 0; j < rows; j++ ) {
switch( maze[i][j] ) {
case 2: maze[i][j] = 3; break;
case 4: maze[i][j] = 0; break;
}
}
}
drawMaze();
return;
}
var neighbours = getFNeighbours( start.x, start.y, 0 );
if( neighbours.length ) {
stack.push( start );
start = neighbours[0];
maze[start.x][start.y] = 2;
} else {
maze[start.x][start.y] = 4;
start = stack.pop();
}
drawMaze();
requestAnimationFrame( solveMaze );
}
function getCursorPos( event ) {
var rect = this.getBoundingClientRect();
var x = Math.floor( ( event.clientX - rect.left ) / grid ),
y = Math.floor( ( event.clientY - rect.top ) / grid );
if( maze[x][y] ) return;
if( start.x == -1 ) {
start = { x: x, y: y };
} else {
end = { x: x, y: y };
maze[start.x][start.y] = 2;
solveMaze();
}
}
function getNeighbours( sx, sy, a ) {
var n = [];
if( sx - 1 > 0 && maze[sx - 1][sy] == a && sx - 2 > 0 && maze[sx - 2][sy] == a ) {
n.push( { x:sx - 1, y:sy } ); n.push( { x:sx - 2, y:sy } );
}
if( sx + 1 < cols - 1 && maze[sx + 1][sy] == a && sx + 2 < cols - 1 && maze[sx + 2][sy] == a ) {
n.push( { x:sx + 1, y:sy } ); n.push( { x:sx + 2, y:sy } );
}
if( sy - 1 > 0 && maze[sx][sy - 1] == a && sy - 2 > 0 && maze[sx][sy - 2] == a ) {
n.push( { x:sx, y:sy - 1 } ); n.push( { x:sx, y:sy - 2 } );
}
if( sy + 1 < rows - 1 && maze[sx][sy + 1] == a && sy + 2 < rows - 1 && maze[sx][sy + 2] == a ) {
n.push( { x:sx, y:sy + 1 } ); n.push( { x:sx, y:sy + 2 } );
}
return n;
}
function createArray( c, r ) {
var m = new Array( c );
for( var i = 0; i < c; i++ ) {
m[i] = new Array( r );
for( var j = 0; j < r; j++ ) {
m[i][j] = 1;
}
}
return m;
}
function createMaze() {
var neighbours = getNeighbours( start.x, start.y, 1 ), l;
if( neighbours.length < 1 ) {
if( stack.length < 1 ) {
drawMaze(); stack = [];
start.x = start.y = -1;
document.getElementById( "canvas" ).addEventListener( "mousedown", getCursorPos, false );
return;
}
start = stack.pop();
} else {
var i = 2 * Math.floor( Math.random() * ( neighbours.length / 2 ) )
l = neighbours[i]; maze[l.x][l.y] = 0;
l = neighbours[i + 1]; maze[l.x][l.y] = 0;
start = l
stack.push( start )
}
drawMaze();
requestAnimationFrame( createMaze );
}
function createCanvas( w, h ) {
var canvas = document.createElement( "canvas" );
wid = w; hei = h;
canvas.width = wid; canvas.height = hei;
canvas.id = "canvas";
ctx = canvas.getContext( "2d" );
ctx.fillStyle = "black"; ctx.fillRect( 0, 0, wid, hei );
document.body.appendChild( canvas );
}
function init() {
cols = 73; rows = 53;
createCanvas( grid * cols, grid * rows );
maze = createArray( cols, rows );
start.x = Math.floor( Math.random() * ( cols / 2 ) );
start.y = Math.floor( Math.random() * ( rows / 2 ) );
if( !( start.x & 1 ) ) start.x++; if( !( start.y & 1 ) ) start.y++;
maze[start.x][start.y] = 0;
createMaze();
}
HTML to test.
<!DOCTYPE html> <html><head><meta charset="UTF-8"> <title>Maze</title> <script src="maze.js"></script></head><body onload="init()"></body></html>
Julia
"""
+ +---+---+
| 1 2 3 |
+---+ + +
| 4 5 | 6
+---+---+---+
julia> const graph = [
0 1 0 0 0 0;
1 0 1 0 1 0;
0 1 0 0 0 1;
0 0 0 0 1 0;
0 1 0 1 0 0;
0 0 1 0 0 0]
julia> dist, path = dijkstra(graph, 1)
(Dict(4=>3,2=>1,3=>2,5=>2,6=>3,1=>0), Dict(4=>5,2=>1,3=>2,5=>2,6=>3,1=>0))
julia> printpath(path, 6) # Display solution of the maze
1 -> 2 -> 3 -> 6
"""
function dijkstra(graph, source::Int=1)
# ensure that the adjacency matrix is squared
@assert size(graph, 1) == size(graph, 2)
inf = typemax(Int64)
n = size(graph, 1)
Q = IntSet(1:n) # Set of unvisited nodes
dist = Dict(n => inf for n in Q) # Unknown distance function from source to v
prev = Dict(n => 0 for n in Q) # Previous node in optimal path from source
dist[source] = 0 # Distance from source to source
function _minimumdist(nodes) # Find the less distant node among nodes
kmin, vmin = nothing, inf
for (k, v) in dist
if k ∈ nodes && v ≤ vmin
kmin, vmin = k, v
end
end
return kmin
end
# Until all nodes are visited...
while !isempty(Q)
u = _minimumdist(Q) # Vertex in Q with smallest dist[]
pop!(Q, u)
if dist[u] == inf break end # All remaining vertices are inaccessible from source
for v in 1:n # Each neighbor v of u
if graph[u, v] != 0 && v ∈ Q # where v has not yet been visited
alt = dist[u] + graph[u, v]
if alt < dist[v] # Relax (u, v, a)
dist[v] = alt
prev[v] = u
end
end
end
end
return dist, prev
end
function printpath(prev::Dict, target::Int)
path = "$target"
while prev[target] != 0
target = prev[target]
path = "$target -> " * path
end
println(path)
end
const graph = [
0 1 0 0 0 0;
1 0 1 0 1 0;
0 1 0 0 0 1;
0 0 0 0 1 0;
0 1 0 1 0 0;
0 0 1 0 0 0]
dist, path = dijkstra(graph)
printpath(path, 6)
Kotlin
// Version 1.2.31
import java.io.File
typealias Maze = List<CharArray>
/**
* Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that
* each cell in the maze is the same size horizontally as vertically.
* (Versus the expanded version, which looks better visually.)
* Also, converts each line of the maze from a String to a
* char[], because we'll want mutability when drawing the solution later.
*/
fun decimateHorizontally(lines: List<String>): Maze {
val width = (lines[0].length + 1) / 2
val c = List(lines.size) { CharArray(width) }
for (i in 0 until lines.size) {
for (j in 0 until width) c[i][j] = lines[i][j * 2]
}
return c
}
/**
* Given the maze, the x and y coordinates (which must be odd),
* and the direction we came from, return true if the maze is
* solvable, and draw the solution if so.
*/
fun solveMazeRecursively(maze: Maze, x: Int, y: Int, d: Int): Boolean {
var ok = false
var i = 0
while (i < 4 && !ok) {
if (i != d) {
// 0 = up, 1 = right, 2 = down, 3 = left
when(i) {
0 -> if (maze[y - 1][x] == ' ') ok = solveMazeRecursively (maze, x, y - 2, 2)
1 -> if (maze[y][x + 1] == ' ') ok = solveMazeRecursively (maze, x + 2, y, 3)
2 -> if (maze[y + 1][x] == ' ') ok = solveMazeRecursively (maze, x, y + 2, 0)
3 -> if (maze[y][x - 1] == ' ') ok = solveMazeRecursively (maze, x - 2, y, 1)
else -> {}
}
}
i++
}
// check for end condition
if (x == 1 && y == 1) ok = true
// once we have found a solution, draw it as we unwind the recursion
if (ok) {
maze[y][x] = '*'
when (d) {
0 -> maze[y - 1][x] = '*'
1 -> maze[y][x + 1] = '*'
2 -> maze[y + 1][x] = '*'
3 -> maze[y][x - 1] = '*'
else -> {}
}
}
return ok
}
/**
* Solve the maze and draw the solution. For simplicity,
* assumes the starting point is the lower right, and the
* ending point is the upper left.
*/
fun solveMaze(maze: Maze) =
solveMazeRecursively(maze, maze[0].size - 2, maze.size - 2, -1)
/**
* Opposite of decimateHorizontally(). Adds extra characters to make
* the maze "look right", and converts each line from char[] to
* String at the same time.
*/
fun expandHorizontally(maze: Maze): Array<String> {
val tmp = CharArray(3)
val lines = Array<String>(maze.size) { "" }
for (i in 0 until maze.size) {
val sb = StringBuilder(maze[i].size * 2)
for (j in 0 until maze[i].size) {
if (j % 2 == 0)
sb.append(maze[i][j])
else {
for (k in 0..2) tmp[k] = maze[i][j]
if (tmp[1] == '*') {
tmp[0] = ' '
tmp[2] = ' '
}
sb.append(tmp)
}
}
lines[i] = sb.toString()
}
return lines
}
/**
* Accepts a maze as generated by:
* http://rosettacode.org/wiki/Maze_generation#Kotlin
* in a file whose name is specified as a command-line argument.
*/
fun main(args: Array<String>) {
if (args.size != 1) {
println("The maze file to be read should be passed as a single command line argument.")
return
}
val f = File(args[0])
if (!f.exists()) {
println("Sorry ${args[0]} does not exist.")
return
}
val lines = f.readLines(Charsets.US_ASCII)
val maze = decimateHorizontally(lines)
solveMaze(maze)
val solvedLines = expandHorizontally(maze)
println(solvedLines.joinToString("\n"))
}
- Output:
Maze (maze.txt) produced by the maze generation program:
+---+---+---+---+---+---+---+---+ | | | | +---+---+ + + +---+ + + | | | | | | | + + + +---+ + +---+ + | | | | | | + + + + +---+---+---+ + | | | | | | +---+ + +---+---+---+ + + | | | | | | + +---+ + +---+ + + + | | | | | | + + +---+---+ +---+---+ + | | | | | + +---+ + +---+ + +---+ | | | | +---+---+---+---+---+---+---+---+
Solution generated by this program when passed maze.txt - follow *'s from bottom right (start) to top left (finish):
+---+---+---+---+---+---+---+---+ | * * * * * | * * * * * | | +---+---+ * + + * +---+ * + + | | * | | * | | * * * | + + + * +---+ * + +---+ * + | | | * | * * * | * | + + + * + * +---+---+---+ * + | | | * | * * * * * * * | * | +---+ + * +---+---+---+ * + * + | | * | * * * * * | * | * | + +---+ * + * +---+ * + * + * + | | * * * | * * * | * * * | * | + + * +---+---+ * +---+---+ * + | * * * | * * * | * * * | * * * | + * +---+ * + * +---+ * + * +---+ | * * * * * | * * * * * | * * * | +---+---+---+---+---+---+---+---+
Mathematica /Wolfram Language
Graph
Solving the maze generated in Maze_generation#Graph:
HighlightGraph[maze, PathGraph@FindShortestPath[maze, 1, 273]]
- Output:
Nim
import random, strutils
type
Direction {.pure.} = enum None, Up, Left, Down, Right
Maze = object
cells: seq[string]
hwalls: seq[string]
vwalls: seq[string]
####################################################################################################
# Maze creation.
func initMaze(rows, cols: Positive): Maze =
## Initialize a maze description.
var h = repeat('-', cols)
var v = repeat("|", cols)
for i in 0..<rows:
result.cells.add newString(cols)
result.hwalls.add h
result.vwalls.add v
proc gen(maze: var Maze; r, c: Natural) =
## Generate a maze starting from point (r, c).
maze.cells[r][c] = ' '
var dirs = [Up, Left, Down, Right]
dirs.shuffle()
for dir in dirs:
case dir
of None:
discard
of Up:
if r > 0 and maze.cells[r-1][c] == '\0':
maze.hwalls[r][c] = chr(0)
maze.gen(r-1, c)
of Left:
if c > 0 and maze.cells[r][c-1] == '\0':
maze.vwalls[r][c] = chr(0)
maze.gen(r, c-1)
of Down:
if r < maze.cells.high and maze.cells[r+1][c] == '\0':
maze.hwalls[r+1][c] = chr(0)
maze.gen(r+1, c)
of Right:
if c < maze.cells[0].high and maze.cells[r][c+1] == '\0':
maze.vwalls[r][c+1] = chr(0)
maze.gen(r, c+1)
proc gen(maze: var Maze) =
## Generate a maze, choosing a random starting point.
maze.gen(rand(maze.cells.high), rand(maze.cells[0].high))
####################################################################################################
# Maze solving.
proc solve(maze: var Maze; ra, ca, rz, cz: Natural) =
## Solve a maze by finding the path from point (ra, ca) to point (rz, cz).
proc rsolve(maze: var Maze; r, c: Natural; dir: Direction): bool {.discardable.} =
## Recursive solver.
if r == rz and c == cz:
maze.cells[r][c] = 'F'
return true
if dir != Down and maze.hwalls[r][c] == '\0':
if maze.rSolve(r-1, c, Up):
maze.cells[r][c] = '^'
maze.hwalls[r][c] = '^'
return true
if dir != Up and r < maze.hwalls.high and maze.hwalls[r+1][c] == '\0':
if maze.rSolve(r+1, c, Down):
maze.cells[r][c] = 'v'
maze.hwalls[r+1][c] = 'v'
return true
if dir != Left and c < maze.vwalls[0].high and maze.vwalls[r][c+1] == '\0':
if maze.rSolve(r, c+1, Right):
maze.cells[r][c] = '>'
maze.vwalls[r][c+1] = '>'
return true
if dir != Right and maze.vwalls[r][c] == '\0':
if maze.rSolve(r, c-1, Left):
maze.cells[r][c] = '<'
maze.vwalls[r][c] = '<'
return true
maze.rsolve(ra, ca, None)
maze.cells[ra][ca] = 'S'
####################################################################################################
# Maze display.
func `$`(maze: Maze): string =
## Return the string representation fo a maze.
const
HWall = "+---"
HOpen = "+ "
VWall = "| "
VOpen = " "
RightCorner = "+\n"
RightWall = "|\n"
for r, hw in maze.hwalls:
for h in hw:
if h == '-' or r == 0:
result.add HWall
else:
result.add HOpen
if h notin {'-', '\0'}: result[^2] = h
result.add RightCorner
for c, vw in maze.vwalls[r]:
if vw == '|' or c == 0:
result.add VWall
else:
result.add VOpen
if vw notin {'|', '\0'}: result[^4] = vw
if maze.cells[r][c] != '\0': result[^2] = maze.cells[r][c]
result.add RightWall
for _ in 1..maze.hwalls[0].len:
result.add HWall
result.add RightCorner
#———————————————————————————————————————————————————————————————————————————————————————————————————
when isMainModule:
const
Width = 8
Height = 8
randomize()
var maze = initMaze(Width, Height)
maze.gen()
var ra, rz = rand(Width - 1)
var ca, cz = rand(Height - 1)
while rz == ra and cz == ca:
# Make sur starting and ending points are different.
rz = rand(Width - 1)
cz = rand(Height - 1)
maze.solve(ra, ca , rz, cz)
echo maze
- Output:
+---+---+---+---+---+---+---+---+ | | > > > > > > > > > > v | + + ^ +---+---+---+---+ v + + | | ^ < < | | v | | + +---+ ^ + +---+ + v +---+ | | ^ | | | > > v | + +---+ ^ +---+---+ +---+ v + | | ^ | | v | +---+ + ^ + + +---+---+ v + | | ^ | | | v | + +---+ ^ +---+---+ + + v + | > > > > ^ | | | | v | + ^ +---+---+ + +---+ + v + | ^ < < | F | | v | +---+ ^ + ^ +---+---+---+---+ v + | S | ^ < < < < < < < < < < | +---+---+---+---+---+---+---+---+
Perl
This example includes maze generation code.
#!perl
use strict;
use warnings;
my ($width, $height) = @ARGV;
$_ ||= 10 for $width, $height;
my %visited;
my $h_barrier = "+" . ("--+" x $width) . "\n";
my $v_barrier = "|" . (" |" x $width) . "\n";
my @output = ($h_barrier, $v_barrier) x $height;
push @output, $h_barrier;
my @dx = qw(-1 1 0 0);
my @dy = qw(0 0 -1 1);
sub visit {
my ($x, $y) = @_;
$visited{$x, $y} = 1;
my $rand = int rand 4;
for my $n ( $rand .. 3, 0 .. $rand-1 ) {
my ($xx, $yy) = ($x + $dx[$n], $y + $dy[$n]);
next if $visited{ $xx, $yy };
next if $xx < 0 or $xx >= $width;
next if $yy < 0 or $yy >= $height;
my $row = $y * 2 + 1 + $dy[$n];
my $col = $x * 3 + 1 + $dx[$n];
substr( $output[$row], $col, 2, ' ' );
no warnings 'recursion';
visit( $xx, $yy );
}
}
visit( int rand $width, int rand $height );
print "Here is the maze:\n";
print @output;
%visited = ();
my @d = ('>>', '<<', 'vv', '^^');
sub solve {
my ($x, $y) = @_;
return 1 if $x == 0 and $y == 0;
$visited{ $x, $y } = 1;
my $rand = int rand 4;
for my $n ( $rand .. 3, 0 .. $rand-1 ) {
my ($xx, $yy) = ($x + $dx[$n], $y + $dy[$n]);
next if $visited{ $xx, $yy };
next if $xx < 0 or $xx >= $width;
next if $yy < 0 or $yy >= $height;
my $row = $y * 2 + 1 + $dy[$n];
my $col = $x * 3 + 1 + $dx[$n];
my $b = substr( $output[$row], $col, 2 );
next if " " ne $b;
no warnings 'recursion';
next if not solve( $xx, $yy );
substr( $output[$row], $col, 2, $d[$n] );
substr( $output[$row-$dy[$n]], $col-$dx[$n], 2, $d[$n] );
return 1;
}
0;
}
if( solve( $width-1, $height-1 ) ) {
print "Here is the solution:\n";
substr( $output[1], 1, 2, '**' );
print @output;
} else {
print "Could not solve!\n";
}
- Output:
Here is the maze: +--+--+--+--+--+--+--+--+--+--+ | | | | + + +--+--+--+ +--+ + + + | | | | | | + +--+--+ + + +--+--+--+ + | | | | | | +--+--+--+--+ +--+ +--+ + + | | | | | | + + +--+ + + +--+ +--+ + | | | | | | | | +--+--+ +--+ + + +--+ +--+ | | | | | | | + +--+--+ +--+--+ + +--+ + | | | | | | + + + + + +--+--+--+--+--+ | | | | | | | + + +--+--+ + + + +--+ + | | | | | | | + + + + +--+--+--+--+--+ + | | | +--+--+--+--+--+--+--+--+--+--+ Here is the solution: +--+--+--+--+--+--+--+--+--+--+ |**| | | +vv+ +--+--+--+ +--+ + + + |vv | ^^>>>| | | | +vv+--+--+^^+vv+ +--+--+--+ + |vv>>>>>>>>>|vv| | | | +--+--+--+--+vv+--+ +--+ + + | |vv| | | | + + +--+ +vv+ +--+ +--+ + | | | |vv| | | | +--+--+ +--+vv+ + +--+ +--+ | |<<<vv| | | | | + +--+--+vv+--+--+ + +--+ + | |<<<^^|vv| | | + +vv+^^+vv+ +--+--+--+--+--+ | |vv|<<<vv| | | | + +vv+--+--+ + + + +--+ + | |vv|^^>>>| | | | + +vv+^^+vv+--+--+--+--+--+ + | vv>>>|vv>>>>>>>>>>>>>>>>>>| +--+--+--+--+--+--+--+--+--+--+
Phix
Combined generator and solver.
-- -- demo\rosetta\Maze_solving.exw -- ============================= -- with javascript_semantics constant w = 11, h = 8 sequence wall = join(repeat("+",w+1),"---")&"\n", cell = join(repeat("|",w+1)," ? ")&"\n", grid = split(join(repeat(wall,h+1),cell),'\n') procedure amaze(integer x, y) grid[y][x] = ' ' -- mark cell visited sequence p = shuffle({{x-4,y},{x,y+2},{x+4,y},{x,y-2}}) for i=1 to length(p) do integer {nx,ny} = p[i] if nx>1 and nx<w*4 and ny>1 and ny<=2*h and grid[ny][nx]='?' then integer mx = (x+nx)/2 grid[(y+ny)/2][mx-1..mx+1] = ' ' -- knock down wall amaze(nx,ny) end if end for end procedure integer dx, dy -- door location (in a wall!) function solve_maze(integer x, y) sequence p = {{x-4,y},{x,y+2},{x+4,y},{x,y-2}} for d=1 to length(p) do integer {nx,ny} = p[d] integer {wx,wy} = {(x+nx)/2,(y+ny)/2} if grid[wy][wx]=' ' then grid[wy][wx] = "-:-:"[d] -- mark path if {wx,wy}={dx,dy} then return true end if if grid[ny][nx]=' ' then grid[ny][nx] = 'o' -- mark cell if solve_maze(nx,ny) then return true end if grid[ny][nx] = ' ' -- unmark cell end if grid[wy][wx] = ' ' -- unmark path end if end for return false end function function heads() return rand(2)=1 -- toin coss 50:50 true(1)/false(0) end function integer {x,y} = {(rand(w)*4)-1,rand(h)*2} amaze(x,y) -- mark start pos grid[y][x] = '*' -- add a random door (heads=rhs/lhs, tails=top/btm) if heads() then {dy,dx} = {rand(h)*2,heads()*w*4+1} grid[dy][dx] = ' ' else {dy,dx} = {heads()*h*2+1,rand(w)*4-1} grid[dy][dx-1..dx+1] = ' ' end if {} = solve_maze(x,y) puts(1,join(grid,'\n'))
- Output:
+---+---+---+---+---+---+---+---+---+---+---+ | o - o - o | o - o - o - o | | | + : +---+ : +---+ : +---+---+ : + + + + | o - o | o | o - o | | o - o | | | +---+ : + : + : +---+ + : +---+---+---+ + | o - o | o - o | | o - o | o - o - o | + : +---+---+---+ +---+---+ : + : +---+ : + | o | | o - o | | o | + : + +---+---+---+---+ +---+---+ + : + | o | | | | o - + : +---+---+ + + +---+---+ +---+---+ | o | | | | + : +---+---+---+ +---+ +---+---+ + + | o - o - o | | * | | | | + +---+ : +---+---+ : + +---+---+ + + | | o - o - o - o | | | +---+---+---+---+---+---+---+---+---+---+---+
Picat
main =>
Maze0 = ["+---+---+---+---+---+---+---+---+",
"| | | | |",
"+ + + +---+ + +---+ +",
"| | | | | |",
"+---+---+ + +---+---+ + +",
"| | | | |",
"+---+ + + +---+---+---+ +",
"| | | | |",
"+ + + +---+---+---+ + +",
"| | | | | |",
"+ + + +---+ +---+---+ +",
"| | | | | |",
"+ +---+---+ +---+---+---+ +",
"| | | |",
"+ +---+ +---+ + +---+ +",
"| | | |",
"+---+---+---+---+---+---+---+---+"],
MaxR = len(Maze0) div 2,
MaxC = len(Maze0[1]) div 4,
Maze = new_array(MaxR,MaxC),
foreach (R in 1..MaxR, C in 1..MaxC)
Maze[R,C] = 0
end,
fill_maze(Maze0,Maze,1),
solve_maze(Maze,(1,1),(MaxR,MaxC),Cost,Plan),
OutputMaze = new_array(MaxR,MaxC),
foreach ((R,C) in Plan)
OutputMaze[R,C] = '*'
end,
output_maze(Maze0,OutputMaze,1).
fill_maze([Line1,Line2|Maze0],Maze,R) =>
fill_maze_cols(Line1,Maze,R,1),
fill_maze_cols(Line2,Maze,R,1),
fill_maze(Maze0,Maze,R+1).
fill_maze(_,_,_) => true.
fill_maze_cols(['+',' ',' ',' '|Line],Maze,R,C) =>
Maze[R,C] := Maze[R,C] \/ 4, % up
Maze[R-1,C] := Maze[R-1,C] \/ 8, % down
fill_maze_cols(Line,Maze,R,C+1).
fill_maze_cols([' ',' ',' ',' '|Line],Maze,R,C) =>
Maze[R,C] := Maze[R,C] \/ 1, % left
Maze[R,C-1] := Maze[R,C-1] \/ 2, % right
fill_maze_cols(Line,Maze,R,C+1).
fill_maze_cols([_,_,_,_|Line],Maze,R,C) =>
fill_maze_cols(Line,Maze,R,C+1).
fill_maze_cols(_,_,_,_) => true.
table (+,+,+,min,-)
solve_maze(_Maze,FPos,FPos,Cost,Plan) =>
Cost = 0,
Plan = [FPos].
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=>
Maze[R,C] /\ 1 == 1, % left
solve_maze(Maze,(R,C-1),FPos,Cost1,Plan1),
Plan = [Pos|Plan1],
Cost = Cost1+1.
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=>
Maze[R,C] /\ 2 == 2, % right
solve_maze(Maze,(R,C+1),FPos,Cost1,Plan1),
Plan = [Pos|Plan1],
Cost = Cost1+1.
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=>
Maze[R,C] /\ 4 == 4, % up
solve_maze(Maze,(R-1,C),FPos,Cost1,Plan1),
Plan = [Pos|Plan1],
Cost = Cost1+1.
solve_maze(Maze,Pos@(R,C),FPos,Cost,Plan) ?=>
Maze[R,C] /\ 8 == 8, % down
solve_maze(Maze,(R+1,C),FPos,Cost1,Plan1),
Plan = [Pos|Plan1],
Cost = Cost1+1.
output_maze([Line1,Line2|Maze0],Maze,R) =>
output_maze_cols(Line1,Maze,R,1),
output_maze_cols(Line2,Maze,R,1),
output_maze(Maze0,Maze,R+1).
output_maze([Line],_,_) => println(Line).
output_maze_cols([' ',' ',' ',' '|Line],Maze,R,C) =>
if Maze[R,C] == '*' then
print(" * ")
else
print(" ")
end,
output_maze_cols(Line,Maze,R,C+1).
output_maze_cols(['|',' ',' ',' '|Line],Maze,R,C) =>
if Maze[R,C] == '*' then
print("| * ")
else
print("| ")
end,
output_maze_cols(Line,Maze,R,C+1).
output_maze_cols(['+',' ',' ',' '|Line],Maze,R,C) =>
if Maze[R,C] == '*' && Maze[R-1,C] == '*' then
print("+ * ")
else
print("+ ")
end,
output_maze_cols(Line,Maze,R,C+1).
output_maze_cols([C1,C2,C3,C4|Line],Maze,R,C) =>
printf("%c%c%c%c",C1,C3,C3,C4),
output_maze_cols(Line,Maze,R,C+1).
output_maze_cols(Line,_,_,_) => println(Line).
- Output:
+---+---+---+---+---+---+---+---+ | * | * * | | | + * + * + * +---+ + +---+ + | * * | * | | | | +---+---+ * + +---+---+ + + | | * | | | +---+ + * + +---+---+---+ + | | * | | | + + + * +---+---+---+ + + | | | * | | | + + + * +---+ +---+---+ + | | | * * | | | + +---+---+ * +---+---+---+ + | | * * | * * * | + +---+ +---+ * + * +---+ * + | | * * | * | +---+---+---+---+---+---+---+---+
PicoLisp
(de shortestPath (Goal This Maze)
(let (Path NIL Best NIL Dir " > ")
(recur (This Path Dir)
(when (and This (not (: mark)))
(push 'Path (cons This Dir))
(if (== Goal This)
(unless (and Best (>= (length Path) (length Best)))
(setq Best Path) )
(=: mark T)
(recurse (: west) Path " > ")
(recurse (: east) Path " < ")
(recurse (: south) Path " \^ ")
(recurse (: north) Path " v ")
(=: mark NIL) ) ) )
(disp Maze 0
'((Fld) (if (asoq Fld Best) (cdr @) " ")) ) ) )
Using the maze produced in Maze generation#PicoLisp, this finds the shortest path from the top-left cell 'a8' to the bottom-right exit 'k1':
: (shortestPath 'a8 'k1 (maze 11 8)) + +---+---+---+---+---+---+---+---+---+---+ 8 | > > v | > v | | + + + + + + +---+ +---+---+ + 7 | | | > ^ | v | | | | | +---+ +---+---+ + + +---+ + + + 6 | | | v | | | | | + +---+ +---+ +---+---+---+ + +---+ 5 | | | > > > v | | | +---+ +---+ +---+---+---+ +---+---+ + 4 | | | | | v | > > v | + +---+ +---+ +---+ + + +---+ + 3 | | | | | v | ^ < | v | + +---+---+ + + + + +---+ + + 2 | | | | | | v | > ^ | v | + + + +---+ + +---+ + +---+ + 1 | | | > ^ | > +---+---+---+---+---+---+---+---+---+---+---+ a b c d e f g h i j k
Prolog
Works with SWI-Prolog and XPCE.
:- dynamic cell/2.
:- dynamic maze/3.
:- dynamic path/1.
maze_solve(Lig,Col) :-
retractall(cell(_,_)),
retractall(maze(_,_,_)),
retractall(path(_)),
% initialisation of the neighbours of the cells
forall(between(0, Lig, I),
( forall(between(0, Col, J), assert(maze(I, J, []))))),
% creation of the window of the maze
new(D, window('Maze')),
forall(between(0,Lig, I),
(XL is 50, YL is I * 30 + 50,
XR is Col * 30 + 50,
new(L, line(XL, YL, XR, YL)),
send(D, display, L))),
forall(between(0,Col, I),
(XT is 50 + I * 30, YT is 50,
YB is Lig * 30 + 50,
new(L, line(XT, YT, XT, YB)),
send(D, display, L))),
SX is Col * 30 + 100,
SY is Lig * 30 + 100,
send(D, size, new(_, size(SX, SY))),
L0 is random(Lig),
C0 is random(Col),
assert(cell(L0, C0)),
\+search(D, Lig, Col, L0, C0),
send(D, open),
% we look for a path from cell(0, 0) to cell(Lig-1, Col-1)
% creation of the entrance
erase_line(D, -1, 0, 0, 0),
% creation of the exit
Lig1 is Lig-1,
Col1 is Col-1,
erase_line(D, Lig1, Col1, Lig, Col1),
% seraching the path
assert(path([[0, 0], [-1, 0]])),
walk(Lig, Col),
path(P),
display_path(D, P).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
walk(Lig, Col) :-
path([[L, C] | _R]),
L is Lig - 1,
C is Col - 1,
retract(path(P)),
assert(path([[Lig, C]|P])).
walk(Lig, Col) :-
retract(path([[L, C] | R])),
maze(L, C, Edge),
member([L1, C1], Edge),
\+member([L1, C1], R),
assert(path([[L1,C1], [L, C] | R])),
walk(Lig, Col).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
display_path(_, []).
display_path(D, [[L, C] | R]):-
new(B, box(10,10)),
send(B, fill_pattern, new(_, colour(@default, 0,0,0))),
X is C * 30 + 60,
Y is L * 30 + 60,
send(D, display, B, point(X,Y)),
display_path(D, R).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
search(D, Lig, Col, L, C) :-
Dir is random(4),
nextcell(Dir, Lig, Col, L, C, L1, C1),
assert(cell(L1,C1)),
assert(cur(L1,C1)),
retract(maze(L, C, Edge)),
assert(maze(L, C, [[L1, C1] | Edge])),
retract(maze(L1, C1, Edge1)),
assert(maze(L1, C1, [[L, C] | Edge1])),
erase_line(D, L, C, L1, C1),
search(D, Lig, Col, L1, C1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
erase_line(D, L, C, L, C1) :-
( C < C1 -> C2 = C1; C2 = C),
XT is C2 * 30 + 50,
YT is L * 30 + 51, YR is (L+1) * 30 + 50,
new(Line, line(XT, YT, XT, YR)),
send(Line, colour, white),
send(D, display, Line).
erase_line(D, L, C, L1, C) :-
XT is 51 + C * 30, XR is 50 + (C + 1) * 30,
( L < L1 -> L2 is L1; L2 is L),
YT is L2 * 30 + 50,
new(Line, line(XT, YT, XR, YT)),
send(Line, colour, white),
send(D, display, Line).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nextcell(Dir, Lig, Col, L, C, L1, C1) :-
next(Dir, Lig, Col, L, C, L1, C1);
( Dir1 is (Dir+3) mod 4,
next(Dir1, Lig, Col, L, C, L1, C1));
( Dir2 is (Dir+1) mod 4,
next(Dir2, Lig, Col, L, C, L1, C1));
( Dir3 is (Dir+2) mod 4,
next(Dir3, Lig, Col, L, C, L1, C1)).
% 0 => northward
next(0, _Lig, _Col, L, C, L1, C) :-
L > 0,
L1 is L - 1,
\+cell(L1, C).
% 1 => rightward
next(1, _Lig, Col, L, C, L, C1) :-
C < Col - 1,
C1 is C + 1,
\+cell(L, C1).
% 2 => southward
next(2, Lig, _Col, L, C, L1, C) :-
L < Lig - 1,
L1 is L + 1,
\+cell(L1, C).
% 3 => leftward
next(2, _Lig, _Col, L, C, L, C1) :-
C > 0,
C1 is C - 1,
\+cell(L, C1).
PureBasic
;code from the maze generation task is place here in its entirety before the rest of the code
Procedure displayMazePath(Array maze(2), List Path.POINT())
Protected x, y, vWall.s, hWall.s
Protected mazeWidth = ArraySize(maze(), 1), mazeHeight = ArraySize(maze(), 2)
Protected Dim mazeOutput.mazeOutput(mazeHeight)
Protected Dim mazeRow.mazeOutput(0)
Static pathChars.s = "@^>v<"
For y = 0 To mazeHeight
makeDisplayMazeRow(mazeRow(), maze(), y): mazeOutput(y) = mazeRow(0)
Next
If ListSize(path())
FirstElement(path())
Protected prevPath.POINT = path()
While NextElement(path())
x = path()\x - prevPath\x
y = path()\y - prevPath\y
Select x
Case -1: dirTaken = #dir_W
Case 1: dirTaken = #dir_E
Default
If y < 0
dirTaken = #dir_N
Else
dirTaken = #dir_S
EndIf
EndSelect
hWall = mazeOutput(prevPath\y)\hWall
mazeOutput(prevPath\y)\hWall = Left(hWall, prevPath\x * #cellDWidth + 2) + Mid(pathChars, dirTaken + 1, 1) + Right(hWall, Len(hWall) - (prevPath\x * #cellDWidth + 3))
prevPath = path()
Wend
hWall = mazeOutput(prevPath\y)\hWall
mazeOutput(prevPath\y)\hWall = Left(hWall, prevPath\x * #cellDWidth + 2) + Mid(pathChars, #dir_ID + 1, 1) + Right(hWall, Len(hWall) - (prevPath\x * #cellDWidth + 3))
For y = 0 To mazeHeight
PrintN(mazeOutput(y)\vWall): PrintN(mazeOutput(y)\hWall)
Next
EndIf
EndProcedure
Procedure solveMaze(Array maze(2), *start.POINT, *finish.POINT, List Path.POINT())
Protected mazeWidth = ArraySize(maze(), 1), mazeHeight = ArraySize(maze(), 2)
Dim visited(mazeWidth + 1, mazeHeight + 1) ;includes padding for easy border detection
Protected i
;mark outside border as already visited (off limits)
For i = 1 To mazeWidth
visited(i, 0) = #True: visited(i, mazeHeight + 1) = #True
Next
For i = 1 To mazeHeight
visited(0, i) = #True: visited(mazeWidth + 1, i) = #True
Next
Protected x = *start\x, y = *start\y, nextCellDir
visited(x + offset(#visited, #dir_ID)\x, y + offset(#visited, #dir_ID)\y) = #True
ClearList(path())
Repeat
If x = *finish\x And y = *finish\y
AddElement(path())
path()\x = x: path()\y = y
Break ;success
EndIf
nextCellDir = #firstDir - 1
For i = #firstDir To #numDirs
If Not visited(x + offset(#visited, i)\x, y + offset(#visited, i)\y)
If maze(x + offset(#wall, i)\x, y + offset(#wall, i)\y) & wallvalue(i) <> #Null
nextCellDir = i: Break ;exit for/next search
EndIf
EndIf
Next
If nextCellDir >= #firstDir
visited(x + offset(#visited, nextCellDir)\x, y + offset(#visited, nextCellDir)\y) = #True
AddElement(path())
path()\x = x: path()\y = y
x + offset(#maze, nextCellDir)\x: y + offset(#maze, nextCellDir)\y
ElseIf ListSize(path()) > 0
x = path()\x: y = path()\y
DeleteElement(path())
Else
Break
EndIf
ForEver
EndProcedure
;demonstration
If OpenConsole()
Define.POINT start, finish
start\x = Random(mazeWidth - 1): start\y = Random(mazeHeight - 1)
finish\x = Random(mazeWidth - 1): finish\y = Random(mazeHeight - 1)
NewList Path.POINT()
solveMaze(maze(), start, finish, path())
If ListSize(path()) > 0
PrintN("Solution found for path between (" + Str(start\x) + ", " + Str(start\y) + ") and (" + Str(finish\x) + ", " + Str(finish\y) + ")")
displayMazePath(maze(), path())
Else
PrintN("No solution found for path between (" + Str(start\x) + ", " + Str(start\y) + ") and (" + Str(finish\x) + ", " + Str(finish\y) + ")")
EndIf
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
CloseConsole()
EndIf
Using the maze produced in Maze generation#PureBasic, this additional code will find and display the path between two random maze cells. A working example requires combining the two code listings by placing the 'maze generation' code at the beginning of the 'maze solving' code.
Sample output:
Solution found for path between (3, 2) and (7, 1) +---+---+---+---+---+---+---+---+---+---+ | v < < < < | | v < < | + +---+---+---+ + + +---+ +---+ | > v | ^ | | v | @ | ^ < | +---+ +---+---+ + + + +---+ + | | v | > ^ | v | ^ | ^ | + + + +---+---+---+ + +---+ + | v < | | > ^ | > ^ | + +---+---+---+---+ +---+ + + + | v | | | | ^ | | + +---+ + +---+---+---+---+ +---+ | > > v | | > v | ^ < | +---+---+ +---+---+---+ + +---+ + | > > > > ^ | > > ^ | +---+---+---+---+---+---+---+---+---+---+
Python
# python 3
def Dijkstra(Graph, source):
'''
+ +---+---+
| 0 1 2 |
+---+ + +
| 3 4 | 5
+---+---+---+
>>> graph = ( # or ones on the diagonal
... (0,1,0,0,0,0,),
... (1,0,1,0,1,0,),
... (0,1,0,0,0,1,),
... (0,0,0,0,1,0,),
... (0,1,0,1,0,0,),
... (0,0,1,0,0,0,),
... )
...
>>> Dijkstra(graph, 0)
([0, 1, 2, 3, 2, 3], [1e+140, 0, 1, 4, 1, 2])
>>> display_solution([1e+140, 0, 1, 4, 1, 2])
5<2<1<0
'''
# Graph[u][v] is the weight from u to v (however 0 means infinity)
infinity = float('infinity')
n = len(graph)
dist = [infinity]*n # Unknown distance function from source to v
previous = [infinity]*n # Previous node in optimal path from source
dist[source] = 0 # Distance from source to source
Q = list(range(n)) # All nodes in the graph are unoptimized - thus are in Q
while Q: # The main loop
u = min(Q, key=lambda n:dist[n]) # vertex in Q with smallest dist[]
Q.remove(u)
if dist[u] == infinity:
break # all remaining vertices are inaccessible from source
for v in range(n): # each neighbor v of u
if Graph[u][v] and (v in Q): # where v has not yet been visited
alt = dist[u] + Graph[u][v]
if alt < dist[v]: # Relax (u,v,a)
dist[v] = alt
previous[v] = u
return dist,previous
def display_solution(predecessor):
cell = len(predecessor)-1
while cell:
print(cell,end='<')
cell = predecessor[cell]
print(0)
Racket
Following function returns a path between two cells in a maze which is created by the build-maze function (See Maze generation).
;; Returns a path connecting two given cells in the maze
;; find-path :: Maze Cell Cell -> (Listof Cell)
(define (find-path m p1 p2)
(match-define (maze N M tbl) m)
(define (alternatives p prev) (remove prev (connections tbl p)))
(define (dead-end? p prev) (empty? (alternatives p prev)))
(define ((next-turn route) p)
(define prev (car route))
(cond
[(equal? p p2) (cons p2 route)]
[(dead-end? p prev) '()]
[else (append-map (next-turn (cons p route))
(alternatives p prev))]))
(reverse
(append-map (next-turn (list p1))
(alternatives p1 (list p1)))))
Reading a maze from a file
;; Reads the maze from the textual form
;; read-maze :: File-path -> Maze
(define (read-maze file)
(define tbl (make-hash))
(with-input-from-file file
(λ ()
; the first line gives us the width of the maze
(define N (/ (- (string-length (read-line)) 1) 4))
; while reading other lines we get the height of the maze
(define M
(for/sum ([h (in-lines)] [v (in-lines)] [j (in-naturals)])
(for ([i (in-range N)])
(when (eq? #\space (string-ref h (* 4 (+ 1 i))))
(connect! tbl (list i j) (list (+ i 1) j)))
(when (eq? #\space (string-ref v (+ 1 (* 4 i))))
(connect! tbl (list i j) (list i (+ j 1)))))
1))
(maze N M tbl))))
Printing out a maze with a path between two given cells
;; Shows a maze with a path connecting two given cells
(define (show-path m p1 p2)
(match-define (maze N M tbl) m)
(define route (find-path m p1 p2))
(for ([i N]) (display "+---"))
(displayln "+")
(for ([j M])
(display "|")
(for ([i (- N 0)])
(if (member (list i j) route)
(display " *")
(display " "))
(if (connected? tbl (list i j) (list (+ 1 i) j))
(display " ")
(display " |")))
(newline)
(for ([i N])
(if (connected? tbl (list i j) (list i (+ j 1)))
(display "+ ")
(display "+---")))
(displayln "+"))
(newline))
Example:
-> (define m (build-maze 14 7)) -> (with-output-to-file "maze" (λ () (show-maze m))) -> (show-maze (read-maze "maze")) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | | | | + +---+---+---+---+ + + + +---+ +---+ + + | | | | | | | | | | +---+ + +---+ +---+ + + + +---+ +---+ + | | | | | | | | | | + + + + +---+---+---+ + + + + + +---+ | | | | | | | | | | + +---+ + + + +---+---+ + +---+---+---+ + | | | | | | | | | | + + +---+---+ +---+---+ + +---+---+ + + + | | | | | | | | + +---+---+---+ + + +---+---+---+ +---+---+ + | | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -> (show-path m '(0 0) '(13 6)) +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | * | * * * | | | + +---+---+---+---+ + + + +---+ +---+ + + | * * | * * * | | * | | * | | | | +---+ + +---+ +---+ + + + +---+ +---+ + | * * | * | | * * * | | * | | | | + + + + +---+---+---+ + + + + + +---+ | * | | * | | | * | | | | + +---+ + + + +---+---+ + +---+---+---+ + | * | * * | | | | * | | | | + + +---+---+ +---+---+ + +---+---+ + + + | * | * * * * | | | * * * | | | + +---+---+---+ + + +---+---+---+ +---+---+ + | * * * * * | | * * * * | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Raku
(formerly Perl 6)
(Includes maze generation code.)
constant mapping = :OPEN(' '),
:N< ╵ >,
:E< ╶ >,
:NE< └ >,
:S< ╷ >,
:NS< │ >,
:ES< ┌ >,
:NES< ├ >,
:W< ╴ >,
:NW< ┘ >,
:EW< ─ >,
:NEW< ┴ >,
:SW< ┐ >,
:NSW< ┤ >,
:ESW< ┬ >,
:NESW< ┼ >,
:TODO< x >,
:TRIED< · >;
enum Sym (mapping.map: *.key);
my @ch = mapping.map: *.value;
enum Direction <DeadEnd Up Right Down Left>;
sub gen_maze ( $X,
$Y,
$start_x = (^$X).pick * 2 + 1,
$start_y = (^$Y).pick * 2 + 1 )
{
my @maze;
push @maze, $[ flat ES, -N, (ESW, EW) xx $X - 1, SW ];
push @maze, $[ flat (NS, TODO) xx $X, NS ];
for 1 ..^ $Y {
push @maze, $[ flat NES, EW, (NESW, EW) xx $X - 1, NSW ];
push @maze, $[ flat (NS, TODO) xx $X, NS ];
}
push @maze, $[ flat NE, (EW, NEW) xx $X - 1, -NS, NW ];
@maze[$start_y][$start_x] = OPEN;
my @stack;
my $current = [$start_x, $start_y];
loop {
if my $dir = pick_direction( $current ) {
@stack.push: $current;
$current = move( $dir, $current );
}
else {
last unless @stack;
$current = @stack.pop;
}
}
return @maze;
sub pick_direction([$x,$y]) {
my @neighbors =
(Up if @maze[$y - 2][$x]),
(Down if @maze[$y + 2][$x]),
(Left if @maze[$y][$x - 2]),
(Right if @maze[$y][$x + 2]);
@neighbors.pick or DeadEnd;
}
sub move ($dir, @cur) {
my ($x,$y) = @cur;
given $dir {
when Up { @maze[--$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y--][$x+1] -= W; }
when Down { @maze[++$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y++][$x+1] -= W; }
when Left { @maze[$y][--$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x--] -= N; }
when Right { @maze[$y][++$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x++] -= N; }
}
@maze[$y][$x] = 0;
[$x,$y];
}
}
sub display (@maze) {
for @maze -> @y {
for @y.rotor(2) -> ($w, $c) {
print @ch[abs $w];
if $c >= 0 { print @ch[$c] x 3 }
else { print ' ', @ch[abs $c], ' ' }
}
say @ch[@y[*-1]];
}
}
sub solve (@maze is copy, @from = [1, 1], @to = [@maze[0] - 2, @maze - 2]) {
my ($x, $y) = @from;
my ($xto, $yto) = @to;
my @stack;
sub drop-crumb($x,$y,$c) { @maze[$y][$x] = -$c }
drop-crumb($x,$y,N);
loop {
my $dir = pick_direction([$x,$y]);
if $dir {
($x, $y) = move($dir, [$x,$y]);
return @maze if $x == $xto and $y == $yto;
}
else {
@maze[$y][$x] = -TRIED;
($x,$y) = @stack.pop;
@maze[$y][$x] = -TRIED;
($x,$y) = @stack.pop;
}
}
sub pick_direction([$x,$y]) {
my @neighbors =
(Up unless @maze[$y - 1][$x]),
(Down unless @maze[$y + 1][$x]),
(Left unless @maze[$y][$x - 1]),
(Right unless @maze[$y][$x + 1]);
@neighbors.pick or DeadEnd;
}
sub move ($dir, @cur) {
my ($x,$y) = @cur;
given $dir {
when Up { for ^2 { push @stack, $[$x,$y--]; drop-crumb $x,$y,S; } }
when Down { for ^2 { push @stack, $[$x,$y++]; drop-crumb $x,$y,N; } }
when Left { for ^2 { push @stack, $[$x--,$y]; drop-crumb $x,$y,E; } }
when Right { for ^2 { push @stack, $[$x++,$y]; drop-crumb $x,$y,W; } }
}
$x,$y;
}
}
display solve gen_maze( 29, 19 );
- Output:
┌ ╵ ────┬───────────────┬───────────────┬───────────────────────────────┬───────────┬───────────────┬───────────────┐ │ ╵ · · │ ╷ ╴ ╴ ╴ ╴ │ │ ╷ ╴ ╴ · · · · · · · · · · · · │ ╷ ╴ ╴ · · │ · · · · · · · │ · · · · · · · │ │ ╵ ╶───┘ ╷ ╶───┐ ╵ ╷ │ ╷ ╶───────┤ ╷ ╷ ╵ ╶───────┬───────┐ · ┌───┘ ╷ ╷ ╵ ╷ · │ · ╶───┬───╴ · │ · ╷ · ┌───╴ · │ │ ╵ ╴ ╴ ╴ ╴ · · │ ╵ │ │ │ │ ╷ │ ╵ ╴ ╴ ╴ ╴ │ ╷ ╴ ╴ │ · │ ╷ ╴ ╴ │ ╵ │ · │ · · · │ · · · │ · │ · │ · · · │ │ · ┌───────────┤ ╵ │ └───┴───────╴ │ ╷ ├───────┐ ╵ ╵ ╷ ╷ ╵ └───┘ ╷ ┌───┘ ╵ │ · │ · ╷ · │ · ┌───┴───┘ · │ · ╶───┤ │ · │ ╶ ╶ ╶ ╶ ╷ │ ╵ │ │ ╷ │ │ ╵ ╴ ╴ │ ╵ ╴ ╴ ╴ ╴ │ ╶ ╶ ╵ │ · │ · │ · │ · │ · · · · · │ · · · │ │ · │ ╵ ╶───┐ ╷ ╵ ╵ ├───────────────╴ │ ╷ └───╴ └───────┼───┬───────┤ ╵ ┌───┤ · ├───┘ · │ · │ · ╷ · ┌───┴───┐ · │ │ · │ ╵ ╴ ╴ │ ╶ ╶ ╵ │ │ ╶ ╶ ╶ ╶ ╶ ╶ ╶ ╶ ╷ │ · │ ╶ ╶ ╷ │ ╵ │ · │ · │ · · · │ · │ · │ · │ · · · │ · │ │ · └───┐ ╵ ├───────┴───────┐ ╶───────┴───────┬───────╴ ╷ │ · │ ╵ ╷ ╷ │ ╵ │ · │ · │ · ┌───┤ · │ · │ · │ · ╷ · ╵ · │ │ · · · │ ╵ │ ╷ ╴ ╴ ╴ ╴ ╴ ╴ │ │ ╷ ╴ ╴ ╴ ╴ │ · │ ╵ │ ╷ │ ╵ │ · │ · │ · │ · │ · │ · │ · │ · │ · · · │ ├───╴ · │ ╵ │ ╷ ╶───────┐ ╵ ├───────────────┐ │ ╷ ╶───┬───┘ · │ ╵ │ ╷ ╵ ╵ │ · ╵ · ╵ · │ · ╵ · └───┘ · │ · ├───────┤ │ · · · │ ╵ │ ╶ ╶ ╶ ╶ ╷ │ ╵ │ ╷ ╴ ╴ ╴ ╴ ╴ ╴ │ │ ╶ ╶ ╷ │ · · · │ ╵ │ ╶ ╶ ╵ │ · · · · · │ · · · · · · · │ · │ · · · │ │ · ┌───┤ ╵ └───────╴ ╷ │ ╵ ╵ ╷ ┌───────┐ ╵ │ └───┐ ╷ └───┐ · │ ╵ ├───────┼───────┬───┴───────┬───┬───┘ · ├───╴ · │ │ · │ · │ ╵ ╴ ╴ ╴ ╴ ╴ ╴ │ ╵ ╴ ╴ │ · · · │ ╵ │ │ ╶ ╶ ╷ │ · │ ╵ │ ╷ ╴ ╴ │ ╷ ╴ ╴ │ ╷ ╴ ╴ ╴ ╴ │ · │ · · · │ · · · │ │ · ╵ · ├───────────────┴───────┴───┐ · │ ╵ └───────┴───┐ ╷ │ · │ ╵ ╵ ╷ ╷ ╵ ╵ ╷ ╷ ╵ │ ╷ ┌───╴ ╵ │ · │ · ╶───┤ · ╶───┤ │ · · · │ · · · · · · · · · · · · · │ · │ ╵ ╴ ╴ ╴ ╴ · · │ ╷ │ · │ ╵ ╴ ╴ │ ╵ ╴ ╴ │ ╵ │ ╷ │ ╶ ╶ ╵ │ · │ · · · │ · · · │ ├───┐ · │ · ┌───────────╴ · ┌───┐ · │ · └───┬───╴ ╵ ┌───┘ ╷ │ · ├───────┴───────┤ ╵ ╵ ╷ │ ╵ ╶───┤ · └───┐ · │ · ╷ · │ │ · │ · │ · │ · · · · · · · │ · │ · │ · · · │ ╶ ╶ ╵ │ ╷ ╴ ╴ │ · │ · · · · · · · │ ╵ ╴ ╴ │ ╵ ╴ ╴ │ · · · │ · │ · │ · │ │ · │ · └───┘ · ┌───────────┘ · │ · ├───╴ · │ ╵ ┌───┘ ╷ ┌───┘ · │ · ╷ · ┌───╴ · └───────┴───┐ ╵ └───┐ · ╵ · ├───┘ · │ │ · │ · · · · · │ · · · · · · · │ · │ · · · │ ╵ │ ╷ ╴ ╴ │ · · · │ · │ · │ · · · · · · · · · │ ╵ ╴ ╴ │ · · · │ · · · │ │ · ├───────╴ · ├───┐ · ┌───┐ · ╵ · │ · ╶───┤ ╵ ╵ ╷ ┌───┘ · ╶───┤ · │ · └───┬───╴ · ┌───────┤ · ╷ ╵ │ · ╶───┘ · ╷ · │ │ · │ · · · · · │ │ · │ · │ · · · │ · · · │ ╵ ╴ ╴ │ · · · · · │ · │ · · · │ · · · │ ╶ ╶ ╷ │ · │ ╵ │ · · · · · │ · │ │ · ╵ · ┌───────┘ │ · ╵ · └───────┤ · ╷ · └───────┴───────┐ · ╵ · └───┐ · └───┐ · │ ╵ ╷ ╷ └───┘ ╵ ├───────────┤ · │ │ · · · │ │ · · · · · · · │ · │ · · · · · · · · · │ · · · · · │ · · · │ · │ ╵ │ ╶ ╶ ╶ ╶ ╵ │ ╷ ╴ ╴ ╴ ╴ │ · │ │ · ╶───┤ ╶───────┴───┬───────┐ · ├───┘ · ┌───────────┐ · ├───────────┴───╴ · │ · │ ╵ └───────┐ · │ ╷ ┌───╴ ╵ │ · │ │ · · · │ │ · · · │ · │ · · · │ · · · · · │ · │ · · · · · · · · · │ · │ ╵ ╴ ╴ ╴ ╴ │ · │ ╷ │ ╶ ╶ ╵ │ · │ ├───┐ · ├───────┬───╴ │ · ╷ · │ · │ · ┌───┤ · ╶───────┤ · │ · ╶───────┬───┐ · ├───┴───────┐ ╵ └───┘ ╷ │ ╵ ┌───┴───┤ │ · │ · │ │ │ · │ · │ · │ · │ · │ · · · · · │ · │ · · · · · │ · │ · │ │ ╵ ╴ ╴ ╴ ╴ │ ╵ │ ╷ ╴ ╴ │ │ · │ · │ ╷ ╵ ┌───┘ · │ · ╵ · ╵ · │ · ╵ · ┌───┐ · ╵ · └───────╴ · │ · ╵ · │ ┌───╴ └───────┐ · │ ╵ ╵ ╷ ╷ ╵ │ │ · │ · │ │ │ · · · │ · · · · · │ · · · │ · │ · · · · · · · · · │ · · · │ │ │ · │ ╵ ╴ ╴ │ ╵ │ │ · │ · │ └───┬───┴───────┴───┬───────┤ · ┌───┘ · ├───────────────────┴───────┤ └───────────┐ └───┴───────┤ ╵ │ │ · │ · │ │ │ │ · │ · · · │ │ │ │ ╵ │ │ · ╵ · ├───┐ │ ╶───────┐ ╵ ╷ │ · ╵ · ╷ · │ ╶───────────────────┐ └───┬───────┐ └───────┬───┐ │ ╵ │ │ · · · │ · │ │ │ │ │ · · · │ · │ │ │ │ │ │ │ ╵ │ │ · ╶───┤ · │ └───────┐ ├───────┤ └───┬───┴───┼───────────┬───────┐ ├───┐ ╵ ╷ ├───────┐ │ │ │ ╵ │ │ · · · │ · │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╵ │ ├───╴ · ╵ · └───────┐ ╵ │ ╷ └───╴ ╵ ╷ ╵ ┌───╴ ╵ ╷ ╵ │ └───────┘ ╵ ╷ ╵ │ ╵ ╵ ╵ │ │ · · · · · · · · · │ │ │ │ │ │ │ │ │ ╵ │ └───────────────────┴───────┴───┴───────────────┴───────┴───────────┴───────┴───────────────────┴───────┴──────── │ ┘
Red
(imports maze generation code, see http://rosettacode.org/wiki/Maze_generation#Red)
Red ["Maze solver"]
do %mazegen.red
print [
"start:" start: random size - 1x1
"end:" end: random size - 1x1
]
isnew?: function [pos] [not find visited pos]
open?: function [pos d] [
o: pos/y * size/x + pos/x + 1
0 = pick walls/:o d
]
expand: function [pos][
either any [
all [pos/x > 0 isnew? p: pos - 1x0 open? p 1]
all [pos/x < (size/x - 1) isnew? p: pos + 1x0 open? pos 1]
all [pos/y > 0 isnew? p: pos - 0x1 open? p 2]
all [pos/y < (size/y - 1) isnew? p: pos + 0x1 open? pos 2]
][append visited p insert path p][remove path]
path/1
]
path: reduce [start]
visited: []
until [end = expand path/1]
print reverse path
- Output:
Maze width: 15 Maze height: 15 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | | | _ |_ | _ _ _| | _|_| |_ _| | | _| |_ | | | |_ _| _ _| | _ _ _|_ _| | |_ |_ | | _ _|_ _| _ | | | _| _| | | _ _ _|_ _|_| |_ _ _| _ _| |_| _ _ |_| | | _| _|_ _ _ | | _|_ _ _| | | | _| _ _ |_ |_ _ | | | | _| _| _|_ |_ _ _|_ _| | |_ | | _ | _| _ _ |_| | | |_ _| | |_ _ _| | | _| | | | _ _| |_ _ _ | |_ _|_ |_| | |_ | |_ |_ _ _|_ _ _ |_ | | _| | |_ | | | _| | |_ _ _ _|_ _|_ _|_ _|_ _ _ _|_| start: 2x4 end: 12x3 2x4 3x4 3x3 2x3 2x2 3x2 3x1 3x0 4x0 4x1 5x1 5x0 6x0 7x0 7x1 7x2 7x3 6x3 5x3 5x4 5x5 4x5 3x5 3x6 2x6 2x7 1x7 1x8 0x8 0x9 1x9 1x10 2x10 2x9 2x8 3x8 3x7 4x7 5x7 6x7 6x8 7x8 7x9 6x9 6x10 7x10 8x10 8x9 9x9 10x9 11x9 11x10 11x11 10x11 10x10 9x10 9x11 9x12 10x12 11x12 12x12 12x13 11x13 11x14 12x14 13x14 13x13 14x13 14x12 13x12 13x11 12x11 12x10 13x10 13x9 14x9 14x8 14x7 14x6 14x5 13x5 13x6 12x6 11x6 11x5 10x5 9x5 8x5 8x6 8x7 7x7 7x6 6x6 6x5 6x4 7x4 8x4 9x4 10x4 10x3 11x3 12x3
Ruby
This solution extends the maze generator script. To get a working script, copy & paste both parts into one file.
class Maze
# Solve via breadth-first algorithm.
# Each queue entry is a path, that is list of coordinates with the
# last coordinate being the one that shall be visited next.
def solve
# Clean up.
reset_visiting_state
# Enqueue start position.
@queue = []
enqueue_cell([], @start_x, @start_y)
# Loop as long as there are cells to visit and no solution has
# been found yet.
path = nil
until path || @queue.empty?
path = solve_visit_cell
end
if path
# Mark the cells that make up the shortest path.
for x, y in path
@path[x][y] = true
end
else
puts "No solution found?!"
end
end
private
# Maze solving visiting method.
def solve_visit_cell
# Get the next path.
path = @queue.shift
# The cell to visit is the last entry in the path.
x, y = path.last
# Have we reached the end yet?
return path if x == @end_x && y == @end_y
# Mark cell as visited.
@visited[x][y] = true
for dx, dy in DIRECTIONS
if dx.nonzero?
# Left / Right
new_x = x + dx
if move_valid?(new_x, y) && !@vertical_walls[ [x, new_x].min ][y]
enqueue_cell(path, new_x, y)
end
else
# Top / Bottom
new_y = y + dy
if move_valid?(x, new_y) && !@horizontal_walls[x][ [y, new_y].min ]
enqueue_cell(path, x, new_y)
end
end
end
nil # No solution yet.
end
# Enqueue a new coordinate to visit.
def enqueue_cell(path, x, y)
# Add new coordinates to the current path and enqueue the new path.
@queue << path + [[x, y]]
end
end
# Demonstration:
maze = Maze.new 20, 10
maze.solve
maze.print
Example output:
+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | * * | * | | * * | | | | + + +---+---+---+ + + +---+---+ + + + +---+ + +---+ +---+ | * | * * * * * | | * | * | | | | + +---+---+---+---+---+---+---+---+---+ + +---+---+ +---+---+---+---+ + | * | * * | * * * | * * | | | | + + +---+---+---+---+ + + +---+---+---+ + + + +---+---+---+ + | * | | * * | * * | * * * * | | | | + +---+---+---+---+ +---+---+---+ +---+---+---+ +---+---+---+---+ + + | * * * * | * * | | * | | | | | +---+---+---+ + +---+---+ +---+ + +---+---+---+ +---+---+ + + + | | * * | | * * | | | | | | | + + +---+---+---+ +---+---+ +---+ + +---+ + +---+---+---+ +---+ | | | * * | * * * | * * | | | | | | +---+ + + + +---+ + +---+ +---+---+ +---+ + +---+ +---+ + | | * | * * | * * | * * | | | | | | | | + + + +---+---+ +---+---+ +---+ + +---+---+ + + + + + + | | | * | | * | * * * | | | | | | | | + +---+ +---+ + + +---+---+ +---+---+---+ +---+ + +---+---+ + | * * | * * | | | +---+ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Rust
This solution reuses the maze generator Rust code. The modified and new parts are marked with the label "MAZE SOLVING".Uses the rand library.
use rand::{thread_rng, Rng, rngs::ThreadRng};
const WIDTH: usize = 16;
const HEIGHT: usize = 16;
#[derive(Clone, Copy, PartialEq)]
struct Cell {
col: usize,
row: usize,
}
impl Cell {
fn from(col: usize, row: usize) -> Cell {
Cell {col, row}
}
}
struct Maze {
cells: [[bool; HEIGHT]; WIDTH], //cell visited/non visited
walls_h: [[bool; WIDTH]; HEIGHT + 1], //horizontal walls existing/removed
walls_v: [[bool; WIDTH + 1]; HEIGHT], //vertical walls existing/removed
thread_rng: ThreadRng, //Random numbers generator
}
impl Maze {
///Inits the maze, with all the cells unvisited and all the walls active
fn new() -> Maze {
Maze {
cells: [[true; HEIGHT]; WIDTH],
walls_h: [[true; WIDTH]; HEIGHT + 1],
walls_v: [[true; WIDTH + 1]; HEIGHT],
thread_rng: thread_rng(),
}
}
///Randomly chooses the starting cell
fn first(&mut self) -> Cell {
Cell::from(self.thread_rng.gen_range(0, WIDTH), self.thread_rng.gen_range(0, HEIGHT))
}
///Opens the enter and exit doors
fn open_doors(&mut self) {
let from_top: bool = self.thread_rng.gen();
let limit = if from_top { WIDTH } else { HEIGHT };
let door = self.thread_rng.gen_range(0, limit);
let exit = self.thread_rng.gen_range(0, limit);
if from_top {
self.walls_h[0][door] = false;
self.walls_h[HEIGHT][exit] = false;
} else {
self.walls_v[door][0] = false;
self.walls_v[exit][WIDTH] = false;
}
}
///Removes a wall between the two Cell arguments
fn remove_wall(&mut self, cell1: &Cell, cell2: &Cell) {
if cell1.row == cell2.row {
self.walls_v[cell1.row][if cell1.col > cell2.col { cell1.col } else { cell2.col }] = false;
} else {
self.walls_h[if cell1.row > cell2.row { cell1.row } else { cell2.row }][cell1.col] = false;
};
}
///Returns a random non-visited neighbor of the Cell passed as argument
fn neighbor(&mut self, cell: &Cell) -> Option<Cell> {
self.cells[cell.col][cell.row] = false;
let mut neighbors = Vec::new();
if cell.col > 0 && self.cells[cell.col - 1][cell.row] { neighbors.push(Cell::from(cell.col - 1, cell.row)); }
if cell.row > 0 && self.cells[cell.col][cell.row - 1] { neighbors.push(Cell::from(cell.col, cell.row - 1)); }
if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] { neighbors.push(Cell::from(cell.col + 1, cell.row)); }
if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] { neighbors.push(Cell::from(cell.col, cell.row + 1)); }
if neighbors.is_empty() {
None
} else {
let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap();
self.remove_wall(cell, next);
Some(*next)
}
}
///Builds the maze (runs the Depth-first search algorithm)
fn build(&mut self) {
let mut cell_stack: Vec<Cell> = Vec::new();
let mut next = self.first();
loop {
while let Some(cell) = self.neighbor(&next) {
cell_stack.push(cell);
next = cell;
}
match cell_stack.pop() {
Some(cell) => next = cell,
None => break,
}
}
}
///MAZE SOLVING: Find the starting cell of the solution
fn solution_first(&self) -> Option<Cell> {
for (i, wall) in self.walls_h[0].iter().enumerate() {
if !wall {
return Some(Cell::from(i, 0));
}
}
for (i, wall) in self.walls_v.iter().enumerate() {
if !wall[0] {
return Some(Cell::from(0, i));
}
}
None
}
///MAZE SOLVING: Find the last cell of the solution
fn solution_last(&self) -> Option<Cell> {
for (i, wall) in self.walls_h[HEIGHT].iter().enumerate() {
if !wall {
return Some(Cell::from(i, HEIGHT - 1));
}
}
for (i, wall) in self.walls_v.iter().enumerate() {
if !wall[WIDTH] {
return Some(Cell::from(WIDTH - 1, i));
}
}
None
}
///MAZE SOLVING: Get the next candidate cell
fn solution_next(&mut self, cell: &Cell) -> Option<Cell> {
self.cells[cell.col][cell.row] = false;
let mut neighbors = Vec::new();
if cell.col > 0 && self.cells[cell.col - 1][cell.row] && !self.walls_v[cell.row][cell.col] { neighbors.push(Cell::from(cell.col - 1, cell.row)); }
if cell.row > 0 && self.cells[cell.col][cell.row - 1] && !self.walls_h[cell.row][cell.col] { neighbors.push(Cell::from(cell.col, cell.row - 1)); }
if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] && !self.walls_v[cell.row][cell.col + 1] { neighbors.push(Cell::from(cell.col + 1, cell.row)); }
if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] && !self.walls_h[cell.row + 1][cell.col] { neighbors.push(Cell::from(cell.col, cell.row + 1)); }
if neighbors.is_empty() {
None
} else {
let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap();
Some(*next)
}
}
///MAZE SOLVING: solve the maze
///Uses self.cells to store the solution cells (true)
fn solve(&mut self) {
self.cells = [[true; HEIGHT]; WIDTH];
let mut solution: Vec<Cell> = Vec::new();
let mut next = self.solution_first().unwrap();
solution.push(next);
let last = self.solution_last().unwrap();
'main: loop {
while let Some(cell) = self.solution_next(&next) {
solution.push(cell);
if cell == last {
break 'main;
}
next = cell;
}
solution.pop().unwrap();
next = *solution.last().unwrap();
}
self.cells = [[false; HEIGHT]; WIDTH];
for cell in solution {
self.cells[cell.col][cell.row] = true;
}
}
///MAZE SOLVING: Ask if cell is part of the solution (cells[col][row] == true)
fn is_solution(&self, col: usize, row: usize) -> bool {
self.cells[col][row]
}
///Displays a wall
///MAZE SOLVING: Leave space for printing '*' if cell is part of the solution
/// (only when painting vertical walls)
///
// fn paint_wall(h_wall: bool, active: bool) {
// if h_wall {
// print!("{}", if active { "+---" } else { "+ " });
// } else {
// print!("{}", if active { "| " } else { " " });
// }
// }
fn paint_wall(h_wall: bool, active: bool, with_solution: bool) {
if h_wall {
print!("{}", if active { "+---" } else { "+ " });
} else {
print!("{}{}", if active { "|" } else { " " }, if with_solution { "" } else { " " });
}
}
///MAZE SOLVING: Paint * if cell is part of the solution
fn paint_solution(is_part: bool) {
print!("{}", if is_part { " * " } else {" "});
}
///Displays a final wall for a row
fn paint_close_wall(h_wall: bool) {
if h_wall { println!("+") } else { println!() }
}
///Displays a whole row of walls
///MAZE SOLVING: Displays a whole row of walls and, optionally, the included solution cells.
// fn paint_row(&self, h_walls: bool, index: usize) {
// let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() };
// for &wall in iter {
// Maze::paint_wall(h_walls, wall);
// }
// Maze::paint_close_wall(h_walls);
// }
fn paint_row(&self, h_walls: bool, index: usize, with_solution: bool) {
let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() };
for (col, &wall) in iter.enumerate() {
Maze::paint_wall(h_walls, wall, with_solution);
if !h_walls && with_solution && col < WIDTH {
Maze::paint_solution(self.is_solution(col, index));
}
}
Maze::paint_close_wall(h_walls);
}
///Paints the maze
///MAZE SOLVING: Displaying the solution is an option
// fn paint(&self) {
// for i in 0 .. HEIGHT {
// self.paint_row(true, i);
// self.paint_row(false, i);
// }
// self.paint_row(true, HEIGHT);
// }
fn paint(&self, with_solution: bool) {
for i in 0 .. HEIGHT {
self.paint_row(true, i, with_solution);
self.paint_row(false, i, with_solution);
}
self.paint_row(true, HEIGHT, with_solution);
}
}
fn main() {
let mut maze = Maze::new();
maze.build();
maze.open_doors();
println!("The maze:");
maze.paint(false);
maze.solve();
println!("The maze, solved:");
maze.paint(true);
}
- Output:
The maze: +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+ | | | | | + +---+ + + +---+ + +---+---+---+---+---+ + + + | | | | | | | | | +---+---+---+---+ + +---+ + +---+---+ + +---+---+ + | | | | | | | | | + +---+---+ + +---+ + + + + + + +---+---+---+ | | | | | | | | | | + +---+ + + + +---+---+---+---+ + +---+---+---+ + | | | | | | | | | | | +---+ +---+ +---+---+ + +---+ + +---+---+ + + + | | | | | | | | | | | + +---+---+---+---+---+ + + + +---+ + + + + + | | | | | | | | | | + +---+ + +---+ +---+---+ + + +---+ + +---+---+ | | | | | | | | | | | | + + + + + + + +---+---+---+---+ + +---+ + + | | | | | | | | | | +---+ +---+---+ +---+---+ +---+---+ +---+---+ + + + | | | | | | | + + +---+---+---+ +---+ +---+---+---+ + +---+---+---+ | | | | | | | + +---+---+---+ +---+ +---+---+ + +---+---+ +---+ + | | | | | | | | + + + +---+---+ +---+---+ +---+---+---+---+---+ + + | | | | | | | | | + + +---+ +---+---+ +---+---+ + +---+---+ + +---+ | | | | | | | | | + +---+ +---+---+---+---+ +---+ +---+ + +---+---+ + | | | | | | | | +---+ + + + +---+ +---+ +---+ +---+---+---+---+ + | | | | | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ The maze, solved: +---+---+---+---+---+---+---+---+---+---+---+---+---+ +---+---+ | | | * * * * * * * * | | + +---+ + + +---+ + +---+---+---+---+---+ + + + | | | * * | | | | | | +---+---+---+---+ + +---+ + +---+---+ + +---+---+ + | * * * * | | * * | | | | | | + +---+---+ + +---+ + + + + + + +---+---+---+ | * * * | * | | * * | | | | | | + +---+ + + + +---+---+---+---+ + +---+---+---+ + | | * * | * | | * * | | | | | | +---+ +---+ +---+---+ + +---+ + +---+---+ + + + | * * | * * * * | | | | | | | | | + +---+---+---+---+---+ + + + +---+ + + + + + | * | | | | | | | | | + +---+ + +---+ +---+---+ + + +---+ + +---+---+ | * | | | | | | | | | | | + + + + + + + +---+---+---+---+ + +---+ + + | * * | | | | | | | | | +---+ +---+---+ +---+---+ +---+---+ +---+---+ + + + | | * | | | | | + + +---+---+---+ +---+ +---+---+---+ + +---+---+---+ | | * * * * | | * * | | * * * | + +---+---+---+ +---+ +---+---+ + +---+---+ +---+ + | * * | * * | | * * | * * * * | | * | + + + +---+---+ +---+---+ +---+---+---+---+---+ + + | * | * | | * * * | * * * | | * * * * | * * | + + +---+ +---+---+ +---+---+ + +---+---+ + +---+ | * | * * | * * * * | | * * | | * * | | + +---+ +---+---+---+---+ +---+ +---+ + +---+---+ + | * * | * | * * | * * * | | * * | | +---+ + + + +---+ +---+ +---+ +---+---+---+---+ + | * * | * * | * * * | * * * | + +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Swift
The following requires the use of the implementation for generation task. Assumes there will be no circular paths through the maze as the problem suggests.
enum errors: Error { //Occurs when a start or end position is given that is outside of bounds case IndexOutsideMazeBoundary } ///Used to solve any maze generated by the swift implementation of maze generator at: ///https://www.rosettacode.org/wiki/Maze_generation#Swift class MazeSolver { ///your starting index position in the maze var startPos: (Int, Int) /// the end index position you are trying to reach within the maze var endPos: (Int, Int) /// a maze of [[INT]] where each value is between 1 and 14. /// each number representing a var maze: [[Int]] var visitedSpaces: [(Int, Int)] init(maze: [[Int]]) { self.startPos = (0, 0) self.endPos = (0, 0) /// a maze that consists of a array of ints from 1-14 organized to /// create a maze like structure. /// Each number represents where the walls and paths will be /// ex. 2 only has a path south and 12 has paths E or W self.maze = maze /// spaces each branch has visited to stop from searching previously explored /// areas of the maze self.visitedSpaces = [] } /// determine if the user has previously visited the given location within the maze func visited(position: (Int, Int)) -> Bool { return visitedSpaces.contains(where: {$0 == position}) } /// used to translate the mazes number scheme to a list of directions available /// for each number. DirectionDiff for N would be (0, -1) for example func getDirections(positionValue: Int) -> [(Int, Int)] { switch positionValue { case 1: return [Direction.north.diff] case 2: return [Direction.south.diff] case 3: return [Direction.north.diff, Direction.south.diff] case 4: return [Direction.east.diff] case 5: return [Direction.north.diff, Direction.east.diff] case 6: return [Direction.south.diff, Direction.east.diff] case 7: return [Direction.north.diff, Direction.east.diff, Direction.south.diff] case 8: return [Direction.west.diff] case 9: return [Direction.north.diff, Direction.west.diff] case 10: return [Direction.west.diff, Direction.south.diff] case 11: return [Direction.north.diff, Direction.south.diff, Direction.west.diff] case 12: return [Direction.west.diff, Direction.east.diff] case 13: return [Direction.north.diff, Direction.east.diff, Direction.west.diff] case 14: return [Direction.east.diff, Direction.south.diff, Direction.west.diff] default: return [] } } /// calculate and return all paths branching from the current position that haven't been explored yet func availablePaths(currentPosition: (Int, Int), lastPos: (Int, Int)) -> [(Int, Int)] { /// all available paths to be returned var paths: [(Int, Int)] = [] /// the maze contents at the given position (ie the maze number) let positionValue = maze[currentPosition.0][ currentPosition.1] /// used to build the new path positions and check them before they /// are added to paths var workingPos: (Int, Int) // is the maze at a dead end and not our first position if ([1, 2, 4, 8].contains(positionValue) && currentPosition != lastPos) { return [] } /// the directions available based on the maze contents of our /// current position let directions: [(Int, Int)] = getDirections(positionValue: positionValue) /// build the paths for pos in directions { workingPos = (currentPosition.0 + pos.0, currentPosition.1 + pos.1) if (currentPosition == lastPos || (workingPos != lastPos && !visited(position: workingPos))) { paths.append(workingPos) } } return paths } /// prints a model of the maze with /// O representing the start point /// X representing the end point /// * representing traveled space /// NOTE: starting pos and end pos represent indexes and thus start at 0 func solveAndDisplay(startPos: (Int, Int), endPos: (Int, Int)) throws { if (startPos.0 >= maze.count || startPos.1 >= maze[0].count) { throw errors.IndexOutsideMazeBoundary } /// the position you are beginning at in the maze self.startPos = startPos /// the position you wish to get to within the maze self.endPos = endPos /// spaces each branch has visited to stop from searching previously explored /// areas of the maze self.visitedSpaces = [] self.displaySolvedMaze(finalPath: solveMaze(startpos: startPos, pathSoFar: [])!) } /// recursively solves our maze private func solveMaze(startpos: (Int, Int), pathSoFar: [(Int, Int)]) -> [(Int, Int)]? { /// marks our current position in the maze var currentPos: (Int, Int) = startpos /// saves the spaces visited on this current branch var currVisitedSpaces : [(Int, Int)] = [startpos] /// the final path (or the path so far if the end pos has not been reached) /// from the start position to the end position var finalPath: [(Int, Int)] = pathSoFar /// all possible paths from our current position that have not been explored var possiblePaths: [(Int, Int)] = availablePaths(currentPosition: startpos, lastPos: pathSoFar.last ?? startpos) // move through the maze until you come to a position that has been explored, the end position, or a dead end (the // possible paths array is empty) while (!possiblePaths.isEmpty) { // found the final path guard (currentPos != self.endPos) else { finalPath.append(contentsOf: currVisitedSpaces) return finalPath } // multiple valid paths from current position found // recursively call new branches for each valid path if (possiblePaths.count > 1) { visitedSpaces.append(contentsOf: currVisitedSpaces) finalPath.append(contentsOf: currVisitedSpaces) // for each valid path recursively call each path // and if it contains the final path // ie it found the endpoint, return that path for pos in possiblePaths { let path = solveMaze(startpos: pos, pathSoFar: finalPath) if (path != nil) { return path } } // otherwise this branch and it's sub-branches // are dead-ends so kill this branch return nil } // continue moving along the only path available else { // update current path to our only available next // position currentPos = possiblePaths[0] // calculate the available paths from our new // current position possiblePaths = availablePaths(currentPosition: currentPos, lastPos: currVisitedSpaces.last ?? currentPos) // update the visited spaces for this branch currVisitedSpaces.append(currentPos) } } // if our new current position is the endpos return our final path if (currentPos == self.endPos) { finalPath.append(contentsOf: currVisitedSpaces) return finalPath } else { return nil } } // Reference: https://www.rosettacode.org/wiki/Maze_generation#Swift // adapts the display method used in the swift implementation of the maze generator we used for this app to display the maze // solved func displaySolvedMaze(finalPath: [(Int, Int)]) { /// all cells are 3 wide in the x axis let cellWidth = 3 for j in 0..<y { // Draw top edge // if mark corner with +, add either (cell width) spaces or - to the topedge var dependin on if it is a wall or not var topEdge = "" for i in 0..<x { topEdge += "+" topEdge += String(repeating: (maze[i][j] & Direction.north.rawValue) == 0 ? "-" : " ", count: cellWidth) } topEdge += "+" print(topEdge) // Draw left edge //through the center of the cell if is a wall add | if not a space // then if it is travelled add " * " otherwise just 3 spaces then //cap it with a | at the end for the right most wall var leftEdge = "" for i in 0..<x { leftEdge += (maze[i][j] & Direction.west.rawValue) == 0 ? "|" : " " if (finalPath.first! == (i, j)) { leftEdge += " O " } else if (finalPath.last! == (i, j)) { leftEdge += " X " } else { if (finalPath.contains(where: {$0 == (i, j)})) { leftEdge += " * " } else { leftEdge += String(repeating: " ", count: cellWidth) } } } leftEdge += "|" print(leftEdge) } // Draw bottom edge // adds + on corners and _ everywhere else var bottomEdge = "" for _ in 0..<x { bottomEdge += "+" bottomEdge += String(repeating: "-", count: cellWidth) } bottomEdge += "+" print(bottomEdge) } }
example implementation using the aforementioned maze generator code for the swift implementation on rosettaCode let x = 20 let y = 10 let maze = MazeGenerator(x, y) var solver: MazeSolver = MazeSolver(maze: maze.maze) do { // making sure the start and end pos are within the maze boundary try solver.solveAndDisplay(startPos: (0, 0), endPos: (5, 5)) } catch errors.IndexOutsideMazeBoundary { print("[ERROR] given start or end point outside of bounds") }
- Output:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | O * | | * * | * * | * * * * * * * * * | | +---+ + +---+ + + + + +---+---+---+---+---+---+---+ + +---+ + | * * | | * | * * | * | * | | * * | * * * * | | | + +---+---+ + +---+---+ + +---+ + + + +---+---+---+---+ + + | * | | * | | * * | * * * | * | * * | | | + +---+ + + + + +---+---+---+ + +---+---+---+ +---+---+---+ + | * | | | * * | * * | * * | * * * * | | | + + +---+ +---+ +---+ + + +---+---+---+---+ + +---+ +---+ + | * | | | * * | * | | * | * * * | * * | | | | | + + + +---+ +---+ + +---+ + +---+ + +---+ + + + +---+ | * | | | | X * | * * | * | * | * * | | | | | | + + +---+ +---+ +---+---+ + + +---+---+---+ + + +---+---+ + | * | | | | | * | * | * * * | | | | + +---+ +---+ +---+---+ + + +---+---+ + +---+---+---+ + + + | * * | | * * | * * * | | | | | +---+ + +---+---+---+---+---+---+---+ +---+---+ + +---+ + +---+ + | | * | | * * * * | * * | * * | | | | | | | + + +---+---+ +---+---+ + + +---+ + + + + +---+---+---+ + | * * * * | * * | * * * | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Tcl
This script assumes that the contents of the generation task have already been source
d. Note that the algorithm implemented here does not assume that the maze is free of circuits, and in the case that there are multiple routes, it finds the shortest one because it is a breadth-first search (by virtue of the queue variable being used as a queue).
oo::define maze {
method solve {} {
### Initialization of visited matrix and location/path queue
set visited [lrepeat $x [lrepeat $y 0]]
set queue {0 0 {}}
### Loop to do the searching ###
while 1 {
# Check for running out of path; an error in maze construction
if {[llength $queue] == 0} {
error "cannot reach finish"
}
# Visit the next square from the queue
set queue [lassign $queue cx cy path]
if {[lindex $visited $cx $cy]} continue
lset visited $cx $cy 1
lappend path $cx $cy
# Check for reaching the goal
if {$cx == $x-1 && $cy == $y-1} break
# Add the square in each direction to the queue if a move there is legal
foreach {dx dy} {0 1 1 0 0 -1 -1 0} {
set nx [expr {$cx + $dx}]; set ny [expr {$cy + $dy}]
if {
$nx >= 0 && $nx < $x && $ny >= 0 && $ny < $y
&& ($dx && idx($verti, min($cx,$nx), $cy) ||
$dy && idx($horiz, $cx, min($cy,$ny)))
} then {
lappend queue $nx $ny $path
}
}
}
### Loop to set up the path rendering ###
# (-2,-2) is just a marker that isn't next to the maze at all, so
# guaranteeing the use of the last 'else' clause
foreach {cx cy} $path {nx ny} [concat [lrange $path 2 end] -2 -2] {
if {$nx-$cx == 1} {
lset content $cx $cy "v"
} elseif {$nx-$cx == -1} {
lset content $cx $cy "^"
} elseif {$ny-$cy == -1} {
lset content $cx $cy "<"
} else {
lset content $cx $cy ">"
}
}
### Return the path ###
return $path
}
}
# Do the solution (we ignore the returned path here...)
m solve
# Print it out
puts [m view]
Example output:
+ +---+---+---+---+---+---+---+---+---+---+ | v | | + +---+ +---+---+---+---+---+---+---+ + | v | | > v | > v | | | + + +---+ + + + + + +---+ + | v | > ^ | v | ^ | v | | | | + +---+ +---+ + + + +---+ +---+ | v | > ^ | v < | ^ | v | | | | + + +---+ +---+ + + +---+ + + | > ^ | v < | > ^ | v | | | +---+---+ +---+ +---+ +---+ +---+---+ | v < < | > ^ | v < | > > > v | + +---+---+ +---+ +---+ +---+---+ + | > v | ^ < | > > ^ | | v | +---+ +---+---+ +---+---+---+ + + + | > > > ^ | | > +---+---+---+---+---+---+---+---+---+---+---+
Uiua
Just add the following lines to the end of the generator. It shows you the maze that has been solved with the path highlighted, the list of moves, and the total path length.
# You can delete from here to SOLVE THE MAZE
# if you're appending this to the maze generator code.
# Experimental!
["+-+-+-+-+-+-+"
"|. . .|. . .|"
"+-+-+ + + + +"
"|.|. .|.|.|.|"
"+ + +-+-+ + +"
"|.|. . . .|.|"
"+ +-+-+-+-+ +"
"|. . . . . .|"
"+-+-+-+-+-+-+"]
H ← 4
W ← 6
Nfour ← +⊙¤[¯2_0 2_0 0_2 0_¯2] # Gives N4
InBounds ← ▽⊸≡(↧⊃(/↧≥1_1|/↧< +1 × 2 H_W))
GetWall ← -:⊡1:÷2/-.
# S O L V E T H E M A Z E #
.
Start ← 1_1
End ← -Start ×2 H_W
Heur ← /+⌵-End # Manhattan distance.
# (pos grid) -> 1-4 next steps, in bounds, without walls in the way.
Ns ← ≡⊡1▽:⟜(≡(=@ ⊡)⊙¤≡GetWall)≡⊟¤:InBounds Nfour.
astar(Ns|Heur|≍End) Start # Solve (costs = 1 => djikstra)
$"_ moves" ⊙⟜(⍜(⊡|+33)):°□⊢
- Output:
╭─ ╷ "+-+-+-+-+-+-+" "|O O O|. O O|" "+-+-+ + + + +" "|.|O O|.|O|O|" "+ + +-+-+ + +" "|.|O O O O|O|" "+ +-+-+-+-+ +" "|. . . . . O|" "+-+-+-+-+-+-+" ╯ ╭─ ╷ 1 1 1 3 1 5 3 5 3 3 5 3 5 5 5 7 5 9 3 9 1 9 1 11 3 11 5 11 7 11 ╯ "14 moves"
Wren
import "os" for Process
import "./ioutil" for File, FileUtil
/*
* Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that
* each cell in the maze is the same size horizontally as vertically.
* (Versus the expanded version, which looks better visually.)
* Also, converts each line of the maze from a string to a
* character list, because we'll want mutability when drawing the solution later.
*/
var decimateHorizontally = Fn.new { |lines|
var width = ((lines[0].count + 1)/2).floor
var c = List.filled(lines.count, null)
for (i in 0...lines.count) {
c[i] = List.filled(width, null)
for (j in 0...width) c[i][j] = lines[i][j*2]
}
return c
}
/*
* Given the maze, the x and y coordinates (which must be odd),
* and the direction we came from, return true if the maze is
* solvable, and draw the solution if so.
*/
var solveMazeRecursively // recursive function
solveMazeRecursively = Fn.new { |maze, x, y, d|
var ok = false
var i = 0
while (i < 4 && !ok) {
if (i != d) {
// 0 = up, 1 = right, 2 = down, 3 = left
if (i == 0) {
if (maze[y - 1][x] == " ") ok = solveMazeRecursively.call(maze, x, y - 2, 2)
} else if (i == 1) {
if (maze[y][x + 1] == " ") ok = solveMazeRecursively.call(maze, x + 2, y, 3)
} else if (i == 2) {
if (maze[y + 1][x] == " ") ok = solveMazeRecursively.call(maze, x, y + 2, 0)
} else if (i == 3) {
if (maze[y][x - 1] == " ") ok = solveMazeRecursively.call(maze, x - 2, y, 1)
}
}
i = i + 1
}
// check for end condition
if (x == 1 && y == 1) ok = true
// once we have found a solution, draw it as we unwind the recursion
if (ok) {
maze[y][x] = "*"
if (d == 0) {
maze[y - 1][x] = "*"
} else if (d == 1) {
maze[y][x + 1] = "*"
} else if (d == 2) {
maze[y + 1][x] = "*"
} else if (d == 3) {
maze[y][x - 1] = "*"
}
}
return ok
}
/*
* Solve the maze and draw the solution. For simplicity,
* assumes the starting point is the lower right, and the
* ending point is the upper left.
*/
var solveMaze = Fn.new { |maze|
solveMazeRecursively.call(maze, maze[0].count - 2, maze.count - 2, -1)
}
/*
* Opposite of decimateHorizontally(). Adds extra characters to make
* the maze "look right", and converts each line from a char list to a
* string at the same time.
*/
var expandHorizontally = Fn.new { |maze|
var tmp = List.filled(3, " ")
var lines = List.filled(maze.count, null)
for (i in 0...maze.count) {
var sb = ""
for (j in 0...maze[i].count) {
if (j % 2 == 0) {
sb = sb + maze[i][j]
} else {
for (k in 0..2) tmp[k] = maze[i][j]
if (tmp[1] == "*") {
tmp[0] = " "
tmp[2] = " "
}
sb = sb + tmp.join()
}
}
lines[i] = sb
}
return lines
}
/*
* Accepts a maze as generated by:
* http://rosettacode.org/wiki/Maze_generation#Wren
* in a file whose name is specified as a command-line argument.
*/
var args = Process.arguments
if (args.count != 1) {
System.print("The maze file to be read should be passed as a single command line argument.")
return
}
if (!File.exists(args[0])) {
System.print("Sorry %(args[0]) does not exist.")
return
}
var lines = FileUtil.readLines(args[0]).where { |l| l != "" }.toList
var maze = decimateHorizontally.call(lines)
solveMaze.call(maze)
var solvedLines = expandHorizontally.call(maze)
System.print(solvedLines.join("\n"))
- Output:
Using the same maze.txt as the Kotlin example:
+---+---+---+---+---+---+---+---+ | * * * * * | * * * * * | | +---+---+ * + + * +---+ * + + | | * | | * | | * * * | + + + * +---+ * + +---+ * + | | | * | * * * | * | + + + * + * +---+---+---+ * + | | | * | * * * * * * * | * | +---+ + * +---+---+---+ * + * + | | * | * * * * * | * | * | + +---+ * + * +---+ * + * + * + | | * * * | * * * | * * * | * | + + * +---+---+ * +---+---+ * + | * * * | * * * | * * * | * * * | + * +---+ * + * +---+ * + * +---+ | * * * * * | * * * * * | * * * | +---+---+---+---+---+---+---+---+
zkl
This entry parses a maze generated by http://rosettacode.org/wiki/Maze_generation#zkl and chooses random start/end points. Parsing the maze and switching between row major (h,w) and row minor (x,y) makes for some pretty vile code.
ver,hor:=make_maze(); // see above link for this code
fcn munge(a,b){ String(a[0,2],b,a[3,*]) } // "+---" --> "+-*-"
fcn solveMaze(ver,hor, a,b, u,v, w,h){
if (a==u and b==v) return(True);
sh:=hor[b][a]; sv:=ver[b][a];
hor[b][a]=munge(sh,"*"); ver[b][a]=munge(sv,"*"); // drop breadcrumb
foreach dx,dy in (T( T(0,-1),T(1,0),T(0,1),T(-1,0) )){
x:=a+dx; y:=b+dy; hy:=hor[y]; vy:=ver[y];
if ( (0<=x<w) and (0<=y<h) and // (x,y) in bounds
(dx==0 or (dx== 1 and vy[x]==" ") or // horizontal
(dx==-1 and vy[a][0]==" " and vy[x][2]!="*")) and
(dy==0 or (dy== 1 and hy[x]=="+ ") or // vertical
(dy==-1 and hor[b][a][1]==" " and hy[x][2]!="*"))
)
{
sh:=hy[x];
if(solveMaze(ver,hor, x,y, u,v, w,h)){
hy[x]=sh; // remove splat on wall but not floor while backing out
return(True);
}
}
}
hor[b][a]=sh; ver[b][a]=sv; // pick up breadcrumb when backtracking
return(False);
}
w:=(hor[0].len() - 1); h:=(hor.len() - 1);
startx:=(0).random(w); starty:=(0).random(h);
endx :=(0).random(w); endy :=(0).random(h);
sh:=hor[starty][startx];
if (not solveMaze(ver,hor, startx,starty, endx,endy, w,h))
println("No solution path found.");
else{
hor[starty][startx]=sh;
ver[starty][startx]=munge(ver[starty][startx],"S");
ver[endy][endx] =munge(ver[endy][endx],"E");
foreach a,b in (hor.zip(ver)) { println(a.concat(),"\n",b.concat()) }
}
- Output:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | | * * | * * | * * * * * * | | +---+---+ + + +---+ + + + + +---+---+---+ + + | | * | * | | * | * * | | * * * | * | S | + +---+ + + +---+---+ +---+---+ + +---+ + + + | | | * | * | * * | * | | * | * * | * | * | + + +---+ + + + + +---+ +---+ + +---+ + + | | | * | * * | * * | | * * | * | * * | * | + + +---+ +---+---+---+---+ +---+ +---+ + +---+ + | | | * * | | | * * | | * * | * * | + +---+ +---+ + + + +---+ +---+ +---+---+ +---+ | E * * | | | | | | * | * * * * * | | + +---+---+---+---+ + + + + + +---+---+---+---+ + | | | | | | | * * | | | + + + + + + + + +---+---+---+ +---+---+ + + | | | | | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
- Programming Tasks
- Games
- Recursion
- Puzzles
- 11l
- Action!
- Ada
- AutoHotkey
- BBC BASIC
- C
- C sharp
- C++
- Clojure
- D
- D examples needing attention
- Examples needing attention
- Delphi
- EasyLang
- EGL
- Emacs Lisp
- Cl-lib
- Erlang
- Frege
- Go
- Haskell
- Icon
- Unicon
- J
- Java
- JavaScript
- Julia
- Kotlin
- Mathematica
- Wolfram Language
- Nim
- Perl
- Phix
- Picat
- PicoLisp
- Prolog
- PureBasic
- Python
- Racket
- Raku
- Red
- Ruby
- Rust
- Swift
- Tcl
- Uiua
- Wren
- Wren-ioutil
- Zkl
- GUISS/Omit