How to enumerate unique paths reaching top right (h8) square sta

ghz 11hours ago ⋅ 2 views

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:

  1. Track Path: Keep track of the current path in each recursive call.
  2. Backtrack: Add logic to backtrack by removing the current position from the path as you return from the recursion.
  3. 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:

  1. Tracking Path: We maintain a path list that records the sequence of nodes (coordinates) as we traverse the tree.
  2. Backtracking: After calling PreOrderTraversal recursively on both left and right children, we pop the current node from the path to backtrack.
  3. 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.