I was looking to solve this with dynamic programming to avoid the exponential time complexity but failed to do so. However, here is a dense version in Javascript which uses DFS (which given this problem is the same as brute-forcing since we are interested in all possible paths) and recursion in a functional style with es6 features. We also make use of a single-dimension array to represent the board with the directions
method accounting for which positions are reachable from a given position. The single-dimension array contains the rows of the board laid out sequentially from top to bottom.
[ 0 1 2 ]
[ 3 4 5 ] => [0, 1, 2, 3, 4, 5, 6, 7, 8]
[ 6 7 8 ]
The algorithm works for any m
by n
grid and so we must specify the width as an input parameter.
const directions = (pos, board, width) => {
const [n, s, w, e] = [pos - width, pos + width, pos - 1, pos + 1]
return [
n >= 0 && board[n] ? n : null,
s < board.length && board[s] ? s : null,
pos % width !== 0 && board[w] ? w : null,
e % width !== 0 && board[e] ? e : null
]
.filter(pos => pos !== null)
}
const solve = (pos, board, width, path) => {
const next = directions(pos, board, width)
return next.length === 0 ?
[[...path, pos]] // have a complete path
:
next
.map(nextPos => solve(nextPos, [...board.slice(0, pos), 0, ...board.slice(pos + 1)], width, [...path, pos]))
.flat()
}
const oneDim2TwoDim = (oneDimCoord, width) =>
[Math.floor(oneDimCoord / width), oneDimCoord % width]
const res = solve(4, [1, 1, 1, 1, 1, 0, 1, 1, 1], 3, [])
console.log(res.map(path => path.map(step => oneDim2TwoDim(step, 3))))
At each step, we check which directions are possible to go in. If no direction is available, we return the current path, otherwise we make a recursive call in each of the directions.
Use it like
solve(4, [1, 1, 1, 1, 1, 0, 1, 1, 1], 3, [])
/*
[
[ 4, 1, 0, 3, 6, 7, 8],
[ 4, 1, 2 ],
[ 4, 7, 6, 3, 0, 1, 2],
[ 4, 7, 8 ],
[ 4, 3, 0, 1, 2 ],
[ 4, 3, 6, 7, 8 ]
]
*/
Each path starts with the starting position and you can easily convert it back to (x, y)
coordinate style with
const oneDim2TwoDim = (oneDimCoord, width) =>
[Math.floor(oneDimCoord / width), oneDimCoord % width]
const res = solve(4, [1, 1, 1, 1, 1, 0, 1, 1, 1], 3, [])
.map(path => path.map(step => oneDim2TwoDim(step, 3)))
/*
[
[ [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 1, 0 ], [ 2, 0 ], [ 2, 1 ], [ 2, 2 ] ],
[ [ 1, 1 ], [ 0, 1 ], [ 0, 2 ] ],
[ [ 1, 1 ], [ 2, 1 ], [ 2, 0 ], [ 1, 0 ], [ 0, 0 ], [ 0, 1 ], [ 0, 2 ] ],
[ [ 1, 1 ], [ 2, 1 ], [ 2, 2 ] ],
[ [ 1, 1 ], [ 1, 0 ], [ 0, 0 ], [ 0, 1 ], [ 0, 2 ] ],
[ [ 1, 1 ], [ 1, 0 ], [ 2, 0 ], [ 2, 1 ], [ 2, 2 ] ]
]
*/