Hy,

here is my solution. First of all thanks for this nice quiz. After  
Knight's Travails and Barrel of Monkeys another "search the shortest path"  
quiz, so once again a nice solution to this quiz is Dijkstra's shortest  
path algorithm.
I have to admit, that I cheated a bit: I googled for the maze generation  
part and found the following page:

http://www.mazeworks.com/mazegen/mazetut/

It explains the perfect maze generation algorithm quite nice.

I solved the 1st bonus part, I didn't try the 2nd part, but I think  
searching for the most turns might be quite expensive.

Dominik

Usage:
ruby maze.rb [-l] width height [from [to]]

if -l is given, it will search for the longest shortest path and print it.
if form or to are ommitted then random positions will be used.

Examples:
$ ruby maze.rb 10 10
$ ruby maze.rb -l 10 10
$ ruby maze.rb 10 10 0,0 9,9
$ ruby maze.rb 10 10 _ 5,5   # random "from"

Complete Example:
$ ruby maze.rb -l 8 8 0,0 7,7
Maze:
+---+---+---+---+---+---+---+---+
|           |                   |
+---+---+   +   +---+---+---+   +
|           |   |       |       |
+   +---+---+   +---+   +   +---+
|           |       |   |       |
+---+---+   +---+   +   +---+   +
|   |       |   |   |   |       |
+   +   +---+   +   +   +   +   +
|   |   |               |   |   |
+   +   +---+---+---+---+   +   +
|       |           |       |   |
+   +---+   +---+   +   +---+   +
|       |   |   |   |   |   |   |
+---+   +   +   +   +   +   +   +
|           |           |       |
+---+---+---+---+---+---+---+---+

Shortest path from [0, 0] to [7, 7]:
+---+---+---+---+---+---+---+---+
| X   X   X |                   |
+---+---+   +   +---+---+---+   +
| X   X   X |   |       |       |
+   +---+---+   +---+   +   +---+
| X   X   X |       |   |       |
+---+---+   +---+   +   +---+   +
|   | X   X |   |   |   | X   X |
+   +   +---+   +   +   +   +   +
|   | X |               | X | X |
+   +   +---+---+---+---+   +   +
| X   X | X   X   X | X   X | X |
+   +---+   +---+   +   +---+   +
| X   X | X |   | X | X |   | X |
+---+   +   +   +   +   +   +   +
|     X   X |     X   X |     X |
+---+---+---+---+---+---+---+---+

Longest shortest path (from [0, 0] to [4, 1]:
+---+---+---+---+---+---+---+---+
| X   X   X | X   X   X   X   X |
+---+---+   +   +---+---+---+   +
| X   X   X | X | X   X | X   X |
+   +---+---+   +---+   +   +---+
| X   X   X | X   X | X | X   X |
+---+---+   +---+   +   +---+   +
|   | X   X |   | X | X | X   X |
+   +   +---+   +   +   +   +   +
|   | X |         X   X | X |   |
+   +   +---+---+---+---+   +   +
| X   X | X   X   X | X   X |   |
+   +---+   +---+   +   +---+   +
| X   X | X |   | X | X |   |   |
+---+   +   +   +   +   +   +   +
|     X   X |     X   X |       |
+---+---+---+---+---+---+---+---+

The performance is quite well:
$ time ruby maze.rb 30 30 > /dev/null

real    0m0.214s
user    0m0.179s
sys     0m0.008s

$ time ruby maze.rb -l 30 30 > /dev/null

real    0m4.068s
user    0m3.946s
sys     0m0.022s

$ time ruby maze.rb 100 100 > /dev/null

real    0m1.033s
user    0m0.992s
sys     0m0.011s


==============================================
maze.rb:

class Hash
       # find the key for with the smallest value, delete it and return it
       def delete_min_value
             return nil if empty?
             minkey=min=nil
             each { |k, v|
                   min, minkey=v, k if !min || v<min
             }
             delete(minkey)
             minkey
       end
end

# Maze represents the maze ;-)
#
# Cells/positions in the maze are represented by Numbers (from 0 to w*h-1),
# each position corresponds to x/y coordinates, you can convert between
# positions and coordinates by coord2pos and pos2coord.
#
# The walls for each position are stored in the String @data. The walls for
# position p are stored in the first two bits of @data[p], the other bits  
are
# unused. If bit one is set then p has a north wall, if bit two is set  
then p
# has a west wall.
#
# Maze#generate generates a (random) maze using the method described at
# http://www.mazeworks.com/mazegen/mazetut/
#
# Maze#shortest_path uses Dijkstra's shortest path algorithm, so it can not
# anly find shortest pathes in perfect mazes, but also in mazes where  
different
# pathes between two position exist.

class Maze
       attr_reader :w, :h # width, height

       def initialize(w, h)
             @w, @h=[w, 1].max, [h, 1].max
             @wh=@w*@h
             @neighbors_cache={}
             set_all_walls
       end

       def set_all_walls
             # set all bits
             @data=3.chr * (@wh)
             nil
       end
       def clear_all_walls
             # all except outer border
             @data=0.chr * (@wh)
             # set north walls of row 0
             w.times { |i| @data[i] |= 1 }
             # set west walls of col 0
             h.times { |i| @data[i*w] |= 2 }
             nil
       end

       # positions in path will be printed as "X"
       def to_s(path=[])
             ph={}
             path.each { |i| ph[i]=true }
             res=""
             h.times { |y|
                   w.times { |x|
                         res << "+" << ((@data[y*w+x] & 1 > 0) ? "---" :  
"   ")
                   }
                   res << "+\n"
                   w.times { |x|
                         res << ((@data[y*w+x] & 2 > 0) ? "|" : " ")
                         res << (ph[y*w+x] ? " X " : "   ")
                   }
                   res << "|\n"
             }
             res << ("+---"*w) << "+"
       end
       def inspect
             "#<#{self.class.name} #{w}x#{h}>"
       end

       # maze positions are cell indices from 0 to w*h-1
       # the following functions do conversions to and from coordinates
       def coord2pos(x, y)
             (y%h)*w+(x%w)
       end
       def pos2coord(p)
             [p%w, (p/w)%h]
       end

       # returns valid neighbors to p, doesn't care about walls
       def neighbors(p)
             if ce=@neighbors_cache[p]; return ce; end
             res=[p-w, p+w]
             res << p-1 if p%w > 0
             res << p+1 if p%w < w-1
             @neighbors_cache[p] = res.find_all { |t| t>=0 && t<@wh }
       end

       def wall_between?(p1, p2)
             p1, p2=[p1, p2].sort
             if p2-p1==w # check north wall of p2
                   @data[p2] & 1 > 0
             elsif p2-p1==1 # check west wall of p2
                   @data[p2] & 2 > 0
             else
                   false
             end
       end
       def set_wall(p1, p2)
             p1, p2=[p1, p2].sort
             if p2-p1==w # set north wall of p2
                   @data[p2] |= 1
             elsif p2-p1==1 # set west wall of p2
                   @data[p2] |= 2
             end
             nil
       end
       def unset_wall(p1, p2)
             p1, p2=[p1, p2].sort
             if p2-p1==w # unset north wall of p2
                   @data[p2] &= ~1
             elsif p2-p1==1 # unset west wall of p2
                   @data[p2] &= ~2
             end
             nil
       end

       # generate a (random) perfect maze
       def generate(random=true)
             set_all_walls
             # (random) depth first search method
             visited={0 => true}
             stack=[0]
             until stack.empty?
                   n=neighbors(stack.last).reject { |p| visited[p] }
                   if n.empty?
                         stack.pop
                   else
                         # choose one unvisited neighbor
                         np=n[random ? rand(n.size) : 0]
                         unset_wall(stack.last, np)
                         visited[np]=true
                         # if all neighbors are visited then here is
                         # nothing left to do
                         stack.pop if n.size==1
                         stack.push np
                   end
             end
             self
       end

       # central part of Dijkstra's shortest path algorithm:
       # returns a hash that associates each reachable (from start) position
       # p, with the previous position on the shortest path from start to p
       # and the length of that path.
       # example: if the shortest path from 0 to 2 is [0, 1, 2], then
       # prev[2]==[1, 2], prev[1]==[0, 1] and prev[0]==[nil, 0].
       # so you can get all shortest paths from start to each reachable
       # position out of the returned hash.
       # if stop_at!=nil the method stops when the previous cell on the
       # shortest path from start to stop_at is found.
       def build_prev_hash(start, stop_at=nil)
             prev={start=>[nil, 0]} # hash to be returned
             return prev if stop_at==start
             # positions which we have seen, but we are not yet sure about
             # the shortest path to them (the value is length of the path,
             # for delete_min_value):
             active={start=>0}
             until active.empty?
                   # get the position with the shortest path from the
                   # active list
                   cur=active.delete_min_value
                   return prev if cur==stop_at
                   newlength=prev[cur][1]+1 # path to cur length + 1
                   # for all reachable neighbors of cur, check if we found
                   # a shorter path to them
                   neighbors(cur).each { |n|
                         # ignore unreachable
                         next if wall_between?(cur, n)
                         if old=prev[n] # was n already visited
                               # if we found a longer path, ignore it
                               next if newlength>=old[1]
                         end
                         # (re)add new position to active list
                         active[n]=newlength
                         # set new prev and length
                         prev[n]=[cur, newlength]
                   }
             end
             prev
       end

       def shortest_path(from, to)
             prev=build_prev_hash(from, to)
             if prev[to]
                   # path found, build it by following the prev hash from
                   # "to" to "from"
                   path=[to]
                   path.unshift(to) while to=prev[to][0]
                   path
             else
                   nil
             end
       end

       # finds the longest shortest path in this maze, only works if there  
is
       # at least one position that can only reach one neighbor, because we
       # search only starting at those positions.
       def longest_shortest_path
             startp=endp=nil
             max=-1
             @wh.times { |p|
                   # if current p can only reach 1 neighbor
                   if neighbors(p).reject { |n| wall_between?(p, n)  
}.size==1
                         prev=build_prev_hash(p)
                         # search longest path from p
                         tend, tmax=nil, -1
                         prev.each { |k, v|
                               if v[1]>tmax
                                     tend=k
                                     tmax=v[1]
                               end
                         }
                         if tmax>max
                               max=tmax
                               startp, endp=p, tend
                         end
                   end
             }
             if startp # path found
                   shortest_path(startp, endp)
             else
                   nil
             end
       end
end

if $0 == __FILE__
       ARGV.shift if search_longest=ARGV[0]=="-l"
       w, h, from, to=ARGV
       m=Maze.new(w.to_i, h.to_i)
       m.generate
       puts "Maze:", m.to_s
       if from=~/(\d+),(\d+)/
             p1=m.coord2pos($1.to_i, $2.to_i)
       else
             p1=rand(m.w*m.h)
       end
       if to=~/(\d+),(\d+)/
             p2=m.coord2pos($1.to_i, $2.to_i)
       else
             p2=rand(m.w*m.h)
       end

       path=m.shortest_path(p1, p2)
       puts "\nShortest path from #{m.pos2coord(p1).inspect} to " \
       "#{m.pos2coord(p2).inspect}:", m.to_s(path)

       if search_longest
             path=m.longest_shortest_path
             puts "\nLongest shortest path (from " \
             "#{m.pos2coord(path[0]).inspect} to " \
             "#{m.pos2coord(path[-1]).inspect}:",
             m.to_s(path)
       end
end