Esempio n. 1
0
    def change_grammar(self, grammar: Grammar) -> None:
        """ Changes the grammar used and regenerate all text. """

        self.grammar = grammar
        _gen_commands = gen_commands_from_actions
        if self.grammar:
            from textworld.generator.inform7 import Inform7Game
            from textworld.generator.text_generation import generate_text_from_grammar
            inform7 = Inform7Game(self)
            _gen_commands = inform7.gen_commands_from_actions
            generate_text_from_grammar(self, self.grammar)

        for quest in self.quests:
            # TODO: should have a generic way of generating text commands from actions
            #       instead of relying on inform7 convention.
            for event in quest.win_events:
                event.commands = _gen_commands(event.actions)

            if quest.win_events:
                quest.commands = quest.win_events[0].commands

        # Check if we can derive a global winning policy from the quests.
        if self.grammar:
            from textworld.generator.text_generation import describe_event
            policy = GameProgression(self).winning_policy
            if policy:
                mapping = {k: info.name for k, info in self._infos.items()}
                commands = [a.format_command(mapping) for a in policy]
                self.metadata["walkthrough"] = commands
                self.objective = describe_event(Event(policy), self,
                                                self.grammar)
Esempio n. 2
0
    def test_game_with_infinite_max_score(self):
        M = textworld.GameMaker()
        museum = M.new_room("Museum")

        statue = M.new(type="o", name="golden statue")
        pedestal = M.new(type="s", name="pedestal")

        pedestal.add(statue)
        museum.add(pedestal)

        M.set_player(museum)

        M.quests = [
            Quest(win_events=[
                Event(conditions=[M.new_fact('in', statue, M.inventory)]),
            ],
                  reward=10,
                  optional=True,
                  repeatable=True),
            Quest(win_events=[
                Event(conditions=[M.new_fact('at', statue, museum)]),
            ],
                  reward=0)
        ]
        M.set_walkthrough(
            ["take golden statue from pedestal", "look", "drop statue"])

        game = M.build()
        assert game.max_score == np.inf

        inform7 = Inform7Game(game)
        game_progress = GameProgression(game)
        assert len(game_progress.quest_progressions) == len(game.quests)

        # Following the actions associated to the last quest actually corresponds
        # to solving the whole game.
        for action in game_progress.winning_policy:
            assert not game_progress.done
            game_progress.update(action)

        assert game_progress.done
        assert not game_progress.quest_progressions[
            0].completed  # Repeatable quests can never be completed.
        assert game_progress.quest_progressions[
            1].completed  # Mandatory quest.

        # Solve the game while completing the optional quests.
        game_progress = GameProgression(game)
        for command in [
                "take golden statue from pedestal", "look", "look",
                "drop golden statue"
        ]:
            _apply_command(command, game_progress, inform7)

        progressions = game_progress.quest_progressions
        assert not progressions[0].done  # Repeatable quests can never be done.
        assert progressions[
            0].nb_completions == 3  # They could have been completed a number of times, though.
        assert progressions[1].done
        assert game_progress.score == 30
    def init(self,
             output: str,
             game: Game,
             state_tracking: bool = False,
             compute_intermediate_reward: bool = False):
        """
        Initialize the game state and set tracking parameters.
        The tracking parameters, state_tracking and compute_intermediate_reward,
        are computationally expensive, so are disabled by default.

        :param output: Introduction text displayed when a game starts.
        :param game: The glulx game to run
        :param state_tracking: Whether to use state tracking
        :param compute_intermediate_reward: Whether to compute the intermediate reward
        """
        output = _strip_input_prompt_symbol(output)
        _, output = _detect_i7_events_debug_tags(output)
        self._extra_infos, output = _detect_extra_infos(output)

        super().init(output)
        self._game = game
        self._game_progression = GameProgression(game,
                                                 track_quests=state_tracking)
        self._inform7 = Inform7Game(game)
        self._state_tracking = state_tracking
        self._compute_intermediate_reward = compute_intermediate_reward and len(
            game.quests) > 0
        self._objective = game.objective
        self._score = 0
        self._max_score = self._game.max_score
def _find_action(command: str, actions: Iterable[Action], inform7: Inform7Game) -> None:
    """ Apply a text command to a game_progression object. """
    commands = inform7.gen_commands_from_actions(actions)
    for action, cmd in zip(actions, commands):
        if command == cmd:
            return action

    raise ValueError("No action found matching command: {}.".format(command))
Esempio n. 5
0
def _apply_command(command: str, game_progression: GameProgression, inform7: Inform7Game) -> None:
    """ Apply a text command to a game_progression object.
    """
    valid_commands = inform7.gen_commands_from_actions(game_progression.valid_actions)
    for action, cmd in zip(game_progression.valid_actions, valid_commands):
        if command == cmd:
            game_progression.update(action)
            return

    raise ValueError("Not a valid command: {}. Expected: {}".format(command, valid_commands))
Esempio n. 6
0
    def main_quest(self):
        if self._main_quest is None:
            from textworld.generator.inform7 import Inform7Game
            from textworld.generator.text_generation import assign_description_to_quest
            inform7 = Inform7Game(self)
            self._main_quest = Quest(actions=GameProgression(self).winning_policy)
            self._main_quest.desc = assign_description_to_quest(self._main_quest, self, self.grammar)
            self._main_quest.commands = inform7.gen_commands_from_actions(self._main_quest.actions)

        return self._main_quest
Esempio n. 7
0
    def load(self, gamefile: str) -> None:
        self._gamefile = os.path.splitext(gamefile)[0] + ".json"
        if not os.path.isfile(self._gamefile):
            raise MissingGameInfosError(self)

        try:
            self._game = self._wrapped_env._game
        except AttributeError:
            self._game = Game.load(self._gamefile)
        self._inform7 = Inform7Game(self._game)
        self._wrapped_env.load(gamefile)
Esempio n. 8
0
    def change_grammar(self, grammar: Grammar) -> None:
        """ Changes the grammar used and regenerate all text. """
        self.grammar = grammar
        if self.grammar is None:
            return

        from textworld.generator.inform7 import Inform7Game
        from textworld.generator.text_generation import generate_text_from_grammar
        inform7 = Inform7Game(self)

        generate_text_from_grammar(self, self.grammar)
        for quest in self.quests:
            # TODO: should have a generic way of generating text commands from actions
            #       instead of relying on inform7 convention.
            quest.commands = inform7.gen_commands_from_actions(quest.actions)
Esempio n. 9
0
    def main_quest(self):
        if self._main_quest is None:
            from textworld.generator.inform7 import Inform7Game
            from textworld.generator.text_generation import assign_description_to_quest
            inform7 = Inform7Game(self)
            # TODO: defining the main quest using the actions might not be precise enough
            #     since it only looks at the final action's postconditions.

            # Check if we can derive a winning policy from the quests.
            policy = GameProgression(self).winning_policy
            if policy:
                win_event = Event(actions=GameProgression(self).winning_policy)
                self._main_quest = Quest(win_events=[win_event])
                self._main_quest.desc = assign_description_to_quest(
                    self._main_quest, self, self.grammar)
                self._main_quest.commands = inform7.gen_commands_from_actions(
                    win_event.actions)

        return self._main_quest
Esempio n. 10
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
Esempio n. 11
0
    def setUpClass(cls):
        M = GameMaker()

        # The goal
        commands = ["go east", "insert carrot into chest"]

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

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

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

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

        cls.eventA = M.new_event_using_commands(commands)
        cls.eventB = Event(conditions={
            M.new_fact("at", carrot, R1),
            M.new_fact("closed", path.door)
        })
        cls.eventC = Event(conditions={M.new_fact("eaten", carrot)})
        cls.eventD = Event(conditions={
            M.new_fact("closed", chest),
            M.new_fact("closed", path.door)
        })
        cls.quest = Quest(win_events=[cls.eventA, cls.eventB],
                          fail_events=[cls.eventC, cls.eventD],
                          reward=2)

        M.quests = [cls.quest]
        cls.game = M.build()
        cls.inform7 = Inform7Game(cls.game)
Esempio n. 12
0
    def change_grammar(self, grammar: Grammar) -> None:
        """ Changes the grammar used and regenerate all text. """
        from textworld.generator.inform7 import Inform7Game
        from textworld.generator.text_generation import generate_text_from_grammar

        self.grammar = grammar
        _gen_commands = gen_commands_from_actions
        if self.grammar:
            inform7 = Inform7Game(self)
            _gen_commands = inform7.gen_commands_from_actions
            generate_text_from_grammar(self, self.grammar)

        for quest in self.quests:
            # TODO: should have a generic way of generating text commands from actions
            #       instead of relying on inform7 convention.
            for event in quest.win_events:
                event.commands = _gen_commands(event.actions)

            quest.commands = quest.win_events[0].commands

        if self.main_quest:
            win_event = self.main_quest.win_events[0]
            self.main_quest.commands = _gen_commands(win_event.actions)
Esempio n. 13
0
    def test_game_with_optional_and_repeatable_quests(self):
        M = textworld.GameMaker()
        museum = M.new_room("Museum")

        weak_statue = M.new(type="o", name="ivory statue")
        normal_statue = M.new(type="o", name="stone statue")
        strong_statue = M.new(type="o", name="granite statue")

        museum.add(weak_statue)
        museum.add(normal_statue)
        museum.add(strong_statue)

        M.set_player(museum)

        M.quests = [
            Quest(win_events=[
                Event(conditions=[M.new_fact('in', weak_statue, M.inventory)]),
            ],
                  reward=-10,
                  optional=True,
                  repeatable=True),
            Quest(win_events=[
                Event(
                    conditions=[M.new_fact('in', normal_statue, M.inventory)]),
            ],
                  reward=3,
                  optional=True),
            Quest(
                win_events=[
                    Event(conditions=[
                        M.new_fact('in', strong_statue, M.inventory)
                    ]),
                ],
                reward=5,
            )
        ]
        M.set_walkthrough(
            ["take ivory", "take stone", "drop ivory", "take granite"])
        game = M.build()

        inform7 = Inform7Game(game)
        game_progress = GameProgression(game)
        assert len(game_progress.quest_progressions) == len(game.quests)

        # Following the actions associated to the last quest actually corresponds
        # to solving the whole game.
        for action in game_progress.winning_policy:
            assert not game_progress.done
            game_progress.update(action)

        assert game_progress.done
        assert not game_progress.quest_progressions[
            0].completed  # Optional, negative quest.
        assert not game_progress.quest_progressions[
            1].completed  # Optional, positive quest.
        assert game_progress.quest_progressions[
            2].completed  # Mandatory quest.

        # Solve the game while completing the optional quests.
        game_progress = GameProgression(game)
        for command in [
                "take ivory statue", "look", "take stone statue",
                "drop ivory statue", "take granite statue"
        ]:
            _apply_command(command, game_progress, inform7)

        progressions = game_progress.quest_progressions
        assert not progressions[0].done  # Repeatable quests can never be done.
        assert progressions[
            0].nb_completions == 3  # They could have been completed a number of times, though.
        assert progressions[
            1].done  # The nonrepeatable-optional quest can be done.
        assert progressions[2].done

        assert game.max_score == 8
        assert game_progress.score == -22
Esempio n. 14
0
    def test_game_with_multiple_quests(self):
        M = GameMaker()

        # The subgoals (needs to be executed in order).
        commands = [
            [
                "open wooden door", "go west", "take carrot", "go east",
                "drop carrot"
            ],
            # Now, the player is back in the kitchen and the wooden door is open.
            ["go west", "take lettuce", "go east", "drop lettuce"],
            # Now, the player is back in the kitchen, there are a carrot and a lettuce on the floor.
            [
                "take lettuce", "take carrot", "insert carrot into chest",
                "insert lettuce into chest", "close chest"
            ]
        ]

        # 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, lettuce)

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

        quest1 = M.new_quest_using_commands(commands[0])
        quest1.desc = "Fetch the carrot and drop it on the kitchen's ground."
        quest2 = M.new_quest_using_commands(commands[0] + commands[1])
        quest2.desc = "Fetch the lettuce and drop it on the kitchen's ground."
        quest3 = M.new_quest_using_commands(commands[0] + commands[1] +
                                            commands[2])
        winning_facts = [
            M.new_fact("in", lettuce, chest),
            M.new_fact("in", carrot, chest),
            M.new_fact("closed", chest)
        ]
        quest3.win_events[0].set_conditions(winning_facts)
        quest3.desc = "Put the lettuce and the carrot into the chest before closing it."

        M.quests = [quest1, quest2, quest3]
        assert len(M.quests) == len(commands)
        game = M.build()

        inform7 = Inform7Game(game)
        game_progress = GameProgression(game)
        assert len(game_progress.quest_progressions) == len(game.quests)

        # Following the actions associated to the last quest actually corresponds
        # to solving the whole game.
        for action in game_progress.winning_policy:
            assert not game_progress.done
            game_progress.update(action)

        assert game_progress.done
        assert all(quest_progression.done
                   for quest_progression in game_progress.quest_progressions)

        # Try solving the game by greedily taking the first action from the current winning policy.
        game_progress = GameProgression(game)
        while not game_progress.done:
            action = game_progress.winning_policy[0]
            game_progress.update(action)
            # print(action.name, [c.name for c in game_progress.winning_policy])

        # Try solving the second quest (i.e. bringing back the lettuce) first.
        game_progress = GameProgression(game)
        for command in [
                "open wooden door", "go west", "take lettuce", "go east",
                "drop lettuce"
        ]:
            _apply_command(command, game_progress, inform7)

        assert not game_progress.quest_progressions[0].done
        assert game_progress.quest_progressions[1].done

        for command in ["go west", "take carrot", "go east", "drop carrot"]:
            _apply_command(command, game_progress, inform7)

        assert game_progress.quest_progressions[0].done
        assert game_progress.quest_progressions[1].done

        for command in [
                "take lettuce", "take carrot", "insert carrot into chest",
                "insert lettuce into chest", "close chest"
        ]:
            _apply_command(command, game_progress, inform7)

        assert game_progress.done

        # Game is done whenever a quest has failed or is unfinishable.
        game_progress = GameProgression(game)

        for command in [
                "open wooden door", "go west", "take carrot", "eat carrot"
        ]:
            assert not game_progress.done
            _apply_command(command, game_progress, inform7)

        assert game_progress.done
Esempio n. 15
0
    def test_cycle_in_winning_policy(self):
        M = GameMaker()

        # Create a map.
        # r0
        #  |
        # r1 -- r2
        #  |    |
        # r3 -- r4
        R0 = M.new_room("r0")
        R1 = M.new_room("r1")
        R2 = M.new_room("r2")
        R3 = M.new_room("r3")
        R4 = M.new_room("r4")
        M.set_player(R1)

        M.connect(R0.south, R1.north),
        M.connect(R1.east, R2.west),
        M.connect(R3.east, R4.west)
        M.connect(R1.south, R3.north)
        M.connect(R2.south, R4.north)

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

        apple = M.new(type='f', name='apple')
        R2.add(apple)

        commands = ["go north", "take carrot"]
        M.set_quest_from_commands(commands)
        game = M.build()
        inform7 = Inform7Game(game)
        game_progression = GameProgression(game)

        _apply_command("go south", game_progression, inform7)
        expected_commands = ["go north"] + commands
        winning_commands = inform7.gen_commands_from_actions(
            game_progression.winning_policy)
        assert winning_commands == expected_commands, "{} != {}".format(
            winning_commands, expected_commands)

        _apply_command("go east", game_progression, inform7)
        _apply_command("go north", game_progression, inform7)
        expected_commands = ["go south", "go west", "go north"] + commands
        winning_commands = inform7.gen_commands_from_actions(
            game_progression.winning_policy)
        assert winning_commands == expected_commands, "{} != {}".format(
            winning_commands, expected_commands)

        _apply_command("go west", game_progression, inform7)  # Found shortcut
        expected_commands = commands
        winning_commands = inform7.gen_commands_from_actions(
            game_progression.winning_policy)
        assert winning_commands == expected_commands, "{} != {}".format(
            winning_commands, expected_commands)

        # Quest where player's has to pick up the carrot first.
        commands = [
            "go east", "take apple", "go west", "go north", "drop apple"
        ]

        M.set_quest_from_commands(commands)
        game = M.build()
        game_progression = GameProgression(game)

        _apply_command("go south", game_progression, inform7)
        expected_commands = ["go north"] + commands
        winning_commands = inform7.gen_commands_from_actions(
            game_progression.winning_policy)
        assert winning_commands == expected_commands, "{} != {}".format(
            winning_commands, expected_commands)

        _apply_command("go east", game_progression, inform7)
        expected_commands = ["go west", "go north"] + commands
        winning_commands = inform7.gen_commands_from_actions(
            game_progression.winning_policy)
        assert winning_commands == expected_commands, "{} != {}".format(
            winning_commands, expected_commands)

        _apply_command("go north", game_progression, inform7)  # Found shortcut
        expected_commands = commands[1:]
        winning_commands = inform7.gen_commands_from_actions(
            game_progression.winning_policy)
        assert winning_commands == expected_commands, "{} != {}".format(
            winning_commands, expected_commands)
Esempio n. 16
0
 def load(self, path: str) -> None:
     self._gamefile = path
     self._game = textworld.Game.load(self._gamefile)
     self._game_progression = None
     self._inform7 = Inform7Game(self._game)