code: def hill_climb(nsteps, start_node, get_next_node):
      
      outputs = []
      best_score = -float('inf')
  
      for step in range(nsteps):
          next_node, score, output = get_next_node(copy.deepcopy(start_node))
  
          # Keep track of best score and the start node becomes finish node
          if score > best_score:
              start_node = copy.deepcopy(next_node)
              best_score = score
              outputs.append(output)
  
      return start_node, best_score, outputs
docs: Modular hill climbing algorithm.
  
      Example:
          >>> def get_next_node(node):
          ...     a, b = random.sample(range(len(node)), 2)
          ...     node[a], node[b] = node[b], node[a]
          ...     plaintext = decrypt(node, ciphertext)
          ...     score = lantern.score(plaintext, *fitness_functions)
          ...     return node, score, Decryption(plaintext, ''.join(node), score)
          >>> final_node, best_score, outputs = hill_climb(10, "ABC", get_next_node)
  
      Args:
          nsteps (int): The number of neighbours to visit
          start_node: The starting node
          get_next_node (function): Function to return the next node
              the score of the current node and any optional output from the current node
  
      Returns:
          The highest node found, the score of this node and the outputs from the best nodes along the way