in textworld/challenges/tw_treasure_hunter/treasure_hunter.py [0:0]
def make_game(mode: str, options: GameOptions) -> textworld.Game:
""" Make a Treasure Hunter game.
Arguments:
mode: Mode for the game where
* `'easy'`: rooms are all empty except where the two objects are
placed. Also, connections between rooms have no door.
* `'medium'`: adding closed doors and containers that might need
to be open in order to find the object.
* `'hard'`: adding locked doors and containers (necessary keys
will in the inventory) that might need to be unlocked (and open)
in order to find the object.
options:
For customizing the game generation (see
:py:class:`textworld.GameOptions <textworld.generator.game.GameOptions>`
for the list of available options).
Returns:
Generated game.
"""
metadata = {} # Collect infos for reproducibility.
metadata["desc"] = "Treasure Hunter"
metadata["mode"] = mode
metadata["seeds"] = options.seeds
metadata["world_size"] = options.nb_rooms
metadata["quest_length"] = options.quest_length
rngs = options.rngs
rng_map = rngs['map']
rng_objects = rngs['objects']
rng_quest = rngs['quest']
rng_grammar = rngs['grammar']
modes = ["easy", "medium", "hard"]
if mode == "easy":
door_states = None
n_distractors = 0
elif mode == "medium":
door_states = ["open"]
n_distractors = 10
elif mode == "hard":
door_states = ["open"]
n_distractors = 20
options.chaining.create_variables = True
options.chaining.allowed_types = ["k"]
# Generate map.
map_ = textworld.generator.make_map(n_rooms=options.nb_rooms, rng=rng_map,
possible_door_states=door_states)
assert len(map_.nodes()) == options.nb_rooms
world = World.from_map(map_)
# Add object the player has to pick up.
types_counts = options.kb.types.count(world.state)
obj_type = options.kb.types.sample(parent_type='o', rng=rng_objects, include_parent=True)
var_id = get_new(obj_type, types_counts)
right_obj = Variable(var_id, obj_type)
world.add_fact(Proposition("in", [right_obj, world.inventory]))
# Add containers and supporters to the world.
types_counts = options.kb.types.count(world.state)
objects = []
distractor_types = uniquify(['c', 's']
+ options.kb.types.descendants('c')
+ options.kb.types.descendants('s'))
for i in range(n_distractors):
obj_type = rng_objects.choice(distractor_types)
var_id = get_new(obj_type, types_counts) # This update the types_counts.
objects.append(Variable(var_id, obj_type))
world.populate_with(objects, rng=rng_objects)
# Add object the player should not pick up.
types_counts = options.kb.types.count(world.state)
obj_type = options.kb.types.sample(parent_type='o', rng=rng_objects, include_parent=True)
var_id = get_new(obj_type, types_counts)
wrong_obj = Variable(var_id, obj_type)
# Place it anywhere in the world.
world.populate_with([wrong_obj], rng=rng_objects)
# Generate a quest that finishes by taking something (i.e. the right
# object since it's the only one in the inventory).
options.chaining.rules_per_depth = [options.kb.rules.get_matching("take.*")]
restricted_rules = options.kb.rules.get_matching("take.*", "go.*", "open.*", "unlock.*")
options.chaining.rules_per_depth += [restricted_rules] * (options.quest_length - 1)
options.chaining.backward = True
options.chaining.rng = rng_quest
# Randomly place the player.
rooms = list(world.rooms)
rng_map.shuffle(rooms)
chain = None
for starting_room in rooms:
player_fact = world.set_player_room(starting_room)
try:
chain = textworld.generator.sample_quest(world.state, options.chaining)
break
except QuestGenerationError:
world.state.remove_fact(player_fact) # We'll try another starting location.
if chain is None:
msg = ("Current map configuration doesn't permit quest of length: {}."
"Try using a different value for the `--seed` argument.")
raise QuestGenerationError(msg.format(options.quest_length))
# Add objects needed for the quest.
world.state = chain.initial_state
event = Event(chain.actions)
quest = Quest(win_events=[event],
fail_events=[Event(conditions={Proposition("in", [wrong_obj, world.inventory])})])
grammar = textworld.generator.make_grammar(options.grammar, rng=rng_grammar)
game = textworld.generator.make_game_with(world, [quest], grammar)
game.metadata.update(metadata)
mode_choice = modes.index(mode)
uuid = "tw-treasure_hunter-{specs}-{grammar}-{seeds}"
uuid = uuid.format(specs=encode_seeds((mode_choice, options.nb_rooms, options.quest_length)),
grammar=options.grammar.uuid,
seeds=encode_seeds([options.seeds[k] for k in sorted(options.seeds)]))
game.metadata["uuid"] = uuid
return game