from __future__ import annotations
import json
import os
from dataclasses import dataclass, field
from typing import Any, Dict, Optional
[docs]
def get_default_database_path() -> str:
"""Get default database path with automatic cleanup"""
try:
from ..utils.database_manager import get_database_path
return get_database_path(auto_cleanup=True)
except ImportError:
# Fallback if database_manager is not available
return "simulation_data.db"
[docs]
@dataclass
class SimulationConfig:
"""Configuration for simulation parameters"""
# World generation
world_width: int = 50
world_height: int = 50
world_seed: int = 42
# Simulation limits
max_ticks: int = 10000
tick_rate: float = 0.0 # 0 = unlimited speed, >0 = ticks per second
# Database and persistence
database_path: str = field(default_factory=get_default_database_path)
save_interval: int = 100 # Save snapshots every N ticks
analytics_interval: int = 50 # Calculate analytics every N ticks
# Agent parameters
max_agents: int = 100
default_agent_vision_range: int = 5
default_agent_stamina: int = 100
default_agent_health: int = 100
# NPC parameters
max_npcs: int = 100
npc_respawn_enabled: bool = True
# Economic parameters
starting_gold: int = 100
trade_cooldown: int = 10 # Ticks between trades
# Performance settings
enable_pathfinding_cache: bool = True
max_pathfinding_distance: int = 50
fog_of_war_enabled: bool = True
# Visualization settings
enable_visualizer: bool = False
visualizer_width: int = 1024
visualizer_height: int = 768
visualizer_tile_size: int = 20
# Logging and debugging
log_level: str = "INFO"
debug_mode: bool = False
log_actions: bool = True
log_file: Optional[str] = None
# Custom parameters
custom_params: Dict[str, Any] = field(default_factory=dict)
[docs]
@classmethod
def from_file(cls, config_path: str) -> "SimulationConfig":
"""Load configuration from JSON file"""
if not os.path.exists(config_path):
raise FileNotFoundError(f"Configuration file not found: {config_path}")
with open(config_path, "r") as f:
data = json.load(f)
return cls.from_dict(data)
[docs]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "SimulationConfig":
"""Create configuration from dictionary"""
# Extract known fields
known_fields = {field.name for field in cls.__dataclass_fields__.values()}
config_data = {}
custom_params = {}
for key, value in data.items():
if key in known_fields:
config_data[key] = value
else:
custom_params[key] = value
config_data["custom_params"] = custom_params
return cls(**config_data)
[docs]
def to_dict(self) -> Dict[str, Any]:
"""Convert configuration to dictionary"""
result = {}
# Add all known fields except custom_params
for field_name, field_def in self.__dataclass_fields__.items():
if field_name != "custom_params":
result[field_name] = getattr(self, field_name)
# Add custom parameters
result.update(self.custom_params)
return result
[docs]
def to_file(self, config_path: str) -> None:
"""Save configuration to JSON file"""
with open(config_path, "w") as f:
json.dump(self.to_dict(), f, indent=2)
[docs]
def get(self, key: str, default: Any = None) -> Any:
"""Get configuration value by key (supports custom params)"""
if hasattr(self, key):
return getattr(self, key)
return self.custom_params.get(key, default)
[docs]
def set(self, key: str, value: Any) -> None:
"""Set configuration value by key"""
if hasattr(self, key):
setattr(self, key, value)
else:
self.custom_params[key] = value
[docs]
def validate(self) -> None:
"""Validate configuration parameters"""
errors = []
# Validate world parameters
if self.world_width <= 0:
errors.append("world_width must be positive")
if self.world_height <= 0:
errors.append("world_height must be positive")
# Validate simulation parameters
if self.max_ticks <= 0:
errors.append("max_ticks must be positive")
if self.tick_rate < 0:
errors.append("tick_rate must be non-negative")
# Validate intervals
if self.save_interval <= 0:
errors.append("save_interval must be positive")
if self.analytics_interval <= 0:
errors.append("analytics_interval must be positive")
# Validate entity limits
if self.max_agents <= 0:
errors.append("max_agents must be positive")
if self.max_npcs < 0:
errors.append("max_npcs must be non-negative")
# Validate agent parameters
if self.default_agent_vision_range <= 0:
errors.append("default_agent_vision_range must be positive")
if self.default_agent_stamina <= 0:
errors.append("default_agent_stamina must be positive")
if self.default_agent_health <= 0:
errors.append("default_agent_health must be positive")
if errors:
raise ValueError(f"Configuration validation failed: {'; '.join(errors)}")
[docs]
def copy(self) -> "SimulationConfig":
"""Create a copy of this configuration"""
return SimulationConfig.from_dict(self.to_dict())
def __str__(self) -> str:
return f"SimulationConfig(world={self.world_width}x{self.world_height}, max_ticks={self.max_ticks})"
def __repr__(self) -> str:
return (
f"SimulationConfig(world_size=({self.world_width}, {self.world_height}), "
f"seed={self.world_seed}, max_ticks={self.max_ticks})"
)