synth-ai 0.2.4.dev4__py3-none-any.whl → 0.2.4.dev6__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.
- synth_ai/environments/examples/__init__.py +1 -0
- synth_ai/environments/examples/crafter_classic/__init__.py +8 -0
- synth_ai/environments/examples/crafter_classic/config_logging.py +111 -0
- synth_ai/environments/examples/crafter_classic/debug_translation.py +0 -0
- synth_ai/environments/examples/crafter_classic/engine.py +579 -0
- synth_ai/environments/examples/crafter_classic/engine_deterministic_patch.py +63 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/action_map.py +5 -0
- synth_ai/environments/examples/crafter_classic/engine_helpers/serialization.py +74 -0
- synth_ai/environments/examples/crafter_classic/engine_serialization_patch_v3.py +266 -0
- synth_ai/environments/examples/crafter_classic/environment.py +364 -0
- synth_ai/environments/examples/crafter_classic/taskset.py +233 -0
- synth_ai/environments/examples/crafter_classic/trace_hooks_v3.py +229 -0
- synth_ai/environments/examples/crafter_classic/world_config_patch_simple.py +298 -0
- synth_ai/environments/examples/crafter_custom/__init__.py +4 -0
- synth_ai/environments/examples/crafter_custom/crafter/__init__.py +7 -0
- synth_ai/environments/examples/crafter_custom/crafter/config.py +182 -0
- synth_ai/environments/examples/crafter_custom/crafter/constants.py +8 -0
- synth_ai/environments/examples/crafter_custom/crafter/engine.py +269 -0
- synth_ai/environments/examples/crafter_custom/crafter/env.py +266 -0
- synth_ai/environments/examples/crafter_custom/crafter/objects.py +418 -0
- synth_ai/environments/examples/crafter_custom/crafter/recorder.py +187 -0
- synth_ai/environments/examples/crafter_custom/crafter/worldgen.py +119 -0
- synth_ai/environments/examples/crafter_custom/dataset_builder.py +373 -0
- synth_ai/environments/examples/crafter_custom/environment.py +312 -0
- synth_ai/environments/examples/crafter_custom/run_dataset.py +305 -0
- synth_ai/environments/examples/enron/art_helpers/email_search_tools.py +156 -0
- synth_ai/environments/examples/enron/art_helpers/local_email_db.py +280 -0
- synth_ai/environments/examples/enron/art_helpers/types_enron.py +24 -0
- synth_ai/environments/examples/enron/engine.py +291 -0
- synth_ai/environments/examples/enron/environment.py +165 -0
- synth_ai/environments/examples/enron/taskset.py +112 -0
- synth_ai/environments/examples/minigrid/__init__.py +48 -0
- synth_ai/environments/examples/minigrid/engine.py +589 -0
- synth_ai/environments/examples/minigrid/environment.py +274 -0
- synth_ai/environments/examples/minigrid/environment_mapping.py +242 -0
- synth_ai/environments/examples/minigrid/puzzle_loader.py +416 -0
- synth_ai/environments/examples/minigrid/taskset.py +583 -0
- synth_ai/environments/examples/nethack/__init__.py +7 -0
- synth_ai/environments/examples/nethack/achievements.py +337 -0
- synth_ai/environments/examples/nethack/engine.py +738 -0
- synth_ai/environments/examples/nethack/environment.py +255 -0
- synth_ai/environments/examples/nethack/helpers/__init__.py +42 -0
- synth_ai/environments/examples/nethack/helpers/action_mapping.py +301 -0
- synth_ai/environments/examples/nethack/helpers/nle_wrapper.py +401 -0
- synth_ai/environments/examples/nethack/helpers/observation_utils.py +433 -0
- synth_ai/environments/examples/nethack/helpers/recording_wrapper.py +201 -0
- synth_ai/environments/examples/nethack/helpers/trajectory_recorder.py +268 -0
- synth_ai/environments/examples/nethack/helpers/visualization/replay_viewer.py +308 -0
- synth_ai/environments/examples/nethack/helpers/visualization/visualizer.py +430 -0
- synth_ai/environments/examples/nethack/taskset.py +323 -0
- synth_ai/environments/examples/red/__init__.py +7 -0
- synth_ai/environments/examples/red/config_logging.py +110 -0
- synth_ai/environments/examples/red/engine.py +693 -0
- synth_ai/environments/examples/red/engine_helpers/__init__.py +1 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +28 -0
- synth_ai/environments/examples/red/engine_helpers/reward_components.py +275 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/__init__.py +142 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/adaptive_rewards.py +56 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/battle_rewards.py +283 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/composite_rewards.py +149 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/economy_rewards.py +137 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/efficiency_rewards.py +56 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/exploration_rewards.py +330 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/novelty_rewards.py +120 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_rewards.py +558 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pokemon_rewards.py +312 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/social_rewards.py +147 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/story_rewards.py +246 -0
- synth_ai/environments/examples/red/engine_helpers/screen_analysis.py +367 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +139 -0
- synth_ai/environments/examples/red/environment.py +235 -0
- synth_ai/environments/examples/red/taskset.py +77 -0
- synth_ai/environments/examples/sokoban/__init__.py +1 -0
- synth_ai/environments/examples/sokoban/engine.py +675 -0
- synth_ai/environments/examples/sokoban/engine_helpers/__init__.py +1 -0
- synth_ai/environments/examples/sokoban/engine_helpers/room_utils.py +656 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/__init__.py +17 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/__init__.py +3 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/boxoban_env.py +129 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/render_utils.py +370 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/room_utils.py +331 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env.py +305 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_fixed_targets.py +66 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_pull.py +114 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_two_player.py +122 -0
- synth_ai/environments/examples/sokoban/engine_helpers/vendored/envs/sokoban_env_variations.py +394 -0
- synth_ai/environments/examples/sokoban/environment.py +228 -0
- synth_ai/environments/examples/sokoban/generate_verified_puzzles.py +438 -0
- synth_ai/environments/examples/sokoban/puzzle_loader.py +311 -0
- synth_ai/environments/examples/sokoban/taskset.py +425 -0
- synth_ai/environments/examples/tictactoe/__init__.py +1 -0
- synth_ai/environments/examples/tictactoe/engine.py +368 -0
- synth_ai/environments/examples/tictactoe/environment.py +239 -0
- synth_ai/environments/examples/tictactoe/taskset.py +214 -0
- synth_ai/environments/examples/verilog/__init__.py +10 -0
- synth_ai/environments/examples/verilog/engine.py +328 -0
- synth_ai/environments/examples/verilog/environment.py +349 -0
- synth_ai/environments/examples/verilog/taskset.py +418 -0
- synth_ai/environments/examples/wordle/__init__.py +29 -0
- synth_ai/environments/examples/wordle/engine.py +391 -0
- synth_ai/environments/examples/wordle/environment.py +154 -0
- synth_ai/environments/examples/wordle/helpers/generate_instances_wordfreq.py +75 -0
- synth_ai/environments/examples/wordle/taskset.py +222 -0
- synth_ai/environments/service/app.py +8 -0
- synth_ai/environments/service/core_routes.py +38 -0
- synth_ai/learning/prompts/banking77_injection_eval.py +163 -0
- synth_ai/learning/prompts/hello_world_in_context_injection_ex.py +201 -0
- synth_ai/learning/prompts/mipro.py +273 -1
- synth_ai/learning/prompts/random_search.py +247 -0
- synth_ai/learning/prompts/run_mipro_banking77.py +160 -0
- synth_ai/learning/prompts/run_random_search_banking77.py +305 -0
- synth_ai/lm/injection.py +81 -0
- synth_ai/lm/overrides.py +204 -0
- synth_ai/lm/provider_support/anthropic.py +39 -12
- synth_ai/lm/provider_support/openai.py +31 -4
- synth_ai/lm/vendors/core/anthropic_api.py +16 -0
- synth_ai/lm/vendors/openai_standard.py +35 -5
- {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev6.dist-info}/METADATA +2 -1
- {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev6.dist-info}/RECORD +123 -13
- {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev6.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev6.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev6.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev4.dist-info → synth_ai-0.2.4.dev6.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,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
|