def get_daily_activities_component(
    model: language_model.LanguageModel,
    config: config_dict.ConfigDict,
    memory: associative_memory.AssociativeMemory,
    players: Sequence[entity_agent.EntityAgent],
    supporting_players: Sequence[entity_agent.EntityAgent],
    player_configs: Sequence[formative_memories.AgentConfig],
    clock_now: Callable[[], datetime.datetime] = datetime.datetime.now,
    basic_setting: str = '',
) -> tuple[
    daily_activities.DailyActivities,
    daily_activities.Payoffs,
]:
  """Get the daily activities tracking component for the game master."""

  village_by_representative = {
      config.main_characters.a.name: config.village_a_name,
      config.main_characters.b.name: config.village_b_name,
  }

  supporting_player_names = [player.name for player in supporting_players]

  activity_configs = tuple(
      [ActivityConfig(name=activity) for activity in config.activities])
  player_initial_activity_distribution = {
      config.name: config.extras['prior_year_activity_distribution']
      for config in player_configs if config.name in supporting_player_names
  }
  activities_component = daily_activities.DailyActivities(
      model=model,
      memory=memory,
      activity_configs=activity_configs,
      resolution_scene=RESOLUTION_SCENE_TYPE,
      players=players,
      player_initial_activity_distribution=player_initial_activity_distribution,
      clock_now=clock_now,
      num_to_retrieve=1,
      basic_setting=basic_setting,
      name='Daily activities\n',
      verbose=False,
  )

  village_a_denizens = [name for name, _ in config.supporting_characters.a]
  village_b_denizens = [name for name, _ in config.supporting_characters.b]

  def _get_overall_activity_product_per_village(
      activity_proportions: Mapping[str, Mapping[str, float]],
      activity_name: str,
  ) -> Mapping[str, float]:
    activity_of_village_a = 0.0
    activity_of_village_b = 0.0
    for player_name, activities in activity_proportions.items():
      if player_name in village_a_denizens:
        activity_of_village_a += _sigmoidlike_fn(activities[activity_name])
      if player_name in village_b_denizens:
        activity_of_village_b += _sigmoidlike_fn(activities[activity_name])
    overall_proportion_activity_of_village_a = (
        activity_of_village_a / len(village_a_denizens))
    overall_proportion_activity_of_village_b = (
        activity_of_village_b / len(village_b_denizens))
    return {config.village_a_name: overall_proportion_activity_of_village_a,
            config.village_b_name: overall_proportion_activity_of_village_b}

  def _are_agricultural_resources_pooled(
      game_master_memory: associative_memory.AssociativeMemory,
      timepoint: str) -> bool:
    """Check if villagers have agreed to pool agricultural resources."""
    retrieved = game_master_memory.retrieve_by_regex(
        regex=r'\[' + timepoint + r'\].*',
        sort_by_time=True,
    )
    result = False
    if retrieved:
      retrieved_str = '\n'.join(retrieved)
      villager_names_str = ', '.join(supporting_player_names)
      chain_of_thought = interactive_document.InteractiveDocument(model)
      chain_of_thought.statement(f'Record of events:\n{retrieved_str}')
      result = chain_of_thought.yes_no_question(
          question=('Is there evidence in the above record of events that '
                    'there is an agreement in place for pooling '
                    'agricultural products between villages such that a '
                    'village with less food can be resupplied by a village '
                    'with more food? Only consider the following villagers: '
                    f'{villager_names_str}. If there is no evidence of a '
                    'particular villager\'s opinion, then assume that villager '
                    'is against sharing agricultural resources.')
      )
    return result

  def player_score_fn(
      current_scene_type: str,
      activity_proportions: Mapping[str, Mapping[str, float]],
      player_name: str,
      game_master_memory: associative_memory.AssociativeMemory,
      timepoint: str,
  ) -> tuple[float, Sequence[str]]:
    """Get the individual part of the score."""
    score = 0.0
    events = []
    if current_scene_type != DEBRIEF_SCENE_TYPE:
      return score, events

    # Shared part of the score (common defense) is calculated first.
    defense_per_village = _get_overall_activity_product_per_village(
        activity_proportions, config.warrior_training_activity)
    # Overall defense is the mean defense over villages because barbarians
    # may attack randomly near any village equiprobably.
    raw_overall_defense = np.mean(list(defense_per_village.values()))
    if raw_overall_defense < config.defense_threshold:
      defense = 0.0
      events.append(config.sample_event_of_failing_to_repel_barbarians())
    else:
      defense = 1.0
      events.append(config.sample_event_of_success_repelling_barbarians())

    # Individual part of the score (farming and free time) is calculated next.
    if player_name in village_by_representative:
      this_village = village_by_representative[player_name]
    else:
      if player_name in  [name for name, _ in config.supporting_characters.a]:
        this_village = config.village_a_name
      elif player_name in [name for name, _ in config.supporting_characters.b]:
        this_village = config.village_b_name
      else:
        raise ValueError(f'Player {player_name} not found in any '
                         f'village: {village_by_representative} -- '
                         f'{config.supporting_characters.a} -- '
                         f'{config.supporting_characters.b}')

    farming_per_village = _get_overall_activity_product_per_village(
        activity_proportions, config.farming_activity)
    if _are_agricultural_resources_pooled(game_master_memory,
                                          timepoint):
      # If villagers have agreed to resupply whichever village has less food
      # then all villages receive the max of their agricultural production, so
      # starvation is unlikely unless all villages starve together.
      raw_agriculture = np.max(list(farming_per_village.values()))
      events.append(config.sample_event_treaty_in_effect())
    else:
      # If villagers did not agree to pool agricultural resources then each
      # village is on its own and villages that do not farm enough starve.
      raw_agriculture = farming_per_village[this_village]
      events.append(config.sample_event_no_treaty_in_effect())

    if raw_agriculture < config.starvation_threshold:
      agriculture = 0.0
      events.append(config.sample_event_of_failing_to_grow_food())
    else:
      agriculture = 1.0
      events.append(config.sample_event_of_success_growing_food())

    # Villager free time contributes positively to the overall score.
    free_time_per_village = _get_overall_activity_product_per_village(
        activity_proportions, config.free_time_activity)
    free_time = free_time_per_village[this_village]

    # The overall score is the product of the activity scores. Note that two of
    # them are constrained to be binary (defense and agriculture) so the only
    # effect they can have is to gate the free time score.
    score = defense * agriculture * free_time

    return score, events

  payoffs = daily_activities.Payoffs(
      memory=memory,
      daily_activities=activities_component,
      players=players,
      player_score_fn=player_score_fn,
      get_timepoint_fn=lambda: str(clock_now().year),
      clock_now=clock_now,
      name='Payoffs',
  )
  return activities_component, payoffs
Get the daily activities tracking component for the game master.
examples/deprecated/modular/environment/state_formation.py