Skip to content

Commit 7349c21

Browse files
committed
WIP
1 parent 7e381d3 commit 7349c21

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1893
-1312
lines changed

textworld/envs/glulx/git_glulx_ml.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ def command_feedback(self):
255255
"""
256256
if not hasattr(self, "_command_feedback"):
257257
command_feedback = self.feedback
258+
259+
# On the first move, command_feedback should be empty.
260+
if self.nb_moves == 0:
261+
command_feedback = ""
262+
258263
# Remove room description from command feedback.
259264
if len(self.description.strip()) > 0:
260265
regex = "\s*" + re.escape(self.description.strip()) + "\s*"
@@ -505,7 +510,7 @@ def render(self, mode: str = "human") -> None:
505510
# Wrap each paragraph.
506511
if mode == "human":
507512
paragraphs = msg.split("\n")
508-
paragraphs = ["\n".join(textwrap.wrap(paragraph, width=80)) for paragraph in paragraphs]
513+
paragraphs = ["\n".join(textwrap.wrap(paragraph, width=120)) for paragraph in paragraphs]
509514
msg = "\n".join(paragraphs)
510515

511516
outfile.write(msg + "\n")

textworld/envs/glulx/tests/test_git_glulx_ml.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ def build_test_game():
4040
path.door.add_property("open")
4141

4242
carrot = M.new(type='f', name='carrot')
43+
M.add_fact("edible", carrot)
4344
M.inventory.add(carrot)
4445

4546
# Add a closed chest in R2.
46-
chest = M.new(type='c', name='chest')
47+
chest = M.new(type='chest', name='chest')
4748
chest.add_property("open")
4849
R2.add(chest)
4950

textworld/generator/chaining.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT license.
33

4-
4+
import re
55
from collections import Counter
66
from functools import total_ordering
77
from numpy.random import RandomState
@@ -103,7 +103,7 @@ def __init__(self):
103103
self.subquests = False
104104
self.independent_chains = False
105105
self.create_variables = False
106-
self.fixed_mapping = data.get_types().constants_mapping
106+
self.fixed_mapping = {Placeholder(t.name): Variable(t.name) for t in data.get_types() if t.constant}
107107
self.rng = None
108108
self.logic = data.get_logic()
109109
self.rules_per_depth = []
@@ -365,9 +365,9 @@ def check_action(self, node: _Node, state: State, action: Action) -> bool:
365365
while nav_parent.action is not None and self._is_navigation(nav_parent.action):
366366
# HACK: Going through a door is always considered navigation unless the previous action was to open that door.
367367
parent = nav_parent.parent
368-
if parent.action is not None and parent.action.name == "open/d":
368+
if parent.action is not None and self._is_open_door(parent.action):
369369
break
370-
if self.backward and action.name == "open/d":
370+
if self.backward and self._is_open_door(action):
371371
break
372372
nav_parent = parent
373373

@@ -389,7 +389,10 @@ def check_action(self, node: _Node, state: State, action: Action) -> bool:
389389
return self.options.check_action(state, action)
390390

391391
def _is_navigation(self, action):
392-
return action.name.startswith("go/")
392+
return re.match("go\(.*\).*", action.name)
393+
394+
def _is_open_door(self, action):
395+
return re.match("open\(d\).*", action.name)
393396

394397
def apply(self, node: _Node, action: Action) -> Optional[State]:
395398
"""Attempt to apply an action to the given state."""

textworld/generator/data/__init__.py

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from typing import Optional
1111

1212
from textworld.logic import GameLogic
13-
from textworld.generator.vtypes import VariableType, VariableTypeTree
1413
from textworld.utils import maybe_mkdir, RegexDict
1514

1615
BUILTIN_DATA_PATH = os.path.dirname(__file__)
@@ -82,17 +81,17 @@ def create_data_files(dest: str = './textworld_data', verbose: bool = False, for
8281
INFORM7_ADDONS_CODE = None
8382

8483

85-
def _to_type_tree(types):
86-
vtypes = []
84+
# def _to_type_tree(types):
85+
# vtypes = []
8786

88-
for vtype in sorted(types):
89-
if vtype.parents:
90-
parent = vtype.parents[0]
91-
else:
92-
parent = None
93-
vtypes.append(VariableType(vtype.name, vtype.name, parent))
87+
# for vtype in sorted(types):
88+
# if vtype.parents:
89+
# parent = vtype.parents[0]
90+
# else:
91+
# parent = None
92+
# vtypes.append(VariableType(vtype.name, vtype.name, parent))
9493

95-
return VariableTypeTree(vtypes)
94+
# return VariableTypeTree(vtypes)
9695

9796

9897
def _to_regex_dict(rules):
@@ -134,7 +133,8 @@ def load_logic(target_dir: str):
134133
logic = GameLogic.load(paths)
135134

136135
global _TYPES
137-
_TYPES = _to_type_tree(logic.types)
136+
# _TYPES = _to_type_tree(logic.types)
137+
_TYPES = logic.types
138138

139139
global _RULES
140140
_RULES = _to_regex_dict(logic.rules.values())
@@ -197,13 +197,57 @@ def get_constraints():
197197

198198

199199
def get_reverse_rules(action):
200+
assert False, "deprecated" # XXX
200201
return _REVERSE_RULES(action)
201202

202203

204+
def get_reverse_action(action):
205+
r_action = action.inverse()
206+
for rule in get_rules().values():
207+
r_action.name = rule.name
208+
if rule.match(r_action):
209+
return r_action
210+
211+
return None
212+
213+
203214
def get_types():
204215
return _TYPES
205216

206217

218+
219+
def sample_type(parent_type, rng, exceptions=[], include_parent=True, probs=None):
220+
""" Sample an object type given the parent's type. """
221+
import numpy as np
222+
types = [t.name for t in get_types().get(parent_type).descendants]
223+
if include_parent:
224+
types = [parent_type] + types
225+
types = [t for t in types if t not in exceptions]
226+
227+
if probs is not None:
228+
probs = np.array([probs[t] for t in types], dtype="float")
229+
probs /= np.sum(probs)
230+
231+
return rng.choice(types, p=probs)
232+
233+
234+
def count_types(state):
235+
""" Counts how many objects there are of each type. """
236+
types_counts = {t.name: 0 for t in get_types()}
237+
for var in state.variables:
238+
if get_types().get(var.type).constant:
239+
continue
240+
241+
if "_" not in var.name:
242+
continue
243+
244+
cpt = int(var.name.split("_")[-1])
245+
var_type = var.type
246+
types_counts[var_type] = max(cpt + 1, types_counts[var_type])
247+
248+
return types_counts
249+
250+
207251
def get_data_path():
208252
return _DATA_PATH
209253

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# A type of container that is portable.
2+
type box : t, container, portable, openable {
3+
predicates {
4+
reachable_contents(box) = reachable(box) & open(box);
5+
reachable(box) = in(box, I);
6+
reachable(box) = at(P, r) & at(box, r);
7+
reachable(box) = reachable(table) & on(box, table);
8+
reachable(box) = reachable_contents(chest) & in(box, chest);
9+
}
10+
11+
constraints {
12+
no_nested_boxes :: in(x: box, y: box) -> fail();
13+
no_box_on_stool :: on(box, stool) -> fail();
14+
}
15+
16+
inform7 {
17+
type {
18+
kind :: "box-like";
19+
}
20+
21+
code :: """
22+
[Avoid nesting box-like objects because of some limitation with alias cycles.]
23+
Instead of inserting a box-like (called source) into a box-like (called dest):
24+
say "You cannot insert [the source] in [the dest]!";
25+
26+
Instead of putting a box-like (called source) on a stool-like (called dest):
27+
say "You cannot put [the source] on [the dest]!";
28+
""";
29+
30+
}
31+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# A type of container that is fixed in place and lockable.
2+
type chest : t, container, fixed_in_place, lockable {
3+
predicates {
4+
reachable_contents(chest) = reachable(chest) & open(chest);
5+
}
6+
7+
inform7 {
8+
type {
9+
kind :: "chest-like";
10+
}
11+
}
12+
}
13+
14+
## Alias for backward compatibility.
15+
#type c : chest {
16+
# inform7 {
17+
# type {
18+
# kind :: "old-c";
19+
# }
20+
# }
21+
#}
Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,17 @@
1-
# container
2-
type c : t {
1+
# Property of an object that can contain other objects.
2+
type container {
33
predicates {
4-
open(c);
5-
closed(c);
6-
locked(c);
7-
8-
in(o, c);
9-
}
10-
11-
rules {
12-
lock/c :: $at(P, r) & $at(c, r) & $in(k, I) & $match(k, c) & closed(c) -> locked(c);
13-
unlock/c :: $at(P, r) & $at(c, r) & $in(k, I) & $match(k, c) & locked(c) -> closed(c);
14-
15-
open/c :: $at(P, r) & $at(c, r) & closed(c) -> open(c);
16-
close/c :: $at(P, r) & $at(c, r) & open(c) -> closed(c);
17-
}
18-
19-
reverse_rules {
20-
lock/c :: unlock/c;
21-
open/c :: close/c;
22-
}
23-
24-
constraints {
25-
c1 :: open(c) & closed(c) -> fail();
26-
c2 :: open(c) & locked(c) -> fail();
27-
c3 :: closed(c) & locked(c) -> fail();
4+
in(portable, container);
285
}
296

307
inform7 {
318
type {
32-
kind :: "container";
33-
definition :: "containers are openable, lockable and fixed in place. containers are usually closed.";
9+
kind :: "";
10+
definition :: "It is a kind of container.";
3411
}
3512

3613
predicates {
37-
open(c) :: "The {c} is open";
38-
closed(c) :: "The {c} is closed";
39-
locked(c) :: "The {c} is locked";
40-
41-
in(o, c) :: "The {o} is in the {c}";
42-
}
43-
44-
commands {
45-
open/c :: "open {c}" :: "opening the {c}";
46-
close/c :: "close {c}" :: "closing the {c}";
47-
48-
lock/c :: "lock {c} with {k}" :: "locking the {c} with the {k}";
49-
unlock/c :: "unlock {c} with {k}" :: "unlocking the {c} with the {k}";
14+
in(portable, container) :: "The {portable} is in the {container}";
5015
}
5116
}
5217
}
Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
# door
2-
type d : t {
2+
type d : t, lockable {
33
predicates {
4-
open(d);
5-
closed(d);
6-
locked(d);
7-
84
link(r, d, r);
5+
6+
north_of/d(r, d, r);
7+
west_of/d(r, d, r);
8+
9+
south_of/d(r, d, r') = north_of/d(r', d, r);
10+
east_of/d(r, d, r') = west_of/d(r', d, r);
11+
12+
reachable(d) = at(P, r) & link(r, d, r') & link(r', d, r);
913
}
1014

1115
rules {
12-
lock/d :: $at(P, r) & $link(r, d, r') & $link(r', d, r) & $in(k, I) & $match(k, d) & closed(d) -> locked(d);
13-
unlock/d :: $at(P, r) & $link(r, d, r') & $link(r', d, r) & $in(k, I) & $match(k, d) & locked(d) -> closed(d);
16+
lock(d, k) :: $reachable(d) & $in(k, I) & $match(k, d) & closed(d) -> locked(d);
17+
unlock(d, k) :: $reachable(d) & $in(k, I) & $match(k, d) & locked(d) -> closed(d);
1418

15-
open/d :: $at(P, r) & $link(r, d, r') & $link(r', d, r) & closed(d) -> open(d) & free(r, r') & free(r', r);
16-
close/d :: $at(P, r) & $link(r, d, r') & $link(r', d, r) & open(d) & free(r, r') & free(r', r) -> closed(d);
19+
open(d) :: $reachable(d) & closed(d) -> open(d) & free(r, r') & free(r', r);
20+
close(d) :: $reachable(d) & open(d) & free(r, r') & free(r', r) -> closed(d);
1721
}
1822

1923
reverse_rules {
20-
lock/d :: unlock/d;
21-
open/d :: close/d;
24+
lock(d, k) :: unlock(d, k);
25+
open(d) :: close(d);
2226
}
2327

2428
constraints {
25-
d1 :: open(d) & closed(d) -> fail();
26-
d2 :: open(d) & locked(d) -> fail();
27-
d3 :: closed(d) & locked(d) -> fail();
28-
2929
# A door can't be used to link more than two rooms.
3030
link1 :: link(r, d, r') & link(r, d, r'') -> fail();
3131
link2 :: link(r, d, r') & link(r'', d, r''') -> fail();
@@ -35,7 +35,7 @@ type d : t {
3535

3636
# There cannot be more than four doors in a room.
3737
too_many_doors :: link(r, d1: d, r1: r) & link(r, d2: d, r2: r) & link(r, d3: d, r3: r) & link(r, d4: d, r4: r) & link(r, d5: d, r5: r) -> fail();
38-
38+
3939
# There cannot be more than four doors in a room.
4040
dr1 :: free(r, r1: r) & link(r, d2: d, r2: r) & link(r, d3: d, r3: r) & link(r, d4: d, r4: r) & link(r, d5: d, r5: r) -> fail();
4141
dr2 :: free(r, r1: r) & free(r, r2: r) & link(r, d3: d, r3: r) & link(r, d4: d, r4: r) & link(r, d5: d, r5: r) -> fail();
@@ -52,18 +52,19 @@ type d : t {
5252
definition :: "door is openable and lockable.";
5353
}
5454

55-
predicates {
56-
open(d) :: "The {d} is open";
57-
closed(d) :: "The {d} is closed";
58-
locked(d) :: "The {d} is locked";
59-
}
60-
6155
commands {
62-
open/d :: "open {d}" :: "opening {d}";
63-
close/d :: "close {d}" :: "closing {d}";
56+
open(d) :: "open {d}" :: "opening {d}";
57+
close(d) :: "close {d}" :: "closing {d}";
6458

65-
unlock/d :: "unlock {d} with {k}" :: "unlocking {d} with the {k}";
66-
lock/d :: "lock {d} with {k}" :: "locking {d} with the {k}";
59+
unlock(d, k) :: "unlock {d} with {k}" :: "unlocking {d} with the {k}";
60+
lock(d, k) :: "lock {d} with {k}" :: "locking {d} with the {k}";
61+
}
62+
63+
predicates {
64+
north_of/d(r, d, r') :: "South of {r} and north of {r'} is a door called {d}";
65+
south_of/d(r, d, r') :: "North of {r} and south of {r'} is a door called {d}";
66+
east_of/d(r, d, r') :: "West of {r} and east of {r'} is a door called {d}";
67+
west_of/d(r, d, r') :: "East of {r} and west of {r'} is a door called {d}";
6768
}
6869
}
6970
}

0 commit comments

Comments
 (0)