code: def graph_search(problem, frontier):
      
      frontier.append(Node(problem.initial))
      explored = set()
      while frontier:
          node = frontier.pop()
          if problem.goal_test(node.state):
              return node
          explored.add(node.state)
          frontier.extend(child for child in node.expand(problem)
                          if child.state not in explored
                          and child not in frontier)
      return None
docs: Search through the successors of a problem to find a goal.
      The argument frontier should be an empty queue.
      If two paths reach a state, only use the first one. [Fig. 3.7]