Exemple #1
0
def test_get_visible_objects_in():
    P = Variable("P")
    room = Variable("room", "r")
    chest = Variable("chest", "c")
    obj = Variable("obj", "o")

    # Closed chest.
    facts = [
        Proposition("at", [P, room]),
        Proposition("at", [chest, room]),
        Proposition("in", [obj, chest]),
        Proposition("closed", [chest])
    ]

    world = World.from_facts(facts)
    objects = world.get_visible_objects_in(world.player_room)
    assert obj in world.objects
    assert obj not in objects

    # Open chest.
    facts = [
        Proposition("at", [P, room]),
        Proposition("at", [chest, room]),
        Proposition("in", [obj, chest]),
        Proposition("open", [chest])
    ]

    world = World.from_facts(facts)
    objects = world.get_visible_objects_in(world.player_room)
    assert obj in world.objects
    assert obj in objects
Exemple #2
0
def build_state(locked_door=False):
    # Set up a world with two rooms and a few objecs.
    P = Variable("P")
    I = Variable("I")
    bedroom = Variable("bedroom", "r")
    kitchen = Variable("kitchen", "r")
    rusty_key = Variable("rusty key", "k")
    small_key = Variable("small key", "k")
    wooden_door = Variable("wooden door", "d")
    chest = Variable("chest", "c")
    cabinet = Variable("cabinet", "c")
    robe = Variable("robe", "o")

    state = State([
        Proposition("at", [P, bedroom]),
        Proposition("south_of", [kitchen, bedroom]),
        Proposition("north_of", [bedroom, kitchen]),
        Proposition("link", [bedroom, wooden_door, kitchen]),
        Proposition("link", [kitchen, wooden_door, bedroom]),

        Proposition("locked" if locked_door else "closed", [wooden_door]),

        Proposition("in", [rusty_key, I]),
        Proposition("match", [rusty_key, chest]),
        Proposition("locked", [chest]),
        Proposition("at", [chest, kitchen]),
        Proposition("in", [small_key, chest]),

        Proposition("match", [small_key, cabinet]),
        Proposition("locked", [cabinet]),
        Proposition("at", [cabinet, bedroom]),
        Proposition("in", [robe, cabinet]),
    ])

    return state
Exemple #3
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])
    ])

    options = ChainingOptions()
    options.backward = True
    options.max_depth = 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
Exemple #4
0
def test_state():
    state = State()

    P = Variable.parse("P")
    kitchen = Variable.parse("kitchen: r")
    study = Variable.parse("study: r")
    stove = Variable.parse("stove: o")
    at_kitchen = Proposition.parse("at(P, kitchen: r)")
    in_kitchen = Proposition.parse("in(stove: o, kitchen: r)")
    at_study = Proposition.parse("at(P, study: r)")

    assert not state.is_fact(at_kitchen)
    assert not state.is_fact(in_kitchen)
    assert not state.is_fact(at_study)
    assert len(state.variables) == 0
    assert len(state.variables_of_type("P")) == 0
    assert len(state.variables_of_type("r")) == 0
    assert len(state.variables_of_type("o")) == 0

    state.add_fact(at_kitchen)
    state.add_fact(in_kitchen)
    assert state.is_fact(at_kitchen)
    assert state.is_fact(in_kitchen)
    assert not state.is_fact(at_study)
    assert set(state.variables) == {P, kitchen, stove}
    assert state.variables_of_type("P") == {P}
    assert state.variables_of_type("r") == {kitchen}
    assert state.variables_of_type("o") == {stove}

    state.remove_fact(at_kitchen)
    assert not state.is_fact(at_kitchen)
    assert state.is_fact(in_kitchen)
    assert not state.is_fact(at_study)
    assert set(state.variables) == {kitchen, stove}
    assert len(state.variables_of_type("P")) == 0
    assert state.variables_of_type("r") == {kitchen}
    assert state.variables_of_type("o") == {stove}

    state.remove_fact(in_kitchen)
    assert not state.is_fact(at_kitchen)
    assert not state.is_fact(in_kitchen)
    assert not state.is_fact(at_study)
    assert len(state.variables) == 0
    assert len(state.variables_of_type("P")) == 0
    assert len(state.variables_of_type("r")) == 0
    assert len(state.variables_of_type("o")) == 0

    state.add_fact(at_study)
    assert not state.is_fact(at_kitchen)
    assert not state.is_fact(in_kitchen)
    assert state.is_fact(at_study)
    assert set(state.variables) == {P, study}
    assert state.variables_of_type("P") == {P}
    assert state.variables_of_type("r") == {study}
    assert len(state.variables_of_type("o")) == 0
Exemple #5
0
def test_serialization_deserialization():
    rule = KnowledgeBase.default().rules["go/east"]
    mapping = {
        Placeholder("r'"): Variable("room1", "r"),
        Placeholder("r"): Variable("room2"),
    }
    mapping.update(KnowledgeBase.default().types.constants_mapping)
    action = rule.instantiate(mapping)
    infos = action.serialize()
    action2 = Action.deserialize(infos)
    assert action == action2
Exemple #6
0
def test_get_objects_in_inventory():
    P = Variable("P")
    I = Variable("I")
    room = Variable("room", "r")
    obj = Variable("obj", "o")

    # Closed chest.
    facts = [Proposition("at", [P, room]), Proposition("in", [obj, I])]

    world = World.from_facts(facts)
    objects = world.get_objects_in_inventory()
    assert obj in world.objects
    assert obj in objects
Exemple #7
0
    def create_variable(self, state, ph, type_counts):
        """Create a new variable of the given type."""

        count = type_counts[ph.type]
        if not self.options.check_new_variable(state, ph.type, count):
            return None

        name = "{}_{}".format(ph.type, count)
        var = Variable(name, ph.type)
        while state.has_variable(var):
            name += "'"
            var = Variable(name, ph.type)

        type_counts[ph.type] += 1
        return var
Exemple #8
0
    def new(self,
            type: str,
            name: Optional[str] = None,
            desc: Optional[str] = None) -> Union[WorldEntity, WorldRoom]:
        """ Creates new entity given its type.

        Args:
            type: The type of the entity.
            name: The name of the entity.
            desc: The description of the entity.

        Returns:
            The newly created entity.

            * If the `type` is `'r'`, then a `WorldRoom` object is returned.
            * Otherwise, a `WorldEntity` is returned.
        """
        var_id = type
        if not KnowledgeBase.default().types.is_constant(type):
            var_id = get_new(type, self._types_counts)

        var = Variable(var_id, type)
        if type == "r":
            entity = WorldRoom(var, name, desc)
            self.rooms.append(entity)
        elif KnowledgeBase.default().types.is_descendant_of(type, ["g"]):
            name = var_id
            desc = "A global variable."
            entity = WorldEntity(var, name, desc)
        else:
            entity = WorldEntity(var, name, desc)

        self._entities[var_id] = entity
        return entity
Exemple #9
0
def test_populate_with():
    # setup
    P = Variable('P')
    I = Variable('I')
    room = Variable('room', 'r')
    facts = [Proposition('at', [P, room])]

    world = World.from_facts(facts)

    # test
    obj = Variable('obj', 'o')
    world.populate_with(objects=[obj])

    assert obj in world.objects
    assert (Proposition('at', [obj, room]) in world.facts
            or Proposition('in', [obj, I]) in world.facts)
Exemple #10
0
def test_type_hierarchy():
    assert list(a.ancestors) == []
    assert list(a.supertypes) == [a]
    assert list(a.descendants) == [b, c, d]
    assert list(a.subtypes) == [a, b, c, d]

    assert list(b.ancestors) == [a]
    assert list(b.supertypes) == [b, a]
    assert list(b.descendants) == [d]
    assert list(b.subtypes) == [b, d]

    assert list(c.ancestors) == [a]
    assert list(c.supertypes) == [c, a]
    assert list(c.descendants) == [d]
    assert list(c.subtypes) == [c, d]

    assert list(d.ancestors) == [b, c, a]
    assert list(d.supertypes) == [d, b, c, a]
    assert list(d.descendants) == []
    assert list(d.subtypes) == [d]

    va = Variable.parse("a")
    assert va.is_a(a)
    assert not va.is_a(b)
    assert not va.is_a(c)
    assert not va.is_a(d)

    vb = Variable.parse("b")
    assert vb.is_a(a)
    assert vb.is_a(b)
    assert not vb.is_a(c)
    assert not vb.is_a(d)

    vc = Variable.parse("c")
    assert vc.is_a(a)
    assert not vc.is_a(b)
    assert vc.is_a(c)
    assert not vc.is_a(d)

    vd = Variable.parse("d")
    assert vd.is_a(a)
    assert vd.is_a(b)
    assert vd.is_a(c)
    assert vd.is_a(d)
Exemple #11
0
def test_automatically_positioning_rooms():
    P = Variable("P")
    r1 = Variable("Room1", "r")
    r2 = Variable("Room2", "r")
    d = Variable("door", "d")
    facts = [Proposition("at", [P, r1])]
    world = World.from_facts(facts)
    assert len(world.rooms) == 1
    assert len(world.find_room_by_id(r1.name).exits) == 0

    world.add_fact(Proposition("link", [r1, d, r2]))
    assert len(world.rooms) == 2
    r1_entity = world.find_room_by_id(r1.name)
    r2_entity = world.find_room_by_id(r2.name)
    assert len(r1_entity.exits) == 1
    assert len(r2_entity.exits) == 1

    assert list(r1_entity.exits.keys())[0] == reverse_direction(
        list(r2_entity.exits.keys())[0])
Exemple #12
0
    def from_tw_actions(self, actions: Iterable[Action]) -> DiGraph:
        """ Construct internal graph given a TextWorld Quest. """
        # TODO verify validity?
        from tw_textlabs.generator.quest_generator import convert_to_compact_action
        G = nx.DiGraph()
        action_vars = [convert_to_compact_action(a) for a in actions]
        for action_var in action_vars:
            action_name = action_var.name
            if action_name == 'op_o_obtain':
                # add change state var as arg to this action so it gets processed
                action_var.vars.insert(0, action_var.change_state_vars[0])
            ents = action_var.vars
            for e in ents:
                self.init_ent(e)

            # ignore actions with single arg
            if len(ents) != 2:
                continue

            is_state_change_action = len(action_var.change_state_vars) > 0

            if not is_state_change_action:
                G.add_edge(self.curr_state(ents[0]),
                           self.curr_state(ents[1]),
                           action=action_name)

            # assuming just one material changes state per action
            else:
                assert (len(action_var.change_state_vars) == 1)
                v = action_var.change_state_vars[0]
                assert (v == ents[0])

                # second argument is the source for new one (such as device)
                source = self.curr_state(ents[1])
                # Add a new state for the device, we assume that each operation
                # on a material is at a new state.
                if self.curr_state(v) in G.nodes():
                    # entity already exists in graph
                    target = self.add_new_state(v)
                else:
                    # entity doesn't exist yet in graph, connect init node
                    target = self.curr_state(v)

                # Add 'result' type edge which corresponds to 'obtain' action
                G.add_edge(source, target, action='obtain')

        # add Generator dummy node to be source of graph (cosmetic)
        self.generator = EntityNode(var=Variable(name='START',
                                                 type=GENERATOR_DUMMY_TYPE),
                                    g=0)
        G.add_node(self.generator)
        self.G = G
        materials = self.get_start_material_nodes()
        for m in materials:
            self.G.add_edge(self.generator, m, action='take')
Exemple #13
0
    def test_count(self):
        rng = np.random.RandomState(1234)
        types_counts = {t: rng.randint(2, 10) for t in self.types.variables}

        state = State()
        for t in self.types.variables:
            v = Variable(get_new(t, types_counts), t)
            state.add_fact(Proposition("dummy", [v]))

        counts = self.types.count(state)
        for t in self.types.variables:
            assert counts[t] == types_counts[t], (counts[t], types_counts[t])
Exemple #14
0
def test_logic_parsing():
    P = Variable("P", "P")
    kitchen = Variable("kitchen", "r")
    egg = Variable("egg", "f")

    assert Variable.parse("P") == P
    assert Variable.parse("kitchen: r") == kitchen

    at_kitchen = Proposition("at", [P, kitchen])
    in_kitchen = Proposition("in", [egg, kitchen])
    raw_egg = Proposition("raw", [egg])
    cooked_egg = Proposition("cooked", [egg])

    assert Proposition.parse("at(P, kitchen: r)") == at_kitchen

    assert Signature.parse("at(P, r)") == at_kitchen.signature

    cook_egg = Action("cook", [at_kitchen, in_kitchen, raw_egg], [at_kitchen, in_kitchen, cooked_egg])
    assert Action.parse("cook :: $at(P, kitchen: r) & $in(egg: f, kitchen: r) & raw(egg: f) -> cooked(egg: f)") == cook_egg

    P = Placeholder("P", "P")
    r = Placeholder("r", "r")
    d = Placeholder("d", "d")
    rp = Placeholder("r'", "r")
    assert Placeholder.parse("P") == P
    assert Placeholder.parse("r") == r
    assert Placeholder.parse("d") == d
    assert Placeholder.parse("r'") == rp

    at_r = Predicate("at", [P, r])
    link = Predicate("link", [r, d, rp])
    unlocked = Predicate("unlocked", [d])
    at_rp = Predicate("at", [P, rp])
    assert Predicate.parse("link(r, d, r')") == link

    go = Rule("go", [at_r, link, unlocked], [link, unlocked, at_rp])
    assert Rule.parse("go :: at(P, r) & $link(r, d, r') & $unlocked(d) -> at(P, r')") == go

    # Make sure the types match in the whole expression
    assert_raises(ValueError, Rule.parse, "take :: $at(P, r) & $in(c, r) & in(o: k, c) -> in(o, I)")
Exemple #15
0
def maybe_instantiate_variables(rule, mapping, state, max_types_counts=None):
    types_counts = KnowledgeBase.default().types.count(state)

    # Instantiate variables if needed
    try:
        for ph in rule.placeholders:
            if mapping.get(ph) is None:
                name = get_new(ph.type, types_counts, max_types_counts)
                mapping[ph] = Variable(name, ph.type)
    except NotEnoughNounsError:
        return None

    return rule.instantiate(mapping)
Exemple #16
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]),
    ])

    options = ChainingOptions()
    options.backward = True
    options.max_depth = 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.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 #17
0
 def from_map(cls, map: networkx.Graph) -> "World":
     """
     Args:
         map: Graph defining the structure of the world.
     """
     world = cls()
     names = [
         d.get("name", "r_{}".format(i))
         for i, (n, d) in enumerate(map.nodes.items())
     ]
     rooms = OrderedDict(
         (n, Variable(names[i], "r")) for i, n in enumerate(map.nodes()))
     world.add_facts(graph2state(map, rooms))
     return world
Exemple #18
0
    def _update(self) -> None:
        """ Update the internal representation of the world.

        This method will create new entities based on facts. It should be called whenever
        backing facts are changed.
        """
        self._entities = OrderedDict()  # Clear entities.
        self.player = self._get_entity(Variable("P"))
        self.inventory = self._get_entity(Variable("I"))
        self._player_room = None
        self._process_rooms()
        self._process_objects()
        self._rooms = [
            entity for entity in self._entities.values()
            if isinstance(entity, WorldRoom)
        ]
        self._objects = [
            entity for entity in self._entities.values()
            if isinstance(entity, WorldObject)
        ]

        self._entities_per_type = defaultdict(list)
        for entity in self._entities.values():
            self._entities_per_type[entity.type].append(entity)
Exemple #19
0
def test_match():
    rule = Rule.parse("go :: at(P, r) & $link(r, d, r') & $free(r, r') & $free(r', r) -> at(P, r')")

    mapping = {
        Placeholder.parse("P"): Variable.parse("P"),
        Placeholder.parse("r"): Variable.parse("r1: r"),
        Placeholder.parse("r'"): Variable.parse("r2: r"),
        Placeholder.parse("d"): Variable.parse("d"),
    }

    action = Action.parse("go :: at(P, r1: r) & $link(r1: r, d, r2: r) & $free(r1: r, r2: r) & $free(r2: r, r1: r) -> at(P, r2: r)")
    assert rule.match(action) == mapping

    # Order shouldn't matter

    action = Action.parse("go :: $link(r1: r, d, r2: r) & $free(r1: r, r2: r) & $free(r2: r, r1: r) & at(P, r1: r) -> at(P, r2: r)")
    assert rule.match(action) == mapping

    action = Action.parse("go :: at(P, r1: r) & $link(r1: r, d, r2: r) & $free(r2: r, r1: r) & $free(r1: r, r2: r) -> at(P, r2: r)")
    assert rule.match(action) == mapping

    # Predicate matches can't conflict
    action = Action.parse("go :: at(P, r1: r) & $link(r1: r, d, r2: r) & $free(r2: r, r1: r) & $free(r1: r, r2: r) -> at(P, r3: r)")
    assert rule.match(action) == None
Exemple #20
0
def test_cannot_automatically_positioning_rooms():
    P = Variable("P")
    r0 = Variable("Room0", "r")
    r1 = Variable("Room1", "r")
    r2 = Variable("Room2", "r")
    r3 = Variable("Room3", "r")
    r4 = Variable("Room4", "r")
    r5 = Variable("Room5", "r")
    d = Variable("door", "d")

    facts = [Proposition("at", [P, r0])]
    facts.extend(connect(r0, 'north', r1))
    facts.extend(connect(r0, 'east', r2))
    facts.extend(connect(r0, 'south', r3))
    facts.extend(connect(r0, 'west', r4))

    world = World.from_facts(facts)
    npt.assert_raises(NoFreeExitError, world.add_fact,
                      Proposition("link", [r0, d, r5]))
Exemple #21
0
    def __init__(self, vtypes: List[VariableType]):
        self.variables_types = {vtype.type: vtype for vtype in vtypes}

        # Make some convenient attributes.
        self.types = [vt.type for vt in vtypes]
        self.names = [vt.name for vt in vtypes]
        self.constants = [t for t in self if self.is_constant(t)]
        self.variables = [t for t in self if not self.is_constant(t)]
        self.constants_mapping = {
            Placeholder(c): Variable(c)
            for c in self.constants
        }

        # Adjust variable type's parent and children references.
        for vt in vtypes:
            if vt.parent is not None:
                vt_parent = self[vt.parent]
                vt_parent.children.append(vt.type)
Exemple #22
0
def graph2state(G: networkx.Graph, rooms: Dict[str,
                                               Variable]) -> List[Proposition]:
    """ Convert Graph object to a list of `Proposition`.

    Args:
        G: Graph defining the structure of the world.
        rooms: information about the rooms in the world.
    """
    state = []
    for src, dest in G.edges():
        d = direction(src, dest)

        d_r = direction(dest, src)
        e = G[src][dest]

        room_src = rooms[src]
        room_dest = rooms[dest]
        if e["has_door"]:
            door = Variable(e['door_name'], "d")
            pred1 = Proposition("{}_of".format(d), [room_dest, room_src])
            pred2 = Proposition("{}_of".format(d_r), [room_src, room_dest])
            state.append(Proposition(e["door_state"], [door]))
            state.append(Proposition("link", [room_src, door, room_dest]))
            state.append(Proposition("link", [room_dest, door, room_src]))
            if e["door_state"] == "open":
                state.append(Proposition("free", [room_dest, room_src]))
                state.append(Proposition("free", [room_src, room_dest]))
        else:
            pred1 = Proposition("{}_of".format(d), [room_dest, room_src])
            pred2 = Proposition("{}_of".format(d_r), [room_src, room_dest])
            state.append(Proposition("free", [room_dest, room_src]))
            state.append(Proposition("free", [room_src, room_dest]))

        state.append(pred1)
        state.append(pred2)

    return state
Exemple #23
0
def test_used_names_is_updated(verbose=False):
    # Make generation throughout the framework reproducible.
    g_rng.set_seed(1234)

    # Generate a map that's shape in a cross with room0 in the middle.
    P = Variable('P')
    r = Variable('r_0', 'r')
    k1 = Variable('k_1', 'k')
    k2 = Variable('k_2', 'k')
    c1 = Variable('c_1', 'c')
    c2 = Variable('c_2', 'c')
    facts = [
        Proposition('at', [P, r]),
        Proposition('at', [k1, r]),
        Proposition('at', [k2, r]),
        Proposition('at', [c1, r]),
        Proposition('at', [c2, r]),
        Proposition('match', [k1, c1]),
        Proposition('match', [k2, c2])
    ]
    world = World.from_facts(facts)
    world.set_player_room()  # Set start room to the middle one.
    world.populate_room(10,
                        world.player_room)  # Add objects to the starting room.

    # Generate the world representation.
    grammar = tw_textlabs.generator.make_grammar({},
                                                 rng=np.random.RandomState(42))
    for k, v in grammar.grammar.items():
        grammar.grammar[k] = v[:2]  # Force reusing variables.

    game = tw_textlabs.generator.make_game_with(world, [], grammar)
    for entity_infos in game.infos.values():
        if entity_infos.name is None:
            continue

        assert entity_infos.name in grammar.used_names
Exemple #24
0
def test_make_world_with():
    r1 = Variable("r_0", "r")
    P = Variable('P')
    world = make_world_with(rooms=[r1])
    assert Proposition('at', [P, r1]) in world.facts
Exemple #25
0
def test_variables():
    for var in [P, bedroom, robe, counter, chest]:
        data = var.serialize()
        loaded_var = Variable.deserialize(data)
        assert loaded_var == var
Exemple #26
0
def make_game(mode: str, options: GameOptions) -> tw_textlabs.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:`tw_textlabs.GameOptions <tw_textlabs.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_ = tw_textlabs.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 = tw_textlabs.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 = tw_textlabs.generator.make_grammar(options.grammar, rng=rng_grammar)
    game = tw_textlabs.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
Exemple #27
0
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.

from tw_textlabs.generator.data import KnowledgeBase
from tw_textlabs.generator.vtypes import NotEnoughNounsError, get_new
from tw_textlabs.logic import Action, Placeholder, Proposition, Rule, State, Variable

P = Variable("P", "P")
I = Variable("I", "I")
bedroom = Variable("bedroom", "r")
kitchen = Variable("kitchen", "r")
old_key = Variable("old key", "k")
rusty_key = Variable("rusty key", "k")
small_key = Variable("small key", "k")
wooden_door = Variable("wooden door", "d")
glass_door = Variable("glass door", "d")
chest = Variable("chest", "c")
cabinet = Variable("cabinet", "c")
counter = Variable("counter", "s")
robe = Variable("robe", "o")


def maybe_instantiate_variables(rule, mapping, state, max_types_counts=None):
    types_counts = KnowledgeBase.default().types.count(state)

    # Instantiate variables if needed
    try:
        for ph in rule.placeholders:
            if mapping.get(ph) is None:
                name = get_new(ph.type, types_counts, max_types_counts)
                mapping[ph] = Variable(name, ph.type)
Exemple #28
0
def test_constraints():
    # Declare some variables.
    P = Variable("P", "P")
    I = Variable("I", "I")
    bedroom = Variable("bedroom", "r")
    kitchen = Variable("kitchen", "r")
    rusty_key = Variable("rusty key", "k")
    small_key = Variable("small key", "k")
    wooden_door = Variable("wooden door", "d")
    glass_door = Variable("glass door", "d")
    chest = Variable("chest", "c")
    cabinet = Variable("cabinet", "c")
    counter = Variable("counter", "s")
    robe = Variable("robe", "o")

    # Make sure the number of basic constraints matches the number
    # of constraints in constraints.txt
    basic_constraints = [
        k for k in KnowledgeBase.default().constraints.keys() if "-" not in k
    ]
    # assert len(basic_constraints) == 32

    # Doors can only have one state.
    door_states = ["open", "closed", "locked"]
    for door_state in door_states:
        state = State([Proposition(door_state, [wooden_door])])
        assert check_state(state)
        for door_state2 in door_states:
            if door_state == door_state2:
                continue

            state2 = state.copy()

            state2.add_fact(Proposition(door_state2, [glass_door]))  # New door
            assert check_state(state2)

            state2.add_fact(Proposition(door_state2, [wooden_door]))
            assert not check_state(state2)

    # Containers can only have one state.
    container_states = ["open", "closed", "locked"]
    for container_state in container_states:
        state = State([Proposition(container_state, [chest])])
        assert check_state(state)
        for container_state2 in container_states:
            if container_state == container_state2:
                continue

            state2 = state.copy()

            state2.add_fact(Proposition(container_state2,
                                        [cabinet]))  # New container
            assert check_state(state2)

            state2.add_fact(Proposition(container_state2, [chest]))
            assert not check_state(state2)

    # A player/supporter/container can only be at one place.
    for obj in [P, chest, counter]:
        assert check_state(State([Proposition("at", [obj, kitchen])]))
        assert check_state(State([Proposition("at", [obj, bedroom])]))
        assert not check_state(
            State([
                Proposition("at", [obj, kitchen]),
                Proposition("at", [obj, bedroom])
            ]))

    # An object is either in the player's inventory, in a container or on a supporter
    obj_locations = [
        Proposition("in", [robe, I]),
        Proposition("in", [robe, chest]),
        Proposition("on", [robe, counter])
    ]
    for obj_location in obj_locations:
        assert check_state(State([obj_location]))
        for obj_location2 in obj_locations:
            if obj_location == obj_location2: break
            assert not check_state(State([obj_location, obj_location2
                                          ])), "{}, {}".format(
                                              obj_location, obj_location2)

    # Only one key can match a container and vice-versa.
    assert check_state(State([Proposition("match", [rusty_key, chest])]))
    assert not check_state(
        State([
            Proposition("match", [small_key, chest]),
            Proposition("match", [rusty_key, chest])
        ]))
    assert not check_state(
        State([
            Proposition("match", [small_key, cabinet]),
            Proposition("match", [small_key, chest])
        ]))

    # Only one key can match a door and vice-versa.
    assert check_state(State([Proposition("match", [rusty_key, chest])]))
    assert not check_state(
        State([
            Proposition("match", [small_key, wooden_door]),
            Proposition("match", [rusty_key, wooden_door])
        ]))
    assert not check_state(
        State([
            Proposition("match", [small_key, glass_door]),
            Proposition("match", [small_key, wooden_door])
        ]))

    # A door can't be used to link more than two rooms.
    door = Variable("door", "d")
    room1 = Variable("room1", "r")
    room2 = Variable("room2", "r")
    room3 = Variable("room3", "r")
    assert not check_state(
        State([
            Proposition("link", [room1, door, room2]),
            Proposition("link", [room1, door, room3]),
        ]))

    door1 = Variable("door1", "d")
    door2 = Variable("door2", "d")
    room1 = Variable("room1", "r")
    room2 = Variable("room2", "r")
    assert not check_state(
        State([
            Proposition("link", [room1, door1, room2]),
            Proposition("link", [room1, door2, room2]),
        ]))
Exemple #29
0
def test_room_connections():
    room0 = Variable("room0", "r")
    room1 = Variable("room1", "r")
    room2 = Variable("room2", "r")

    # Only one connection can exist between two rooms.
    # r1
    # |
    # r0 - r1
    state = State([
        Proposition("north_of", [room1, room0]),
        Proposition("south_of", [room0, room1]),
        Proposition("east_of", [room1, room0]),
        Proposition("west_of", [room0, room1])
    ])

    assert not check_state(state)

    # Non Cartesian layout are allowed.
    # r1
    # |
    # r0 - r2 - r1
    state = State([
        Proposition("north_of", [room1, room0]),
        Proposition("south_of", [room0, room1]),
        Proposition("east_of", [room2, room0]),
        Proposition("west_of", [room0, room2]),
        Proposition("east_of", [room1, room2]),
        Proposition("west_of", [room2, room1])
    ])

    assert check_state(state)

    # A room cannot have more than 4 'link' propositions.
    room3 = Variable("room3", "r")
    room4 = Variable("room4", "r")
    room5 = Variable("room5", "r")
    door1 = Variable("door1", "d")
    door2 = Variable("door2", "d")
    door3 = Variable("door3", "d")
    door4 = Variable("door4", "d")
    door5 = Variable("door5", "d")

    state = State([
        Proposition("link", [room0, door1, room1]),
        Proposition("link", [room0, door2, room2]),
        Proposition("link", [room0, door3, room3]),
        Proposition("link", [room0, door4, room4]),
        Proposition("link", [room0, door5, room5])
    ])

    assert not check_state(state)
Exemple #30
0
def test_match_complex():
    rule = Rule.parse("combine/3 :: $at(P, r) & $correct_location(r) & $in(tool, I) & $in(tool', I) & $in(tool'', I) & in(o, I) & in(o', I) & in(o'', I) & $out(o''') & $used(slot) & used(slot') & used(slot'') -> in(o''', I) & free(slot') & free(slot'')")

    mapping = {
        Placeholder.parse("P"): Variable.parse("P"),
        Placeholder.parse("I"): Variable.parse("I"),
        Placeholder.parse("r"): Variable.parse("r"),
        Placeholder.parse("o"): Variable.parse("o1: o"),
        Placeholder.parse("o'"): Variable.parse("o2: o"),
        Placeholder.parse("o''"): Variable.parse("o3: o"),
        Placeholder.parse("o'''"): Variable.parse("o4: o"),
        Placeholder.parse("tool"): Variable.parse("tool1: tool"),
        Placeholder.parse("tool'"): Variable.parse("tool2: tool"),
        Placeholder.parse("tool''"): Variable.parse("tool3: tool"),
        Placeholder.parse("slot"): Variable.parse("slot1: slot"),
        Placeholder.parse("slot'"): Variable.parse("slot2: slot"),
        Placeholder.parse("slot''"): Variable.parse("slot3: slot"),
    }

    action = Action.parse("combine/3 :: $at(P, r) & $correct_location(r) & $in(tool1: tool, I) & $in(tool2: tool, I) & $in(tool3: tool, I) & in(o1: o, I) & in(o2: o, I) & in(o3: o, I) & $out(o4: o) & $used(slot1: slot) & used(slot2: slot) & used(slot3: slot) -> in(o4: o, I) & free(slot2: slot) & free(slot3: slot)")
    for _ in range(10000):
        assert rule.match(action) == mapping