Search code examples
ralgorithmpath-finding

How do I get the shortest route in a labyrinth?


I want to make a code giving the shortest route when given a labyrinth as a matrix.

enter image description here

In this case, the matrix representation of this labyrinth is as follows.

## [,1] [,2] [,3] [,4]
## [1,] 2 0 0 0
## [2,] 1 1 0 1
## [3,] 0 1 0 0
## [4,] 1 1 1 3
 , where  0 denotes inaccessible points, 1 denotes accessible points.
          2 denotes the starting point, and 3 denotes the destination.

And, the desired result is this : c(4,1,4,4,1,1), where 1 denotes East, 2 denotes North, 3 denotes West, and 4 denotes South.

I guess that one possible code might be a function giving the shortest route as a vector when it is given the matrix representation of a labyrinth.

In addition to this case, I want to know if the coverage could be extended to general cases, though it seems rather redundant. I would like to know whether a desirable code can be made so that it covers arbitrary n by m size matrix, although just 4 by 4 case suffices. And I wonder if the start point and the destination could be located at arbitrary points other than vertices, though vertices case is sufficient.


Solution

  • You could construct a graph to represent the valid moves between positions in the matrix:

    # Construct nodes and edges from matrix
    (nodes <- which(m == 1 | m == 2 | m == 3, arr.ind=TRUE))
    #       row col
    #  [1,]   1   1
    #  [2,]   2   1
    #  [3,]   4   1
    #  [4,]   2   2
    #  [5,]   3   2
    #  [6,]   4   2
    #  [7,]   4   3
    #  [8,]   2   4
    #  [9,]   4   4
    edges <- which(outer(seq_len(nrow(nodes)),seq_len(nrow(nodes)), function(x, y) abs(nodes[x,"row"] - nodes[y,"row"]) + abs(nodes[x,"col"] - nodes[y,"col"]) == 1), arr.ind=T)
    (edges <- edges[edges[,"col"] > edges[,"row"],])
    #      row col
    # [1,]   1   2
    # [2,]   2   4
    # [3,]   4   5
    # [4,]   3   6
    # [5,]   5   6
    # [6,]   6   7
    # [7,]   7   9
    
    library(igraph)
    g <- graph.data.frame(edges, directed=FALSE, vertices=seq_len(nrow(nodes)))
    

    Then you could solve the shortest path problem between the specified start and end location:

    start.pos <- which(m == 2, arr.ind=TRUE)
    start.node <- which(paste(nodes[,"row"], nodes[,"col"]) == paste(start.pos[,"row"], start.pos[,"col"]))
    end.pos <- which(m == 3, arr.ind=TRUE)
    end.node <- which(paste(nodes[,"row"], nodes[,"col"]) == paste(end.pos[,"row"], end.pos[,"col"]))
    (sp <- nodes[get.shortest.paths(g, start.node, end.node)$vpath[[1]],])
    #      row col
    # [1,]   1   1
    # [2,]   2   1
    # [3,]   2   2
    # [4,]   3   2
    # [5,]   4   2
    # [6,]   4   3
    # [7,]   4   4
    

    Finally, you could determine the direction (1: east; 2: north; 3: west; 4: south) as a simple manipulation of the final set of nodes selected:

    dx <- diff(sp[,"col"])
    dy <- -diff(sp[,"row"])
    (dirs <- ifelse(dx == 1, 1, ifelse(dy == 1, 2, ifelse(dx == -1, 3, 4))))
    # [1] 4 1 4 4 1 1
    

    This code will work for arbitrarily sized input matrices.

    Data:

    (m <- matrix(c(2, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 3), nrow=4))
    #      [,1] [,2] [,3] [,4]
    # [1,]    2    0    0    0
    # [2,]    1    1    0    1
    # [3,]    0    1    0    0
    # [4,]    1    1    1    3