Advertisement
iccaka

Tower of Hanoi (iterative & recursive)

Feb 18th, 2025 (edited)
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.69 KB | None | 0 0
  1. hanoi_recursive_counter = 0
  2.  
  3. def __validate_hanoi_input(
  4.         n: int,
  5.         source: str,
  6.         mid: str,
  7.         dest: str) -> None:
  8.     """
  9.    Function whose job is to validate the inputs passed onto the hanoi functions. Raises an appropriate error if the
  10.    inputs are not right.
  11.    :param n: The current move number.
  12.    :param source: The name of the first peg.
  13.    :param mid: The name of the second peg.
  14.    :param dest: The name of the third peg.
  15.    :return: None
  16.    """
  17.     if not isinstance(n, int):
  18.         raise TypeError('Expected an int, but got {} instead.'.format(type(n).__name__))
  19.     if n <= 0:
  20.         raise ValueError('n must be greater than zero, but got {} instead.'.format(n))
  21.  
  22.     for i, param in enumerate((source, mid, dest), start=2):
  23.         if not isinstance(param, str):
  24.             raise TypeError('Argument {} must be a string, but got {} instead.'.format(i, type(param).__name__))
  25.  
  26. def __get_disc_move_msg(
  27.         move_num: int,
  28.         disc: int,
  29.         source: str,
  30.         dest: str) -> str:
  31.     """
  32.    Function whose job is to generate and return a move message given the move number, disc, source and destination peg.
  33.    :param move_num: The current move number.
  34.    :param disc: The disc to be moved value.
  35.    :param source: The name of the peg to be moved from.
  36.    :param dest: The name of peg to be moved to.
  37.    :return: A string with the generated move message.
  38.    """
  39.     return '{}. Move disc {} from {} -> {}\n'.format(move_num, disc, source, dest)
  40.  
  41. def hanoi_iterative(
  42.         n: int,
  43.         source: str = 'A',
  44.         mid: str = 'B',
  45.         dest: str = 'C') -> str:
  46.     """
  47.    An iterative way of solving the Tower of Hanoi problem. The basic rules the program follows are as such:
  48.        1. In odd-numbered moves, the smallest disk (1) is moved to the right or left, depending on whether n is odd
  49.        (left) or even (right).
  50.        2. In even-numbered moves, the only possible move is made that doesn't involve the smallest disk (1).
  51.        3. A larger disk is not placed on top of a smaller one.
  52.    :param n: The number of discs used.
  53.    :param source: (Optional) The name of the first peg.
  54.    :param mid: (Optional) The name of the second peg.
  55.    :param dest: (Optional) The name of the destination or initially third peg.
  56.    :return: A list containing all (2^n - 1) number of move messages.
  57.    """
  58.     __validate_hanoi_input(n, source, mid, dest)
  59.  
  60.     result = ''
  61.     discs_dict = {0: source, 1: mid, 2: dest}
  62.     pegs = [{x for x in range(1, n + 1)}, set(), set()]
  63.     is_n_odd = -1 if n % 2 == 0 else 1
  64.     smallest_disc_loc = 0
  65.  
  66.     for x in range(1, 2 ** n):
  67.         if x % 2 == 1:
  68.             dest = (smallest_disc_loc - is_n_odd) % 3
  69.             pegs[smallest_disc_loc].remove(1)
  70.             pegs[dest].add(1)
  71.             result += __get_disc_move_msg(
  72.                 x,
  73.                 1,
  74.                 discs_dict[smallest_disc_loc],
  75.                 discs_dict[dest])
  76.             smallest_disc_loc = dest
  77.             continue
  78.  
  79.         non_smallest_disc_locs = {j: pegs[j] for j in range(len(pegs)) if j != smallest_disc_loc}
  80.         first_non_smallest_peg_loc, second_non_smallest_peg_loc = non_smallest_disc_locs.keys()
  81.         first_peg, second_peg = list(non_smallest_disc_locs.values())
  82.  
  83.         if not bool(first_peg):
  84.             second_peg_smallest_disc = min(second_peg)
  85.             pegs[second_non_smallest_peg_loc].remove(second_peg_smallest_disc)
  86.             pegs[first_non_smallest_peg_loc].add(second_peg_smallest_disc)
  87.             result += __get_disc_move_msg(
  88.                 x,
  89.                 second_peg_smallest_disc,
  90.                 discs_dict[second_non_smallest_peg_loc],
  91.                 discs_dict[first_non_smallest_peg_loc])
  92.         elif not bool(second_peg):
  93.             first_peg_smallest_disc = min(first_peg)
  94.             pegs[first_non_smallest_peg_loc].remove(first_peg_smallest_disc)
  95.             pegs[second_non_smallest_peg_loc].add(first_peg_smallest_disc)
  96.             result += __get_disc_move_msg(
  97.                 x,
  98.                 first_peg_smallest_disc,
  99.                 discs_dict[first_non_smallest_peg_loc],
  100.                 discs_dict[second_non_smallest_peg_loc])
  101.         else:
  102.             first_peg_min, second_peg_min = min(first_peg), min(second_peg)
  103.  
  104.             if first_peg_min < second_peg_min:
  105.                 pegs[first_non_smallest_peg_loc].remove(first_peg_min)
  106.                 pegs[second_non_smallest_peg_loc].add(first_peg_min)
  107.                 result += __get_disc_move_msg(
  108.                     x,
  109.                     first_peg_min,
  110.                     discs_dict[first_non_smallest_peg_loc],
  111.                     discs_dict[second_non_smallest_peg_loc])
  112.                 continue
  113.  
  114.             pegs[second_non_smallest_peg_loc].remove(second_peg_min)
  115.             pegs[first_non_smallest_peg_loc].add(second_peg_min)
  116.             result += __get_disc_move_msg(
  117.                 x,
  118.                 second_peg_min,
  119.                 discs_dict[second_non_smallest_peg_loc],
  120.                 discs_dict[first_non_smallest_peg_loc])
  121.  
  122.     return result
  123.  
  124. def hanoi_recursive(
  125.         n: int,
  126.         source: str = 'A',
  127.         mid: str = 'B',
  128.         dest: str = 'C') -> str:
  129.     """
  130.    The classic way of solving the Tower of Hanoi problem recursively. The idea is that to move n discs from peg A to
  131.    peg C, you need to move n-1 discs from peg A to peg B, then move disc number n (which is the largest among them)
  132.    from peg A to peg C, and finally move the remaining n-1 discs from peg B to peg C.
  133.    :param n: The number of discs used.
  134.    :param source: (Optional) The name of the first peg.
  135.    :param mid: (Optional) The name of the second peg.
  136.    :param dest: (Optional) The name of the destination or initially third peg.
  137.    :return: A string containing all (2^n - 1) number of move messages seperated by newline (\n).
  138.    """
  139.     def _hanoi_recursive(
  140.             _n: int,
  141.             _source: str,
  142.             _mid: str,
  143.             _dest: str) -> str:
  144.         global hanoi_recursive_counter
  145.  
  146.         if _n == 1:
  147.             hanoi_recursive_counter += 1
  148.             return __get_disc_move_msg(
  149.                 hanoi_recursive_counter,
  150.                 1,
  151.                 _source,
  152.                 _dest)
  153.  
  154.         return (
  155.             _hanoi_recursive(_n - 1, _source, _dest, _mid) +
  156.             __get_disc_move_msg(hanoi_recursive_counter := hanoi_recursive_counter + 1, _n, _source, _dest) +
  157.             _hanoi_recursive(_n - 1, _mid, _source, _dest)
  158.         )
  159.  
  160.     __validate_hanoi_input(n, source, mid, dest)
  161.  
  162.     global hanoi_recursive_counter
  163.     hanoi_recursive_counter = 0
  164.  
  165.     return _hanoi_recursive(n, source, mid, dest)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement