Example #1
0
    def get_win_conditions(self, chain: Chain) -> Collection[Proposition]:
        """
        Given a chain of actions comprising a quest, return the set of propositions
        which must hold in a winning state. By default this will be the post-conditions
        of the final action.
        
        Parameters
        ----------
        chain:
            Chain of actions leading to goal state.
            
        Returns
        -------
        win_conditions:
           Set of propositions which must hold to end the quest succesfully.
        """

        win_conditions = set()

        # add post-conditions from last action
        post_props = set(chain.actions[-1].postconditions)
        win_conditions.update(post_props)

        return Event(conditions=win_conditions)
Example #2
0
    def get_win_conditions(self, chain: Chain) -> Collection[Proposition]:
        """
        Given a chain of actions comprising a quest, return the set of propositions
        which must hold in a winning state.
        
        Parameters
        ----------
        chain:
            Chain of actions leading to goal state.
            
        Returns
        -------
        win_conditions:
            Set of propositions which must hold to end the quest succesfully.
        """

        win_condition_type = self.options.win_condition

        win_conditions = set()

        final_state = chain.final_state
        pg = ProcessGraph()
        pg.from_tw_actions(chain.actions)

        if win_condition_type in [WinConditionType.OPS, WinConditionType.ALL]:

            # require all operations to have the correct inputs
            processed_props = set([
                prop for prop in final_state.facts if prop.name == 'processed'
            ])
            component_props = set([
                prop for prop in final_state.facts if prop.name == 'component'
            ])
            win_conditions.update(processed_props)
            win_conditions.update(component_props)

            # require all operations to be set to correct type
            op_type_props = set([
                prop for prop in final_state.facts
                if prop.name == 'tlq_op_type'
            ])
            win_conditions.update(op_type_props)

            # precedence propositions enforcing minimal ordering restraints between ops
            tG = nx.algorithms.dag.transitive_closure(pg.G)
            op_nodes = [
                n for n in tG.nodes()
                if KnowledgeBase.default().types.is_descendant_of(
                    n.var.type, ["op"])
            ]
            op_sg = nx.algorithms.dag.transitive_reduction(
                tG.subgraph(op_nodes))
            for e in op_sg.edges():
                op_1_node, op_2_node = e
                prec_prop = Proposition('preceeds',
                                        [op_1_node.var, op_2_node.var])
                win_conditions.update({prec_prop})

        if win_condition_type in [WinConditionType.ARGS, WinConditionType.ALL]:
            # require all descriptions to be set correctly
            desc_props = set([
                prop for prop in final_state.facts if prop.name == 'describes'
            ])
            win_conditions.update(desc_props)

        # add post-conditions from last action
        post_props = set(chain.actions[-1].postconditions)
        win_conditions.update(post_props)

        return Event(conditions=win_conditions)
Example #3
0
 def test_serialization(self):
     data = self.event.serialize()
     event = Event.deserialize(data)
     assert event == self.event
Example #4
0
    def setUpClass(cls):
        M = GameMaker()

        # Create a 'bedroom' room.
        R1 = M.new_room("bedroom")
        R2 = M.new_room("kitchen")
        M.set_player(R2)

        path = M.connect(R1.east, R2.west)
        path.door = M.new(type='d', name='wooden door')
        path.door.add_property("closed")

        carrot = M.new(type='f', name='carrot')
        lettuce = M.new(type='f', name='lettuce')
        R1.add(carrot)
        R1.add(lettuce)

        # Add a closed chest in R2.
        chest = M.new(type='c', name='chest')
        chest.add_property("open")
        R2.add(chest)

        # The goals
        commands = [
            "open wooden door", "go west", "take carrot", "go east",
            "drop carrot"
        ]
        cls.eventA = M.new_event_using_commands(commands)

        commands = [
            "open wooden door", "go west", "take lettuce", "go east",
            "insert lettuce into chest"
        ]
        cls.eventB = M.new_event_using_commands(commands)

        cls.losing_eventA = Event(conditions={M.new_fact("eaten", carrot)})
        cls.losing_eventB = Event(conditions={M.new_fact("eaten", lettuce)})

        cls.questA = Quest(win_events=[cls.eventA],
                           fail_events=[cls.losing_eventA])
        cls.questB = Quest(win_events=[cls.eventB],
                           fail_events=[cls.losing_eventB])
        cls.questC = Quest(win_events=[],
                           fail_events=[cls.losing_eventA, cls.losing_eventB])

        commands = [
            "open wooden door", "go west", "take lettuce", "go east",
            "insert lettuce into chest"
        ]
        cls.questC = M.new_quest_using_commands(commands)

        commands = ["open wooden door", "go west", "take carrot", "eat carrot"]
        cls.eating_carrot = M.new_event_using_commands(commands)
        commands = [
            "open wooden door", "go west", "take lettuce", "eat lettuce"
        ]
        cls.eating_lettuce = M.new_event_using_commands(commands)
        commands = [
            "open wooden door", "go west", "take lettuce", "go east",
            "insert lettuce into chest"
        ]

        M.quests = [cls.questA, cls.questB]
        cls.game = M.build()
Example #5
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