4

Here's an interesting problem to solve in minimal amounts of code. I expect the recursive solutions will be most popular.

We have a maze that's defined as a map of characters, where = is a wall, a space is a path, + is your starting point, and # is your ending point. An incredibly simple example is like so:

====
+  =
= ==
=  #
====

Can you write a program to find the shortest path to solve a maze in this style, in as little code as possible?

Bonus points if it works for all maze inputs, such as those with a path that crosses over itself or with huge numbers of branches. The program should be able to work for large mazes (say, 1024x1024 - 1 MB), and how you pass the maze to the program is not important.

The "player" may move diagonally. The input maze will never have a diagonal passage, so your base set of movements will be up, down, left, right. A diagonal movement would be merely looking ahead a little to determine if a up/down and left/right could be merged.

Output must be the maze itself with the shortest path highlighted using the asterisk character (*).

false
  • 10,264
  • 13
  • 101
  • 209
Matthew Iselin
  • 10,400
  • 4
  • 51
  • 62
  • Good question but looks pretty challenging... – RCIX Aug 25 '09 at 06:19
  • Can the player (+) move diagonally or only horizontal/vertical? – Alex Aug 25 '09 at 06:20
  • The player may move diagonally, which should make things just a little bit easier. – Matthew Iselin Aug 25 '09 at 06:22
  • 'The program should be able to work for any size maze' - that isn't reasonable given that computers have a finite amount of memory. – Chi Aug 25 '09 at 06:27
  • 3
    How do you change your weapon? – Alex Aug 25 '09 at 06:29
  • @Alex, was that comment meant for my (non-)answer? – paxdiablo Aug 25 '09 at 06:33
  • @Brian: Yeah, I'm realising that now. Looks like Tuesday afternoon, right after a long day at work, isn't the best choice of time to write a question which needs some thought. – Matthew Iselin Aug 25 '09 at 06:44
  • 3
    I like code-golf problems, but this is way too underspecified. As it stands, I think perhaps (printfn "I found the shortest path!") would be a valid answer. – Brian Aug 25 '09 at 06:45
  • 2
    Why are there four close votes? There are a million other code golf questions, and this one is pretty damn interesting, so don't vote to close it just because the specifications are a little rough. And if you just don't like code golf, well - wait! What's that? Ssh, I hear something... Is that...? Yes! It is! It is the Waambulance. They've heard your whining and have come to rescue you! – Chris Lutz Aug 25 '09 at 06:49
  • I was one who voted to close, @Chris, as I do all code golf questions (and despite the fact I answered humorously (I hope)). While they're sometimes fun, I don't agree they have a place on SO, long term. Others disagree (and I only get one vote, just like everyone else) but it's my right to use my vote as I see fit. Just like it's your right to vote to reopen if it gets closed down. – paxdiablo Aug 25 '09 at 07:00
  • Very few of the code golf ones get closed anyway, I'm surprised this one got to 4 votes (and I suspect it'll be opened again shortly after that). – paxdiablo Aug 25 '09 at 07:02
  • 1
    I respectfully disagree, Pax. (And fear not, your humor is not lost on me.) If you have a community (and SO is definitely meant to have a community), you are going to have some forms of community activities, and I think code golf is a good way to funnel the urge to ask "social" questions into a healthy, on-topic task, so even if it may not strictly "fit the bill," I think they are a) inevitable, and b) better (closer to the intention of SO) than a lot of other questions of this type. Also, I wanted to say Waambulance. It's a favorite word of mine. So no harm meant. – Chris Lutz Aug 25 '09 at 07:07
  • I wasn't offended, Chris, and it's good that there's disagreement, otherwise it'd be pretty boring here. I'd probably go over and taunt people on Yahoo Answers or Slashdot if that were the case :-) – paxdiablo Aug 25 '09 at 07:23
  • Yahoo Answers makes me laugh. I need to shut up and come up with a solution already, though. – Chris Lutz Aug 25 '09 at 07:30
  • I'm with Pax in *theory*. Code golf isn't really within the remit of StackOverflow. But this naughty little indulgence goes back quite a long way (437 or 10118 depending on how you count) and has pretty wide acceptance (as evidenced by how few such questions get closed), so as long as we don't face a plague of them I'm leaving them alone. That said, I think CW is mandatory. – dmckee --- ex-moderator kitten Aug 25 '09 at 14:26
  • 1
    This challenge is framed incredibly poorly, even after all of the Updates. The "diagonal" answer goes in my folder of answers that are worse than no answer at all (FYI: correct answer should have been just "no"). "Able to work for large mazes" is a meaningless and confusing addition. Finally, -1 for slanting the few undersatndable clarifications to console stream languages, which already have all of the advantages for Code Golf. – RBarryYoung Aug 28 '09 at 20:12
  • @RBarryYoung: I agree - the idea was right, but I absolutely destroyed it by not thinking it through enough and just putting it up here. – Matthew Iselin Aug 29 '09 at 00:09
  • 1
    Should be moved to: http://codegolf.stackexchange.com/ – John Riselvato Sep 15 '14 at 15:55

4 Answers4

8

Works for any (fixed-size) maze with a minimum of CPU cycles (given a big enough BFG2000). Source size is irrelevant since the compiler is incredibly efficient.

while curr.x != target.x and curr.y != target.y:
    case:
        target.x > curr.x : dx =  1
        target.x < curr.x : dx = -1
        else              : dx =  0
    case:
        target.y > curr.y : dy =  1
        target.y < curr.y : dy = -1
        else              : dy =  0
    if cell[curr.x+dx,curr.y+dy] == wall:
        destroy cell[curr.x+dx,curr.y+dy] with patented BFG2000 gun.
   curr.x += dx
   curr.y += dy
survey shattered landscape
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 2
    +1, just because it'd be awesome if this was possible. I guess this is how the Red Faction devs would look at solving mazes? – Matthew Iselin Aug 25 '09 at 06:31
7

F#, not very short (72 non-blank lines), but readable. I changed/honed the spec a bit; I assume the original maze is a rectangle fully surrounded by walls, I use different characters (that don't hurt my eyes), I only allow orthogonal moves (not diagonal). I only tried one sample maze. Except for a bug about flipping x and y indicies, this worked the first time, so I expect it is right (I've done nothing to validate it other than eyeball the solution on the one sample I gave it).

open System

[<Literal>]
let WALL  = '#'
[<Literal>]
let OPEN  = ' '
[<Literal>]
let START = '^'
[<Literal>]
let END   = '$'
[<Literal>]
let WALK  = '.'

let sampleMaze = @"###############
#  # #        #
# ^# # # ###  #
#  # # # # #  #
#      #   #  #
############  #
#    $        #
###############"

let lines = sampleMaze.Split([|'\r';'\n'|], StringSplitOptions.RemoveEmptyEntries)
let width = lines |> Array.map (fun l -> l.Length) |> Array.max 
let height = lines.Length 
type BestInfo = (int * int) list * int // path to here, num steps
let bestPathToHere : BestInfo option [,] = Array2D.create width height None

let mutable startX = 0
let mutable startY = 0
for x in 0..width-1 do
    for y in 0..height-1 do
        if lines.[y].[x] = START then
            startX <- x
            startY <- y
bestPathToHere.[startX,startY] <- Some([],0)

let q = new System.Collections.Generic.Queue<_>()
q.Enqueue((startX,startY))
let StepTo newX newY (path,count) =
    match lines.[newY].[newX] with
    | WALL -> ()
    | OPEN | START | END -> 
        match bestPathToHere.[newX,newY] with
        | None ->
            bestPathToHere.[newX,newY] <- Some((newX,newY)::path,count+1)
            q.Enqueue((newX,newY))
        | Some(_,oldCount) when oldCount > count+1 ->
            bestPathToHere.[newX,newY] <- Some((newX,newY)::path,count+1)
            q.Enqueue((newX,newY))
        | _ -> ()
    | c -> failwith "unexpected maze char: '%c'" c
while not(q.Count = 0) do
    let x,y = q.Dequeue()
    let (Some(path,count)) = bestPathToHere.[x,y]
    StepTo (x+1) (y) (path,count)
    StepTo (x) (y+1) (path,count)
    StepTo (x-1) (y) (path,count)
    StepTo (x) (y-1) (path,count)

let mutable endX = 0
let mutable endY = 0
for x in 0..width-1 do
    for y in 0..height-1 do
        if lines.[y].[x] = END then
            endX <- x
            endY <- y

printfn "Original maze:"
printfn "%s" sampleMaze
let bestPath, bestCount = bestPathToHere.[endX,endY].Value
printfn "The best path takes %d steps." bestCount
let resultMaze = Array2D.init width height (fun x y -> lines.[y].[x])
bestPath |> List.tl |> List.iter (fun (x,y) -> resultMaze.[x,y] <- WALK)
for y in 0..height-1 do
    for x in 0..width-1 do
        printf "%c" resultMaze.[x,y]
    printfn ""

//Output:
//Original maze:
//###############
//#  # #        #
//# ^# # # ###  #
//#  # # # # #  #
//#      #   #  #
//############  #
//#    $        #
//###############
//The best path takes 27 steps.
//###############
//#  # #....... #
//# ^# #.# ###. #
//# .# #.# # #. #
//# .....#   #. #
//############. #
//#    $....... #
//###############
Brian
  • 117,631
  • 17
  • 236
  • 300
6

Python

387 Characters

Takes input from stdin.

import sys
m,n,p=sys.stdin.readlines(),[],'+'
R=lambda m:[r.replace(p,'*')for r in m]
while'#'in`m`:n+=[R(m)[:r]+[R(m)[r][:c]+p+R(m)[r][c+1:]]+R(m)[r+1:]for r,c in[(r,c)for r,c in[map(sum,zip((m.index(filter(lambda i:p in i,m)[0]),[w.find(p)for w in m if p in w][0]),P))for P in zip((-1,0,1,0),(0,1,0,-1))]if 0<=r<len(m)and 0<=c<len(m[0])and m[r][c]in'# ']];m=n.pop(0)
print''.join(R(m))
Steve Losh
  • 19,642
  • 2
  • 51
  • 44
0

I did this sort of thing for a job interview once (it was a pre-interview programming challenge)

Managed to get it working to some degree of success and it's a fun little challenge.

djhworld
  • 6,726
  • 4
  • 30
  • 44