def __init__(
      self,
      model: language_model.LanguageModel,
      embedder: Callable[[str], np.ndarray],
      measurements: measurements_lib.Measurements,
      agent_module: types.ModuleType = basic_agent,
      override_agent_model: language_model.LanguageModel | None = None,
      resident_visitor_modules: Sequence[types.ModuleType] | None = None,
      supporting_agent_module: (
          bots_lib.SupportingAgentFactory | types.ModuleType
      ) = rational_agent_supporting,
      time_and_place_module: str | None = None,
      seed: int | None = None,
  ):
    """Initialize the simulation object.

    Args:
      model: the language model to use.
      embedder: the sentence transformer to use.
      measurements: the measurements object to use.
      agent_module: the agent module to use for all main characters.
      override_agent_model: optionally, override the model for all agents. The
        model will be copied for every agent.
      resident_visitor_modules: optionally, use different modules for majority
        and minority parts of the focal population.
      supporting_agent_module: agent module to use for all supporting players. A
        supporting player is a non-player character with a persistent memory
        that plays a specific role in defining the task environment. Their role
        is not incidental but rather is critcal to making the task what it is.
        Supporting players are not necessarily interchangeable with focal or
        background players.
      time_and_place_module: optionally, specify a module containing settings
        that create a sense of setting in a specific time and place. If not
        specified, a random module will be chosen from the default options.
      seed: the random seed to use.
    """
    if resident_visitor_modules is None:
      self._resident_visitor_mode = False
      self._agent_module = agent_module
    else:
      self._resident_visitor_mode = True
      self._resident_agent_module, self._visitor_agent_module = (
          resident_visitor_modules
      )
    self._agent_model = model

    if override_agent_model:
      self._agent_model = override_agent_model

    self._supporting_agent_module = supporting_agent_module

    self._model = model
    self._embedder = embedder
    self._measurements = measurements

    self._time_and_place_module = time_and_place_module
    time_and_place_params, sampled_settings = (
        helper_functions.load_time_and_place_module(
            time_and_place_module=time_and_place_module,
            default_time_and_place_modules=DEFAULT_TIME_AND_PLACE_MODULES,
            seed=seed,
        )
    )

    self._rng = random.Random(sampled_settings.seed)
    start_time = datetime.datetime(
        year=time_and_place_params.YEAR,
        month=time_and_place_params.MONTH,
        day=time_and_place_params.DAY,
    )
    scenario_premise = [(
        'A group of workers consider their options after Boss '
        f'{sampled_settings.antagonist} cut their pay '
        f'from {time_and_place_params.ORIGINAL_DAILY_PAY} coin to '
        f'{time_and_place_params.LOW_DAILY_PAY} coin.'
    )]

    # The setup clock time is arbitrary.
    setup_clock_time = start_time - datetime.timedelta(days=1)

    self._clock = game_clock.MultiIntervalClock(
        start=setup_clock_time, step_sizes=[MAJOR_TIME_STEP, MINOR_TIME_STEP]
    )

    importance_model = importance_function.ConstantImportanceModel()
    importance_model_gm = importance_function.ConstantImportanceModel()

    self._blank_memory_factory = blank_memories.MemoryFactory(
        model=self._model,
        embedder=self._embedder,
        importance=importance_model.importance,
        clock_now=self._clock.now,
    )
    shared_memories, shared_context = get_shared_memories_and_context(
        model=model, sampled_settings=sampled_settings
    )
    self._formative_memory_factory = formative_memories.FormativeMemoryFactory(
        model=self._model,
        shared_memories=shared_memories,
        blank_memory_factory_call=self._blank_memory_factory.make_blank_memory,
        current_date=setup_clock_time,
    )

    main_player_configs, supporting_player_configs, antagonist_config, _ = (
        configure_players(
            model=model,
            sampled_settings=sampled_settings,
            time_and_place_params=time_and_place_params,
            rng=self._rng,
        )
    )
    self._rng.shuffle(main_player_configs)

    tasks = {
        config.name: functools.partial(
            self._make_player_memories, config=config
        )
        for config in main_player_configs + supporting_player_configs
    }
    self._all_memories = concurrency.run_tasks(tasks)

    main_players = []
    self._resident_names = []
    self._visitor_names = []
    for idx, player_config in enumerate(main_player_configs):
      kwargs = dict(
          config=player_config,
          model=copy.copy(self._agent_model),
          memory=self._all_memories[player_config.name],
          clock=self._clock,
          update_time_interval=MAJOR_TIME_STEP,
      )
      if self._resident_visitor_mode:
        if idx == 0:
          player = self._visitor_agent_module.build_agent(**kwargs)  # pylint: disable=attribute-error
          self._visitor_names.append(player.name)
        else:
          player = self._resident_agent_module.build_agent(**kwargs)  # pylint: disable=attribute-error
          self._resident_names.append(player.name)
      else:
        player = self._agent_module.build_agent(**kwargs)  # pylint: disable=attribute-error
        self._resident_names.append(player.name)

      main_players.append(player)

    antagonist_player = None
    organizer_player = None

    supporting_players = []
    for player_config in supporting_player_configs:
      conversation_style = agent_components.constant.Constant(
          pre_act_key='guiding principle of good conversation',
          state=player_traits_and_styles.get_conversation_style(
              player_config.name
          ),
      )
      supporting_player_kwargs = dict(
          config=player_config,
          model=self._model,
          memory=self._all_memories[player_config.name],
          clock=self._clock,
          update_time_interval=MAJOR_TIME_STEP,
          additional_components={
              'Guiding principle of good conversation': conversation_style
          },
      )
      if player_config.name == sampled_settings.antagonist:
        # The antagonist can be configured externally by passing in a
        # supporting player module.
        player = self._supporting_agent_module.build_agent(
            **supporting_player_kwargs
        )
      else:
        # The labor organizer character is not currently configurable. It is
        # always set to use the same supporting agent factory for now. This is
        # probably fine since the labor organizer character is not really the
        # focus of investigation here. The character's purpose is just to nudge
        # the focal players to discuss the labor strike.
        player = basic_agent_supporting.build_agent(
            **supporting_player_kwargs
        )
      supporting_players.append(player)

      if player_config.name == sampled_settings.antagonist:
        antagonist_player = player
      elif player_config.name == sampled_settings.organizer:
        organizer_player = player

    self._all_players = main_players + supporting_players

    game_master_memory = associative_memory.AssociativeMemory(
        sentence_embedder=self._embedder,
        importance=importance_model_gm.importance,
        clock=self._clock.now,
    )

    # The organizer has called for the railroad construction workers to strike.
    if antagonist_player is not None and organizer_player is not None:
      called_for_a_strike = (
          f'{organizer_player.name} has called for a strike, demanding that '
          f' boss {antagonist_player.name} rescind their decision to reduce '
          ' wages.'
      )
      game_master_memory.add(called_for_a_strike)
      for player in self._all_players:
        player.observe(called_for_a_strike)

    setting = generic_components.constant.ConstantComponent(
        state=(
            f'The year is {sampled_settings.year} and '
            f'the location is {sampled_settings.location}.'
        ),
        name='Setting',
    )
    magic_is_not_real = generic_components.constant.ConstantComponent(
        state='Magic is not real. Supernatural events are impossible.',
        name='Important Fact',
    )
    no_frivolous_talk = generic_components.constant.ConstantComponent(
        state=(
            f'{sampled_settings.antagonist} does not engage in frivolous '
            'conversation with workers. They are not worth the time.'
        ),
        name='Another fact',
    )

    inventory_component, self._score = get_inventories_component(
        model=model,
        memory=game_master_memory,
        players=self._all_players,
        main_players=main_players,
        player_configs=main_player_configs + supporting_player_configs,
        clock_now=self._clock.now,
    )

    def inventory_effect_function(
        args: _TriggeredInventoryEffectPreEventFnArgsT,
        inventories: gm_components.inventory.InventoryType,
    ) -> gm_components.inventory.InventoryType:
      player_name = args.player_name
      player_choice = args.player_choice
      current_scene_type = args.current_scene_type
      memory = args.memory
      player = args.player
      # Determine wage by searching memory for the latest wage setting event.
      wage = _get_wage_from_game_master_memory(
          memory, initial_wage=time_and_place_params.LOW_DAILY_PAY)
      # Modify inventory based on player choice, scene type, and wage.
      player_inventory = dict(inventories[player_name])
      antagonist_inventory = dict(inventories[antagonist_player.name])
      if DECISION_SCENE_TYPE in current_scene_type:
        if player_choice == DAILY_OPTIONS['defection']:
          # Players only get paid if they go to work i.e. defecting against
          # the strike.
          player_inventory['coin'] += wage
          antagonist_inventory['coin'] += -wage
          player.observe(f'{player_name} went to work and earned {wage} coin.')
          if antagonist_player is not None:
            antagonist_player.observe(
                f'{antagonist_player.name} paid {player_name} {wage} coin for '
                "their day's work."
            )
      elif DISCUSSION_SCENE_TYPE in current_scene_type:
        # Apply daily expenses.
        player_inventory['coin'] += time_and_place_params.DAILY_EXPENSES
        player.observe(
            (f'{player_name} spent {time_and_place_params.DAILY_EXPENSES} coin '
             'on daily expenses.')
        )
        if player_inventory['coin'] <= 0:
          player.observe(
              f'{player_name} has run out of money and cannot afford daily '
              'necessities. Debts are piling up. The situation is dire.'
          )
      inventories = dict(inventories)
      inventories.update({
          player_name: player_inventory,
          antagonist_player.name: antagonist_inventory,
      })
      return inventories

    paid_labor = (
        gm_components.triggered_inventory_effect.TriggeredInventoryEffect(
            function=inventory_effect_function,
            inventory=inventory_component,
            memory=game_master_memory,
            players=main_players,
            clock_now=self._clock.now,
            name='paid labor',
        )
    )

    def set_wage_function(args: _TriggeredFunctionPreEventFnArgsT) -> str:
      player_name = args.player_name
      player_choice = args.player_choice
      current_scene_type = args.current_scene_type
      players = args.players
      memory = args.memory
      wage = _get_wage_from_game_master_memory(
          memory, initial_wage=time_and_place_params.LOW_DAILY_PAY)
      log = f'old wage: {wage}'
      if current_scene_type == BOSS_DECISION_SCENE_TYPE:
        if player_name == sampled_settings.antagonist:
          # The antagonist is the boss and has the power to set wages.
          if player_choice == time_and_place_params.BOSS_OPTIONS[
              'cave to pressure']:
            # The boss caves to pressure and raises wages.
            wage = wage * time_and_place_params.WAGE_INCREASE_FACTOR
            result_str = (f'Boss {sampled_settings.antagonist} caves '
                          f'to pressure and raises wages to {wage} '
                          'coin per day!')
            for player in players:
              player.observe(result_str)
          else:
            # The boss holds firm and leaves wages unchanged.
            result_str = (f'Boss {sampled_settings.antagonist} holds '
                          f'firm and leaves wages unchanged at {wage} coin '
                          'per day.')
            for player in players:
              player.observe(result_str)
          memory.add(result_str)
          memory.add(f'[set wage] {wage}')

      log += f' --> new wage: {wage}'
      return log

    wage_setting = gm_components.triggered_function.TriggeredFunction(
        memory=game_master_memory,
        players=self._all_players,
        clock_now=self._clock.now,
        pre_event_fn=set_wage_function,
        name='wage setting',
    )

    additional_gm_components = [
        setting,
        magic_is_not_real,
        no_frivolous_talk,
        inventory_component,
        paid_labor,
        wage_setting,
        self._score,
    ]

    self._primary_environment, self._game_master_memory = (
        basic_game_master.build_game_master(
            model=self._model,
            embedder=self._embedder,
            importance_model=importance_model_gm,
            clock=self._clock,
            players=self._all_players,
            shared_memories=shared_memories,
            shared_context=shared_context,
            blank_memory_factory=self._blank_memory_factory,
            max_conversation_length=3,
            cap_nonplayer_characters_in_conversation=0,
            memory=game_master_memory,
            supporting_players_at_fixed_locations=(
                sampled_settings.supporting_player_locations
            ),
            additional_components=additional_gm_components,
            seed=seed,
        )
    )
    self._scenes, decision_env, industrial_action = configure_scenes(
        model=self._model,
        game_master_memory=self._game_master_memory,
        players=self._all_players,
        clock=self._clock,
        main_player_configs=main_player_configs,
        supporting_player_configs=supporting_player_configs,
        antagonist_config=antagonist_config,
        time_and_place_params=time_and_place_params,
        sampled_settings=sampled_settings,
        start_time=start_time,
    )
    self._primary_environment.add_component(industrial_action)
    decision_env.add_component(paid_labor)
    decision_env.add_component(wage_setting)
    decision_env.add_component(self._score)
    self._industrial_action = industrial_action

    self._secondary_environments = [decision_env]

    self._init_premise_memories(
        setup_time=setup_clock_time,
        main_player_configs=main_player_configs,
        supporting_player_configs=supporting_player_configs,
        shared_memories=shared_memories,
        scenario_premise=scenario_premise,
    )
Initialize the simulation object.

    Args:
      model: the language model to use.
      embedder: the sentence transformer to use.
      measurements: the measurements object to use.
      agent_module: the agent module to use for all main characters.
      override_agent_model: optionally, override the model for all agents. The
        model will be copied for every agent.
      resident_visitor_modules: optionally, use different modules for majority
        and minority parts of the focal population.
      supporting_agent_module: agent module to use for all supporting players. A
        supporting player is a non-player character with a persistent memory
        that plays a specific role in defining the task environment. Their role
        is not incidental but rather is critcal to making the task what it is.
        Supporting players are not necessarily interchangeable with focal or
        background players.
      time_and_place_module: optionally, specify a module containing settings
        that create a sense of setting in a specific time and place. If not
        specified, a random module will be chosen from the default options.
      seed: the random seed to use.
    
examples/deprecated/modular/environment/labor_collective_action.py