microsoft / microsoft/TextWorld

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

Open
#373 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Jupyter Notebook
Stars
1.4k
Forks
207
Avg merge
8d 22h
Merged PRs (30d)
1

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in game.py at change_grammar() and inspect the objective preservation logic in build(); review the existing test_manually_defined_objective case. Add coverage for a quest with a winning policy and verify that a manually set objective remains unchanged after compile and load.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
game-dev
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.