1: ## Yes, I know it's been done before.  But has it been done this way?
   2: 
   3: # maze solver - solves a maze (shortest path) from a file or stdin
   4: #
   5: # Takes mazes with their headers in the format:
   6: # Maze #. 1,1-8,8
   7: # (without the initial '# ')
   8: # (grid with # being wall and . being floor, entrance at (1,1), exit at (8,8))
   9: #
  10: # Note that movement cannot occur diagonally, and that the maze wraps
  11: # around, so that the top is adjacent to the bottom and the left side
  12: # is adjacent to the right.  Put walls there if you don't want that.
  13: # The top left corner is (0, 0).
  14: # Also note that in the maze file, all whitespace is ignored.
  15: #
  16: # Prints out the route in the format (row, col) (row, col) ... inclusive of entrance and exit.
  17: # If no route exists, exits with code 1 and prints "No route found."
  18: 
  19: #!/usr/bin/perl -w
  20: use strict;
  21: 
  22: my (@maze, $wall, $floor, @in, @out);
  23: 
  24: chomp ($_ = <>);
  25: tr/ \n\t\f//d;
  26: m<^Maze(.)(.)(\d+),(\d+)-(\d+),(\d+)$> ?
  27:     (($wall, $floor, @in[0,1], @out[0,1]) =
  28:      ($1, $2, $3, $4, $5, $6)) : die "Invalid maze header; stopped";
  29: 
  30: while (<>)
  31: {
  32:     chomp;
  33:     tr/ \t\n\f//d;
  34:     my @F = split//;
  35:     push @maze, [ ];
  36:     foreach (@F)
  37:     {
  38: 	($_ eq $wall) and push @ {$maze[-1]}, [ 0, 0 ];
  39: 	($_ eq $floor) and push @ {$maze[-1]}, [ 1, 0 ];
  40: 	($_ ne $wall and
  41: 	 $_ ne $floor) and die "Unrecognized character: \'$_\'; stopped";
  42:     }
  43: }
  44: 
  45: (grep { scalar @$_ != scalar @ {$maze[0]} } @maze) and
  46:     die "Number of columns not the same in every row; stopped";
  47: 
  48: my ($rows, $cols) = (scalar @maze, scalar @ {$maze[0]});
  49: $maze[$in[0]][$in[1]][0] = 0;
  50: $maze[$in[0]][$in[1]][1] = 1;
  51: 
  52: for (;;)
  53: {
  54:     if ($maze[$out[0]][$out[1]][1])
  55:     {
  56: 	my @sol = ();
  57: 	my (@c) = ($out[0], $out[1]);
  58: 	while ($c[0] != $in[0] or $c[1] != $in[1])
  59: 	{
  60: 	    push @sol, [ @c ];
  61: 	    (@c) = ($maze[$c[0]][$c[1]][1][0],
  62: 		    $maze[$c[0]][$c[1]][1][1]);
  63: 	}
  64: 	push @sol, [ $in[0], $in[1] ];
  65: 	@sol = reverse @sol;
  66: 	foreach (@sol)
  67: 	{
  68: 	    print "($$_[0], $$_[1]) ";
  69: 	}
  70: 	print "\n";
  71: 	exit 0;
  72:     }
  73: 	
  74:     my ($any) = (0);
  75:     
  76:     foreach my $row (0..($rows-1))
  77:     {
  78: 	foreach my $col (0..($cols-1))
  79: 	{
  80: 	    next if $maze[$row][$col][0];
  81: 	    next unless $maze[$row][$col][1];
  82: 
  83: 	    foreach my $rc ([ $row, $col+1 ],
  84: 			    [ $row+1, $col ],
  85: 			    [ $row, $col-1 ],
  86: 			    [ $row-1, $col ])
  87: 	    {
  88: 		$maze[$$rc[0]][$$rc[1]][0] && !$maze[$$rc[0]][$$rc[1]][1]
  89: 		    and ($maze[$$rc[0]][$$rc[1]][0] = 0,
  90: 			 $maze[$$rc[0]][$$rc[1]][1] = [ $row, $col ],
  91: 			 $any = 1);
  92: 	    }
  93: 	}
  94:     }
  95:     if ($any == 0)
  96:     {
  97: 	print "No route found.\n";
  98: 	exit 1;
  99:     }
 100: }

Replies are listed 'Best First'.
Re: shortest-path maze solver
by TGI (Parson) on Nov 20, 2001 at 11:04 UTC

    In one of my hippy college CS classes, we had to write a maze solver. I did a cute trick to make it easier to handle backtracking.

    The trck did not lie in the program, but in the maze encoding. Assign a prime number to each direction that you might want a maze walker to go. Then each square gets the product of the directions that are available.

    Backtracking is easy in this case, you just start at your highest (or lowest) possible factor, and work your way down the list. Plus you can define an infinite number of directions for your maze to turn ;)

    If up is 2, right is 3, down 5 and left 7, your maze above would be:

    Initial reduction:
     15, 21,105, 21,105, 21, 35
      2,  0, 10,  0, 10,  0, 10
      0,  0, 30, 21, 70,  0, 10
      3,105, 14,  0, 30, 21, 14
      0, 10,  0,  0,  0, 10,  0
      0, 30, 21,  7,  0, 30, 35
      0,  0,  0,  0,  0,  0,  2
    
    Remove any rows w/ only verticals:
    15, 21,105, 21,105, 21, 35
     0,  0, 30, 21, 70,  0, 10
     3,105, 14,  0, 30, 21, 14
     0, 30, 21,  7,  0, 30, 35
    
    Remove any columns w/ only horizontals:
    15, 21,105,105, 21, 35
     0,  0, 30, 70,  0, 10
     3,105, 14, 30, 21, 14
     0, 30, 21,  0, 30, 35
    

    I think I spent to much time on Godel's proof!


    TGI says moo

Re: shortest-path maze solver
by premchai21 (Curate) on Sep 24, 2001 at 22:53 UTC
    Sample run. Input:
    Maze#.1,1-7,7 ######### #.......# #.#.#.#.# ###...#.# #...#...# ##.###.## ##...#..# #######.# #########
    Trailing newlines will confuse the noded code -- later I added a test for empty lines. Outputs:
    (1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (2, 5) (3, 5) (4, 5) (4, 6) (5, 6) +(6, 6) (6, 7) (7, 7)
    which corresponds to the path marked in ?:
    ######### #?????..# #.#.#?#.# ###..?#.# #...#??.# ##.###?## ##...#??# #######?# #########
    which is indeed the shortest path from (1, 1) to (7, 7). Note that there are walls around the maze; that is because the universe is toroidal in shape, that is, (0, 0) is next to (0, -1) which is the same (in this case) as (0, 8).

    The maze header is simply "Maze", followed by the character for walls, the character for floors, entrance row, comma, entrance column, comma, dash, exit row, comma, exit column, newline.