From bb22fe4718a456fa12618b12a1faf4d2aab72f1d Mon Sep 17 00:00:00 2001 From: Daizhi Liao <156556011+dafahaha@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:52:57 +0800 Subject: [PATCH 1/3] Make extra-info regex non-greedy and take the last block --- textworld/envs/wrappers/tw_inform7.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/textworld/envs/wrappers/tw_inform7.py b/textworld/envs/wrappers/tw_inform7.py index 0c115ea2..59128d12 100644 --- a/textworld/envs/wrappers/tw_inform7.py +++ b/textworld/envs/wrappers/tw_inform7.py @@ -50,12 +50,12 @@ def _detect_extra_infos(text: str, tracked_infos: Optional[List[str]] = None) -> if tag not in AVAILABLE_INFORM7_EXTRA_INFOS: raise ValueError("TW game doesn't support tag: {}".format(tag)) - regex = re.compile(r"<{tag}>\n(.*)".format(tag=tag), re.DOTALL) - match = re.search(regex, text) - if match: - _, cleaned_text = _detect_i7_events_debug_tags(match.group(1)) + regex = re.compile(r"<{tag}>\n(.*?)".format(tag=tag), re.DOTALL) + all_matches = regex.findall(text) + if all_matches: + _, cleaned_text = _detect_i7_events_debug_tags(all_matches[-1]) matches[tag] = cleaned_text.strip() - text = re.sub(regex, "", text) + text = regex.sub("", text) else: matches[tag] = None From 2343a7b90e68e40aef4417a31248bb5f540fb333 Mon Sep 17 00:00:00 2001 From: dafahaha Date: Thu, 24 Sep 2026 13:11:58 +0800 Subject: [PATCH 2/3] Fix bare except and add test for multiple commands --- tests/test_tw_inform7.py | 56 +++++++++++++++++++++++++++ textworld/envs/wrappers/tw_inform7.py | 2 +- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/test_tw_inform7.py diff --git a/tests/test_tw_inform7.py b/tests/test_tw_inform7.py new file mode 100644 index 00000000..5d002cd9 --- /dev/null +++ b/tests/test_tw_inform7.py @@ -0,0 +1,56 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. + +"""Tests for the Inform7 wrapper helpers.""" + +import sys +from unittest import mock + +# Jericho imports cleanly on Linux/macOS but not on every platform. +# The parsing helpers tested here do not use it, so stub it out when +# the real package cannot be loaded. +try: + import jericho # noqa: F401 +except Exception: # pragma: no cover - platform dependent + sys.modules["jericho"] = mock.MagicMock() + sys.modules["jericho.jericho"] = mock.MagicMock() + +from textworld.envs.wrappers.tw_inform7 import _detect_extra_infos + + +def test_detect_extra_infos_with_multiple_commands(): + # Several commands on one line (e.g. "go north. go east.") make the + # Inform7 interpreter print one set of extra-info tags per command. + text = ( + "You head north.\n" + "\n0\n\n" + "\n1\n\n" + "You head east.\n" + "\n0\n\n" + "\n2\n\n" + ) + matches, cleaned = _detect_extra_infos( + text, tracked_infos=["score", "moves"] + ) + + # The value from the last command is the one that reflects the + # current game state. + assert matches["score"] == "0" + assert matches["moves"] == "2" + assert "" not in cleaned + assert "" not in cleaned + + +def test_detect_extra_infos_single_command(): + text = ( + "You head north.\n" + "\n0\n\n" + "\n1\n\n" + ) + matches, cleaned = _detect_extra_infos( + text, tracked_infos=["score", "moves"] + ) + assert matches["score"] == "0" + assert matches["moves"] == "1" + assert "" not in cleaned + assert "" not in cleaned diff --git a/textworld/envs/wrappers/tw_inform7.py b/textworld/envs/wrappers/tw_inform7.py index 59128d12..163d523a 100644 --- a/textworld/envs/wrappers/tw_inform7.py +++ b/textworld/envs/wrappers/tw_inform7.py @@ -137,7 +137,7 @@ def _gather_infos(self): if self.state[info] is not None and type(self.state[info]) is not int: try: self.state[info] = int(self.state[info].strip()) - except: + except ValueError: self.state[info] = int(self.state[info].strip().split("\n")[0]) self.state["won"] = '*** The End ***' in self.state["feedback"] From 9e8caa5ce1ced8aa4b7e21c8e400fde3bd205865 Mon Sep 17 00:00:00 2001 From: dafahaha Date: Fri, 25 Sep 2026 13:43:57 +0800 Subject: [PATCH 3/3] Move multiple-command test to wrappers test suite --- tests/test_tw_inform7.py | 56 ------------------- .../envs/wrappers/tests/test_tw_inform7.py | 10 ++++ 2 files changed, 10 insertions(+), 56 deletions(-) delete mode 100644 tests/test_tw_inform7.py diff --git a/tests/test_tw_inform7.py b/tests/test_tw_inform7.py deleted file mode 100644 index 5d002cd9..00000000 --- a/tests/test_tw_inform7.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT license. - -"""Tests for the Inform7 wrapper helpers.""" - -import sys -from unittest import mock - -# Jericho imports cleanly on Linux/macOS but not on every platform. -# The parsing helpers tested here do not use it, so stub it out when -# the real package cannot be loaded. -try: - import jericho # noqa: F401 -except Exception: # pragma: no cover - platform dependent - sys.modules["jericho"] = mock.MagicMock() - sys.modules["jericho.jericho"] = mock.MagicMock() - -from textworld.envs.wrappers.tw_inform7 import _detect_extra_infos - - -def test_detect_extra_infos_with_multiple_commands(): - # Several commands on one line (e.g. "go north. go east.") make the - # Inform7 interpreter print one set of extra-info tags per command. - text = ( - "You head north.\n" - "\n0\n\n" - "\n1\n\n" - "You head east.\n" - "\n0\n\n" - "\n2\n\n" - ) - matches, cleaned = _detect_extra_infos( - text, tracked_infos=["score", "moves"] - ) - - # The value from the last command is the one that reflects the - # current game state. - assert matches["score"] == "0" - assert matches["moves"] == "2" - assert "" not in cleaned - assert "" not in cleaned - - -def test_detect_extra_infos_single_command(): - text = ( - "You head north.\n" - "\n0\n\n" - "\n1\n\n" - ) - matches, cleaned = _detect_extra_infos( - text, tracked_infos=["score", "moves"] - ) - assert matches["score"] == "0" - assert matches["moves"] == "1" - assert "" not in cleaned - assert "" not in cleaned diff --git a/textworld/envs/wrappers/tests/test_tw_inform7.py b/textworld/envs/wrappers/tests/test_tw_inform7.py index 537cdad5..85ba2fe5 100644 --- a/textworld/envs/wrappers/tests/test_tw_inform7.py +++ b/textworld/envs/wrappers/tests/test_tw_inform7.py @@ -108,6 +108,16 @@ def test_score(self): game_state, _, _ = env.step("close chest") assert game_state.score == 3 + def test_multiple_commands(self): + # Multiple commands on one line (e.g. "go east. go west.") must + # not crash: the Inform7 interpreter prints one set of extra-info + # tags per command, so the reported state must come from the last + # command. + for env in [self.env_z8]: + env.reset() + game_state, _, _ = env.step("go east. go west.") + assert game_state.moves == 2 + def test_moves(self): for env in [self.env_z8]: initial_state = env.reset()