This commit is contained in:
Ingy döt Net 2013-04-10 21:29:02 -07:00
parent 764da6cbbb
commit db842d013d
19005 changed files with 197040 additions and 7 deletions

View file

@ -0,0 +1,5 @@
For a maze generated by [[Maze generation|this task]], write a function 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, and a simple depth-first tree search
can be used.

View file

@ -0,0 +1,4 @@
---
category:
- Recursion
note: Games

View file

@ -0,0 +1,73 @@
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;

View file

@ -0,0 +1,86 @@
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

View file

@ -0,0 +1,50 @@
import std.stdio, std.random, std.string, std.array, std.algorithm,
std.traits;
enum int cx = 4; // Cell size x.
enum int cy = 2; // Cell size y.
enum int cx2 = cx / 2;
enum int cy2 = cy / 2;
enum char pathSymbol = '.';
struct V2 { int x, y; }
bool solveMaze(char[][] maze, in V2 s, in V2 end) pure nothrow {
if (s == end)
return true;
foreach (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] == ' ') {
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 = File("maze.txt")
.byLine()
.map!(r => r.strip().dup)()
.filter!(r => !r.empty)()
.array();
immutable int h = (maze.length - 1) / cy;
assert (h > 0);
immutable int w = (maze[0].length - 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)) {
maze[start.y][start.x] = 'S';
maze[end.y][end.x] = 'E';
writefln("%-(%s\n%)", maze);
} else
writeln("No solution path found.");
}

View file

@ -0,0 +1,262 @@
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

View file

@ -0,0 +1,73 @@
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

View file

@ -0,0 +1,163 @@
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'
}

View file

@ -0,0 +1,62 @@
#!/usr/bin/runhaskell
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)) = ' ' == (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) = substitute maze y $ substitute 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 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 = interact main'
where main' x = unlines $ fromMaybe ["can't solve"] $ solve $ lines x

View file

@ -0,0 +1,13 @@
procedure main(A)
/mh := \A[1] | 12
/mw := \A[2] | 16
mz := DisplayMaze(GenerateMaze(mh,mw))
WriteImage(mz.filename) # save file
WAttrib(mz.window,"canvas=normal") # show it
until Event() == &lpress # wait for left mouse press
Solver(mz.maze)
DisplayMazeSolution(mz)
WriteImage(mz.filename ?:= (="maze-", "maze-solved-" || tab(0)))
until Event() == &lpress # wait
close(mz.window)
end

View file

@ -0,0 +1,37 @@
procedure Solver(r,c)
static maze,h,w,rd
if type(r) == "list" then { # ------------------- Top Level (r == maze)
h := *(maze := r) # height
w := *maze[1] # width
every r := 1 to h & c := 1 to w do # remove breadcrumbs
maze[r,c] := iand(maze[r,c],NORTH+EAST+SOUTH+WEST+START+FINISH)
every ((r := 1 | h) & (c := 1 to w)) | # search perimiter
((r := 1 to h) & (c := 1 | w)) do
if iand(maze[r,c],START) > 0 then break # until start found
Solver(r,c) # recurse through maze
return 1(.maze,maze := &null) # return maze and reset
}
else # ------------------- Recurse way through maze
if iand(x := maze[r,c],SEEN) = 0 then { # in bounds and not seen?
(iand(x,FINISH) > 0, maze[r,c] +:= PATH, return ) # at finish? - done!
maze[r,c] +:= SEEN # drop bread crumb
(iand(x,NORTH) > 0, Solver(r-1,c), maze[r,c] +:= PATH, return)
(iand(x,EAST) > 0, Solver(r,c+1), maze[r,c] +:= PATH, return)
(iand(x,SOUTH) > 0, Solver(r+1,c), maze[r,c] +:= PATH, return)
(iand(x,WEST) > 0, Solver(r,c-1), maze[r,c] +:= PATH, return)
}
end
procedure DisplayMazeSolution(mz) #: draw marked PATH
&window := mz.window
maze := mz.maze
WAttrib("dx="||(dxy:=BORDER+CELL/2),"dy="||dxy)
every (r := 1 to *maze) & (c := 1 to *maze[1]) do {
if fg ~=== "blue" then Fg(fg := "blue")
if iand(maze[r,c],START) > 0 then Fg(fg := "red")
if iand(maze[r,c],PATH) > 0 then
FillCircle(x := CELL*(c-1),y := CELL*(r-1),rad := CELL/5)
}
return mz
end

View file

@ -0,0 +1,81 @@
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 NB. Monadic display copied from maze generation task
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
'+' cells } a NB. exercise, replace cells with a gerund to draw arrows on the path.
)
4 (display~ solve)@maze 9
┌ ┬───┬───┬───┬───┬───┬───┬───┬───┐
│ + │ │ │
├ ┼───┼ ┼ ┼ ┼───┼ ┼ ┼ ┤
│ + + │ │ │ │ │
├───┼ ┼───┼───┼───┼───┼───┼ ┼───┤
│ │ + + + │ + + + │ │
├ ┼───┼───┼ ┼ ┼───┼ ┼───┼───┤
│ + + │ + + +
└───┴───┴───┴───┴───┴───┴───┴───┴───┘

View file

@ -0,0 +1,149 @@
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]);
}
}

View file

@ -0,0 +1 @@
HighlightGraph[maze, PathGraph@FindShortestPath[maze, 1, 273]]

View file

@ -0,0 +1,16 @@
(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 @) " ")) ) ) )

View file

@ -0,0 +1,147 @@
:- 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).

View file

@ -0,0 +1,50 @@
# 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)

View file

@ -0,0 +1,88 @@
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
queue = []
path = nil
# Enqueue start position.
enqueue_cell queue, [], @start_x, @start_y
# Loop as long as there are cells to visit and no solution has
# been found yet.
while !queue.empty? && !path
path = solve_visit_cell queue
end
# Clean up.
reset_visiting_state
puts "No solution found?!" unless path
# Mark the cells that make up the shortest path.
for x, y in path
@path[y][x] = true
end
end
private
# Maze solving visiting method.
def solve_visit_cell(queue)
# 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?
if x == @end_x && y == @end_y
# Yes, we have!
return path
end
# Mark cell as visited.
@visited[y][x] = true
# Left
new_x = x - 1
if move_valid?(new_x, y) && !@vertical_walls[y][new_x]
enqueue_cell queue, path, new_x, y
end
# Right
new_x = x + 1
if move_valid?(new_x, y) && !@vertical_walls[y][x]
enqueue_cell queue, path, new_x, y
end
# Top
new_y = y - 1
if move_valid?(x, new_y) && !@horizontal_walls[new_y][x]
enqueue_cell queue, path, x, new_y
end
# Bottom
new_y = y + 1
if move_valid?(x, new_y) && !@horizontal_walls[y][x]
enqueue_cell queue, path, x, new_y
end
# No solution yet.
return nil
end
# Enqueue a new coordinate to visit.
def enqueue_cell(queue, path, x, y)
# Copy the current path, add the new coordinates and enqueue
# the new path.
path = path.dup
path << [x, y]
queue << path
end
end
# Demonstration:
maze = Maze.new 20, 10
maze.solve
maze.print

View file

@ -0,0 +1,56 @@
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]