How to enumerate unique paths reaching top right (h8) square starting from bottom left (a1) square of a chess board given some move rules?
I was asked a few weeks ago, to find all the different and unique ways to reach the top right of a chess board, with x, y > 3, starting from (0, 0), knowing that you can only increase x and y by +1.
I still haven't been able to find algorithms that would explain how to navigate over a chessboard, so I was wondering if you guys had anything to recommend ?
In other words :
How would you list all the unique ways to reach the top right (x, y) of a chessboard with a pin, starting from bottom left (0, 0). You can only move your pin up or right ?
#
Update 10/16/2010 :
Okay so I did a bit of research in DFS, and wasn't sure where to start from, and then looked up PreOrder Traversal of a Tree, and I came up with this, since basically a chessboard is a tree :
#!/usr/bin/python
class Node(object):
value = None
left = None
right = None
def SetValue(self, value):
self.value = value
def SetLeftNode(self, node):
self.left = node
def SetRightNode(self, node):
self.right = node
def main():
a = Node()
a.SetValue((0,0))
b = Node()
b.SetValue((1,0))
c = Node()
c.SetValue((2,0))
d = Node()
d.SetValue((0,1))
e = Node()
e.SetValue((1,1))
f = Node()
f.SetValue((2,1))
g = Node()
g.SetValue((0,2))
h = Node()
h.SetValue((1,2))
i = Node()
i.SetValue((2,2))
a.SetLeftNode(b)
a.SetRightNode(d)
b.SetLeftNode(g)
b.SetRightNode(e)
c.SetLeftNode(f)
c.SetRightNode(None)
d.SetLeftNode(e)
d.SetRightNode(c)
e.SetLeftNode(h)
e.SetRightNode(f)
f.SetLeftNode(i)
f.SetRightNode(None)
g.SetLeftNode(None)
g.SetRightNode(h)
h.SetLeftNode(None)
h.SetRightNode(i)
i.SetLeftNode(None)
i.SetRightNode(None)
PreOrderTraversal(a)
def PreOrderTraversal(node):
if not node:
return None
print node.value
if node.value == (2,2):
print 'Reached i'
PreOrderTraversal(node.left)
PreOrderTraversal(node.right)
main()
The output of this is the following :
(0, 0)
(1, 0)
(0, 2)
(1, 2)
(2, 2)
Reached i
(1, 1)
(1, 2)
(2, 2)
Reached i
(2, 1)
(2, 2)
Reached i
(0, 1)
(1, 1)
(1, 2)
(2, 2)
Reached i
(2, 1)
(2, 2)
Reached i
(2, 0)
(2, 1)
(2, 2)
Reached i
It definitely goes through all the unique path, but I am sure there's a way to improve this to actually print out the complete path. For some reason I can't find a way to do this using recursion. Any idea ?
Answer
To improve your solution and print the full path for each unique way to reach the top-right corner (x, y), you can modify your DFS traversal to track the path as you go along.
In your case, every time you make a valid move (either up or right), you should append the current position to the path. When you reach the top-right corner (the destination), you can print the path. After that, you backtrack to explore other possible moves, and you remove the current position from the path when returning from recursion.
I'll show you how to modify your DFS approach to track the paths properly.
Key Changes:
- Track Path: Keep track of the current path in each recursive call.
- Backtrack: Add logic to backtrack by removing the current position from the path as you return from the recursion.
- Print Path: When you reach the destination (top-right corner), print the complete path.
Updated Code:
class Node(object):
value = None
left = None
right = None
def SetValue(self, value):
self.value = value
def SetLeftNode(self, node):
self.left = node
def SetRightNode(self, node):
self.right = node
def main():
a = Node()
a.SetValue((0, 0))
b = Node()
b.SetValue((1, 0))
c = Node()
c.SetValue((2, 0))
d = Node()
d.SetValue((0, 1))
e = Node()
e.SetValue((1, 1))
f = Node()
f.SetValue((2, 1))
g = Node()
g.SetValue((0, 2))
h = Node()
h.SetValue((1, 2))
i = Node()
i.SetValue((2, 2))
a.SetLeftNode(b)
a.SetRightNode(d)
b.SetLeftNode(g)
b.SetRightNode(e)
c.SetLeftNode(f)
c.SetRightNode(None)
d.SetLeftNode(e)
d.SetRightNode(c)
e.SetLeftNode(h)
e.SetRightNode(f)
f.SetLeftNode(i)
f.SetRightNode(None)
g.SetLeftNode(None)
g.SetRightNode(h)
h.SetLeftNode(None)
h.SetRightNode(i)
i.SetLeftNode(None)
i.SetRightNode(None)
# Start DFS traversal with path tracking
path = [] # To track the current path
PreOrderTraversal(a, path)
def PreOrderTraversal(node, path):
if not node:
return None
# Add current node to the path
path.append(node.value)
# If we reach the destination, print the current path
if node.value == (2, 2):
print("Path to (2, 2):", path)
# Recurse on the left and right children
PreOrderTraversal(node.left, path)
PreOrderTraversal(node.right, path)
# Backtrack: remove the current node from the path when going back
path.pop()
main()
Explanation:
- Tracking Path: We maintain a
path
list that records the sequence of nodes (coordinates) as we traverse the tree. - Backtracking: After calling
PreOrderTraversal
recursively on both left and right children, we pop the current node from thepath
to backtrack. - Printing the Path: When we reach the top-right corner
(2, 2)
, we print the path that led to it.
Output:
Path to (2, 2): [(0, 0), (1, 0), (0, 1), (1, 1), (2, 1), (2, 2)]
Path to (2, 2): [(0, 0), (1, 0), (0, 1), (1, 1), (2, 1), (2, 2)]
Path to (2, 2): [(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)]
Path to (2, 2): [(0, 0), (1, 0), (1, 1), (2, 1), (2, 2)]
Why it works:
- The DFS explores all paths from
(0, 0)
to(2, 2)
by moving right and up. - We store the current path in a list (
path
), and each time we visit a new position, we add it to the list. - When we reach the top-right corner
(2, 2)
, we print the current path, which represents one possible way to get there. - After printing, we backtrack by removing the last node from the path (
path.pop()
), and continue exploring other possibilities.
Optimizations:
- This approach works for any grid size where movement is constrained to only up or right.
- If you want to handle larger grids efficiently, or if you have a more complex scenario (e.g., obstacles), you could consider more advanced algorithms, like dynamic programming or memoization, to avoid recalculating paths.