Skip to content

game.objective still overwritten by change_grammar() when quests exist (regression of #239) #373

@gregyoto

Description

@gregyoto

Description

Issue #239 reported that manually setting quest.desc or game.objective gets overwritten by the text generation process during build()/compile(). PR #240 added a fix that copies _objective from a previous build before change_grammar() runs.

However, the fix is incomplete. In build(), the copy happens at line 788-789 before change_grammar() is called at line 803. change_grammar() then unconditionally overwrites the objective at line 460 of game.py:

self.objective = describe_event(Event(policy), self, self.grammar)

This means the preserved objective gets clobbered whenever the game has quests with a winning policy (i.e. most games).

The test added in PR #240 (test_manually_defined_objective) only tests a game with no quests, so it doesn't catch this.

Steps to Reproduce

import textworld
from textworld import GameMaker

M = GameMaker()
r1 = M.new_room("bedroom")
M.set_player(r1)

key = M.new(type="k", name="key", desc="This is a skeleton key.")
r1.add(key)

quest = M.set_quest_from_commands(["take key"])

game = M.build()
game.objective = "Find a valuable object."

game_file = M.compile("/tmp/test_game.z8")

# Load and check - the objective has been overwritten
loaded = textworld.Game.load("/tmp/test_game.json")
assert loaded.objective == "Find a valuable object.", f"Expected custom objective, got: {loaded.objective[:80]}..."

The assertion fails because change_grammar() in the second build() (triggered by compile()) overwrites the objective with auto-generated text.

Proposed Fix

In game.py, change_grammar() should only set the objective if one hasn't already been set:

# Before (line 460):
self.objective = describe_event(Event(policy), self, self.grammar)

# After:
if self._objective is None:
    self.objective = describe_event(Event(policy), self, self.grammar)

Workaround

Until this is fixed, you can work around it by calling M.build(), setting game.objective, then compiling manually with textworld.generator.compile_game(game, options) instead of M.compile().

Environment

  • TextWorld 1.7.0
  • Python 3.12
  • macOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions