Search code examples
pythonrecursiontreeartificial-intelligence

How to represent different possible movement (state) of Tower of Hanoi?


I am trying to write a code to solve the ToH problem by using UCS and A* algorithms and then compare the results. So, the first thing I should do is representing the possible movements as a tree, then the UCS and A* algorithms can go through the tree easily.

Suppose we have 3 stacks and 5 disks, so the initial state will be:

# The Num of stacks is the number of elements in the list.
# The Num of disks is the length of each element in the list.
key = ["12345","00000","00000"]
#         A       B        C

What I need is to build a function that predicts the possible next movements, so the next step could be:

["12340","00005","00000"] or ["12340","00000","00005"]
# so these states will saved as:
dic ={}
x= repr(key)
dic[x] = ["12340","00005","00000"]
# the result:
# ["12345","00000","00000"] : ["12340","00005","00000"]

I tried this naive code:

# there are bugs but I am still working on it 
for indx, block in enumerate(key):
    for i, char in enumerate(reversed(block)):
        if char != "0":
            for indx2, block2 in enumerate(key):
                if indx2 == indx: pass
                for ii, char2 in enumerate(reversed(block2)):
                    if char2 == "0":
                        temp=block[i]
                        block[i]= block2[ii]
                        block2[ii]=temp
                        print(key)

Is there any pseudocode or ideas that could help?


Solution

  • For solving the Towers of Hanoi problem there is no need to build the graph of states, as there is a mathematical formula to determine which next move to make.

    Still, if you are going to make the graph, then I would suggest to shorten the key to a this format: 12345||. The pipes separate the piles. So if you would move the 5 to the middle stack, its key would be 1234|5|. If then you move the 4 to the last stack: 123|5|4, ...etc.

    During the creation of the graph, you could switch to a list of lists, like [["1","2","3"], [], []], so it is easier to manipulate. Your idea to use a dictionary is fine, however, there can be several valid moves from one state, so you would need a list of states.

    Nodes in a graph are often implemented with a Node class, so I would suggest this code:

    class Node:
        def __init__(self, key):
            self.key = key
            self.children = []
    
    def create_tree(stacks, tree=None):
        if not tree:
            tree = {}
        key = "|".join(["".join(discs) for discs in stacks])
        if key in tree:  # this node was already created
            return tree[key]
        tree[key] = node = Node(key)
        for source in stacks:
            if source:
                disc = source[-1]
                for target in stacks:
                    if not target or disc > target[-1]:
                        # perform the move:
                        target.append(source.pop())
                        # recursion
                        node.children.append(create_tree(stacks, tree))
                        # undo the move
                        source.append(target.pop())
        return node
    
    
    root = create_tree([["1","2","3","4","5"], [], []])
    

    After running this, root will be a Node instance with two nodes in its children property. Each of those children will have three children, including a back reference to the root, representing the inverse move.

    I kept the dictionary (called tree) hidden in the implementation. If you feel you need access to it, then you could of course make it a non-local variable, or pass it as argument.