synth-ai 0.2.4.dev4__py3-none-any.whl → 0.2.4.dev5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. synth_ai/environments/examples/__init__.py +1 -0
  2. synth_ai/environments/examples/crafter_classic/__init__.py +8 -0
  3. synth_ai/environments/examples/crafter_classic/config_logging.py +111 -0
  4. synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
  5. synth_ai/environments/examples/crafter_classic/engine.py +575 -0
  6. synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +63 -0
  7. synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +5 -0
  8. synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +74 -0
  9. synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +266 -0
  10. synth_ai/environments/examples/crafter_classic/environment.py +364 -0
  11. synth_ai/environments/examples/crafter_classic/taskset.py +233 -0
  12. synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +229 -0
  13. synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +298 -0
  14. synth_ai/environments/examples/crafter_custom/__init__.py +4 -0
  15. synth_ai/environments/examples/crafter_custom/crafter/__init__.py +7 -0
  16. synth_ai/environments/examples/crafter_custom/crafter/config.py +182 -0
  17. synth_ai/environments/examples/crafter_custom/crafter/constants.py +8 -0
  18. synth_ai/environments/examples/crafter_custom/crafter/engine.py +269 -0
  19. synth_ai/environments/examples/crafter_custom/crafter/env.py +266 -0
  20. synth_ai/environments/examples/crafter_custom/crafter/objects.py +418 -0
  21. synth_ai/environments/examples/crafter_custom/crafter/recorder.py +187 -0
  22. synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +119 -0
  23. synth_ai/environments/examples/crafter_custom/dataset_builder.py +373 -0
  24. synth_ai/environments/examples/crafter_custom/environment.py +312 -0
  25. synth_ai/environments/examples/crafter_custom/run_dataset.py +305 -0
  26. synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +156 -0
  27. synth_ai/environments/examples/enron/art_helpers/local_email_db.py +280 -0
  28. synth_ai/environments/examples/enron/art_helpers/types_enron.py +24 -0
  29. synth_ai/environments/examples/enron/engine.py +291 -0
  30. synth_ai/environments/examples/enron/environment.py +165 -0
  31. synth_ai/environments/examples/enron/taskset.py +112 -0
  32. synth_ai/environments/examples/minigrid/__init__.py +48 -0
  33. synth_ai/environments/examples/minigrid/engine.py +589 -0
  34. synth_ai/environments/examples/minigrid/environment.py +274 -0
  35. synth_ai/environments/examples/minigrid/environment_mapping.py +242 -0
  36. synth_ai/environments/examples/minigrid/puzzle_loader.py +416 -0
  37. synth_ai/environments/examples/minigrid/taskset.py +583 -0
  38. synth_ai/environments/examples/nethack/__init__.py +7 -0
  39. synth_ai/environments/examples/nethack/achievements.py +337 -0
  40. synth_ai/environments/examples/nethack/engine.py +738 -0
  41. synth_ai/environments/examples/nethack/environment.py +255 -0
  42. synth_ai/environments/examples/nethack/helpers/__init__.py +42 -0
  43. synth_ai/environments/examples/nethack/helpers/action_mapping.py +301 -0
  44. synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +401 -0
  45. synth_ai/environments/examples/nethack/helpers/observation_utils.py +433 -0
  46. synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +201 -0
  47. synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +268 -0
  48. synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +308 -0
  49. synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +430 -0
  50. synth_ai/environments/examples/nethack/taskset.py +323 -0
  51. synth_ai/environments/examples/red/__init__.py +7 -0
  52. synth_ai/environments/examples/red/config_logging.py +110 -0
  53. synth_ai/environments/examples/red/engine.py +693 -0
  54. synth_ai/environments/examples/red/engine_helpers/__init__.py +1 -0
  55. synth_ai/environments/examples/red/engine_helpers/memory_map.py +28 -0
  56. synth_ai/environments/examples/red/engine_helpers/reward_components.py +275 -0
  57. synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +142 -0
  58. synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +56 -0
  59. synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +283 -0
  60. synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +149 -0
  61. synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +137 -0
  62. synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +56 -0
  63. synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +330 -0
  64. synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +120 -0
  65. synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +558 -0
  66. synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +312 -0
  67. synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +147 -0
  68. synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +246 -0
  69. synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +367 -0
  70. synth_ai/environments/examples/red/engine_helpers/state_extraction.py +139 -0
  71. synth_ai/environments/examples/red/environment.py +235 -0
  72. synth_ai/environments/examples/red/taskset.py +77 -0
  73. synth_ai/environments/examples/sokoban/__init__.py +1 -0
  74. synth_ai/environments/examples/sokoban/engine.py +675 -0
  75. synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +1 -0
  76. synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +656 -0
  77. synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +17 -0
  78. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +3 -0
  79. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +129 -0
  80. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +370 -0
  81. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +331 -0
  82. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +305 -0
  83. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +66 -0
  84. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +114 -0
  85. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +122 -0
  86. synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +394 -0
  87. synth_ai/environments/examples/sokoban/environment.py +228 -0
  88. synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +438 -0
  89. synth_ai/environments/examples/sokoban/puzzle_loader.py +311 -0
  90. synth_ai/environments/examples/sokoban/taskset.py +425 -0
  91. synth_ai/environments/examples/tictactoe/__init__.py +1 -0
  92. synth_ai/environments/examples/tictactoe/engine.py +368 -0
  93. synth_ai/environments/examples/tictactoe/environment.py +239 -0
  94. synth_ai/environments/examples/tictactoe/taskset.py +214 -0
  95. synth_ai/environments/examples/verilog/__init__.py +10 -0
  96. synth_ai/environments/examples/verilog/engine.py +328 -0
  97. synth_ai/environments/examples/verilog/environment.py +349 -0
  98. synth_ai/environments/examples/verilog/taskset.py +418 -0
  99. {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev5.dist-info}/METADATA +1 -1
  100. {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev5.dist-info}/RECORD +104 -6
  101. {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev5.dist-info}/WHEEL +0 -0
  102. {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev5.dist-info}/entry_points.txt +0 -0
  103. {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev5.dist-info}/licenses/LICENSE +0 -0
  104. {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,182 @@
1
+ """
2
+ Configuration system for customizable Crafter environment.
3
+ """
4
+
5
+ import json
6
+ import os
7
+ from dataclasses import dataclass, field, asdict
8
+ from typing import Dict, Any, Optional
9
+
10
+
11
+ @dataclass
12
+ class WorldGenConfig:
13
+ """Configuration for world generation parameters."""
14
+
15
+ # Material generation thresholds and probabilities
16
+ coal_threshold: float = 0.0
17
+ coal_probability: float = 0.15
18
+ iron_threshold: float = 0.4
19
+ iron_probability: float = 0.25
20
+ diamond_threshold: float = 0.18
21
+ diamond_probability: float = 0.006
22
+ tree_threshold: float = 0.0
23
+ tree_probability: float = 0.2
24
+ lava_threshold: float = 0.35
25
+
26
+ # Cave generation
27
+ cave_threshold: float = 0.6
28
+
29
+ # Initial spawn configuration
30
+ cow_spawn_probability: float = 0.015
31
+ cow_min_distance: int = 3
32
+ zombie_spawn_probability: float = 0.007
33
+ zombie_min_distance: int = 10
34
+ skeleton_spawn_probability: float = 0.05
35
+
36
+ # Dynamic spawn configuration for zombies
37
+ zombie_spawn_rate: float = 0.3
38
+ zombie_despawn_rate: float = 0.4
39
+ zombie_min_spawn_distance: int = 6
40
+ zombie_max_count: float = 3.5
41
+ zombie_min_count: float = 0.0
42
+
43
+ # Dynamic spawn configuration for skeletons
44
+ skeleton_spawn_rate: float = 0.1
45
+ skeleton_despawn_rate: float = 0.1
46
+ skeleton_min_spawn_distance: int = 7
47
+ skeleton_max_count: float = 2.0
48
+ skeleton_min_count: float = 0.0
49
+
50
+ # Dynamic spawn configuration for cows
51
+ cow_spawn_rate: float = 0.01
52
+ cow_despawn_rate: float = 0.1
53
+ cow_min_spawn_distance: int = 5
54
+ cow_max_count: float = 2.5
55
+ cow_min_count: float = 0.0
56
+
57
+ # World generation areas
58
+ spawn_clear_radius: int = 4
59
+ water_threshold: float = -0.3
60
+ sand_threshold: float = -0.2
61
+ mountain_threshold: float = 0.15
62
+
63
+ @classmethod
64
+ def from_json(cls, path: str) -> "WorldGenConfig":
65
+ """Load configuration from JSON file."""
66
+ with open(path, "r") as f:
67
+ data = json.load(f)
68
+ return cls(**data)
69
+
70
+ @classmethod
71
+ def from_preset(cls, preset: str) -> "WorldGenConfig":
72
+ """Load configuration from preset name."""
73
+ config_dir = os.path.dirname(__file__)
74
+ preset_path = os.path.join(config_dir, "config", f"{preset}.json")
75
+ if os.path.exists(preset_path):
76
+ return cls.from_json(preset_path)
77
+ else:
78
+ print(f"Warning: preset '{preset}' not found, using default config")
79
+ return cls()
80
+
81
+ def to_json(self, path: str) -> None:
82
+ """Save configuration to JSON file."""
83
+ with open(path, "w") as f:
84
+ json.dump(asdict(self), f, indent=2)
85
+
86
+
87
+ # Preset configurations
88
+ PRESETS = {
89
+ "easy": WorldGenConfig(
90
+ # More resources
91
+ coal_threshold=-0.2,
92
+ coal_probability=0.25,
93
+ iron_threshold=0.2,
94
+ iron_probability=0.35,
95
+ diamond_threshold=0.15,
96
+ diamond_probability=0.02,
97
+ tree_threshold=-0.2,
98
+ tree_probability=0.35,
99
+ lava_threshold=0.5,
100
+ # More friendly spawns
101
+ cow_spawn_probability=0.03,
102
+ cow_min_distance=2,
103
+ zombie_spawn_probability=0.003,
104
+ zombie_min_distance=15,
105
+ skeleton_spawn_probability=0.01,
106
+ # Fewer dynamic enemies
107
+ zombie_spawn_rate=0.1,
108
+ zombie_despawn_rate=0.6,
109
+ zombie_min_spawn_distance=10,
110
+ zombie_max_count=1.5,
111
+ skeleton_spawn_rate=0.02,
112
+ skeleton_despawn_rate=0.3,
113
+ skeleton_min_spawn_distance=12,
114
+ skeleton_max_count=0.5,
115
+ cow_spawn_rate=0.03,
116
+ cow_despawn_rate=0.05,
117
+ cow_min_spawn_distance=3,
118
+ cow_max_count=4.0,
119
+ ),
120
+ "hard": WorldGenConfig(
121
+ # Fewer resources
122
+ coal_threshold=0.2,
123
+ coal_probability=0.08,
124
+ iron_threshold=0.5,
125
+ iron_probability=0.15,
126
+ diamond_threshold=0.25,
127
+ diamond_probability=0.002,
128
+ tree_threshold=0.2,
129
+ tree_probability=0.1,
130
+ lava_threshold=0.25,
131
+ # More dangerous spawns
132
+ cow_spawn_probability=0.005,
133
+ cow_min_distance=5,
134
+ zombie_spawn_probability=0.02,
135
+ zombie_min_distance=6,
136
+ skeleton_spawn_probability=0.15,
137
+ # Many dynamic enemies
138
+ zombie_spawn_rate=0.5,
139
+ zombie_despawn_rate=0.2,
140
+ zombie_min_spawn_distance=4,
141
+ zombie_max_count=6.0,
142
+ skeleton_spawn_rate=0.3,
143
+ skeleton_despawn_rate=0.05,
144
+ skeleton_min_spawn_distance=5,
145
+ skeleton_max_count=4.0,
146
+ cow_spawn_rate=0.002,
147
+ cow_despawn_rate=0.2,
148
+ cow_min_spawn_distance=8,
149
+ cow_max_count=1.0,
150
+ ),
151
+ "peaceful": WorldGenConfig(
152
+ # Abundant resources
153
+ coal_threshold=-0.3,
154
+ coal_probability=0.3,
155
+ iron_threshold=0.1,
156
+ iron_probability=0.4,
157
+ diamond_threshold=0.1,
158
+ diamond_probability=0.03,
159
+ tree_threshold=-0.3,
160
+ tree_probability=0.4,
161
+ lava_threshold=0.6,
162
+ # No enemies, many cows
163
+ cow_spawn_probability=0.05,
164
+ cow_min_distance=2,
165
+ zombie_spawn_probability=0.0,
166
+ zombie_min_distance=100,
167
+ skeleton_spawn_probability=0.0,
168
+ # No dynamic enemies
169
+ zombie_spawn_rate=0.0,
170
+ zombie_despawn_rate=1.0,
171
+ zombie_min_spawn_distance=100,
172
+ zombie_max_count=0.0,
173
+ skeleton_spawn_rate=0.0,
174
+ skeleton_despawn_rate=1.0,
175
+ skeleton_min_spawn_distance=100,
176
+ skeleton_max_count=0.0,
177
+ cow_spawn_rate=0.05,
178
+ cow_despawn_rate=0.02,
179
+ cow_min_spawn_distance=2,
180
+ cow_max_count=5.0,
181
+ ),
182
+ }
@@ -0,0 +1,8 @@
1
+ import pathlib
2
+
3
+ import ruamel.yaml
4
+
5
+ root = pathlib.Path(__file__).parent
6
+ yaml = ruamel.yaml.YAML(typ="safe", pure=True)
7
+ for key, value in yaml.load((root / "data.yaml").read_text()).items():
8
+ globals()[key] = value
@@ -0,0 +1,269 @@
1
+ import collections
2
+ import functools
3
+ import pathlib
4
+
5
+ import imageio.v3 as imageio
6
+ import numpy as np
7
+ from PIL import Image, ImageEnhance
8
+
9
+
10
+ class AttrDict(dict):
11
+ __getattr__ = dict.__getitem__
12
+
13
+
14
+ class staticproperty:
15
+ def __init__(self, function):
16
+ self.function = function
17
+
18
+ def __get__(self, instance, owner=None):
19
+ return self.function()
20
+
21
+
22
+ class World:
23
+ def __init__(self, area, materials, chunk_size):
24
+ self.area = area
25
+ self._chunk_size = chunk_size
26
+ self._mat_names = {i: x for i, x in enumerate([None] + materials)}
27
+ self._mat_ids = {x: i for i, x in enumerate([None] + materials)}
28
+ self.reset()
29
+
30
+ def reset(self, seed=None):
31
+ self.random = np.random.RandomState(seed)
32
+ self.daylight = 0.0
33
+ self._chunks = collections.defaultdict(set)
34
+ self._objects = [None]
35
+ self._mat_map = np.zeros(self.area, np.uint8)
36
+ self._obj_map = np.zeros(self.area, np.uint32)
37
+
38
+ @property
39
+ def objects(self):
40
+ # Return a new list so the objects cannot change while being iterated over.
41
+ return [obj for obj in self._objects if obj]
42
+
43
+ @property
44
+ def chunks(self):
45
+ return self._chunks.copy()
46
+
47
+ def add(self, obj):
48
+ assert hasattr(obj, "pos")
49
+ obj.pos = np.array(obj.pos)
50
+ assert self._obj_map[tuple(obj.pos)] == 0
51
+ index = len(self._objects)
52
+ self._objects.append(obj)
53
+ self._obj_map[tuple(obj.pos)] = index
54
+ self._chunks[self.chunk_key(obj.pos)].add(obj)
55
+
56
+ def remove(self, obj):
57
+ if obj.removed:
58
+ return
59
+ self._objects[self._obj_map[tuple(obj.pos)]] = None
60
+ self._obj_map[tuple(obj.pos)] = 0
61
+ self._chunks[self.chunk_key(obj.pos)].remove(obj)
62
+ obj.removed = True
63
+
64
+ def move(self, obj, pos):
65
+ if obj.removed:
66
+ return
67
+ pos = np.array(pos)
68
+ assert self._obj_map[tuple(pos)] == 0
69
+ index = self._obj_map[tuple(obj.pos)]
70
+ self._obj_map[tuple(pos)] = index
71
+ self._obj_map[tuple(obj.pos)] = 0
72
+ old_chunk = self.chunk_key(obj.pos)
73
+ new_chunk = self.chunk_key(pos)
74
+ if old_chunk != new_chunk:
75
+ self._chunks[old_chunk].remove(obj)
76
+ self._chunks[new_chunk].add(obj)
77
+ obj.pos = pos
78
+
79
+ def __setitem__(self, pos, material):
80
+ if material not in self._mat_ids:
81
+ id_ = len(self._mat_ids)
82
+ self._mat_ids[material] = id_
83
+ self._mat_map[tuple(pos)] = self._mat_ids[material]
84
+
85
+ def __getitem__(self, pos):
86
+ if not _inside((0, 0), pos, self.area):
87
+ return None, None
88
+ material = self._mat_names[self._mat_map[tuple(pos)]]
89
+ obj = self._objects[self._obj_map[tuple(pos)]]
90
+ return material, obj
91
+
92
+ def nearby(self, pos, distance):
93
+ (x, y), d = pos, distance
94
+ ids = set(self._mat_map[x - d : x + d + 1, y - d : y + d + 1].flatten().tolist())
95
+ materials = tuple(self._mat_names[x] for x in ids)
96
+ indices = self._obj_map[x - d : x + d + 1, y - d : y + d + 1].flatten().tolist()
97
+ objs = {self._objects[i] for i in indices if i > 0}
98
+ return materials, objs
99
+
100
+ def mask(self, xmin, xmax, ymin, ymax, material):
101
+ region = self._mat_map[xmin:xmax, ymin:ymax]
102
+ return region == self._mat_ids[material]
103
+
104
+ def count(self, material):
105
+ return (self._mat_map == self._mat_ids[material]).sum()
106
+
107
+ def chunk_key(self, pos):
108
+ (x, y), (csx, csy) = pos, self._chunk_size
109
+ xmin, ymin = (x // csx) * csx, (y // csy) * csy
110
+ xmax = min(xmin + csx, self.area[0])
111
+ ymax = min(ymin + csy, self.area[1])
112
+ return (xmin, xmax, ymin, ymax)
113
+
114
+
115
+ class Textures:
116
+ def __init__(self, directory):
117
+ self._originals = {}
118
+ self._textures = {}
119
+ for filename in pathlib.Path(directory).glob("*.png"):
120
+ image = imageio.imread(filename.read_bytes())
121
+ image = image.transpose((1, 0) + tuple(range(2, len(image.shape))))
122
+ self._originals[filename.stem] = image
123
+ self._textures[(filename.stem, image.shape[:2])] = image
124
+
125
+ def get(self, name, size):
126
+ if name is None:
127
+ name = "unknown"
128
+ size = int(size[0]), int(size[1])
129
+ key = name, size
130
+ if key not in self._textures:
131
+ image = self._originals[name]
132
+ image = Image.fromarray(image)
133
+ image = image.resize(size[::-1], resample=Image.NEAREST)
134
+ image = np.array(image)
135
+ self._textures[key] = image
136
+ return self._textures[key]
137
+
138
+
139
+ class GlobalView:
140
+ pass
141
+
142
+
143
+ class UncoverView:
144
+ pass
145
+
146
+
147
+ class LocalView:
148
+ def __init__(self, world, textures, grid):
149
+ self._world = world
150
+ self._textures = textures
151
+ self._grid = np.array(grid)
152
+ self._offset = self._grid // 2
153
+ self._area = np.array(self._world.area)
154
+ self._center = None
155
+
156
+ def __call__(self, player, unit):
157
+ self._unit = np.array(unit)
158
+ self._center = np.array(player.pos)
159
+ canvas = np.zeros(tuple(self._grid * unit) + (3,), np.uint8) + 127
160
+ for x in range(self._grid[0]):
161
+ for y in range(self._grid[1]):
162
+ pos = self._center + np.array([x, y]) - self._offset
163
+ if not _inside((0, 0), pos, self._area):
164
+ continue
165
+ texture = self._textures.get(self._world[pos][0], unit)
166
+ _draw(canvas, np.array([x, y]) * unit, texture)
167
+ for obj in self._world.objects:
168
+ pos = obj.pos - self._center + self._offset
169
+ if not _inside((0, 0), pos, self._grid):
170
+ continue
171
+ texture = self._textures.get(obj.texture, unit)
172
+ _draw_alpha(canvas, pos * unit, texture)
173
+ canvas = self._light(canvas, self._world.daylight)
174
+ if player.sleeping:
175
+ canvas = self._sleep(canvas)
176
+ # if player.health < 1:
177
+ # canvas = self._tint(canvas, (128, 0, 0), 0.6)
178
+ return canvas
179
+
180
+ def _light(self, canvas, daylight):
181
+ night = canvas
182
+ if daylight < 0.5:
183
+ night = self._noise(night, 2 * (0.5 - daylight), 0.5)
184
+ night = np.array(ImageEnhance.Color(Image.fromarray(night.astype(np.uint8))).enhance(0.4))
185
+ night = self._tint(night, (0, 16, 64), 0.5)
186
+ return daylight * canvas + (1 - daylight) * night
187
+
188
+ def _sleep(self, canvas):
189
+ canvas = np.array(ImageEnhance.Color(Image.fromarray(canvas.astype(np.uint8))).enhance(0.0))
190
+ canvas = self._tint(canvas, (0, 0, 16), 0.5)
191
+ return canvas
192
+
193
+ def _tint(self, canvas, color, amount):
194
+ color = np.array(color)
195
+ return (1 - amount) * canvas + amount * color
196
+
197
+ def _noise(self, canvas, amount, stddev):
198
+ noise = self._world.random.uniform(32, 127, canvas.shape[:2])[..., None]
199
+ mask = amount * self._vignette(canvas.shape, stddev)[..., None]
200
+ return (1 - mask) * canvas + mask * noise
201
+
202
+ @functools.lru_cache(10)
203
+ def _vignette(self, shape, stddev):
204
+ xs, ys = np.meshgrid(np.linspace(-1, 1, shape[0]), np.linspace(-1, 1, shape[1]))
205
+ return 1 - np.exp(-0.5 * (xs**2 + ys**2) / (stddev**2)).T
206
+
207
+
208
+ class ItemView:
209
+ def __init__(self, textures, grid):
210
+ self._textures = textures
211
+ self._grid = np.array(grid)
212
+
213
+ def __call__(self, inventory, unit):
214
+ unit = np.array(unit)
215
+ canvas = np.zeros(tuple(self._grid * unit) + (3,), np.uint8)
216
+ for index, (item, amount) in enumerate(inventory.items()):
217
+ if amount < 1:
218
+ continue
219
+ self._item(canvas, index, item, unit)
220
+ self._amount(canvas, index, amount, unit)
221
+ return canvas
222
+
223
+ def _item(self, canvas, index, item, unit):
224
+ pos = index % self._grid[0], index // self._grid[0]
225
+ pos = (pos * unit + 0.1 * unit).astype(np.int32)
226
+ texture = self._textures.get(item, 0.8 * unit)
227
+ _draw_alpha(canvas, pos, texture)
228
+
229
+ def _amount(self, canvas, index, amount, unit):
230
+ pos = index % self._grid[0], index // self._grid[0]
231
+ pos = (pos * unit + 0.4 * unit).astype(np.int32)
232
+ text = str(amount) if amount in list(range(10)) else "unknown"
233
+ texture = self._textures.get(text, 0.6 * unit)
234
+ _draw_alpha(canvas, pos, texture)
235
+
236
+
237
+ class SemanticView:
238
+ def __init__(self, world, obj_types):
239
+ self._world = world
240
+ self._mat_ids = world._mat_ids.copy()
241
+ self._obj_ids = {c: len(self._mat_ids) + i for i, c in enumerate(obj_types)}
242
+
243
+ def __call__(self):
244
+ canvas = self._world._mat_map.copy()
245
+ for obj in self._world.objects:
246
+ canvas[tuple(obj.pos)] = self._obj_ids[type(obj)]
247
+ return canvas
248
+
249
+
250
+ def _inside(lhs, mid, rhs):
251
+ return (lhs[0] <= mid[0] < rhs[0]) and (lhs[1] <= mid[1] < rhs[1])
252
+
253
+
254
+ def _draw(canvas, pos, texture):
255
+ (x, y), (w, h) = pos, texture.shape[:2]
256
+ if texture.shape[-1] == 4:
257
+ texture = texture[..., :3]
258
+ canvas[x : x + w, y : y + h] = texture
259
+
260
+
261
+ def _draw_alpha(canvas, pos, texture):
262
+ (x, y), (w, h) = pos, texture.shape[:2]
263
+ if texture.shape[-1] == 4:
264
+ alpha = texture[..., 3:].astype(np.float32) / 255
265
+ texture = texture[..., :3].astype(np.float32) / 255
266
+ current = canvas[x : x + w, y : y + h].astype(np.float32) / 255
267
+ blended = alpha * texture + (1 - alpha) * current
268
+ texture = (255 * blended).astype(np.uint8)
269
+ canvas[x : x + w, y : y + h] = texture