Skip to content

Commit

Permalink
encapsulate default game master instructions inside game_master class.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 607307868
Change-Id: Ic44fd96f0f91148731f9721264ca0549ad38e095
  • Loading branch information
jzleibo authored and copybara-github committed Feb 15, 2024
1 parent 9e58cbb commit 6946283
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 219 deletions.
22 changes: 12 additions & 10 deletions concordia/components/game_master/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.


"""Externality for the Game Master, which generates conversations."""
"""Externality component for the Game Master, which generates conversations."""

from collections.abc import Sequence
import datetime
Expand All @@ -25,6 +24,7 @@
from concordia.clocks import game_clock
from concordia.components import agent as sim_components
from concordia.document import interactive_document
from concordia.environment import game_master
from concordia.environment.scenes import conversation as conversation_scene
from concordia.language_model import language_model
from concordia.typing import clock as clock_lib
Expand All @@ -44,13 +44,13 @@ def __init__(
clock: game_clock.MultiIntervalClock,
burner_memory_factory: blank_memories.MemoryFactory,
cap_nonplayer_characters: int = 3,
game_master_instructions: str = '',
shared_context: str = '',
components: Sequence[component.Component] | None = None,
allow_self_talk: bool = False,
review_participants: bool = True,
verbose: bool = False,
print_colour: str = 'magenta',
npc_instructions: str = game_master.DEFAULT_GAME_MASTER_INSTRUCTIONS,
log_color: str = 'magenta',
):
"""Initializes the generator of conversations.
Expand All @@ -64,23 +64,25 @@ def __init__(
npcs and conversation gm
cap_nonplayer_characters: The maximum number of non-player characters
allowed in the conversation.
game_master_instructions: A string to use as the game master instructions.
shared_context: A string to use as the generic context for the NPCs.
components: components that contextualise the conversation
allow_self_talk: allow players to have a conversation with themselves
review_participants: whether or not to start each scene by declaring
who its participants are.
verbose: Whether to print debug messages or not.
print_colour: colour in which to print logs
npc_instructions: by default use the standard game master instructions
for non-player characters. Otherwise override this with custom
instructions.
log_color: color in which to print logs
"""
self._players = players
self._model = model
self._cap_nonplayer_characters = cap_nonplayer_characters
self._game_master_instructions = game_master_instructions
self._npc_instructions = npc_instructions
self._shared_context = shared_context
self._history = []
self._verbose = verbose
self._print_colour = print_colour
self._log_color = log_color
self._components = components or []
self._clock = clock
self._burner_memory_factory = burner_memory_factory
Expand All @@ -104,7 +106,7 @@ def get_player_names(self):
return [player.name for player in self._players]

def _log(self, entry):
print(termcolor.colored(entry, self._print_colour))
print(termcolor.colored(entry, self._log_color))

def _make_npc(
self, name: str, scene_clock: clock_lib.GameClock
Expand All @@ -123,7 +125,7 @@ def _make_npc(
clock=scene_clock,
components=[
generic_components.constant.ConstantComponent(
name='Instructions:', state=self._game_master_instructions
name='Instructions:', state=self._npc_instructions
),
generic_components.constant.ConstantComponent(
name='General knowledge:', state=context
Expand Down
40 changes: 34 additions & 6 deletions concordia/environment/game_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.


"""A Generic Game Master."""

from collections.abc import Callable, Sequence
import concurrent.futures
import random

from concordia import components as generic_components
from concordia.agents import basic_agent
from concordia.associative_memory import associative_memory
from concordia.document import interactive_document
Expand All @@ -37,6 +37,23 @@
)


DEFAULT_GAME_MASTER_INSTRUCTIONS = (
'This is a social science experiment. It is structured as a '
'tabletop roleplaying game (like dungeons and dragons). You are the '
'game master. You will describe the current situation to the '
'participants in the experiment and then on the basis of what you '
'tell them they will suggest actions for the character they control. '
'Aside from you, each other participant controls just one character. '
'You are the game master so you may control any non-player '
'character. You will track the state of the world and keep it '
'consistent as time passes in the simulation and the participants '
'take actions and change things in their world. Remember that this '
'is a serious social science experiment. It is not just a game. It '
'need not be fun for the participants. Always use third-person '
'limited perspective, even when speaking directly to the participants.'
)


class GameMaster(simulacrum_game_master.GameMaster):
"""A generic game master."""

Expand All @@ -63,7 +80,8 @@ def __init__(
verbose: bool = False,
concurrent_externalities: bool = True,
concurrent_action: bool = False,
log_colour: str = 'red',
use_default_instructions: bool = True,
log_color: str = 'red',
):
"""Game master constructor.
Expand All @@ -89,20 +107,30 @@ def __init__(
concurrent_externalities: if true, runs externalities in separate threads
concurrent_action: if true, runs player actions and events in separate
threads
log_colour: colour in which to print logs
use_default_instructions: set to False if you want to skip the standard
instructions used for the game master, e.g. do this if you plan to pass
custom instructions as a constant component instead.
log_color: color in which to print logs
"""
self._name = name
self._model = model
self._memory = memory
self._clock = clock
self._players = players
self._log_colour = log_colour
self._log_color = log_color
self._randomise_initiative = randomise_initiative
self._player_observes_event = player_observes_event
self._players_act_simultaneously = players_act_simultaneously
self._action_spec = action_spec or simulacrum_agent.DEFAULT_ACTION_SPEC
self._concurrent_action = concurrent_action

components = list(components or [])
if use_default_instructions:
instructions_component = generic_components.constant.ConstantComponent(
state=DEFAULT_GAME_MASTER_INSTRUCTIONS,
name='Instructions')
components.insert(0, instructions_component)

self._components = {}
for comp in components:
if comp.name() in self._components:
Expand Down Expand Up @@ -130,8 +158,8 @@ def get_history(self):
def get_data_frame(self):
return self._memory.get_data_frame()

def _print(self, entry, colour=None):
print(termcolor.colored(entry, colour or self._log_colour))
def _print(self, entry, color=None):
print(termcolor.colored(entry, color or self._log_color))

def reset(self):
self._last_chain = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def terminate_episode(self) -> bool:
return False


class GameMaterTest(parameterized.TestCase):
class GameMasterTest(parameterized.TestCase):

def test_calls_sequence(self):
gm_call_tracker = CallTrackingComponent()
Expand Down
27 changes: 10 additions & 17 deletions concordia/tests/concordia_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def _make_agent(
model: mock_model.MockModel,
clock: game_clock.MultiIntervalClock,
player_names: Sequence[str],
game_master_instructions: str,
agent_instructions: str,
mem_factory: blank_memories.MemoryFactory,
) -> basic_agent.BasicAgent:
"""Creates two agents with the same game master instructions."""
"""Creates two agents with same instructions."""
mem = mem_factory.make_blank_memory()

goal_metric = goal_achievement.GoalAchievementMetric(
Expand Down Expand Up @@ -146,7 +146,7 @@ def _make_agent(
clock,
[
components.constant.ConstantComponent(
'Instructions:', game_master_instructions
'Instructions:', agent_instructions
),
persona,
observation,
Expand Down Expand Up @@ -175,7 +175,6 @@ def _make_environment(
model: mock_model.MockModel,
clock: game_clock.MultiIntervalClock,
players: Sequence[basic_agent.BasicAgent],
game_master_instructions: str,
importance_model_gm: importance_function.ImportanceModel,
) -> game_master.GameMaster:
"""Creates a game master environment."""
Expand All @@ -190,9 +189,6 @@ def _make_environment(

shared_context = 'There is a hamlet named Riverbend.'

instructions_construct = components.constant.ConstantComponent(
game_master_instructions, 'Instructions'
)
facts_on_village = components.constant.ConstantComponent(
' '.join(shared_memories), 'General knowledge of Riverbend'
)
Expand All @@ -215,7 +211,6 @@ def _make_environment(
burner_memory_factory=mem_factory,
components=[player_status],
cap_nonplayer_characters=2,
game_master_instructions=game_master_instructions,
shared_context=shared_context,
verbose=False,
)
Expand Down Expand Up @@ -252,7 +247,6 @@ def _make_environment(
clock=clock,
players=players,
components=[
instructions_construct,
facts_on_village,
player_status,
schedule_construct,
Expand Down Expand Up @@ -281,7 +275,7 @@ def test_full_run(self):
],
)

game_master_instructions = 'This is a social science experiment.'
agent_instructions = 'This is a social science experiment.'

mem_factory = blank_memories.MemoryFactory(
model=model,
Expand All @@ -295,26 +289,25 @@ def test_full_run(self):
model=model,
clock=clock,
player_names=['Alice', 'Bob'],
game_master_instructions=game_master_instructions,
agent_instructions=agent_instructions,
mem_factory=mem_factory,
)
bob = _make_agent(
name='Bob',
model=model,
clock=clock,
player_names=['Alice', 'Bob'],
game_master_instructions=game_master_instructions,
agent_instructions=agent_instructions,
mem_factory=mem_factory,
)

players = [alice, bob]

env = _make_environment(
model,
clock,
players,
game_master_instructions,
importance_model,
model=model,
clock=clock,
players=players,
importance_model_gm=importance_model,
)

env.run_episode(12)
Expand Down
32 changes: 1 addition & 31 deletions examples/cyberball/cyberball.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -518,31 +518,6 @@
"## Build GM"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "siwglxrc6z2j"
},
"outputs": [],
"source": [
"game_master_instructions = (\n",
" 'This is a social science experiment. It is structured as a '\n",
" 'tabletop roleplaying game (like dungeons and dragons). You are the '\n",
" 'game master. You will describe the current situation to the '\n",
" 'participants in the experiment and then on the basis of what you '\n",
" 'tell them they will suggest actions for the character they control. '\n",
" 'Aside from you, each other participant controls just one character. '\n",
" 'You are the game master so you may control any non-player '\n",
" 'character. You will track the state of the world and keep it '\n",
" 'consistent as time passes in the simulation and the participants '\n",
" 'take actions and change things in their world. Remember that this '\n",
" 'is a serious social science experiment. It is not just a game. It '\n",
" 'need not be fun for the participants. Always use third-person '\n",
" 'limited perspective, even when speaking directly to the participants.'\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -566,8 +541,6 @@
"# @title Create components and externalities\n",
"player_names = [player.name for player in players]\n",
"\n",
"instructions = generic_components.constant.ConstantComponent(\n",
" game_master_instructions, 'Instructions')\n",
"general_knowledge_of_premise = generic_components.constant.ConstantComponent(\n",
" ', '.join(generic_memories), 'General knowledge of the game')\n",
"important_facts = generic_components.constant.ConstantComponent(\n",
Expand Down Expand Up @@ -614,7 +587,6 @@
" ball_status_component,\n",
" ],\n",
" cap_nonplayer_characters=2,\n",
" game_master_instructions=game_master_instructions,\n",
" shared_context=generic_context,\n",
" verbose=True,\n",
")\n",
Expand Down Expand Up @@ -674,8 +646,7 @@
" thought_chains_lib.attempt_to_most_likely_outcome,\n",
" thought_chains_lib.result_to_effect_caused_by_active_player,\n",
" account_for_agency_of_others\n",
"]\n",
"thought_chain.append(account_for_agency_of_others)"
"]"
]
},
{
Expand All @@ -694,7 +665,6 @@
" players=players,\n",
" update_thought_chain=thought_chain,\n",
" components=[\n",
" instructions,\n",
" general_knowledge_of_premise,\n",
" important_facts,\n",
" rules_of_the_game,\n",
Expand Down
Loading

0 comments on commit 6946283

Please sign in to comment.