This commit is contained in:
Ingy döt Net 2013-10-27 22:24:23 +00:00
parent 6f050a029e
commit 776bba907c
3887 changed files with 59894 additions and 7280 deletions

View file

@ -1,11 +1,9 @@
import std.stdio, std.random, std.string, std.array, std.algorithm,
std.traits;
std.file;
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 = '.';
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 {
@ -25,15 +23,10 @@ bool solveMaze(char[][] maze, in V2 s, in V2 end) pure nothrow {
}
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;
auto maze = "maze.txt".readText.splitLines.map!(r => r.dup).array;
immutable int h = ((cast(int)maze.length) - 1) / cy;
assert (h > 0);
immutable int w = (maze[0].length - 1) / cx;
immutable int w = ((cast(int)maze[0].length) - 1) / cx;
immutable start = V2(cx2 + cx * uniform(0, w),
cy2 + cy * uniform(0, h));
@ -41,10 +34,9 @@ void main() {
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.");
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);
}

View file

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

View file

@ -0,0 +1,125 @@
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)) # 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, path, val)
method getPath(); return path; 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 | (*path <= *bestMouse.path) then { # Keep going?
mark(maze, r,c)
unlock(region[r,c])
return Position(r,c)
}
}
end
initially (m, l, p)
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
val := maze[loc.r,loc.c] | fail # Fail if outside maze
/path := []
if \p then path := copy(p)
put(path, loc)
insert(qMice, self)
thread {
if atEnd() then {
critical bestMouseLock:
if /bestMouse | (*path < bestMouse.path) then bestMouse := self
}
else { # Spawn more mice to look for finish
QMouse(maze, goNorth(), path)
QMouse(maze, goSouth(), path)
QMouse(maze, goEast(), path)
QMouse(maze, goWest(), path)
}
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 \bestMouse then { # Mark it in maze
every p := !bestMouse.path do maze[p.r,p.c] +:= PATH
return
}
end
procedure waitForCompletion()
critical qMiceEmpty: while *qMice > 0 do wait(qMiceEmpty)
end

View file

@ -0,0 +1,77 @@
#!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";
}

View file

@ -3,82 +3,69 @@ class Maze
# 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
# Enqueue start position.
@queue = []
enqueue_cell([], @start_x, @start_y)
# Mark the cells that make up the shortest path.
for x, y in path
@path[y][x] = true
# 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(queue)
def solve_visit_cell
# Get the next path.
path = queue.shift
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
return path if x == @end_x && y == @end_y
# Mark cell as visited.
@visited[y][x] = true
@visited[x][y] = true
# Left
new_x = x - 1
if move_valid?(new_x, y) && !@vertical_walls[y][new_x]
enqueue_cell queue, path, new_x, y
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
# 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
nil # No solution yet.
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
def enqueue_cell(path, x, y)
# Add new coordinates to the current path and enqueue the new path.
@queue << path + [[x, y]]
end
end