synth-ai 0.2.4.dev3__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.
- 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 +575 -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/tracing_v3/examples/basic_usage.py +188 -0
- {synth_ai-0.2.4.dev3.dist-info → synth_ai-0.2.4.dev5.dist-info}/METADATA +1 -1
- {synth_ai-0.2.4.dev3.dist-info → synth_ai-0.2.4.dev5.dist-info}/RECORD +105 -6
- {synth_ai-0.2.4.dev3.dist-info → synth_ai-0.2.4.dev5.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.4.dev3.dist-info → synth_ai-0.2.4.dev5.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.4.dev3.dist-info → synth_ai-0.2.4.dev5.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.4.dev3.dist-info → synth_ai-0.2.4.dev5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,229 @@
|
|
1
|
+
"""
|
2
|
+
Trace hooks for Crafter Classic environment - v3 version.
|
3
|
+
Updated to use the v3 tracing architecture with async support.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from typing import Any, Optional, Dict, Set
|
7
|
+
from datetime import datetime
|
8
|
+
|
9
|
+
from synth_ai.tracing_v3.hooks import HookManager
|
10
|
+
from synth_ai.tracing_v3.abstractions import BaseEvent, EnvironmentEvent
|
11
|
+
|
12
|
+
|
13
|
+
# Achievement categorization by difficulty
|
14
|
+
EASY_ACHIEVEMENTS = {
|
15
|
+
"collect_wood",
|
16
|
+
"collect_stone",
|
17
|
+
"collect_sapling",
|
18
|
+
"collect_drink",
|
19
|
+
"place_stone",
|
20
|
+
"place_table",
|
21
|
+
"wake_up",
|
22
|
+
"eat_plant",
|
23
|
+
}
|
24
|
+
|
25
|
+
MEDIUM_ACHIEVEMENTS = {
|
26
|
+
"make_wood_pickaxe",
|
27
|
+
"make_wood_sword",
|
28
|
+
"place_furnace",
|
29
|
+
"place_plant",
|
30
|
+
"collect_coal",
|
31
|
+
"collect_iron",
|
32
|
+
"eat_cow",
|
33
|
+
}
|
34
|
+
|
35
|
+
HARD_ACHIEVEMENTS = {
|
36
|
+
"make_stone_pickaxe",
|
37
|
+
"make_stone_sword",
|
38
|
+
"make_iron_pickaxe",
|
39
|
+
"make_iron_sword",
|
40
|
+
"collect_diamond",
|
41
|
+
"defeat_skeleton",
|
42
|
+
"defeat_zombie",
|
43
|
+
}
|
44
|
+
|
45
|
+
|
46
|
+
async def check_easy_achievements(event_obj: BaseEvent, **kwargs) -> Optional[Dict[str, Any]]:
|
47
|
+
"""Hook that fires when an easy achievement is unlocked."""
|
48
|
+
# Only process EnvironmentEvents
|
49
|
+
if not isinstance(event_obj, EnvironmentEvent):
|
50
|
+
return None
|
51
|
+
|
52
|
+
# Get achievements before and after
|
53
|
+
before_achievements = {}
|
54
|
+
after_achievements = {}
|
55
|
+
|
56
|
+
state_before = event_obj.system_state_before
|
57
|
+
state_after = event_obj.system_state_after
|
58
|
+
|
59
|
+
if isinstance(state_before, dict):
|
60
|
+
before_achievements = state_before.get("public_state", {}).get("achievements_status", {})
|
61
|
+
if isinstance(state_after, dict):
|
62
|
+
after_achievements = state_after.get("public_state", {}).get("achievements_status", {})
|
63
|
+
|
64
|
+
# Find new easy achievements
|
65
|
+
new_easy_achievements = []
|
66
|
+
for achievement, status in after_achievements.items():
|
67
|
+
if status and not before_achievements.get(achievement, False):
|
68
|
+
if achievement in EASY_ACHIEVEMENTS:
|
69
|
+
new_easy_achievements.append(achievement)
|
70
|
+
|
71
|
+
if new_easy_achievements:
|
72
|
+
# print(f"🎯 Easy achievement(s) unlocked: {', '.join(new_easy_achievements)}") # Disabled for clean output
|
73
|
+
return {
|
74
|
+
"achievements": new_easy_achievements,
|
75
|
+
"difficulty": "easy",
|
76
|
+
"timestamp": datetime.now().isoformat(),
|
77
|
+
}
|
78
|
+
return None
|
79
|
+
|
80
|
+
|
81
|
+
async def check_medium_achievements(event_obj: BaseEvent, **kwargs) -> Optional[Dict[str, Any]]:
|
82
|
+
"""Hook that fires when a medium achievement is unlocked."""
|
83
|
+
# Only process EnvironmentEvents
|
84
|
+
if not isinstance(event_obj, EnvironmentEvent):
|
85
|
+
return None
|
86
|
+
|
87
|
+
# Get achievements before and after
|
88
|
+
before_achievements = {}
|
89
|
+
after_achievements = {}
|
90
|
+
|
91
|
+
state_before = event_obj.system_state_before
|
92
|
+
state_after = event_obj.system_state_after
|
93
|
+
|
94
|
+
if isinstance(state_before, dict):
|
95
|
+
before_achievements = state_before.get("public_state", {}).get("achievements_status", {})
|
96
|
+
if isinstance(state_after, dict):
|
97
|
+
after_achievements = state_after.get("public_state", {}).get("achievements_status", {})
|
98
|
+
|
99
|
+
# Find new medium achievements
|
100
|
+
new_medium_achievements = []
|
101
|
+
for achievement, status in after_achievements.items():
|
102
|
+
if status and not before_achievements.get(achievement, False):
|
103
|
+
if achievement in MEDIUM_ACHIEVEMENTS:
|
104
|
+
new_medium_achievements.append(achievement)
|
105
|
+
|
106
|
+
if new_medium_achievements:
|
107
|
+
# print(f"⭐ Medium achievement(s) unlocked: {', '.join(new_medium_achievements)}") # Disabled for clean output
|
108
|
+
return {
|
109
|
+
"achievements": new_medium_achievements,
|
110
|
+
"difficulty": "medium",
|
111
|
+
"timestamp": datetime.now().isoformat(),
|
112
|
+
}
|
113
|
+
return None
|
114
|
+
|
115
|
+
|
116
|
+
async def check_hard_achievements(event_obj: BaseEvent, **kwargs) -> Optional[Dict[str, Any]]:
|
117
|
+
"""Hook that fires when a hard achievement is unlocked."""
|
118
|
+
# Only process EnvironmentEvents
|
119
|
+
if not isinstance(event_obj, EnvironmentEvent):
|
120
|
+
return None
|
121
|
+
|
122
|
+
# Get achievements before and after
|
123
|
+
before_achievements = {}
|
124
|
+
after_achievements = {}
|
125
|
+
|
126
|
+
state_before = event_obj.system_state_before
|
127
|
+
state_after = event_obj.system_state_after
|
128
|
+
|
129
|
+
if isinstance(state_before, dict):
|
130
|
+
before_achievements = state_before.get("public_state", {}).get("achievements_status", {})
|
131
|
+
if isinstance(state_after, dict):
|
132
|
+
after_achievements = state_after.get("public_state", {}).get("achievements_status", {})
|
133
|
+
|
134
|
+
# Find new hard achievements
|
135
|
+
new_hard_achievements = []
|
136
|
+
for achievement, status in after_achievements.items():
|
137
|
+
if status and not before_achievements.get(achievement, False):
|
138
|
+
if achievement in HARD_ACHIEVEMENTS:
|
139
|
+
new_hard_achievements.append(achievement)
|
140
|
+
|
141
|
+
if new_hard_achievements:
|
142
|
+
# print(f"🏆 Hard achievement(s) unlocked: {', '.join(new_hard_achievements)}") # Disabled for clean output
|
143
|
+
return {
|
144
|
+
"achievements": new_hard_achievements,
|
145
|
+
"difficulty": "hard",
|
146
|
+
"timestamp": datetime.now().isoformat(),
|
147
|
+
}
|
148
|
+
return None
|
149
|
+
|
150
|
+
|
151
|
+
async def log_invalid_actions(event_obj: BaseEvent, **kwargs) -> Optional[Dict[str, Any]]:
|
152
|
+
"""Hook that logs invalid actions."""
|
153
|
+
from synth_ai.tracing_v3.abstractions import RuntimeEvent
|
154
|
+
|
155
|
+
# Only process RuntimeEvents
|
156
|
+
if not isinstance(event_obj, RuntimeEvent):
|
157
|
+
return None
|
158
|
+
|
159
|
+
# Check if action was invalid
|
160
|
+
if event_obj.metadata.get("valid") is False:
|
161
|
+
action_name = event_obj.metadata.get("action_name", "unknown")
|
162
|
+
# print(f"⚠️ Invalid action attempted: {action_name}") # Disabled for clean output
|
163
|
+
return {
|
164
|
+
"action": action_name,
|
165
|
+
"valid": False,
|
166
|
+
"timestamp": datetime.now().isoformat(),
|
167
|
+
}
|
168
|
+
return None
|
169
|
+
|
170
|
+
|
171
|
+
async def track_reward_milestones(event_obj: BaseEvent, **kwargs) -> Optional[Dict[str, Any]]:
|
172
|
+
"""Hook that tracks significant reward events."""
|
173
|
+
# Only process EnvironmentEvents
|
174
|
+
if not isinstance(event_obj, EnvironmentEvent):
|
175
|
+
return None
|
176
|
+
|
177
|
+
reward = event_obj.reward
|
178
|
+
if reward and reward >= 1.0: # Significant positive reward
|
179
|
+
# print(f"💰 High reward received: {reward}") # Disabled for clean output
|
180
|
+
return {
|
181
|
+
"reward": reward,
|
182
|
+
"timestamp": datetime.now().isoformat(),
|
183
|
+
}
|
184
|
+
return None
|
185
|
+
|
186
|
+
|
187
|
+
# Create the global CRAFTER_HOOKS instance
|
188
|
+
CRAFTER_HOOKS = HookManager()
|
189
|
+
|
190
|
+
# Register all hooks
|
191
|
+
CRAFTER_HOOKS.register(
|
192
|
+
"event_recorded",
|
193
|
+
check_easy_achievements,
|
194
|
+
name="easy_achievements",
|
195
|
+
priority=10,
|
196
|
+
event_types=["environment"],
|
197
|
+
)
|
198
|
+
|
199
|
+
CRAFTER_HOOKS.register(
|
200
|
+
"event_recorded",
|
201
|
+
check_medium_achievements,
|
202
|
+
name="medium_achievements",
|
203
|
+
priority=10,
|
204
|
+
event_types=["environment"],
|
205
|
+
)
|
206
|
+
|
207
|
+
CRAFTER_HOOKS.register(
|
208
|
+
"event_recorded",
|
209
|
+
check_hard_achievements,
|
210
|
+
name="hard_achievements",
|
211
|
+
priority=10,
|
212
|
+
event_types=["environment"],
|
213
|
+
)
|
214
|
+
|
215
|
+
CRAFTER_HOOKS.register(
|
216
|
+
"event_recorded",
|
217
|
+
log_invalid_actions,
|
218
|
+
name="invalid_actions",
|
219
|
+
priority=5,
|
220
|
+
event_types=["runtime"],
|
221
|
+
)
|
222
|
+
|
223
|
+
CRAFTER_HOOKS.register(
|
224
|
+
"event_recorded",
|
225
|
+
track_reward_milestones,
|
226
|
+
name="reward_milestones",
|
227
|
+
priority=5,
|
228
|
+
event_types=["environment"],
|
229
|
+
)
|
@@ -0,0 +1,298 @@
|
|
1
|
+
"""
|
2
|
+
Simplified monkey patch for configurable world generation in crafter.
|
3
|
+
This version modifies generation parameters rather than rewriting functions.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import json
|
7
|
+
import os
|
8
|
+
from typing import Dict, Any, Optional
|
9
|
+
import crafter
|
10
|
+
|
11
|
+
print("[PATCH] Attempting to apply simplified Crafter world configuration patch...")
|
12
|
+
|
13
|
+
# World configuration presets
|
14
|
+
WORLD_CONFIGS = {
|
15
|
+
"easy": {
|
16
|
+
"name": "Easy Mode",
|
17
|
+
"description": "More resources, fewer enemies",
|
18
|
+
# Modify spawn probabilities by multiplying original values
|
19
|
+
"spawn_multipliers": {
|
20
|
+
"tree": 1.5, # 50% more trees
|
21
|
+
"coal": 1.5, # 50% more coal
|
22
|
+
"iron": 1.5, # 50% more iron
|
23
|
+
"diamond": 3.0, # 3x more diamonds
|
24
|
+
"cow": 2.0, # 2x more cows
|
25
|
+
"zombie": 0.3, # 70% fewer zombies
|
26
|
+
"skeleton": 0.2, # 80% fewer skeletons
|
27
|
+
},
|
28
|
+
# Modify spawn distances
|
29
|
+
"spawn_distances": {
|
30
|
+
"zombie": 15, # Farther away (default 10)
|
31
|
+
"skeleton": 10, # Farther away (default varies)
|
32
|
+
"cow": 2, # Closer (default 3)
|
33
|
+
},
|
34
|
+
},
|
35
|
+
"normal": {
|
36
|
+
"name": "Normal Mode",
|
37
|
+
"description": "Standard crafter experience",
|
38
|
+
"spawn_multipliers": {
|
39
|
+
"tree": 1.0,
|
40
|
+
"coal": 1.0,
|
41
|
+
"iron": 1.0,
|
42
|
+
"diamond": 1.0,
|
43
|
+
"cow": 1.0,
|
44
|
+
"zombie": 1.0,
|
45
|
+
"skeleton": 1.0,
|
46
|
+
},
|
47
|
+
"spawn_distances": {"zombie": 10, "skeleton": 7, "cow": 3},
|
48
|
+
},
|
49
|
+
"hard": {
|
50
|
+
"name": "Hard Mode",
|
51
|
+
"description": "Fewer resources, more enemies",
|
52
|
+
"spawn_multipliers": {
|
53
|
+
"tree": 0.5, # 50% fewer trees
|
54
|
+
"coal": 0.5, # 50% less coal
|
55
|
+
"iron": 0.5, # 50% less iron
|
56
|
+
"diamond": 0.3, # 70% fewer diamonds
|
57
|
+
"cow": 0.3, # 70% fewer cows
|
58
|
+
"zombie": 3.0, # 3x more zombies
|
59
|
+
"skeleton": 3.0, # 3x more skeletons
|
60
|
+
},
|
61
|
+
"spawn_distances": {
|
62
|
+
"zombie": 5, # Much closer
|
63
|
+
"skeleton": 4, # Much closer
|
64
|
+
"cow": 8, # Farther away
|
65
|
+
},
|
66
|
+
},
|
67
|
+
"peaceful": {
|
68
|
+
"name": "Peaceful Mode",
|
69
|
+
"description": "No enemies, more resources",
|
70
|
+
"spawn_multipliers": {
|
71
|
+
"tree": 2.0,
|
72
|
+
"coal": 2.0,
|
73
|
+
"iron": 2.0,
|
74
|
+
"diamond": 5.0,
|
75
|
+
"cow": 3.0,
|
76
|
+
"zombie": 0.0, # No zombies
|
77
|
+
"skeleton": 0.0, # No skeletons
|
78
|
+
},
|
79
|
+
"spawn_distances": {"zombie": 100, "skeleton": 100, "cow": 1},
|
80
|
+
},
|
81
|
+
}
|
82
|
+
|
83
|
+
# Store active configuration
|
84
|
+
_active_config = WORLD_CONFIGS["normal"]
|
85
|
+
_original_balance_chunk = None
|
86
|
+
_last_loaded_config = None # Track what was last loaded to avoid duplicate prints
|
87
|
+
|
88
|
+
|
89
|
+
def load_world_config(
|
90
|
+
config_name: str = "normal", config_path: Optional[str] = None
|
91
|
+
) -> Dict[str, Any]:
|
92
|
+
"""Load world configuration."""
|
93
|
+
global _active_config, _last_loaded_config
|
94
|
+
|
95
|
+
# Create a config identifier
|
96
|
+
config_id = config_path if config_path else config_name
|
97
|
+
|
98
|
+
# Only print if configuration actually changed
|
99
|
+
if _last_loaded_config != config_id:
|
100
|
+
if config_path and os.path.exists(config_path):
|
101
|
+
with open(config_path, "r") as f:
|
102
|
+
_active_config = json.load(f)
|
103
|
+
# print(f"[PATCH] Loaded custom world config from {config_path}")
|
104
|
+
elif config_name in WORLD_CONFIGS:
|
105
|
+
_active_config = WORLD_CONFIGS[config_name]
|
106
|
+
# print(f"[PATCH] Loaded '{config_name}' world configuration")
|
107
|
+
else:
|
108
|
+
_active_config = WORLD_CONFIGS["normal"]
|
109
|
+
# print(f"[PATCH] Unknown config '{config_name}', using 'normal'")
|
110
|
+
|
111
|
+
_last_loaded_config = config_id
|
112
|
+
else:
|
113
|
+
# Configuration hasn't changed, just return the active one
|
114
|
+
if config_path and os.path.exists(config_path):
|
115
|
+
with open(config_path, "r") as f:
|
116
|
+
_active_config = json.load(f)
|
117
|
+
elif config_name in WORLD_CONFIGS:
|
118
|
+
_active_config = WORLD_CONFIGS[config_name]
|
119
|
+
else:
|
120
|
+
_active_config = WORLD_CONFIGS["normal"]
|
121
|
+
|
122
|
+
return _active_config
|
123
|
+
|
124
|
+
|
125
|
+
# Patch world generation to use configuration
|
126
|
+
original_generate_world = crafter.worldgen.generate_world
|
127
|
+
|
128
|
+
|
129
|
+
def patched_generate_world(world, player):
|
130
|
+
"""Patched world generation that applies spawn multipliers."""
|
131
|
+
# Apply configuration without modifying numpy RandomState
|
132
|
+
multipliers = _active_config.get("spawn_multipliers", {})
|
133
|
+
distances = _active_config.get("spawn_distances", {})
|
134
|
+
|
135
|
+
# Call original generation first
|
136
|
+
result = original_generate_world(world, player)
|
137
|
+
|
138
|
+
# Post-process to adjust spawns based on multipliers
|
139
|
+
# This is a simpler approach that modifies objects after generation
|
140
|
+
if multipliers:
|
141
|
+
# Remove some objects based on multipliers < 1.0
|
142
|
+
objects_to_remove = []
|
143
|
+
for obj in world._objects:
|
144
|
+
if obj is None or obj is player:
|
145
|
+
continue
|
146
|
+
|
147
|
+
obj_type = type(obj).__name__.lower()
|
148
|
+
|
149
|
+
# Check if we should remove this object based on multiplier
|
150
|
+
multiplier = 1.0
|
151
|
+
if "tree" in obj_type or hasattr(obj, "kind") and getattr(obj, "kind") == "tree":
|
152
|
+
multiplier = multipliers.get("tree", 1.0)
|
153
|
+
elif "cow" in obj_type:
|
154
|
+
multiplier = multipliers.get("cow", 1.0)
|
155
|
+
elif "zombie" in obj_type:
|
156
|
+
multiplier = multipliers.get("zombie", 1.0)
|
157
|
+
elif "skeleton" in obj_type:
|
158
|
+
multiplier = multipliers.get("skeleton", 1.0)
|
159
|
+
|
160
|
+
# Remove objects if multiplier < 1.0
|
161
|
+
if multiplier < 1.0 and world.random.random() > multiplier:
|
162
|
+
objects_to_remove.append(obj)
|
163
|
+
|
164
|
+
# Remove marked objects
|
165
|
+
for obj in objects_to_remove:
|
166
|
+
world.remove(obj)
|
167
|
+
|
168
|
+
# Add extra objects if multiplier > 1.0
|
169
|
+
# (This is more complex and would require spawning new objects)
|
170
|
+
|
171
|
+
# Post-process to adjust spawn distances
|
172
|
+
if distances:
|
173
|
+
# Adjust initial enemy positions based on distance config
|
174
|
+
for obj in list(world._objects):
|
175
|
+
if obj is None:
|
176
|
+
continue
|
177
|
+
|
178
|
+
obj_type = type(obj).__name__.lower()
|
179
|
+
if obj_type in distances:
|
180
|
+
min_dist = distances[obj_type]
|
181
|
+
player_pos = player.pos
|
182
|
+
obj_pos = obj.pos
|
183
|
+
dist = abs(obj_pos[0] - player_pos[0]) + abs(obj_pos[1] - player_pos[1])
|
184
|
+
|
185
|
+
if dist < min_dist:
|
186
|
+
# Remove objects too close to player
|
187
|
+
world.remove(obj)
|
188
|
+
|
189
|
+
return result
|
190
|
+
|
191
|
+
|
192
|
+
# Patch the balance function for dynamic spawning
|
193
|
+
def patched_balance_chunk(self, chunk, objs=None):
|
194
|
+
"""Patched chunk balancing with config support."""
|
195
|
+
global _original_balance_chunk
|
196
|
+
if _original_balance_chunk is None:
|
197
|
+
return
|
198
|
+
|
199
|
+
multipliers = _active_config.get("spawn_multipliers", {})
|
200
|
+
distances = _active_config.get("spawn_distances", {})
|
201
|
+
|
202
|
+
# Call original balance function with objs parameter
|
203
|
+
if objs is not None:
|
204
|
+
_original_balance_chunk(self, chunk, objs)
|
205
|
+
else:
|
206
|
+
_original_balance_chunk(self, chunk)
|
207
|
+
|
208
|
+
# Post-process spawned objects based on multipliers
|
209
|
+
# Check if any new objects were spawned and adjust them
|
210
|
+
if multipliers:
|
211
|
+
# Get the chunk bounds
|
212
|
+
chunk_x = chunk[0] * self._world._chunk_size
|
213
|
+
chunk_y = chunk[1] * self._world._chunk_size
|
214
|
+
chunk_w = self._world._chunk_size
|
215
|
+
chunk_h = self._world._chunk_size
|
216
|
+
|
217
|
+
objects_to_remove = []
|
218
|
+
for obj in self._world._objects:
|
219
|
+
if obj is None:
|
220
|
+
continue
|
221
|
+
|
222
|
+
# Check if object is in this chunk
|
223
|
+
try:
|
224
|
+
pos_x, pos_y = obj.pos[0], obj.pos[1]
|
225
|
+
if chunk_x <= pos_x < chunk_x + chunk_w and chunk_y <= pos_y < chunk_y + chunk_h:
|
226
|
+
obj_type = type(obj).__name__.lower()
|
227
|
+
multiplier = 1.0
|
228
|
+
|
229
|
+
if "zombie" in obj_type:
|
230
|
+
multiplier = multipliers.get("zombie", 1.0)
|
231
|
+
elif "skeleton" in obj_type:
|
232
|
+
multiplier = multipliers.get("skeleton", 1.0)
|
233
|
+
elif "cow" in obj_type:
|
234
|
+
multiplier = multipliers.get("cow", 1.0)
|
235
|
+
|
236
|
+
# Remove objects based on multiplier
|
237
|
+
if multiplier < 1.0 and self._world.random.random() > multiplier:
|
238
|
+
objects_to_remove.append(obj)
|
239
|
+
except (ValueError, AttributeError):
|
240
|
+
# Skip objects with invalid position data
|
241
|
+
continue
|
242
|
+
|
243
|
+
# Remove marked objects
|
244
|
+
for obj in objects_to_remove:
|
245
|
+
self._world.remove(obj)
|
246
|
+
|
247
|
+
|
248
|
+
# Apply patches
|
249
|
+
crafter.worldgen.generate_world = patched_generate_world
|
250
|
+
|
251
|
+
# Store original balance_chunk
|
252
|
+
_original_balance_chunk = crafter.Env._balance_chunk
|
253
|
+
crafter.Env._balance_chunk = patched_balance_chunk
|
254
|
+
|
255
|
+
# Extend Env.__init__ to accept world_config
|
256
|
+
original_env_init = crafter.Env.__init__
|
257
|
+
|
258
|
+
|
259
|
+
def patched_env_init(
|
260
|
+
self,
|
261
|
+
area=(64, 64),
|
262
|
+
view=(9, 9),
|
263
|
+
length=10000,
|
264
|
+
seed=None,
|
265
|
+
world_config="normal",
|
266
|
+
world_config_path=None,
|
267
|
+
):
|
268
|
+
"""Extended Env.__init__ that accepts world configuration."""
|
269
|
+
# Load configuration
|
270
|
+
load_world_config(world_config, world_config_path)
|
271
|
+
|
272
|
+
# Call original init
|
273
|
+
original_env_init(self, area=area, view=view, length=length, seed=seed)
|
274
|
+
|
275
|
+
# Store config name
|
276
|
+
self._world_config_name = world_config if not world_config_path else "custom"
|
277
|
+
|
278
|
+
|
279
|
+
crafter.Env.__init__ = patched_env_init
|
280
|
+
|
281
|
+
print("[PATCH] Simplified Crafter world configuration patch complete.")
|
282
|
+
print("[PATCH] Available configs: easy, normal, hard, peaceful")
|
283
|
+
|
284
|
+
# Example custom config
|
285
|
+
EXAMPLE_CUSTOM_CONFIG = {
|
286
|
+
"name": "Custom Config",
|
287
|
+
"description": "My custom world",
|
288
|
+
"spawn_multipliers": {
|
289
|
+
"tree": 1.2,
|
290
|
+
"coal": 1.3,
|
291
|
+
"iron": 1.4,
|
292
|
+
"diamond": 2.0,
|
293
|
+
"cow": 1.5,
|
294
|
+
"zombie": 0.5,
|
295
|
+
"skeleton": 0.5,
|
296
|
+
},
|
297
|
+
"spawn_distances": {"zombie": 12, "skeleton": 8, "cow": 4},
|
298
|
+
}
|