Gena Playing Hanoi

  • + 0 comments

    Most straightforward solution, in my opinion:

    from sys import stdin
    from functools import reduce
    from queue import deque
    
    
    def get_states(state: int, n: int):
        """returns a list of states that can be achieved from the given state"""
        top_disks = []
        positions = set()
        for size in range(n):
            pos = (state >> 2 * size) & 3
            if pos not in positions:
                top_disks.append((size, pos))
                positions.add(pos)
        top_disks.sort()
        allowed_positions = set(range(4))
        states = []
        for size, pos in top_disks:
            allowed_positions.discard(pos)
            for pos_ in allowed_positions:
                new_state = (pos_ << 2 * size) | (state ^ (pos << 2 * size))
                states.append(new_state)
        return states
    
    
    def hanoi(init_state: int, n: int):
        """
        represent each possible arrangement as a binary number
        of length 2 * n, where n is the number of disks. the rightmost 2 digits
        represent the position of disk 1, the next two digits represent the
        position of disk 2, etc., then do a bfs of the graph starting at init_state...   
        """
        queue = deque([(init_state, 0)])
        visited = {init_state}
        while queue:
            state, num_moves = queue.popleft()
            if state == 0:
                return num_moves
            else:
                states = get_states(state, n)
                for state in states:
                    if not state in visited:
                        queue.append((state, num_moves + 1)) 
                        visited.add(state)   
    
    
    def main():
        n = int(stdin.readline().strip())
        disks = map(int, stdin.readline().strip().split())
        state = reduce(
            lambda x, y: x | y, 
            ((pos - 1) << 2 * size for size, pos in enumerate(disks)))
        print(hanoi(state, n))
    
    
    if __name__ == "__main__":
        main()