def run_scenes(
    scenes: Sequence[scene_lib.ExperimentalSceneSpec],
    players: Sequence[entity_agent.EntityAgent],
    clock: game_clock.GameClock,
    verbose: bool = False,
    compute_metrics: Mapping[str, logging_lib.Metric] | None = None,
    log: list[Mapping[str, Any]] | None = None,
) -> None:
  """Run a sequence of scenes.

  Args:
    scenes: sequence of scene configurations
    players: full list of players (a subset may participate in each scene)
    clock: the game clock which may be advanced between scenes
    verbose: if true then print intermediate outputs
    compute_metrics: Optionally, a function to compute metrics.
    log: Optionally, a log to append debug information to.
  """
  players_by_name = {player.name: player for player in players}
  if len(players_by_name) != len(players):
    raise ValueError('Duplicate player names')

  for scene_idx, scene in enumerate(scenes):
    scene_simulation = scene.scene_type.engine
    game_master = scene.scene_type.game_master

    scene_type_spec = scene.scene_type
    possible_participants = scene_type_spec.possible_participants
    participants_from_scene = scene.participants
    if possible_participants is None:
      participant_names = participants_from_scene
    else:
      if participants_from_scene:
        participant_names = list(set(possible_participants).intersection(
            participants_from_scene
        ))
      else:
        participant_names = possible_participants

    participants_str = _PARTICIPANTS_DELIMITER.join(participant_names)
    participants = [players_by_name[name] for name in participant_names]

    if verbose:
      print(f'\n\n    Scene {scene_idx}    Participants: {participants_str}\n')

    # Prepare to run the scene
    clock.set(scene.start_time)

    all_premises = ''
    for participant in participants:
      premise_messages = _get_interscene_messages(
          key='premise',
          agent_name=participant.name,
          scene_type_spec=scene.scene_type,
      )
      for message in premise_messages:
        all_premises += f'{participant.name} -- premise: {message}      \n'
        if verbose:
          print(f'{participant.name} -- premise: {message}')
        participant.observe(message)
        game_master.observe(message)

    # Run the scene
    for _ in range(scene.num_rounds):
      game_master.observe(f'{_SCENE_TYPE_TAG} {scene.scene_type.name}')
      game_master.observe(f'{_SCENE_PARTICIPANTS_TAG} {participants_str}')
      # run_loop modifies log in place by appending to it
      scene_simulation.run_loop(
          game_masters=[game_master],
          entities=participants,
          max_steps=scene.num_rounds * len(participants),
          verbose=verbose,
          log=log,
      )

    # Conclude the scene
    for participant in participants:
      conclusion_messages = _get_interscene_messages(
          key='conclusion',
          agent_name=participant.name,
          scene_type_spec=scene.scene_type,
      )
      for message in conclusion_messages:
        if verbose:
          print(f'{participant.name} -- conclusion: {message}')
        participant.observe(message)
        game_master.observe(message)

    # Branch off a metric scene if applicable
    if scene.scene_type.save_after_each_scene:
      serialized_agents = {}
      for participant in participants:
        serialized_agents = {}
        json_representation = json_lib.save_to_json(participant)
        serialized_agents[participant.name] = json_representation

      if compute_metrics is not None:
        compute_metrics(serialized_agents)
Run a sequence of scenes.

  Args:
    scenes: sequence of scene configurations
    players: full list of players (a subset may participate in each scene)
    clock: the game clock which may be advanced between scenes
    verbose: if true then print intermediate outputs
    compute_metrics: Optionally, a function to compute metrics.
    log: Optionally, a log to append debug information to.
  
concordia/environment/scenes/runner.py