def test_going_through_door():
    P = Variable("P", "P")
    room = Variable("room", "r")
    kitchen = Variable("kitchen", "r")
    state = State(KnowledgeBase.default().logic)
    state.add_facts([
        Proposition("at", [P, room]),
        Proposition("north_of", [kitchen, room]),
        Proposition("free", [kitchen, room]),
        Proposition("free", [room, kitchen]),
        Proposition("south_of", [room, kitchen])
    ])

    options = ChainingOptions()
    options.backward = True
    options.max_depth = 3
    options.max_length = 3
    options.subquests = True
    options.create_variables = True
    options.rules_per_depth = [
        [KnowledgeBase.default().rules["take/c"], KnowledgeBase.default().rules["take/s"]],
        KnowledgeBase.default().rules.get_matching("go.*"),
        [KnowledgeBase.default().rules["open/d"]],
    ]

    chains = list(get_chains(state, options))
    assert len(chains) == 18
def test_parallel_quests():
    logic = GameLogic.parse("""
        type foo {
            rules {
                do_a :: not_a(foo) & $not_c(foo) -> a(foo);
                do_b :: not_b(foo) & $not_c(foo) -> b(foo);
                do_c :: $a(foo) & $b(foo) & not_c(foo) -> c(foo);
            }

            constraints {
                a_or_not_a :: a(foo) & not_a(foo) -> fail();
                b_or_not_b :: b(foo) & not_b(foo) -> fail();
                c_or_not_c :: c(foo) & not_c(foo) -> fail();
            }
        }
    """)
    kb = KnowledgeBase(logic, "")

    state = State(kb.logic, [
        Proposition.parse("a(foo)"),
        Proposition.parse("b(foo)"),
        Proposition.parse("c(foo)"),
    ])

    options = ChainingOptions()
    options.backward = True
    options.kb = kb

    options.max_depth = 3
    options.max_breadth = 1
    options.max_length = 3
    chains = list(get_chains(state, options))
    assert len(chains) == 2

    options.max_breadth = 2
    chains = list(get_chains(state, options))
    assert len(chains) == 3

    options.min_breadth = 2
    chains = list(get_chains(state, options))
    assert len(chains) == 1
    assert len(chains[0].actions) == 3
    assert chains[0].nodes[0].depth == 2
    assert chains[0].nodes[0].breadth == 2
    assert chains[0].nodes[0].parent == chains[0].nodes[2]
    assert chains[0].nodes[1].depth == 2
    assert chains[0].nodes[1].breadth == 1
    assert chains[0].nodes[1].parent == chains[0].nodes[2]
    assert chains[0].nodes[2].depth == 1
    assert chains[0].nodes[2].breadth == 1
    assert chains[0].nodes[2].parent is None

    options.min_breadth = 1
    options.create_variables = True
    state = State(kb.logic)
    chains = list(get_chains(state, options))
    assert len(chains) == 5
Exemple #3
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)
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
Exemple #5
0
def test_backward_chaining():
    P = Variable("P", "P")
    room = Variable("room", "r")
    kitchen = Variable("kitchen", "r")
    state = State(KnowledgeBase.default().logic, [
        Proposition("at", [P, room]),
        Proposition("north_of", [kitchen, room]),
        Proposition("south_of", [room, kitchen]),
    ])

    options = ChainingOptions()
    options.backward = True
    options.max_depth = 2
    options.max_length = 2
    options.subquests = True
    options.create_variables = True
    options.rules_per_depth = [
        [
            KnowledgeBase.default().rules["take/c"],
            KnowledgeBase.default().rules["take/s"]
        ],
        [KnowledgeBase.default().rules["open/c"]],
    ]
    options.restricted_types = {"d"}

    chains = list(get_chains(state, options))
    assert len(chains) == 3

    options = ChainingOptions()
    options.backward = True
    options.max_depth = 3
    options.max_length = 3
    options.subquests = True
    options.create_variables = True
    options.rules_per_depth = [
        [KnowledgeBase.default().rules["put"]],
        [KnowledgeBase.default().rules["go/north"]],
        [KnowledgeBase.default().rules["take/c"]],
    ]
    options.restricted_types = {"d"}

    chains = list(get_chains(state, options))
    assert len(chains) == 3
Exemple #6
0
def make_game(world_size: int,
              nb_objects: int,
              quest_length: int,
              grammar_flags: Mapping = {},
              rngs: Optional[Dict[str, RandomState]] = None) -> Game:
    """
    Make a game (map + objects + quest).

    Arguments:
        world_size: Number of rooms in the world.
        nb_objects: Number of objects in the world.
        quest_length: Minimum nb. of actions the quest requires to be completed.
        grammar_flags: Options for the grammar.

    Returns:
        Generated game.
    """
    if rngs is None:
        rngs = {}
        rng = g_rng.next()
        rngs['rng_map'] = RandomState(rng.randint(65635))
        rngs['rng_objects'] = RandomState(rng.randint(65635))
        rngs['rng_quest'] = RandomState(rng.randint(65635))
        rngs['rng_grammar'] = RandomState(rng.randint(65635))

    # Generate only the map for now (i.e. without any objects)
    world = make_world(world_size, nb_objects=0, rngs=rngs)

    # Sample a quest according to quest_length.
    options = ChainingOptions()
    options.backward = True
    options.max_depth = quest_length
    options.create_variables = True
    options.rng = rngs['rng_quest']
    options.restricted_types = {"r", "d"}
    chain = sample_quest(world.state, options)
    quest = Quest(chain.actions)

    # Set the initial state required for the quest.
    world.state = chain.initial_state

    # Add distractors objects (i.e. not related to the quest)
    world.populate(nb_objects, rng=rngs['rng_objects'])

    grammar = make_grammar(grammar_flags, rng=rngs['rng_grammar'])
    game = make_game_with(world, [quest], grammar)
    return game
Exemple #7
0
def main():
    args = parse_args()

    # Load game for which to sample quests for.
    game = Game.load(args.game.replace(".ulx", ".json"))

    options = ChainingOptions()
    options.backward = False
    options.max_depth = args.quest_length
    options.max_breadth = args.quest_breadth
    options.rules_per_depth = {}
    options.create_variables = False
    options.rng = np.random.RandomState(args.seed)

    # Sample quests.
    chains = []
    for i in range(args.nb_quests):
        chain = sample_quest(game.world.state, options)
        chains.append(chain)

    inform7 = Inform7Game(game)
    print_chains(chains, inform7)

    # Convert chains to networkx graph/tree
    filename_world = pjoin(args.output, "sample_world.png")
    filename_tree = pjoin(args.output, "sample_tree.svg")
    filename_graph = pjoin(args.output, "sample_graph.svg")
    G, labels = build_tree_from_chains(chains, inform7)
    if len(G) > 0:
        image = visualize(game)
        image.save(filename_world)
        tree = nx.bfs_tree(G, "root")
        save_graph_to_svg(tree, labels, filename_tree)
        save_graph_to_svg(G, labels, filename_graph)
    else:
        try:
            os.remove(filename_world)
            os.remove(filename_tree)
            os.remove(filename_graph)
        except:
            pass
Exemple #8
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)
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
def test_parallel_quests_navigation():
    logic = GameLogic.parse("""
        type P {
        }

        type I {
        }

        type r {
            rules {
                move :: at(P, r) & $free(r, r') -> at(P, r');
            }

            constraints {
                atat :: at(P, r) & at(P, r') -> fail();
            }
        }

        type o {
            rules {
                take :: $at(P, r) & at(o, r) -> in(o, I);
            }

            constraints {
                inat :: in(o, I) & at(o, r) -> fail();
            }
        }

        type flour : o {
        }

        type eggs : o {
        }

        type cake {
            rules {
                bake :: in(flour, I) & in(eggs, I) -> in(cake, I) & in(flour, cake) & in(eggs, cake);
            }

            constraints {
                inincake :: in(o, I) & in(o, cake) -> fail();
                atincake :: at(o, r) & in(o, cake) -> fail();
            }
        }
    """)
    kb = KnowledgeBase(logic, "")

    state = State(kb.logic, [
        Proposition.parse("at(P, r3: r)"),
        Proposition.parse("free(r2: r, r3: r)"),
        Proposition.parse("free(r1: r, r2: r)"),
    ])

    bake = [kb.logic.rules["bake"]]
    non_bake = [r for r in kb.logic.rules.values() if r.name != "bake"]

    options = ChainingOptions()
    options.backward = True
    options.create_variables = True
    options.min_depth = 3
    options.max_depth = 3
    options.min_breadth = 2
    options.max_breadth = 2
    options.max_length = 6
    options.kb = kb
    options.rules_per_depth = [bake, non_bake, non_bake]
    options.restricted_types = {"P", "r"}
    chains = list(get_chains(state, options))
    assert len(chains) == 2