Ejemplo n.º 1
0
def test_making_a_game_without_a_quest(play_the_game=False):
    rng_map = np.random.RandomState(1234)
    map_ = textworld.generator.make_small_map(1, rng_map)
    world = World.from_map(map_)
    world.set_player_room()  # First room generated (i.e. the only one).

    rng_objects = np.random.RandomState(1234)
    nb_objects = 10
    world.populate(nb_objects, rng=rng_objects)

    quests = []

    # Define the grammar we'll use.
    rng_grammar = np.random.RandomState(1234)
    grammar_flags = {
        "theme": "house",
        "include_adj": False,
        "only_last_action": True,
        "blend_instructions": True,
        "blend_descriptions": True,
        "refer_by_name_only": True,
        "instruction_extension": [],
    }
    grammar = textworld.generator.make_grammar(grammar_flags, rng=rng_grammar)

    # Generate the world representation.
    game = textworld.generator.make_game_with(world, quests, grammar)

    with make_temp_directory(prefix="test_render_wrapper") as tmpdir:
        options = textworld.GameOptions()
        options.path = tmpdir
        game_file = compile_game(game, options)

        if play_the_game:
            textworld.play(game_file)
Ejemplo n.º 2
0
    def test_win_action(self):
        g_rng.set_seed(2018)
        map_ = make_small_map(n_rooms=5, possible_door_states=["open"])
        world = World.from_map(map_)

        for max_depth in range(1, 3):
            for rule in data.get_rules().values():
                chain = sample_quest(world.state, rng=None, max_depth=max_depth,
                                     nb_retry=30, allow_partial_match=True, backward=True,
                                     rules_per_depth={0: [rule]}, exceptions=["r"])
                assert len(chain) == max_depth, rule.name

                # Build the quest by providing the actions.
                actions = [c.action for c in chain]
                quest = Quest(actions)
                tmp_world = World.from_facts(chain[0].state.facts)

                state = tmp_world.state
                for action in actions:
                    assert not state.is_applicable(quest.win_action)
                    state.apply(action)

                assert state.is_applicable(quest.win_action)

                # Build the quest by only providing the winning conditions.
                quest = Quest(actions=None, winning_conditions=actions[-1].postconditions)
                tmp_world = World.from_facts(chain[0].state.facts)

                state = tmp_world.state
                for action in actions:
                    assert not state.is_applicable(quest.win_action)
                    state.apply(action)

                assert state.is_applicable(quest.win_action)
Ejemplo n.º 3
0
def test_quest_winning_condition():
    g_rng.set_seed(2018)
    map_ = make_small_map(n_rooms=5, possible_door_states=["open"])
    world = World.from_map(map_)

    def _rule_to_skip(rule):
        # Examine, look and inventory shouldn't be used for chaining.
        if rule.name.startswith("look"):
            return True

        if rule.name.startswith("inventory"):
            return True

        if rule.name.startswith("examine"):
            return True

        return False

    for rule in KnowledgeBase.default().rules.values():
        if _rule_to_skip(rule):
            continue

        options = ChainingOptions()
        options.backward = True
        options.max_depth = 1
        options.create_variables = True
        options.rules_per_depth = [[rule]]
        options.restricted_types = {"r"}
        chain = sample_quest(world.state, options)
        assert len(chain.actions) > 0, rule.name
        event = Event(chain.actions)
        quest = Quest(win_events=[event])

        # Set the initial state required for the quest.
        tmp_world = World.from_facts(chain.initial_state.facts)
        game = make_game_with(tmp_world, [quest], make_grammar({}))

        if tmp_world.player_room is None:
            # Randomly place the player in the world since
            # the action doesn't care about where the player is.
            tmp_world.set_player_room()

        game_name = "test_quest_winning_condition_" + rule.name.replace(
            "/", "_")
        with make_temp_directory(prefix=game_name) as tmpdir:
            game_file = _compile_game(game, path=tmpdir)

            env = textworld.start(game_file)
            env.reset()
            game_state, _, done = env.step("look")
            assert not done
            assert not game_state.won

            game_state, _, done = env.step(event.commands[0])
            assert done
            assert game_state.won
Ejemplo n.º 4
0
    def test_generating_quests(self):
        g_rng.set_seed(2018)
        map_ = make_small_map(n_rooms=5, possible_door_states=["open"])
        world = World.from_map(map_)

        def _rule_to_skip(rule):
            # Examine, look and inventory shouldn't be used for chaining.
            if rule.name.startswith("look"):
                return True

            if rule.name.startswith("inventory"):
                return True

            if rule.name.startswith("examine"):
                return True

            return False

        for max_depth in range(1, 3):
            for rule in KnowledgeBase.default().rules.values():
                if _rule_to_skip(rule):
                    continue

                options = ChainingOptions()
                options.backward = True
                options.max_depth = max_depth
                options.max_length = max_depth
                options.create_variables = True
                options.rules_per_depth = [[rule]]
                options.restricted_types = {"r"}
                chain = sample_quest(world.state, options)

                # Build the quest by providing the actions.
                actions = chain.actions
                assert len(actions) == max_depth, rule.name
                quest = Quest(win_events=[Event(actions)])
                tmp_world = World.from_facts(chain.initial_state.facts)

                state = tmp_world.state
                for action in actions:
                    assert not quest.is_winning(state)
                    state.apply(action)

                assert quest.is_winning(state)

                # Build the quest by only providing the winning conditions.
                quest = Quest(
                    win_events=[Event(conditions=actions[-1].postconditions)])
                tmp_world = World.from_facts(chain.initial_state.facts)

                state = tmp_world.state
                for action in actions:
                    assert not quest.is_winning(state)
                    state.apply(action)

                assert quest.is_winning(state)
Ejemplo n.º 5
0
    def test_win_action(self):
        g_rng.set_seed(2018)
        map_ = make_small_map(n_rooms=5, possible_door_states=["open"])
        world = World.from_map(map_)

        for max_depth in range(1, 3):
            for rule in data.get_rules().values():
                options = ChainingOptions()
                options.backward = True
                options.max_depth = max_depth
                options.create_variables = True
                options.rules_per_depth = [[rule]]
                options.restricted_types = {"r"}
                chain = sample_quest(world.state, options)

                # Build the quest by providing the actions.
                actions = chain.actions
                if len(actions) != max_depth:
                    print(chain)
                assert len(actions) == max_depth, rule.name
                quest = Quest(actions)
                tmp_world = World.from_facts(chain.initial_state.facts)

                state = tmp_world.state
                for action in actions:
                    assert not state.is_applicable(quest.win_action)
                    state.apply(action)

                assert state.is_applicable(quest.win_action)

                # Build the quest by only providing the winning conditions.
                quest = Quest(actions=None,
                              winning_conditions=actions[-1].postconditions)
                tmp_world = World.from_facts(chain.initial_state.facts)

                state = tmp_world.state
                for action in actions:
                    assert not state.is_applicable(quest.win_action)
                    state.apply(action)

                assert state.is_applicable(quest.win_action)
Ejemplo n.º 6
0
def test_quest_winning_condition():
    g_rng.set_seed(2018)
    map_ = make_small_map(n_rooms=5, possible_door_states=["open"])
    world = World.from_map(map_)

    for rule in data.get_rules().values():
        chain = sample_quest(world.state,
                             rng=None,
                             max_depth=1,
                             nb_retry=20,
                             allow_partial_match=True,
                             backward=True,
                             rules_per_depth={0: [rule]},
                             exceptions=["r"])
        assert len(chain) > 0, rule.name
        quest = Quest([c.action for c in chain])

        # Set the initial state required for the quest.
        tmp_world = World.from_facts(chain[0].state.facts)
        game = make_game_with(tmp_world, [quest], make_grammar({}))

        if tmp_world.player_room is None:
            # Randomly place the player in the world since
            # the action doesn't care about where the player is.
            tmp_world.set_player_room()

        game_name = "test_quest_winning_condition_" + rule.name.replace(
            "/", "_")
        with make_temp_directory(prefix=game_name) as tmpdir:
            game_file = compile_game(game, game_name, games_folder=tmpdir)

            env = textworld.start(game_file)
            env.reset()
            game_state, _, done = env.step("look")
            assert not done
            assert not game_state.has_won

            game_state, _, done = env.step(quest.commands[0])
            assert done
            assert game_state.has_won
Ejemplo n.º 7
0
def generate_never_ending_game_old(args):
    g_rng.set_seed(args.seed)
    msg = "--max-steps {} --nb-objects {} --nb-rooms {} --seed {}"
    print(
        msg.format(args.max_steps, args.nb_objects, args.nb_rooms, g_rng.seed))
    print("Generating game...")

    map_ = textworld.generator.make_map(n_rooms=args.nb_rooms)
    world = World.from_map(map_)
    world.set_player_room()
    world.populate(nb_objects=args.nb_objects)
    grammar = textworld.generator.make_grammar(flags={"theme": "house"})

    quests = []  # No quest
    game = textworld.generator.make_game_with(world, quests, grammar)

    game_name = "neverending"
    game_file = textworld.generator.compile_game(game,
                                                 game_name,
                                                 force_recompile=True,
                                                 games_folder=args.output)
    return game_file
Ejemplo n.º 8
0
def test_quest_winning_condition():
    g_rng.set_seed(2018)
    map_ = make_small_map(n_rooms=5, possible_door_states=["open"])
    world = World.from_map(map_)

    for rule in data.get_rules().values():
        options = ChainingOptions()
        options.backward = True
        options.max_depth = 1
        options.create_variables = True
        options.rules_per_depth = [[rule]]
        options.restricted_types = {"r"}
        chain = sample_quest(world.state, options)
        assert len(chain.actions) > 0, rule.name
        quest = Quest(chain.actions)

        # Set the initial state required for the quest.
        tmp_world = World.from_facts(chain.initial_state.facts)
        game = make_game_with(tmp_world, [quest], make_grammar({}))

        if tmp_world.player_room is None:
            # Randomly place the player in the world since
            # the action doesn't care about where the player is.
            tmp_world.set_player_room()

        game_name = "test_quest_winning_condition_" + rule.name.replace(
            "/", "_")
        with make_temp_directory(prefix=game_name) as tmpdir:
            game_file = compile_game(game, game_name, games_folder=tmpdir)

            env = textworld.start(game_file)
            env.reset()
            game_state, _, done = env.step("look")
            assert not done
            assert not game_state.has_won

            game_state, _, done = env.step(quest.commands[0])
            assert done
            assert game_state.has_won
Ejemplo n.º 9
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.
    """
    kb = KnowledgeBase.default()

    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", "closed"]
        n_distractors = 10
    elif mode == "hard":
        door_states = ["open", "closed", "locked"]
        n_distractors = 20

    # 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_)

    # Randomly place the player.
    starting_room = None
    if len(world.rooms) > 1:
        starting_room = rng_map.choice(world.rooms)

    world.set_player_room(starting_room)
    # Add object the player has to pick up.
    types_counts = kb.types.count(world.state)
    obj_type = 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 = kb.types.count(world.state)
    objects = []
    distractor_types = uniquify(['c', 's'] + kb.types.descendants('c') +
                                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 = kb.types.count(world.state)
    obj_type = 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 = [kb.rules.get_matching("take.*")]
    options.chaining.backward = True
    options.chaining.rng = rng_quest
    #options.chaining.restricted_types = exceptions
    #exceptions = ["r", "c", "s", "d"] if mode == "easy" else ["r"]
    chain = textworld.generator.sample_quest(world.state, options.chaining)

    # 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 = 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
Ejemplo n.º 10
0
def make_game(mode: str, n_rooms: int, quest_length: int,
              grammar_flags: Mapping = {},
              seeds: Optional[Union[int, Dict[str, int]]] = None
              ) -> 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.
        n_rooms: Number of rooms in the game.
        quest_length: How far from the player the object to find should ideally
                      be placed.
        grammar_flags: Options for the grammar controlling the text generation
                       process.
        seeds: Seeds for the different generation processes.

               * If `None`, seeds will be sampled from
                 :py:data:`textworld.g_rng <textworld.utils.g_rng>`.
               * If `int`, it acts as a seed for a random generator that will be
                 used to sample the other seeds.
               * If dict, the following keys can be set:

                 * `'seed_map'`: control the map generation;
                 * `'seed_objects'`: control the type of objects and their
                   location;
                 * `'seed_quest'`: control the quest generation;
                 * `'seed_grammar'`: control the text generation.

                 For any key missing, a random number gets assigned (sampled
                 from :py:data:`textworld.g_rng <textworld.utils.g_rng>`).

    Returns:
        Generated game.
    """
    # Deal with any missing random seeds.
    seeds = get_seeds_for_game_generation(seeds)

    metadata = {}  # Collect infos for reproducibility.
    metadata["desc"] = "Treasure Hunter"
    metadata["mode"] = mode
    metadata["seeds"] = seeds
    metadata["world_size"] = n_rooms
    metadata["quest_length"] = quest_length
    metadata["grammar_flags"] = grammar_flags

    rng_map = np.random.RandomState(seeds['seed_map'])
    rng_objects = np.random.RandomState(seeds['seed_objects'])
    rng_quest = np.random.RandomState(seeds['seed_quest'])
    rng_grammar = np.random.RandomState(seeds['seed_grammar'])

    modes = ["easy", "medium", "hard"]
    if mode == "easy":
        door_states = None
        n_distractors = 0
    elif mode == "medium":
        door_states = ["open", "closed"]
        n_distractors = 10
    elif mode == "hard":
        door_states = ["open", "closed", "locked"]
        n_distractors = 20

    # Generate map.
    map_ = textworld.generator.make_map(n_rooms=n_rooms, rng=rng_map,
                                        possible_door_states=door_states)
    assert len(map_.nodes()) == n_rooms

    world = World.from_map(map_)

    # Randomly place the player.
    starting_room = None
    if len(world.rooms) > 1:
        starting_room = rng_map.choice(world.rooms)

    world.set_player_room(starting_room)

    # Add object the player has to pick up.
    types_counts = data.get_types().count(world.state)
    obj_type = data.get_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 = data.get_types().count(world.state)
    objects = []
    distractor_types = uniquify(['c', 's'] +
                                data.get_types().descendants('c') +
                                data.get_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 = data.get_types().count(world.state)
    obj_type = data.get_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).
    rules_per_depth = {0: data.get_rules().get_matching("take.*")}
    exceptions = ["r", "c", "s", "d"] if mode == "easy" else ["r"]
    chain = textworld.generator.sample_quest(world.state, rng_quest,
                                             max_depth=quest_length,
                                             allow_partial_match=False,
                                             exceptions=exceptions,
                                             rules_per_depth=rules_per_depth,
                                             nb_retry=5,
                                             backward=True)

    # Add objects needed for the quest.
    world.state = chain[0].state
    quest = Quest([c.action for c in chain])
    quest.set_failing_conditions([Proposition("in", [wrong_obj, world.inventory])])

    grammar = textworld.generator.make_grammar(flags=grammar_flags,
                                               rng=rng_grammar)
    game = textworld.generator.make_game_with(world, [quest], grammar)
    game.metadata = metadata
    mode_choice = modes.index(mode)
    uuid = "tw-treasure_hunter-{specs}-{flags}-{seeds}"
    uuid = uuid.format(specs=encode_seeds((mode_choice, n_rooms, quest_length)),
                       flags=encode_flags(grammar_flags),
                       seeds=encode_seeds([seeds[k] for k in sorted(seeds)]))
    game.metadata["uuid"] = uuid
    return game