Пример #1
0
def test_print_chains_backwards():
    from textworld.generator import print_chains

    allowed_rules = data.get_rules().get_matching("take/.*")
    allowed_rules += data.get_rules().get_matching("go.*")
    allowed_rules += data.get_rules().get_matching("insert.*", "put.*")
    allowed_rules += data.get_rules().get_matching("open.*", "close.*")
    allowed_rules += data.get_rules().get_matching("lock.*", "unlock.*")
    allowed_rules += data.get_rules().get_matching("eat.*")

    # No possible action since the wooden door is locked and
    # the player doesn't have the key.
    state = build_state(locked_door=True)
    tree = chaining.get_chains(
        state,
        max_depth=5,
        rules_per_depth={i: allowed_rules
                         for i in range(5)})
    chains = list(tree.traverse_preorder())
    assert len(chains) == 0

    # The door is now closed instead of locked.
    state = build_state(locked_door=False)
    tree = chaining.get_chains(
        state,
        max_depth=5,
        rules_per_depth={i: allowed_rules
                         for i in range(5)})
    chains = list(tree.traverse_preorder())

    # Only validates that printing chains does not raise exception.
    with testing.capture_stdout() as stdout:
        print_chains(chains, backward=True)
        stdout.seek(0)
        assert len(stdout.read()) > 0
Пример #2
0
def test_to_networkx():
    # Below copied from test_chaining()
    allowed_rules = data.get_rules().get_matching("take/.*")
    allowed_rules += data.get_rules().get_matching("go.*")
    allowed_rules += data.get_rules().get_matching("insert.*", "put.*")
    allowed_rules += data.get_rules().get_matching("open.*", "close.*")
    allowed_rules += data.get_rules().get_matching("lock.*", "unlock.*")
    allowed_rules += data.get_rules().get_matching("eat.*")

    # No possible action since the wooden door is locked and
    # the player doesn't have the key.
    state = build_state(locked_door=True)
    tree = chaining.get_chains(
        state,
        max_depth=5,
        rules_per_depth={i: allowed_rules
                         for i in range(5)})
    chains = list(tree.traverse_preorder())
    assert len(chains) == 0

    # The door is now closed instead of locked.
    state = build_state(locked_door=False)
    tree = chaining.get_chains(
        state,
        max_depth=5,
        rules_per_depth={i: allowed_rules
                         for i in range(5)})
    G, labels = tree.to_networkx()
    assert G is not None
    assert labels is not None
    assert len(labels) > 0
Пример #3
0
def test_chaining():
    # The following test depends on the available rules,
    # so instead of depending on what is in rules.txt,
    # we define the allowed_rules to used.
    allowed_rules = data.get_rules().get_matching("take/.*")
    allowed_rules += data.get_rules().get_matching("go.*")
    allowed_rules += data.get_rules().get_matching("insert.*", "put.*")
    allowed_rules += data.get_rules().get_matching("open.*", "close.*")
    allowed_rules += data.get_rules().get_matching("lock.*", "unlock.*")
    allowed_rules += data.get_rules().get_matching("eat.*")

    class Options(ChainingOptions):
        def get_rules(self, depth):
            return allowed_rules

    options = Options()
    options.max_depth = 5

    # No possible action since the wooden door is locked and
    # the player doesn't have the key.
    state = build_state(locked_door=True)
    chains = list(get_chains(state, options))
    assert len(chains) == 0

    # The door is now closed instead of locked.
    state = build_state(locked_door=False)
    chains = list(get_chains(state, options))
    assert len(chains) == 5

    # With more depth.
    state = build_state(locked_door=False)
    options.max_depth = 20
    chains = list(get_chains(state, options))
    assert len(chains) == 9
Пример #4
0
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
Пример #5
0
def test_backward_chaining():
    P = Variable("P", "P")
    room = Variable("room", "r")
    kitchen = Variable("kitchen", "r")
    state = State([
        Proposition("at", [P, room]),
        Proposition("north_of", [kitchen, room]),
        Proposition("south_of", [room, kitchen]),
    ])

    rules_per_depth = {
        0: [data.get_rules()["take/c"],
            data.get_rules()["take/s"]],
        1: [data.get_rules()["open/c"]]
    }

    tree_of_possible = chaining.get_chains(state,
                                           max_depth=2,
                                           allow_partial_match=True,
                                           exceptions=['d'],
                                           rules_per_depth=rules_per_depth,
                                           backward=True)

    chains = list(tree_of_possible.traverse_preorder(subquests=True))
    assert len(chains) == 3
    for chain in chains:
        for depth, action in enumerate(chain):
            assert action.action.name in [
                rule.name for rule in rules_per_depth[depth]
            ]

    rules_per_depth = {
        0: [data.get_rules()["put"]],
        1: [data.get_rules()["go/north"]],
        2: [data.get_rules()["take/c"]]
    }

    tree_of_possible = chaining.get_chains(state,
                                           max_depth=3,
                                           allow_partial_match=True,
                                           exceptions=['d'],
                                           rules_per_depth=rules_per_depth,
                                           backward=True)

    chains = list(tree_of_possible.traverse_preorder(subquests=True))
    assert len(chains) == 3
    for chain in chains:
        for depth, action in enumerate(chain):
            assert action.action.name in [
                rule.name for rule in rules_per_depth[depth]
            ]
Пример #6
0
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
Пример #7
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
Пример #8
0
def test_chaining():
    # The following test depends on the available rules,
    # so instead of depending on what is in rules.txt,
    # we define the allowed_rules to used.
    allowed_rules = data.get_rules().get_matching("take/.*")
    allowed_rules += data.get_rules().get_matching("go.*")
    allowed_rules += data.get_rules().get_matching("insert.*", "put.*")
    allowed_rules += data.get_rules().get_matching("open.*", "close.*")
    allowed_rules += data.get_rules().get_matching("lock.*", "unlock.*")
    allowed_rules += data.get_rules().get_matching("eat.*")

    # No possible action since the wooden door is locked and
    # the player doesn't have the key.
    state = build_state(locked_door=True)
    tree = chaining.get_chains(
        state,
        max_depth=5,
        rules_per_depth={i: allowed_rules
                         for i in range(5)})
    chains = list(tree.traverse_preorder())
    assert len(chains) == 0

    # The door is now closed instead of locked.
    state = build_state(locked_door=False)
    tree = chaining.get_chains(
        state,
        max_depth=5,
        rules_per_depth={i: allowed_rules
                         for i in range(5)})
    chains = list(tree.traverse_preorder())
    # chaining.print_chains(chains)
    assert len(chains) == 5

    # With more depth.
    state = build_state(locked_door=False)
    tree = chaining.get_chains(
        state,
        max_depth=20,
        rules_per_depth={i: allowed_rules
                         for i in range(20)})
    chains = list(tree.traverse_preorder())
    assert len(chains) == 9
Пример #9
0
def test_chaining():
    # The following test depends on the available rules,
    # so instead of depending on what is in rules.txt,
    # we define the allowed_rules to used.
    allowed_rules = KnowledgeBase.default().rules.get_matching("take/.*")
    allowed_rules += KnowledgeBase.default().rules.get_matching("go.*")
    allowed_rules += KnowledgeBase.default().rules.get_matching("insert.*", "put.*")
    allowed_rules += KnowledgeBase.default().rules.get_matching("open.*", "close.*")
    allowed_rules += KnowledgeBase.default().rules.get_matching("lock.*", "unlock.*")
    allowed_rules += KnowledgeBase.default().rules.get_matching("eat.*")

    class Options(ChainingOptions):
        def get_rules(self, depth):
            return allowed_rules

    options = Options()
    options.max_depth = 5
    options.max_length = 5

    # No possible action since the wooden door is locked and
    # the player doesn't have the key.
    state = build_state(locked_door=True)
    chains = list(get_chains(state, options))
    assert len(chains) == 0

    # Since there are no chains, trying to sample a quest will raise an error.
    npt.assert_raises(QuestGenerationError, sample_quest, state, options)

    # The door is now closed instead of locked.
    state = build_state(locked_door=False)
    chains = list(get_chains(state, options))
    assert len(chains) == 5

    # With more depth.
    state = build_state(locked_door=False)
    options.max_depth = 20
    options.max_length = 20
    chains = list(get_chains(state, options))
    assert len(chains) == 9
Пример #10
0
def test_applying_actions():
    state = build_state(locked_door=False)
    options = ChainingOptions()
    options.backward = True
    options.max_depth = 5
    chains = list(get_chains(state, options))

    expected_state = state
    for chain in chains:
        state = chain.initial_state.copy()
        for action in chain.actions:
            assert state.apply(action)

        assert expected_state == state
Пример #11
0
def test_reversing_action():
    state = build_state(locked_door=False)
    tree = chaining.get_chains(state, max_depth=5)
    chains = list(tree.traverse_preorder())

    expected_state = state
    for chain in chains:
        state = chain[-1].state.copy()
        for node in chain[0:][::-1]:
            action = node.action
            assert state.apply(action.inverse())
            assert chaining.check_state(state)

        assert expected_state == state
Пример #12
0
def test_going_through_door():
    P = Variable("P", "P")
    room = Variable("room", "r")
    kitchen = Variable("kitchen", "r")
    state = State()
    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])
    ])

    # Sample quests.
    chains = []
    rules_per_depth = {
        0: [data.get_rules()["take/c"],
            data.get_rules()["take/s"]],
        1: data.get_rules().get_matching("go.*"),
        2: [data.get_rules()["open/d"]]
    }
    tree_of_possible = chaining.get_chains(state,
                                           max_depth=3,
                                           allow_partial_match=True,
                                           exceptions=[],
                                           rules_per_depth=rules_per_depth,
                                           backward=True)
    chains = list(tree_of_possible.traverse_preorder(subquests=True))
    # chaining.print_chains(chains)
    # 1. take/c(P, room, c_0, o_0, I)
    # 2. take/c(P, room, c_0, o_0, I) -> go/north(P, r_0, room)
    # 3. take/c(P, room, c_0, o_0, I) -> go/north(P, r_0, room) -> open/d(P, r_0, d_0, room)
    # 4. take/c(P, room, c_0, o_0, I) -> go/south(P, kitchen, room)
    # 5. take/c(P, room, c_0, o_0, I) -> go/south(P, kitchen, room) -> open/d(P, kitchen, d_0, room)
    # 6. take/c(P, room, c_0, o_0, I) -> go/east(P, r_0, room)
    # 7. take/c(P, room, c_0, o_0, I) -> go/east(P, r_0, room) -> open/d(P, r_0, d_0, room)
    # 8. take/c(P, room, c_0, o_0, I) -> go/west(P, r_0, room)
    # 9. take/c(P, room, c_0, o_0, I) -> go/west(P, r_0, room) -> open/d(P, r_0, d_0, room)
    # 10. take/s(P, room, s_0, o_0, I)
    # 11. take/s(P, room, s_0, o_0, I) -> go/north(P, r_0, room)
    # 12. take/s(P, room, s_0, o_0, I) -> go/north(P, r_0, room) -> open/d(P, r_0, d_0, room)
    # 13. take/s(P, room, s_0, o_0, I) -> go/south(P, kitchen, room)
    # 14. take/s(P, room, s_0, o_0, I) -> go/south(P, kitchen, room) -> open/d(P, kitchen, d_0, room)
    # 15. take/s(P, room, s_0, o_0, I) -> go/east(P, r_0, room)
    # 16. take/s(P, room, s_0, o_0, I) -> go/east(P, r_0, room) -> open/d(P, r_0, d_0, room)
    # 17. take/s(P, room, s_0, o_0, I) -> go/west(P, r_0, room)
    # 18. take/s(P, room, s_0, o_0, I) -> go/west(P, r_0, room) -> open/d(P, r_0, d_0, room)
    assert len(chains) == 18
Пример #13
0
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