We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Seed and sample behaviour in Tuple space
If I create a Tuple space containing Discrete spaces, fix the seed, and then generate a sample, all elements of the sampled tuple are the same.  For example, (0, 0) or (4, 4).  Conversely, if I create a MultiDiscrete space, fix the seed, and then generate a sample, elements of the sampled tuple are not necessarily the same.  For example, (0, 4) or (1, 3).

This is especially problematic when a Tuple space contains Tuple spaces (i.e. where MultiDiscrete is not an adequate replacement):

```python
from gym.spaces import Tuple, MultiDiscrete, Discrete

my_seed = 0

# Test 1

print("Test 1:")

a1 = Tuple([Tuple([Discrete(5) for _ in range(2)]) for _ in range(2)])
b1 = Tuple([MultiDiscrete([5 for _ in range(2)]) for _ in range(2)])

a1.seed(my_seed)
b1.seed(my_seed)

for _ in range(5):
    print(a1.sample(), b1.sample())

# Test 2

print("Test 2:")

a2 = Tuple([Tuple([Discrete(5) for _ in range(2)]) for _ in range(2)])
b2 = Tuple([MultiDiscrete([5 for _ in range(2)]) for _ in range(2)])

for a2_space, b2_space in zip(a2, b2):
    my_seed += 1
    a2_space.seed(my_seed)
    b2_space.seed(my_seed)

for _ in range(5):
    print(a2.sample(), b2.sample())
```
```
Test 1:
((0, 0), (0, 0)) (array([0, 4]), array([0, 4]))
((3, 3), (3, 3)) (array([3, 1]), array([3, 1]))
((4, 4), (4, 4)) (array([0, 3]), array([0, 3]))
((3, 3), (3, 3)) (array([1, 3]), array([1, 3]))
((3, 3), (3, 3)) (array([0, 2]), array([0, 2]))
Test 2:
((0, 0), (4, 4)) (array([4, 2]), array([4, 3]))
((4, 4), (0, 0)) (array([0, 0]), array([3, 3]))
((3, 3), (0, 0)) (array([3, 0]), array([4, 2]))
((2, 2), (4, 4)) (array([0, 2]), array([1, 2]))
((4, 4), (1, 1)) (array([3, 2]), array([2, 2]))
```

Is this expected/desirable behaviour for Tuple or am I using it incorrectly?  I would have expected Tuple to behave for arbitrary tuples as MultiDiscrete behaves for a single tuple.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gym/spaces/dict.py`
Content:
```
1 from collections import OrderedDict
2 from .space import Space
3 
4 
5 class Dict(Space):
6     """
7     A dictionary of simpler spaces.
8 
9     Example usage:
10     self.observation_space = spaces.Dict({"position": spaces.Discrete(2), "velocity": spaces.Discrete(3)})
11 
12     Example usage [nested]:
13     self.nested_observation_space = spaces.Dict({
14         'sensors':  spaces.Dict({
15             'position': spaces.Box(low=-100, high=100, shape=(3,)),
16             'velocity': spaces.Box(low=-1, high=1, shape=(3,)),
17             'front_cam': spaces.Tuple((
18                 spaces.Box(low=0, high=1, shape=(10, 10, 3)),
19                 spaces.Box(low=0, high=1, shape=(10, 10, 3))
20             )),
21             'rear_cam': spaces.Box(low=0, high=1, shape=(10, 10, 3)),
22         }),
23         'ext_controller': spaces.MultiDiscrete((5, 2, 2)),
24         'inner_state':spaces.Dict({
25             'charge': spaces.Discrete(100),
26             'system_checks': spaces.MultiBinary(10),
27             'job_status': spaces.Dict({
28                 'task': spaces.Discrete(5),
29                 'progress': spaces.Box(low=0, high=100, shape=()),
30             })
31         })
32     })
33     """
34 
35     def __init__(self, spaces=None, **spaces_kwargs):
36         assert (spaces is None) or (
37             not spaces_kwargs
38         ), "Use either Dict(spaces=dict(...)) or Dict(foo=x, bar=z)"
39         if spaces is None:
40             spaces = spaces_kwargs
41         if isinstance(spaces, dict) and not isinstance(spaces, OrderedDict):
42             spaces = OrderedDict(sorted(list(spaces.items())))
43         if isinstance(spaces, list):
44             spaces = OrderedDict(spaces)
45         self.spaces = spaces
46         for space in spaces.values():
47             assert isinstance(
48                 space, Space
49             ), "Values of the dict should be instances of gym.Space"
50         super(Dict, self).__init__(
51             None, None
52         )  # None for shape and dtype, since it'll require special handling
53 
54     def seed(self, seed=None):
55         [space.seed(seed) for space in self.spaces.values()]
56 
57     def sample(self):
58         return OrderedDict([(k, space.sample()) for k, space in self.spaces.items()])
59 
60     def contains(self, x):
61         if not isinstance(x, dict) or len(x) != len(self.spaces):
62             return False
63         for k, space in self.spaces.items():
64             if k not in x:
65                 return False
66             if not space.contains(x[k]):
67                 return False
68         return True
69 
70     def __getitem__(self, key):
71         return self.spaces[key]
72 
73     def __setitem__(self, key, value):
74         self.spaces[key] = value
75 
76     def __iter__(self):
77         for key in self.spaces:
78             yield key
79 
80     def __len__(self):
81         return len(self.spaces)
82 
83     def __contains__(self, item):
84         return self.contains(item)
85 
86     def __repr__(self):
87         return (
88             "Dict("
89             + ", ".join([str(k) + ":" + str(s) for k, s in self.spaces.items()])
90             + ")"
91         )
92 
93     def to_jsonable(self, sample_n):
94         # serialize as dict-repr of vectors
95         return {
96             key: space.to_jsonable([sample[key] for sample in sample_n])
97             for key, space in self.spaces.items()
98         }
99 
100     def from_jsonable(self, sample_n):
101         dict_of_list = {}
102         for key, space in self.spaces.items():
103             dict_of_list[key] = space.from_jsonable(sample_n[key])
104         ret = []
105         for i, _ in enumerate(dict_of_list[key]):
106             entry = {}
107             for key, value in dict_of_list.items():
108                 entry[key] = value[i]
109             ret.append(entry)
110         return ret
111 
112     def __eq__(self, other):
113         return isinstance(other, Dict) and self.spaces == other.spaces
114
```

Path: `gym/spaces/tuple.py`
Content:
```
1 import numpy as np
2 from .space import Space
3 
4 
5 class Tuple(Space):
6     """
7     A tuple (i.e., product) of simpler spaces
8 
9     Example usage:
10     self.observation_space = spaces.Tuple((spaces.Discrete(2), spaces.Discrete(3)))
11     """
12 
13     def __init__(self, spaces):
14         self.spaces = spaces
15         for space in spaces:
16             assert isinstance(
17                 space, Space
18             ), "Elements of the tuple must be instances of gym.Space"
19         super(Tuple, self).__init__(None, None)
20 
21     def seed(self, seed=None):
22         [space.seed(seed) for space in self.spaces]
23 
24     def sample(self):
25         return tuple([space.sample() for space in self.spaces])
26 
27     def contains(self, x):
28         if isinstance(x, list):
29             x = tuple(x)  # Promote list to tuple for contains check
30         return (
31             isinstance(x, tuple)
32             and len(x) == len(self.spaces)
33             and all(space.contains(part) for (space, part) in zip(self.spaces, x))
34         )
35 
36     def __repr__(self):
37         return "Tuple(" + ", ".join([str(s) for s in self.spaces]) + ")"
38 
39     def to_jsonable(self, sample_n):
40         # serialize as list-repr of tuple of vectors
41         return [
42             space.to_jsonable([sample[i] for sample in sample_n])
43             for i, space in enumerate(self.spaces)
44         ]
45 
46     def from_jsonable(self, sample_n):
47         return [
48             sample
49             for sample in zip(
50                 *[
51                     space.from_jsonable(sample_n[i])
52                     for i, space in enumerate(self.spaces)
53                 ]
54             )
55         ]
56 
57     def __getitem__(self, index):
58         return self.spaces[index]
59 
60     def __len__(self):
61         return len(self.spaces)
62 
63     def __eq__(self, other):
64         return isinstance(other, Tuple) and self.spaces == other.spaces
65
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gym/spaces/dict.py b/gym/spaces/dict.py
--- a/gym/spaces/dict.py
+++ b/gym/spaces/dict.py
@@ -1,4 +1,5 @@
 from collections import OrderedDict
+import numpy as np
 from .space import Space
 
 
@@ -52,7 +53,24 @@
         )  # None for shape and dtype, since it'll require special handling
 
     def seed(self, seed=None):
-        [space.seed(seed) for space in self.spaces.values()]
+        seed = super().seed(seed)
+        try:
+            subseeds = self.np_random.choice(
+                np.iinfo(int).max,
+                size=len(self.spaces),
+                replace=False,  # unique subseed for each subspace
+            )
+        except ValueError:
+            subseeds = self.np_random.choice(
+                np.iinfo(int).max,
+                size=len(self.spaces),
+                replace=True,  # we get more than INT_MAX subspaces
+            )
+
+        for subspace, subseed in zip(self.spaces.values(), subseeds):
+            seed.append(subspace.seed(int(subseed))[0])
+
+        return seed
 
     def sample(self):
         return OrderedDict([(k, space.sample()) for k, space in self.spaces.items()])
diff --git a/gym/spaces/tuple.py b/gym/spaces/tuple.py
--- a/gym/spaces/tuple.py
+++ b/gym/spaces/tuple.py
@@ -19,7 +19,24 @@
         super(Tuple, self).__init__(None, None)
 
     def seed(self, seed=None):
-        [space.seed(seed) for space in self.spaces]
+        seed = super().seed(seed)
+        try:
+            subseeds = self.np_random.choice(
+                np.iinfo(int).max,
+                size=len(self.spaces),
+                replace=False,  # unique subseed for each subspace
+            )
+        except ValueError:
+            subseeds = self.np_random.choice(
+                np.iinfo(int).max,
+                size=len(self.spaces),
+                replace=True,  # we get more than INT_MAX subspaces
+            )
+
+        for subspace, subseed in zip(self.spaces, subseeds):
+            seed.append(subspace.seed(int(subseed))[0])
+
+        return seed
 
     def sample(self):
         return tuple([space.sample() for space in self.spaces])

{"golden_diff": "diff --git a/gym/spaces/dict.py b/gym/spaces/dict.py\n--- a/gym/spaces/dict.py\n+++ b/gym/spaces/dict.py\n@@ -1,4 +1,5 @@\n from collections import OrderedDict\n+import numpy as np\n from .space import Space\n \n \n@@ -52,7 +53,24 @@\n         )  # None for shape and dtype, since it'll require special handling\n \n     def seed(self, seed=None):\n-        [space.seed(seed) for space in self.spaces.values()]\n+        seed = super().seed(seed)\n+        try:\n+            subseeds = self.np_random.choice(\n+                np.iinfo(int).max,\n+                size=len(self.spaces),\n+                replace=False,  # unique subseed for each subspace\n+            )\n+        except ValueError:\n+            subseeds = self.np_random.choice(\n+                np.iinfo(int).max,\n+                size=len(self.spaces),\n+                replace=True,  # we get more than INT_MAX subspaces\n+            )\n+\n+        for subspace, subseed in zip(self.spaces.values(), subseeds):\n+            seed.append(subspace.seed(int(subseed))[0])\n+\n+        return seed\n \n     def sample(self):\n         return OrderedDict([(k, space.sample()) for k, space in self.spaces.items()])\ndiff --git a/gym/spaces/tuple.py b/gym/spaces/tuple.py\n--- a/gym/spaces/tuple.py\n+++ b/gym/spaces/tuple.py\n@@ -19,7 +19,24 @@\n         super(Tuple, self).__init__(None, None)\n \n     def seed(self, seed=None):\n-        [space.seed(seed) for space in self.spaces]\n+        seed = super().seed(seed)\n+        try:\n+            subseeds = self.np_random.choice(\n+                np.iinfo(int).max,\n+                size=len(self.spaces),\n+                replace=False,  # unique subseed for each subspace\n+            )\n+        except ValueError:\n+            subseeds = self.np_random.choice(\n+                np.iinfo(int).max,\n+                size=len(self.spaces),\n+                replace=True,  # we get more than INT_MAX subspaces\n+            )\n+\n+        for subspace, subseed in zip(self.spaces, subseeds):\n+            seed.append(subspace.seed(int(subseed))[0])\n+\n+        return seed\n \n     def sample(self):\n         return tuple([space.sample() for space in self.spaces])\n", "issue": "Seed and sample behaviour in Tuple space\nIf I create a Tuple space containing Discrete spaces, fix the seed, and then generate a sample, all elements of the sampled tuple are the same.  For example, (0, 0) or (4, 4).  Conversely, if I create a MultiDiscrete space, fix the seed, and then generate a sample, elements of the sampled tuple are not necessarily the same.  For example, (0, 4) or (1, 3).\r\n\r\nThis is especially problematic when a Tuple space contains Tuple spaces (i.e. where MultiDiscrete is not an adequate replacement):\r\n\r\n```python\r\nfrom gym.spaces import Tuple, MultiDiscrete, Discrete\r\n\r\nmy_seed = 0\r\n\r\n# Test 1\r\n\r\nprint(\"Test 1:\")\r\n\r\na1 = Tuple([Tuple([Discrete(5) for _ in range(2)]) for _ in range(2)])\r\nb1 = Tuple([MultiDiscrete([5 for _ in range(2)]) for _ in range(2)])\r\n\r\na1.seed(my_seed)\r\nb1.seed(my_seed)\r\n\r\nfor _ in range(5):\r\n    print(a1.sample(), b1.sample())\r\n\r\n# Test 2\r\n\r\nprint(\"Test 2:\")\r\n\r\na2 = Tuple([Tuple([Discrete(5) for _ in range(2)]) for _ in range(2)])\r\nb2 = Tuple([MultiDiscrete([5 for _ in range(2)]) for _ in range(2)])\r\n\r\nfor a2_space, b2_space in zip(a2, b2):\r\n    my_seed += 1\r\n    a2_space.seed(my_seed)\r\n    b2_space.seed(my_seed)\r\n\r\nfor _ in range(5):\r\n    print(a2.sample(), b2.sample())\r\n```\r\n```\r\nTest 1:\r\n((0, 0), (0, 0)) (array([0, 4]), array([0, 4]))\r\n((3, 3), (3, 3)) (array([3, 1]), array([3, 1]))\r\n((4, 4), (4, 4)) (array([0, 3]), array([0, 3]))\r\n((3, 3), (3, 3)) (array([1, 3]), array([1, 3]))\r\n((3, 3), (3, 3)) (array([0, 2]), array([0, 2]))\r\nTest 2:\r\n((0, 0), (4, 4)) (array([4, 2]), array([4, 3]))\r\n((4, 4), (0, 0)) (array([0, 0]), array([3, 3]))\r\n((3, 3), (0, 0)) (array([3, 0]), array([4, 2]))\r\n((2, 2), (4, 4)) (array([0, 2]), array([1, 2]))\r\n((4, 4), (1, 1)) (array([3, 2]), array([2, 2]))\r\n```\r\n\r\nIs this expected/desirable behaviour for Tuple or am I using it incorrectly?  I would have expected Tuple to behave for arbitrary tuples as MultiDiscrete behaves for a single tuple.\n", "before_files": [{"content": "from collections import OrderedDict\nfrom .space import Space\n\n\nclass Dict(Space):\n    \"\"\"\n    A dictionary of simpler spaces.\n\n    Example usage:\n    self.observation_space = spaces.Dict({\"position\": spaces.Discrete(2), \"velocity\": spaces.Discrete(3)})\n\n    Example usage [nested]:\n    self.nested_observation_space = spaces.Dict({\n        'sensors':  spaces.Dict({\n            'position': spaces.Box(low=-100, high=100, shape=(3,)),\n            'velocity': spaces.Box(low=-1, high=1, shape=(3,)),\n            'front_cam': spaces.Tuple((\n                spaces.Box(low=0, high=1, shape=(10, 10, 3)),\n                spaces.Box(low=0, high=1, shape=(10, 10, 3))\n            )),\n            'rear_cam': spaces.Box(low=0, high=1, shape=(10, 10, 3)),\n        }),\n        'ext_controller': spaces.MultiDiscrete((5, 2, 2)),\n        'inner_state':spaces.Dict({\n            'charge': spaces.Discrete(100),\n            'system_checks': spaces.MultiBinary(10),\n            'job_status': spaces.Dict({\n                'task': spaces.Discrete(5),\n                'progress': spaces.Box(low=0, high=100, shape=()),\n            })\n        })\n    })\n    \"\"\"\n\n    def __init__(self, spaces=None, **spaces_kwargs):\n        assert (spaces is None) or (\n            not spaces_kwargs\n        ), \"Use either Dict(spaces=dict(...)) or Dict(foo=x, bar=z)\"\n        if spaces is None:\n            spaces = spaces_kwargs\n        if isinstance(spaces, dict) and not isinstance(spaces, OrderedDict):\n            spaces = OrderedDict(sorted(list(spaces.items())))\n        if isinstance(spaces, list):\n            spaces = OrderedDict(spaces)\n        self.spaces = spaces\n        for space in spaces.values():\n            assert isinstance(\n                space, Space\n            ), \"Values of the dict should be instances of gym.Space\"\n        super(Dict, self).__init__(\n            None, None\n        )  # None for shape and dtype, since it'll require special handling\n\n    def seed(self, seed=None):\n        [space.seed(seed) for space in self.spaces.values()]\n\n    def sample(self):\n        return OrderedDict([(k, space.sample()) for k, space in self.spaces.items()])\n\n    def contains(self, x):\n        if not isinstance(x, dict) or len(x) != len(self.spaces):\n            return False\n        for k, space in self.spaces.items():\n            if k not in x:\n                return False\n            if not space.contains(x[k]):\n                return False\n        return True\n\n    def __getitem__(self, key):\n        return self.spaces[key]\n\n    def __setitem__(self, key, value):\n        self.spaces[key] = value\n\n    def __iter__(self):\n        for key in self.spaces:\n            yield key\n\n    def __len__(self):\n        return len(self.spaces)\n\n    def __contains__(self, item):\n        return self.contains(item)\n\n    def __repr__(self):\n        return (\n            \"Dict(\"\n            + \", \".join([str(k) + \":\" + str(s) for k, s in self.spaces.items()])\n            + \")\"\n        )\n\n    def to_jsonable(self, sample_n):\n        # serialize as dict-repr of vectors\n        return {\n            key: space.to_jsonable([sample[key] for sample in sample_n])\n            for key, space in self.spaces.items()\n        }\n\n    def from_jsonable(self, sample_n):\n        dict_of_list = {}\n        for key, space in self.spaces.items():\n            dict_of_list[key] = space.from_jsonable(sample_n[key])\n        ret = []\n        for i, _ in enumerate(dict_of_list[key]):\n            entry = {}\n            for key, value in dict_of_list.items():\n                entry[key] = value[i]\n            ret.append(entry)\n        return ret\n\n    def __eq__(self, other):\n        return isinstance(other, Dict) and self.spaces == other.spaces\n", "path": "gym/spaces/dict.py"}, {"content": "import numpy as np\nfrom .space import Space\n\n\nclass Tuple(Space):\n    \"\"\"\n    A tuple (i.e., product) of simpler spaces\n\n    Example usage:\n    self.observation_space = spaces.Tuple((spaces.Discrete(2), spaces.Discrete(3)))\n    \"\"\"\n\n    def __init__(self, spaces):\n        self.spaces = spaces\n        for space in spaces:\n            assert isinstance(\n                space, Space\n            ), \"Elements of the tuple must be instances of gym.Space\"\n        super(Tuple, self).__init__(None, None)\n\n    def seed(self, seed=None):\n        [space.seed(seed) for space in self.spaces]\n\n    def sample(self):\n        return tuple([space.sample() for space in self.spaces])\n\n    def contains(self, x):\n        if isinstance(x, list):\n            x = tuple(x)  # Promote list to tuple for contains check\n        return (\n            isinstance(x, tuple)\n            and len(x) == len(self.spaces)\n            and all(space.contains(part) for (space, part) in zip(self.spaces, x))\n        )\n\n    def __repr__(self):\n        return \"Tuple(\" + \", \".join([str(s) for s in self.spaces]) + \")\"\n\n    def to_jsonable(self, sample_n):\n        # serialize as list-repr of tuple of vectors\n        return [\n            space.to_jsonable([sample[i] for sample in sample_n])\n            for i, space in enumerate(self.spaces)\n        ]\n\n    def from_jsonable(self, sample_n):\n        return [\n            sample\n            for sample in zip(\n                *[\n                    space.from_jsonable(sample_n[i])\n                    for i, space in enumerate(self.spaces)\n                ]\n            )\n        ]\n\n    def __getitem__(self, index):\n        return self.spaces[index]\n\n    def __len__(self):\n        return len(self.spaces)\n\n    def __eq__(self, other):\n        return isinstance(other, Tuple) and self.spaces == other.spaces\n", "path": "gym/spaces/tuple.py"}], "after_files": [{"content": "from collections import OrderedDict\nimport numpy as np\nfrom .space import Space\n\n\nclass Dict(Space):\n    \"\"\"\n    A dictionary of simpler spaces.\n\n    Example usage:\n    self.observation_space = spaces.Dict({\"position\": spaces.Discrete(2), \"velocity\": spaces.Discrete(3)})\n\n    Example usage [nested]:\n    self.nested_observation_space = spaces.Dict({\n        'sensors':  spaces.Dict({\n            'position': spaces.Box(low=-100, high=100, shape=(3,)),\n            'velocity': spaces.Box(low=-1, high=1, shape=(3,)),\n            'front_cam': spaces.Tuple((\n                spaces.Box(low=0, high=1, shape=(10, 10, 3)),\n                spaces.Box(low=0, high=1, shape=(10, 10, 3))\n            )),\n            'rear_cam': spaces.Box(low=0, high=1, shape=(10, 10, 3)),\n        }),\n        'ext_controller': spaces.MultiDiscrete((5, 2, 2)),\n        'inner_state':spaces.Dict({\n            'charge': spaces.Discrete(100),\n            'system_checks': spaces.MultiBinary(10),\n            'job_status': spaces.Dict({\n                'task': spaces.Discrete(5),\n                'progress': spaces.Box(low=0, high=100, shape=()),\n            })\n        })\n    })\n    \"\"\"\n\n    def __init__(self, spaces=None, **spaces_kwargs):\n        assert (spaces is None) or (\n            not spaces_kwargs\n        ), \"Use either Dict(spaces=dict(...)) or Dict(foo=x, bar=z)\"\n        if spaces is None:\n            spaces = spaces_kwargs\n        if isinstance(spaces, dict) and not isinstance(spaces, OrderedDict):\n            spaces = OrderedDict(sorted(list(spaces.items())))\n        if isinstance(spaces, list):\n            spaces = OrderedDict(spaces)\n        self.spaces = spaces\n        for space in spaces.values():\n            assert isinstance(\n                space, Space\n            ), \"Values of the dict should be instances of gym.Space\"\n        super(Dict, self).__init__(\n            None, None\n        )  # None for shape and dtype, since it'll require special handling\n\n    def seed(self, seed=None):\n        seed = super().seed(seed)\n        try:\n            subseeds = self.np_random.choice(\n                np.iinfo(int).max,\n                size=len(self.spaces),\n                replace=False,  # unique subseed for each subspace\n            )\n        except ValueError:\n            subseeds = self.np_random.choice(\n                np.iinfo(int).max,\n                size=len(self.spaces),\n                replace=True,  # we get more than INT_MAX subspaces\n            )\n\n        for subspace, subseed in zip(self.spaces.values(), subseeds):\n            seed.append(subspace.seed(int(subseed))[0])\n\n        return seed\n\n    def sample(self):\n        return OrderedDict([(k, space.sample()) for k, space in self.spaces.items()])\n\n    def contains(self, x):\n        if not isinstance(x, dict) or len(x) != len(self.spaces):\n            return False\n        for k, space in self.spaces.items():\n            if k not in x:\n                return False\n            if not space.contains(x[k]):\n                return False\n        return True\n\n    def __getitem__(self, key):\n        return self.spaces[key]\n\n    def __setitem__(self, key, value):\n        self.spaces[key] = value\n\n    def __iter__(self):\n        for key in self.spaces:\n            yield key\n\n    def __len__(self):\n        return len(self.spaces)\n\n    def __contains__(self, item):\n        return self.contains(item)\n\n    def __repr__(self):\n        return (\n            \"Dict(\"\n            + \", \".join([str(k) + \":\" + str(s) for k, s in self.spaces.items()])\n            + \")\"\n        )\n\n    def to_jsonable(self, sample_n):\n        # serialize as dict-repr of vectors\n        return {\n            key: space.to_jsonable([sample[key] for sample in sample_n])\n            for key, space in self.spaces.items()\n        }\n\n    def from_jsonable(self, sample_n):\n        dict_of_list = {}\n        for key, space in self.spaces.items():\n            dict_of_list[key] = space.from_jsonable(sample_n[key])\n        ret = []\n        for i, _ in enumerate(dict_of_list[key]):\n            entry = {}\n            for key, value in dict_of_list.items():\n                entry[key] = value[i]\n            ret.append(entry)\n        return ret\n\n    def __eq__(self, other):\n        return isinstance(other, Dict) and self.spaces == other.spaces\n", "path": "gym/spaces/dict.py"}, {"content": "import numpy as np\nfrom .space import Space\n\n\nclass Tuple(Space):\n    \"\"\"\n    A tuple (i.e., product) of simpler spaces\n\n    Example usage:\n    self.observation_space = spaces.Tuple((spaces.Discrete(2), spaces.Discrete(3)))\n    \"\"\"\n\n    def __init__(self, spaces):\n        self.spaces = spaces\n        for space in spaces:\n            assert isinstance(\n                space, Space\n            ), \"Elements of the tuple must be instances of gym.Space\"\n        super(Tuple, self).__init__(None, None)\n\n    def seed(self, seed=None):\n        seed = super().seed(seed)\n        try:\n            subseeds = self.np_random.choice(\n                np.iinfo(int).max,\n                size=len(self.spaces),\n                replace=False,  # unique subseed for each subspace\n            )\n        except ValueError:\n            subseeds = self.np_random.choice(\n                np.iinfo(int).max,\n                size=len(self.spaces),\n                replace=True,  # we get more than INT_MAX subspaces\n            )\n\n        for subspace, subseed in zip(self.spaces, subseeds):\n            seed.append(subspace.seed(int(subseed))[0])\n\n        return seed\n\n    def sample(self):\n        return tuple([space.sample() for space in self.spaces])\n\n    def contains(self, x):\n        if isinstance(x, list):\n            x = tuple(x)  # Promote list to tuple for contains check\n        return (\n            isinstance(x, tuple)\n            and len(x) == len(self.spaces)\n            and all(space.contains(part) for (space, part) in zip(self.spaces, x))\n        )\n\n    def __repr__(self):\n        return \"Tuple(\" + \", \".join([str(s) for s in self.spaces]) + \")\"\n\n    def to_jsonable(self, sample_n):\n        # serialize as list-repr of tuple of vectors\n        return [\n            space.to_jsonable([sample[i] for sample in sample_n])\n            for i, space in enumerate(self.spaces)\n        ]\n\n    def from_jsonable(self, sample_n):\n        return [\n            sample\n            for sample in zip(\n                *[\n                    space.from_jsonable(sample_n[i])\n                    for i, space in enumerate(self.spaces)\n                ]\n            )\n        ]\n\n    def __getitem__(self, index):\n        return self.spaces[index]\n\n    def __len__(self):\n        return len(self.spaces)\n\n    def __eq__(self, other):\n        return isinstance(other, Tuple) and self.spaces == other.spaces\n", "path": "gym/spaces/tuple.py"}]}