DynamicAbstractionSystem/core/simulation_engine.py
Sam 3a34759094
Some checks failed
Build Simulation and Test / Run All Tests (push) Failing after 8m17s
Add core simulation components and configuration classes
Major rewrite.
2025-11-08 19:17:40 -06:00

293 lines
11 KiB
Python

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
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)
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.input_handler.set_action_callback('toggle_pause', self.simulation_core.toggle_pause)
self.input_handler.set_action_callback('step_forward', self.simulation_core.step)
self.input_handler.set_action_callback('set_speed', self.simulation_core.set_speed_multiplier)
self.input_handler.set_action_callback('set_custom_tps', self.simulation_core.set_tps)
self.input_handler.set_action_callback('toggle_sprint', self.simulation_core.toggle_sprint_mode)
# 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 _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._handle_hud_actions(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()
# Render frame
self._update(deltatime)
self._render()
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 _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 _handle_hud_actions(self, action):
"""Handle actions from HUD simulation controls by forwarding to simulation core."""
if action == 'toggle_pause':
self.simulation_core.toggle_pause()
elif action == 'step_forward':
self.simulation_core.step()
elif action == 'toggle_sprint':
self.simulation_core.toggle_sprint_mode()
elif isinstance(action, tuple) and action[0] == 'set_speed':
self.simulation_core.set_speed_multiplier(action[1])
elif isinstance(action, tuple) and action[0] == 'set_custom_tps':
self.simulation_core.set_tps(action[1])
elif isinstance(action, tuple) and action[0] == 'reset_tps_display':
# Reset TPS display to current value
if self.hud.custom_tps_entry:
self.hud.custom_tps_entry.set_text(str(int(self.simulation_core.timing.state.tps)))
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)
def _update(self, deltatime):
"""Update camera based on input."""
keys = pygame.key.get_pressed()
self.input_handler.update_camera(keys, deltatime)
def _render(self):
"""Render the simulation frame."""
self.screen.fill(BLACK)
self.renderer.clear_screen()
if not self.hud.dragging_splitter:
# Render world
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))
# Update HUD displays with simulation core state
self.hud.update_simulation_controls(self.simulation_core)
# Draw UI elements
self.hud.manager.draw_ui(self.screen)
self.hud.draw_splitters(self.screen)
# Render HUD overlays
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]
)
pygame.display.flip()
self.clock.tick(MAX_FPS)