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