import pygame import time import sys from pygame_gui import UIManager from world.simulation_interface import Camera from config.constants import * from core.input_handler import InputHandler from core.renderer import Renderer from core.simulation_core import SimulationCore, SimulationConfig from core.event_bus import EventBus from ui.hud import HUD import cProfile import pstats class SimulationEngine: """Interactive simulation engine with UI (wrapper around SimulationCore).""" def __init__(self): pygame.init() self.event_bus = EventBus() self._init_window() self._init_simulation() self._init_ui() self.running = True # HUD action handlers registry for extensibility self._hud_action_handlers = { 'toggle_pause': self.simulation_core.toggle_pause, 'step_forward': self.simulation_core.step, 'toggle_sprint': self.simulation_core.toggle_sprint_mode, 'viewport_resized': self._update_simulation_view, 'set_speed': self.simulation_core.set_speed_multiplier, 'set_custom_tps': self.simulation_core.set_tps, } def _profile_single_tick(self): """Profile a single tick for performance analysis.""" profiler = cProfile.Profile() profiler.enable() self.simulation_core.world.tick_all() profiler.disable() profiler.dump_stats('profile_tick.prof') # Save to file def _init_window(self): info = pygame.display.Info() self.window_width = int(info.current_w // 1.5) self.window_height = int(info.current_h // 1.5) self.screen = pygame.display.set_mode( (self.window_width, self.window_height), pygame.RESIZABLE, vsync=1 ) pygame.display.set_caption("Dynamic Abstraction System Testing") self.clock = pygame.time.Clock() def _init_ui(self): self.ui_manager = UIManager((self.window_width, self.window_height)) self.hud = HUD(self.ui_manager, self.window_width, self.window_height) self.hud.update_layout(self.window_width, self.window_height) # Initialize tree widget with the world self.hud.initialize_tree_widget(self.simulation_core.world) self._update_simulation_view() def _init_simulation(self): # Initialize default sim view rect (will be updated by _init_ui) self.sim_view_width = self.window_width - 400 # Rough estimate for inspector width self.sim_view_height = self.window_height - 200 # Rough estimate for control bar height self.sim_view = pygame.Surface((self.sim_view_width, self.sim_view_height)) self.sim_view_rect = self.sim_view.get_rect(topleft=(200, 48)) # Rough estimate # Create simulation core sim_config = SimulationConfig( grid_width=GRID_WIDTH, grid_height=GRID_HEIGHT, cell_size=CELL_SIZE, initial_cells=50, initial_food=FOOD_OBJECTS_COUNT, food_spawning=FOOD_SPAWNING, random_seed=RANDOM_SEED, default_tps=DEFAULT_TPS ) self.simulation_core = SimulationCore(sim_config, self.event_bus) # Setup input handler with simulation core world self.input_handler = InputHandler( self.simulation_core.camera, self.simulation_core.world, self.sim_view_rect ) self.input_handler.tps = self.simulation_core.timing.state.tps self.input_handler.default_tps = DEFAULT_TPS # Set up action callbacks for input handler self._setup_input_handler_callbacks() # Setup renderer self.renderer = Renderer(self.sim_view) # Profile a single tick for performance analysis self._profile_single_tick() def _update_simulation_view(self): viewport_rect = self.hud.get_viewport_rect() self.sim_view_width = viewport_rect.width self.sim_view_height = viewport_rect.height self.sim_view = pygame.Surface((self.sim_view_width, self.sim_view_height)) self.sim_view_rect = self.sim_view.get_rect(topleft=(viewport_rect.left, viewport_rect.top)) self.ui_manager.set_window_resolution((self.window_width, self.window_height)) self.renderer = Renderer(self.sim_view) # Update simulation core camera dimensions self.simulation_core.camera.screen_width = self.sim_view_width self.simulation_core.camera.screen_height = self.sim_view_height # Update input handler simulation view rect self.input_handler.update_sim_view_rect(self.sim_view_rect) def _setup_input_handler_callbacks(self): """Set up action callbacks for input handler.""" callbacks = { 'toggle_pause': self.simulation_core.toggle_pause, 'step_forward': self.simulation_core.step, 'set_speed': self.simulation_core.set_speed_multiplier, 'set_custom_tps': self.simulation_core.set_tps, 'toggle_sprint': self.simulation_core.toggle_sprint_mode, } for action, callback in callbacks.items(): self.input_handler.set_action_callback(action, callback) def _count_cells(self): """Count cells in the simulation.""" # Import locally to avoid circular import from world.objects import DefaultCell return self.simulation_core.count_entities_by_type(DefaultCell) def run(self): """Run the interactive simulation engine.""" print(f"World buffer: {self.simulation_core.world.current_buffer}") self.simulation_core.start() while self.running: self._handle_frame() self.simulation_core.stop() pygame.quit() sys.exit() def _handle_frame(self): """Handle a single frame in the interactive simulation.""" deltatime = self.clock.get_time() / 1000.0 # Handle events events = pygame.event.get() self.running = self.input_handler.handle_events(events, self.hud.manager) # Process HUD events and window events for event in events: hud_action = self.hud.process_event(event) self._process_hud_action(hud_action) if event.type == pygame.VIDEORESIZE: self._handle_window_resize(event) # Sync input handler state with simulation core timing self._sync_input_and_timing() # Handle sprint mode if self.input_handler.sprint_mode: self._handle_sprint_mode() return # Update UI manager every frame self.hud.manager.update(deltatime) # Handle step-forward mode if self.input_handler.is_stepping: self.simulation_core.step() self.input_handler.is_stepping = False else: # Update simulation using core self.simulation_core.update(deltatime) # Update selected objects in input handler self.input_handler.update_selected_objects() # Sync tree selection with world selection self.hud.update_tree_selection(self.input_handler.selected_objects) # Render frame self._update_frame(deltatime) self._render_frame(deltatime) def _sync_input_and_timing(self): """Synchronize input handler state with simulation core timing.""" timing_state = self.simulation_core.timing.state # Sync TPS self.input_handler.tps = timing_state.tps # Sync pause state self.input_handler.is_paused = timing_state.is_paused # Sync sprint mode self.input_handler.sprint_mode = timing_state.sprint_mode # Sync speed multiplier self.input_handler.speed_multiplier = timing_state.speed_multiplier def _update_frame(self, deltatime): """Update camera and input state.""" keys = pygame.key.get_pressed() self.input_handler.update_camera(keys, deltatime) def _render_frame(self, deltatime): """Render the complete frame.""" self.screen.fill(BLACK) self.renderer.clear_screen() # Render simulation world self._render_simulation_world() # Update and render UI self._update_and_render_ui(deltatime) # Render HUD overlays self._render_hud_overlays() pygame.display.flip() self.clock.tick(MAX_FPS) def _render_simulation_world(self): """Render the simulation world if not dragging splitter.""" if not self.hud.dragging_splitter: self.renderer.draw_grid(self.simulation_core.camera, self.input_handler.show_grid) self.renderer.render_world(self.simulation_core.world, self.simulation_core.camera) self.renderer.render_interaction_radius( self.simulation_core.world, self.simulation_core.camera, self.input_handler.selected_objects, self.input_handler.show_interaction_radius ) self.renderer.render_selection_rectangle( self.input_handler.get_selection_rect(), self.sim_view_rect ) self.renderer.render_selected_objects_outline( self.input_handler.selected_objects, self.simulation_core.camera ) self.screen.blit(self.sim_view, (self.sim_view_rect.left, self.sim_view_rect.top)) def _update_and_render_ui(self, deltatime): """Update UI elements and render them.""" # Update HUD displays with simulation core state self.hud.update_simulation_controls(self.simulation_core) # Update tree widget self.hud.update_tree_widget(deltatime) # Draw UI elements self.hud.manager.draw_ui(self.screen) self.hud.draw_splitters(self.screen) # Render tree widget self.hud.render_tree_widget(self.screen) def _render_hud_overlays(self): """Render HUD overlay elements.""" self.hud.render_fps(self.screen, self.clock) self.hud.render_tps(self.screen, self.simulation_core.state.actual_tps) self.hud.render_selected_objects_info(self.screen, self.input_handler.selected_objects) self.hud.render_legend(self.screen, self.input_handler.show_legend) self.hud.render_pause_indicator(self.screen, self.simulation_core.timing.state.is_paused) # Render neural network visualization for selected object if self.input_handler.selected_objects: self.hud.render_neural_network_visualization( self.screen, self.input_handler.selected_objects[0] ) def _handle_window_resize(self, event): """Handle window resize event.""" self.window_width, self.window_height = event.w, event.h self.screen = pygame.display.set_mode( (self.window_width, self.window_height), pygame.RESIZABLE ) self._update_simulation_view() self.hud.update_layout(self.window_width, self.window_height) def _process_hud_action(self, action): """Process a single HUD action using the handler registry.""" if not action: return # Handle tree selection changes if isinstance(action, tuple) and action[0] == 'tree_selection_changed': selected_entities = action[1] # Sync world selection with tree selection self.input_handler.selected_objects = selected_entities return # Handle simple actions directly if action in self._hud_action_handlers: self._hud_action_handlers[action]() return # Handle parameterized actions if isinstance(action, tuple) and len(action) >= 2: action_type, param = action[0], action[1] if action_type == 'set_speed': self.simulation_core.set_speed_multiplier(param) elif action_type == 'set_custom_tps': self.simulation_core.set_tps(param) elif action_type == 'reset_tps_display': self._reset_tps_display() def _reset_tps_display(self): """Reset TPS display to current simulation value.""" if self.hud.custom_tps_entry: current_tps = int(self.simulation_core.timing.state.tps) self.hud.custom_tps_entry.set_text(str(current_tps)) def register_hud_action(self, action_name: str, handler): """Register a new HUD action handler for extensibility. Args: action_name: Name of the HUD action handler: Callable that handles the action """ self._hud_action_handlers[action_name] = handler def _handle_sprint_mode(self): """Handle sprint mode by running multiple simulation ticks quickly.""" current_time = time.perf_counter() while time.perf_counter() - current_time < 0.05: # 50ms of sprint self.simulation_core.update(0.016) # Update simulation self.input_handler.update_selected_objects() pygame.event.pump() # Prevent event queue overflow # Render sprint debug info self.screen.fill(BLACK) self.renderer.clear_screen() cell_count = self._count_cells() self.hud.render_sprint_debug( self.screen, self.simulation_core.state.actual_tps, self.simulation_core.state.total_ticks, cell_count ) pygame.display.flip() self.clock.tick(MAX_FPS)