Compare commits
	
		
			No commits in common. "4a4f7a75c590ef39e05eb12f516c7c1fdd2b60de" and "8c8d8f7925bc7324d3b1697c2d7f736ed29b0134" have entirely different histories.
		
	
	
		
			4a4f7a75c5
			...
			8c8d8f7925
		
	
		
| @ -6,7 +6,7 @@ from config.constants import * | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class InputHandler: | class InputHandler: | ||||||
|     def __init__(self, camera, world, sim_view_rect): |     def __init__(self, camera, world): | ||||||
|         self.camera = camera |         self.camera = camera | ||||||
|         self.world = world |         self.world = world | ||||||
| 
 | 
 | ||||||
| @ -27,13 +27,6 @@ class InputHandler: | |||||||
|         self.default_tps = DEFAULT_TPS |         self.default_tps = DEFAULT_TPS | ||||||
|         self.sprint_mode = False |         self.sprint_mode = False | ||||||
| 
 | 
 | ||||||
|         # sim-view rect for mouse position calculations |  | ||||||
|         self.sim_view_rect = sim_view_rect |  | ||||||
| 
 |  | ||||||
|     def update_sim_view_rect(self, sim_view_rect): |  | ||||||
|         """Update the sim_view rectangle.""" |  | ||||||
|         self.sim_view_rect = sim_view_rect |  | ||||||
| 
 |  | ||||||
|     def handle_events(self, events, ui_manager): |     def handle_events(self, events, ui_manager): | ||||||
|         """Process all pygame events and return game state.""" |         """Process all pygame events and return game state.""" | ||||||
|         running = True |         running = True | ||||||
| @ -122,27 +115,20 @@ class InputHandler: | |||||||
|         """Process object selection logic.""" |         """Process object selection logic.""" | ||||||
|         self.selecting = False |         self.selecting = False | ||||||
| 
 | 
 | ||||||
|         # Map screen to sim_view coordinates |         # Convert screen to world coordinates | ||||||
|         sx1 = self.select_start[0] - self.sim_view_rect.left |         x1, y1 = self.camera.get_real_coordinates(*self.select_start) | ||||||
|         sy1 = self.select_start[1] - self.sim_view_rect.top |         x2, y2 = self.camera.get_real_coordinates(*self.select_end) | ||||||
|         sx2 = self.select_end[0] - self.sim_view_rect.left |  | ||||||
|         sy2 = self.select_end[1] - self.sim_view_rect.top |  | ||||||
| 
 |  | ||||||
|         # Convert sim_view to world coordinates |  | ||||||
|         x1, y1 = self.camera.get_real_coordinates(sx1, sy1) |  | ||||||
|         x2, y2 = self.camera.get_real_coordinates(sx2, sy2) |  | ||||||
| 
 | 
 | ||||||
|         # Check if selection is a click or drag |         # Check if selection is a click or drag | ||||||
|         if (abs(sx1 - sx2) < SELECTION_THRESHOLD and |         if (abs(self.select_start[0] - self.select_end[0]) < SELECTION_THRESHOLD and | ||||||
|                 abs(sy1 - sy2) < SELECTION_THRESHOLD): |                 abs(self.select_start[1] - self.select_end[1]) < SELECTION_THRESHOLD): | ||||||
|             self._handle_click_selection() |             self._handle_click_selection() | ||||||
|         else: |         else: | ||||||
|             self._handle_drag_selection(x1, y1, x2, y2) |             self._handle_drag_selection(x1, y1, x2, y2) | ||||||
| 
 | 
 | ||||||
|     def _handle_click_selection(self): |     def _handle_click_selection(self): | ||||||
|         """Handle single click selection.""" |         """Handle single click selection.""" | ||||||
|         sx, sy = self.select_start[0] - self.sim_view_rect.left, self.select_start[1] - self.sim_view_rect.top |         mouse_world_x, mouse_world_y = self.camera.get_real_coordinates(*self.select_start) | ||||||
|         mouse_world_x, mouse_world_y = self.camera.get_real_coordinates(sx, sy) |  | ||||||
|         obj = self.world.query_closest_object(mouse_world_x, mouse_world_y) |         obj = self.world.query_closest_object(mouse_world_x, mouse_world_y) | ||||||
|         self.selected_objects = [] |         self.selected_objects = [] | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,14 +8,12 @@ from world.base.brain import CellBrain | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Renderer: | class Renderer: | ||||||
|     def __init__(self, render_area): |     def __init__(self, screen): | ||||||
|         self.render_area = render_area |         self.screen = screen | ||||||
|         self.render_height = render_area.get_height() |  | ||||||
|         self.render_width = render_area.get_width() |  | ||||||
| 
 | 
 | ||||||
|     def clear_screen(self): |     def clear_screen(self): | ||||||
|         """Clear the screen with a black background.""" |         """Clear the screen with a black background.""" | ||||||
|         self.render_area.fill(BLACK) |         self.screen.fill(BLACK) | ||||||
| 
 | 
 | ||||||
|     def draw_grid(self, camera, showing_grid=True): |     def draw_grid(self, camera, showing_grid=True): | ||||||
|         """Draw the reference grid.""" |         """Draw the reference grid.""" | ||||||
| @ -30,8 +28,8 @@ class Renderer: | |||||||
|         grid_world_height = GRID_HEIGHT * effective_cell_size |         grid_world_height = GRID_HEIGHT * effective_cell_size | ||||||
| 
 | 
 | ||||||
|         # Calculate grid position relative to camera (with grid centered at 0,0) |         # Calculate grid position relative to camera (with grid centered at 0,0) | ||||||
|         grid_center_x = self.render_width // 2 - camera.x * camera.zoom |         grid_center_x = SCREEN_WIDTH // 2 - camera.x * camera.zoom | ||||||
|         grid_center_y = self.render_height // 2 - camera.y * camera.zoom |         grid_center_y = SCREEN_HEIGHT // 2 - camera.y * camera.zoom | ||||||
| 
 | 
 | ||||||
|         grid_left = grid_center_x - grid_world_width // 2 |         grid_left = grid_center_x - grid_world_width // 2 | ||||||
|         grid_top = grid_center_y - grid_world_height // 2 |         grid_top = grid_center_y - grid_world_height // 2 | ||||||
| @ -39,20 +37,20 @@ class Renderer: | |||||||
|         grid_bottom = grid_top + grid_world_height |         grid_bottom = grid_top + grid_world_height | ||||||
| 
 | 
 | ||||||
|         # Check if grid is visible on screen |         # Check if grid is visible on screen | ||||||
|         if (grid_right < 0 or grid_left > self.render_width or |         if (grid_right < 0 or grid_left > SCREEN_WIDTH or | ||||||
|                 grid_bottom < 0 or grid_top > self.render_height): |                 grid_bottom < 0 or grid_top > SCREEN_HEIGHT): | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         # Fill the grid area with dark gray background |         # Fill the grid area with dark gray background | ||||||
|         grid_rect = pygame.Rect( |         grid_rect = pygame.Rect( | ||||||
|             max(0, grid_left), |             max(0, grid_left), | ||||||
|             max(0, grid_top), |             max(0, grid_top), | ||||||
|             min(self.render_width, grid_right) - max(0, grid_left), |             min(SCREEN_WIDTH, grid_right) - max(0, grid_left), | ||||||
|             min(self.render_height, grid_bottom) - max(0, grid_top), |             min(SCREEN_HEIGHT, grid_bottom) - max(0, grid_top), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         if grid_rect.width > 0 and grid_rect.height > 0: |         if grid_rect.width > 0 and grid_rect.height > 0: | ||||||
|             pygame.draw.rect(self.render_area, DARK_GRAY, grid_rect) |             pygame.draw.rect(self.screen, DARK_GRAY, grid_rect) | ||||||
| 
 | 
 | ||||||
|         # Draw grid lines only if zoom is high enough |         # Draw grid lines only if zoom is high enough | ||||||
|         if effective_cell_size > 4: |         if effective_cell_size > 4: | ||||||
| @ -67,30 +65,30 @@ class Renderer: | |||||||
|             # Vertical lines |             # Vertical lines | ||||||
|             if i <= GRID_WIDTH: |             if i <= GRID_WIDTH: | ||||||
|                 line_x = grid_left + i * effective_cell_size |                 line_x = grid_left + i * effective_cell_size | ||||||
|                 if 0 <= line_x <= self.render_width: |                 if 0 <= line_x <= SCREEN_WIDTH: | ||||||
|                     start_y = max(0, grid_top) |                     start_y = max(0, grid_top) | ||||||
|                     end_y = min(self.render_height, grid_bottom) |                     end_y = min(SCREEN_HEIGHT, grid_bottom) | ||||||
|                     if start_y < end_y: |                     if start_y < end_y: | ||||||
|                         vertical_lines.append(((line_x, start_y), (line_x, end_y))) |                         vertical_lines.append(((line_x, start_y), (line_x, end_y))) | ||||||
| 
 | 
 | ||||||
|             # Horizontal lines |             # Horizontal lines | ||||||
|             if i <= GRID_HEIGHT: |             if i <= GRID_HEIGHT: | ||||||
|                 line_y = grid_top + i * effective_cell_size |                 line_y = grid_top + i * effective_cell_size | ||||||
|                 if 0 <= line_y <= self.render_height: |                 if 0 <= line_y <= SCREEN_HEIGHT: | ||||||
|                     start_x = max(0, grid_left) |                     start_x = max(0, grid_left) | ||||||
|                     end_x = min(self.render_width, grid_right) |                     end_x = min(SCREEN_WIDTH, grid_right) | ||||||
|                     if start_x < end_x: |                     if start_x < end_x: | ||||||
|                         horizontal_lines.append(((start_x, line_y), (end_x, line_y))) |                         horizontal_lines.append(((start_x, line_y), (end_x, line_y))) | ||||||
| 
 | 
 | ||||||
|         # Draw all lines |         # Draw all lines | ||||||
|         for start, end in vertical_lines: |         for start, end in vertical_lines: | ||||||
|             pygame.draw.line(self.render_area, GRAY, start, end) |             pygame.draw.line(self.screen, GRAY, start, end) | ||||||
|         for start, end in horizontal_lines: |         for start, end in horizontal_lines: | ||||||
|             pygame.draw.line(self.render_area, GRAY, start, end) |             pygame.draw.line(self.screen, GRAY, start, end) | ||||||
| 
 | 
 | ||||||
|     def render_world(self, world, camera): |     def render_world(self, world, camera): | ||||||
|         """Render all world objects.""" |         """Render all world objects.""" | ||||||
|         world.render_all(camera, self.render_area) |         world.render_all(camera, self.screen) | ||||||
| 
 | 
 | ||||||
|     def render_interaction_radius(self, world, camera, selected_objects, show_radius=False): |     def render_interaction_radius(self, world, camera, selected_objects, show_radius=False): | ||||||
|         """Render interaction radius and debug vectors for objects.""" |         """Render interaction radius and debug vectors for objects.""" | ||||||
| @ -110,7 +108,7 @@ class Renderer: | |||||||
| 
 | 
 | ||||||
|                 if screen_radius > 0: |                 if screen_radius > 0: | ||||||
|                     # Draw interaction radius circle |                     # Draw interaction radius circle | ||||||
|                     pygame.draw.circle(self.render_area, RED, (screen_x, screen_y), screen_radius, 1) |                     pygame.draw.circle(self.screen, RED, (screen_x, screen_y), screen_radius, 1) | ||||||
| 
 | 
 | ||||||
|                     # Draw direction arrow |                     # Draw direction arrow | ||||||
|                     self._draw_direction_arrow(obj, screen_x, screen_y, camera) |                     self._draw_direction_arrow(obj, screen_x, screen_y, camera) | ||||||
| @ -127,7 +125,7 @@ class Renderer: | |||||||
|         end_y = screen_y + arrow_length * math.sin(math.radians(rotation_angle)) |         end_y = screen_y + arrow_length * math.sin(math.radians(rotation_angle)) | ||||||
| 
 | 
 | ||||||
|         # Draw arrow line |         # Draw arrow line | ||||||
|         pygame.draw.line(self.render_area, WHITE, (screen_x, screen_y), (end_x, end_y), 2) |         pygame.draw.line(self.screen, WHITE, (screen_x, screen_y), (end_x, end_y), 2) | ||||||
| 
 | 
 | ||||||
|         # Draw arrowhead |         # Draw arrowhead | ||||||
|         tip_size = DIRECTION_TIP_SIZE * camera.zoom |         tip_size = DIRECTION_TIP_SIZE * camera.zoom | ||||||
| @ -137,7 +135,7 @@ class Renderer: | |||||||
|         right_tip_y = end_y - tip_size * math.sin(math.radians(rotation_angle - 150 + 180)) |         right_tip_y = end_y - tip_size * math.sin(math.radians(rotation_angle - 150 + 180)) | ||||||
| 
 | 
 | ||||||
|         pygame.draw.polygon( |         pygame.draw.polygon( | ||||||
|             self.render_area, WHITE, |             self.screen, WHITE, | ||||||
|             [(end_x, end_y), (left_tip_x, left_tip_y), (right_tip_x, right_tip_y)] |             [(end_x, end_y), (left_tip_x, left_tip_y), (right_tip_x, right_tip_y)] | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| @ -169,7 +167,7 @@ class Renderer: | |||||||
|         angular_acc_end_x = end_x + angular_accel_magnitude * math.cos(math.radians(angular_direction)) |         angular_acc_end_x = end_x + angular_accel_magnitude * math.cos(math.radians(angular_direction)) | ||||||
|         angular_acc_end_y = end_y + angular_accel_magnitude * math.sin(math.radians(angular_direction)) |         angular_acc_end_y = end_y + angular_accel_magnitude * math.sin(math.radians(angular_direction)) | ||||||
| 
 | 
 | ||||||
|         pygame.draw.line(self.render_area, LIGHT_BLUE, (end_x, end_y), (angular_acc_end_x, angular_acc_end_y), 2) |         pygame.draw.line(self.screen, LIGHT_BLUE, (end_x, end_y), (angular_acc_end_x, angular_acc_end_y), 2) | ||||||
| 
 | 
 | ||||||
|         # Draw arrowhead |         # Draw arrowhead | ||||||
|         self._draw_arrowhead(angular_acc_end_x, angular_acc_end_y, angular_direction, |         self._draw_arrowhead(angular_acc_end_x, angular_acc_end_y, angular_direction, | ||||||
| @ -186,7 +184,7 @@ class Renderer: | |||||||
|             acc_end_x = screen_x + acc_vector_length * math.cos(math.radians(acc_direction)) |             acc_end_x = screen_x + acc_vector_length * math.cos(math.radians(acc_direction)) | ||||||
|             acc_end_y = screen_y + acc_vector_length * math.sin(math.radians(acc_direction)) |             acc_end_y = screen_y + acc_vector_length * math.sin(math.radians(acc_direction)) | ||||||
| 
 | 
 | ||||||
|             pygame.draw.line(self.render_area, RED, (screen_x, screen_y), (acc_end_x, acc_end_y), 2) |             pygame.draw.line(self.screen, RED, (screen_x, screen_y), (acc_end_x, acc_end_y), 2) | ||||||
|             self._draw_arrowhead(acc_end_x, acc_end_y, acc_direction, |             self._draw_arrowhead(acc_end_x, acc_end_y, acc_direction, | ||||||
|                                  ARROW_TIP_SIZE * camera.zoom, RED) |                                  ARROW_TIP_SIZE * camera.zoom, RED) | ||||||
| 
 | 
 | ||||||
| @ -201,7 +199,7 @@ class Renderer: | |||||||
|             vel_end_x = screen_x + vel_vector_length * math.cos(math.radians(vel_direction)) |             vel_end_x = screen_x + vel_vector_length * math.cos(math.radians(vel_direction)) | ||||||
|             vel_end_y = screen_y + vel_vector_length * math.sin(math.radians(vel_direction)) |             vel_end_y = screen_y + vel_vector_length * math.sin(math.radians(vel_direction)) | ||||||
| 
 | 
 | ||||||
|             pygame.draw.line(self.render_area, BLUE, (screen_x, screen_y), (vel_end_x, vel_end_y), 2) |             pygame.draw.line(self.screen, BLUE, (screen_x, screen_y), (vel_end_x, vel_end_y), 2) | ||||||
|             self._draw_arrowhead(vel_end_x, vel_end_y, vel_direction, |             self._draw_arrowhead(vel_end_x, vel_end_y, vel_direction, | ||||||
|                                  ARROW_TIP_SIZE * camera.zoom, BLUE) |                                  ARROW_TIP_SIZE * camera.zoom, BLUE) | ||||||
| 
 | 
 | ||||||
| @ -213,29 +211,24 @@ class Renderer: | |||||||
|         right_tip_y = end_y - tip_size * math.sin(math.radians(direction - 150 + 180)) |         right_tip_y = end_y - tip_size * math.sin(math.radians(direction - 150 + 180)) | ||||||
| 
 | 
 | ||||||
|         pygame.draw.polygon( |         pygame.draw.polygon( | ||||||
|             self.render_area, color, |             self.screen, color, | ||||||
|             [(end_x, end_y), (left_tip_x, left_tip_y), (right_tip_x, right_tip_y)] |             [(end_x, end_y), (left_tip_x, left_tip_y), (right_tip_x, right_tip_y)] | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def render_selection_rectangle(self, selection_rect, sim_view_rect=None): |     def render_selection_rectangle(self, selection_rect): | ||||||
|         """Render the selection rectangle, offset for sim_view if sim_view_rect is provided.""" |         """Render the selection rectangle.""" | ||||||
|         if not selection_rect: |         if not selection_rect: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         left, top, width, height = selection_rect |         left, top, width, height = selection_rect | ||||||
| 
 | 
 | ||||||
|         # Offset for sim_view if sim_view_rect is given |  | ||||||
|         if sim_view_rect is not None: |  | ||||||
|             left -= sim_view_rect.left |  | ||||||
|             top -= sim_view_rect.top |  | ||||||
| 
 |  | ||||||
|         # Draw semi-transparent fill |         # Draw semi-transparent fill | ||||||
|         s = pygame.Surface((width, height), pygame.SRCALPHA) |         s = pygame.Surface((width, height), pygame.SRCALPHA) | ||||||
|         s.fill(SELECTION_GRAY) |         s.fill(SELECTION_GRAY) | ||||||
|         self.render_area.blit(s, (left, top)) |         self.screen.blit(s, (left, top)) | ||||||
| 
 | 
 | ||||||
|         # Draw border |         # Draw border | ||||||
|         pygame.draw.rect(self.render_area, SELECTION_BORDER, |         pygame.draw.rect(self.screen, SELECTION_BORDER, | ||||||
|                          pygame.Rect(left, top, width, height), 1) |                          pygame.Rect(left, top, width, height), 1) | ||||||
| 
 | 
 | ||||||
|     def render_selected_objects_outline(self, selected_objects, camera): |     def render_selected_objects_outline(self, selected_objects, camera): | ||||||
| @ -246,4 +239,4 @@ class Renderer: | |||||||
|             screen_x, screen_y = camera.world_to_screen(obj_x, obj_y) |             screen_x, screen_y = camera.world_to_screen(obj_x, obj_y) | ||||||
|             size = camera.get_relative_size(width) |             size = camera.get_relative_size(width) | ||||||
|             rect = pygame.Rect(screen_x - size // 2, screen_y - size // 2, size, size) |             rect = pygame.Rect(screen_x - size // 2, screen_y - size // 2, size, size) | ||||||
|             pygame.draw.rect(self.render_area, SELECTION_BLUE, rect, 1) |             pygame.draw.rect(self.screen, SELECTION_BLUE, rect, 1) | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ import time | |||||||
| import random | import random | ||||||
| import sys | import sys | ||||||
| 
 | 
 | ||||||
| from pygame_gui import UIManager |  | ||||||
| 
 |  | ||||||
| from world.base.brain import CellBrain, FlexibleNeuralNetwork | from world.base.brain import CellBrain, FlexibleNeuralNetwork | ||||||
| from world.world import World, Position, Rotation | from world.world import World, Position, Rotation | ||||||
| from world.objects import FoodObject, DefaultCell | from world.objects import FoodObject, DefaultCell | ||||||
| @ -18,29 +16,11 @@ from ui.hud import HUD | |||||||
| class SimulationEngine: | class SimulationEngine: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         pygame.init() |         pygame.init() | ||||||
|         self._init_window() |         self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), vsync=1) | ||||||
|         self._init_ui() |  | ||||||
|         self._init_simulation() |  | ||||||
|         self.running = True |  | ||||||
| 
 |  | ||||||
|     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") |         pygame.display.set_caption("Dynamic Abstraction System Testing") | ||||||
|         self.clock = pygame.time.Clock() |         self.clock = pygame.time.Clock() | ||||||
|  |         self.camera = Camera(SCREEN_WIDTH, SCREEN_HEIGHT, RENDER_BUFFER) | ||||||
| 
 | 
 | ||||||
|     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): |  | ||||||
|         self.last_tick_time = time.perf_counter() |         self.last_tick_time = time.perf_counter() | ||||||
|         self.last_tps_time = time.perf_counter() |         self.last_tps_time = time.perf_counter() | ||||||
|         self.tick_counter = 0 |         self.tick_counter = 0 | ||||||
| @ -48,28 +28,11 @@ class SimulationEngine: | |||||||
|         self.total_ticks = 0 |         self.total_ticks = 0 | ||||||
| 
 | 
 | ||||||
|         self.world = self._setup_world() |         self.world = self._setup_world() | ||||||
|         self.input_handler = InputHandler(self.camera, self.world, self.sim_view_rect) |         self.input_handler = InputHandler(self.camera, self.world) | ||||||
|         self.renderer = Renderer(self.sim_view) |         self.renderer = Renderer(self.screen) | ||||||
|  |         self.hud = HUD() | ||||||
| 
 | 
 | ||||||
|     def _update_simulation_view(self): |         self.running = True | ||||||
|         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) |  | ||||||
| 
 |  | ||||||
|         if hasattr(self, 'camera'): |  | ||||||
|             self.camera.screen_width = self.sim_view_width |  | ||||||
|             self.camera.screen_height = self.sim_view_height |  | ||||||
| 
 |  | ||||||
|         if hasattr(self, 'input_handler'): |  | ||||||
|             self.input_handler.update_sim_view_rect(self.sim_view_rect) |  | ||||||
| 
 |  | ||||||
|         if not hasattr(self, 'camera'): |  | ||||||
|             self.camera = Camera(self.sim_view_width, self.sim_view_height, RENDER_BUFFER) |  | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _setup_world(): |     def _setup_world(): | ||||||
| @ -86,11 +49,10 @@ class SimulationEngine: | |||||||
|                 world.add_object(FoodObject(Position(x=x, y=y))) |                 world.add_object(FoodObject(Position(x=x, y=y))) | ||||||
| 
 | 
 | ||||||
|         for _ in range(300): |         for _ in range(300): | ||||||
|             new_cell = DefaultCell( |             new_cell = DefaultCell(Position(x=random.randint(-half_width, half_width), y=random.randint(-half_height, half_height)), Rotation(angle=0)) | ||||||
|                 Position(x=random.randint(-half_width, half_width), y=random.randint(-half_height, half_height)), | 
 | ||||||
|                 Rotation(angle=0) |  | ||||||
|             ) |  | ||||||
|             new_cell.behavioral_model = new_cell.behavioral_model.mutate(3) |             new_cell.behavioral_model = new_cell.behavioral_model.mutate(3) | ||||||
|  | 
 | ||||||
|             world.add_object(new_cell) |             world.add_object(new_cell) | ||||||
| 
 | 
 | ||||||
|         return world |         return world | ||||||
| @ -106,6 +68,7 @@ class SimulationEngine: | |||||||
|     def run(self): |     def run(self): | ||||||
|         while self.running: |         while self.running: | ||||||
|             self._handle_frame() |             self._handle_frame() | ||||||
|  | 
 | ||||||
|         pygame.quit() |         pygame.quit() | ||||||
|         sys.exit() |         sys.exit() | ||||||
| 
 | 
 | ||||||
| @ -113,16 +76,50 @@ class SimulationEngine: | |||||||
|         deltatime = self.clock.get_time() / 1000.0 |         deltatime = self.clock.get_time() / 1000.0 | ||||||
|         tick_interval = 1.0 / self.input_handler.tps |         tick_interval = 1.0 / self.input_handler.tps | ||||||
| 
 | 
 | ||||||
|  |         # Handle events | ||||||
|         events = pygame.event.get() |         events = pygame.event.get() | ||||||
|         self.running = self.input_handler.handle_events(events, self.hud.manager) |         self.running = self.input_handler.handle_events(events, self.hud.manager) | ||||||
|         self._handle_window_events(events) |  | ||||||
| 
 | 
 | ||||||
|         if self.input_handler.sprint_mode: |         if self.input_handler.sprint_mode: | ||||||
|             self._handle_sprint_mode() |             # Sprint mode: run as many ticks as possible, skip rendering | ||||||
|  |             current_time = time.perf_counter() | ||||||
|  |             while True: | ||||||
|  |                 self.input_handler.update_selected_objects() | ||||||
|  |                 self.world.tick_all() | ||||||
|  |                 self.tick_counter += 1 | ||||||
|  |                 self.total_ticks += 1 | ||||||
|  |                 # Optionally break after some time to allow event processing | ||||||
|  |                 if time.perf_counter() - current_time > 0.05:  # ~50ms per batch | ||||||
|  |                     break | ||||||
|  |             # Update TPS every second | ||||||
|  |             if time.perf_counter() - self.last_tps_time >= 1.0: | ||||||
|  |                 self.actual_tps = self.tick_counter | ||||||
|  |                 self.tick_counter = 0 | ||||||
|  |                 self.last_tps_time = time.perf_counter() | ||||||
|  |             # No rendering or camera update | ||||||
|  | 
 | ||||||
|  |             self.renderer.clear_screen() | ||||||
|  |             cell_count = self._count_cells() | ||||||
|  |             self.hud.render_sprint_debug(self.screen, self.actual_tps, self.total_ticks, cell_count) | ||||||
|  |             pygame.display.flip() | ||||||
|  |             self.clock.tick(MAX_FPS) | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         if not self.input_handler.is_paused: |         if not self.input_handler.is_paused: | ||||||
|             self._handle_simulation_ticks(tick_interval, deltatime) |             current_time = time.perf_counter() | ||||||
|  |             while current_time - self.last_tick_time >= tick_interval: | ||||||
|  |                 self.last_tick_time += tick_interval | ||||||
|  |                 self.tick_counter += 1 | ||||||
|  |                 self.total_ticks += 1 | ||||||
|  | 
 | ||||||
|  |                 self.input_handler.update_selected_objects() | ||||||
|  |                 self.world.tick_all() | ||||||
|  |                 self.hud.manager.update(deltatime) | ||||||
|  | 
 | ||||||
|  |             if current_time - self.last_tps_time >= 1.0: | ||||||
|  |                 self.actual_tps = self.tick_counter | ||||||
|  |                 self.tick_counter = 0 | ||||||
|  |                 self.last_tps_time += 1.0 | ||||||
|         else: |         else: | ||||||
|             self.last_tick_time = time.perf_counter() |             self.last_tick_time = time.perf_counter() | ||||||
|             self.last_tps_time = time.perf_counter() |             self.last_tps_time = time.perf_counter() | ||||||
| @ -131,82 +128,26 @@ class SimulationEngine: | |||||||
|         self._update(deltatime) |         self._update(deltatime) | ||||||
|         self._render() |         self._render() | ||||||
| 
 | 
 | ||||||
|     def _handle_window_events(self, events): |  | ||||||
|         for event in events: |  | ||||||
|             self.hud.process_event(event) |  | ||||||
|             if event.type == pygame.VIDEORESIZE: |  | ||||||
|                 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) |  | ||||||
| 
 |  | ||||||
|         self.hud.update_layout(self.window_width, self.window_height) |  | ||||||
|         self._update_simulation_view() |  | ||||||
| 
 |  | ||||||
|     def _handle_sprint_mode(self): |  | ||||||
|         current_time = time.perf_counter() |  | ||||||
|         while True: |  | ||||||
|             self.input_handler.update_selected_objects() |  | ||||||
|             self.world.tick_all() |  | ||||||
|             self.tick_counter += 1 |  | ||||||
|             self.total_ticks += 1 |  | ||||||
|             if time.perf_counter() - current_time > 0.05: |  | ||||||
|                 break |  | ||||||
|         if time.perf_counter() - self.last_tps_time >= 1.0: |  | ||||||
|             self.actual_tps = self.tick_counter |  | ||||||
|             self.tick_counter = 0 |  | ||||||
|             self.last_tps_time = time.perf_counter() |  | ||||||
|         self.screen.fill(BLACK) |  | ||||||
|         self.renderer.clear_screen() |  | ||||||
|         self.hud.render_sprint_debug(self.screen, self.actual_tps, self.total_ticks) |  | ||||||
|         pygame.display.flip() |  | ||||||
|         self.clock.tick(MAX_FPS) |  | ||||||
| 
 |  | ||||||
|     def _handle_simulation_ticks(self, tick_interval, deltatime): |  | ||||||
|         current_time = time.perf_counter() |  | ||||||
|         while current_time - self.last_tick_time >= tick_interval: |  | ||||||
|             self.last_tick_time += tick_interval |  | ||||||
|             self.tick_counter += 1 |  | ||||||
|             self.total_ticks += 1 |  | ||||||
|             self.input_handler.update_selected_objects() |  | ||||||
|             self.world.tick_all() |  | ||||||
|             self.hud.manager.update(deltatime) |  | ||||||
|         if current_time - self.last_tps_time >= 1.0: |  | ||||||
|             self.actual_tps = self.tick_counter |  | ||||||
|             self.tick_counter = 0 |  | ||||||
|             self.last_tps_time += 1.0 |  | ||||||
| 
 |  | ||||||
|     def _update(self, deltatime): |     def _update(self, deltatime): | ||||||
|         keys = pygame.key.get_pressed() |         keys = pygame.key.get_pressed() | ||||||
|         self.input_handler.update_camera(keys, deltatime) |         self.input_handler.update_camera(keys, deltatime) | ||||||
| 
 | 
 | ||||||
|     def _render(self): |     def _render(self): | ||||||
|         self.screen.fill(BLACK) |  | ||||||
|         self.renderer.clear_screen() |         self.renderer.clear_screen() | ||||||
| 
 |  | ||||||
|         if not self.hud.dragging_splitter: |  | ||||||
|         self.renderer.draw_grid(self.camera, self.input_handler.show_grid) |         self.renderer.draw_grid(self.camera, self.input_handler.show_grid) | ||||||
|         self.renderer.render_world(self.world, self.camera) |         self.renderer.render_world(self.world, self.camera) | ||||||
|             self.renderer.render_interaction_radius( |         self.renderer.render_interaction_radius(self.world, self.camera, self.input_handler.selected_objects, self.input_handler.show_interaction_radius) | ||||||
|                 self.world, self.camera, self.input_handler.selected_objects, self.input_handler.show_interaction_radius |         self.renderer.render_selection_rectangle(self.input_handler.get_selection_rect()) | ||||||
|             ) |  | ||||||
|             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.camera) |         self.renderer.render_selected_objects_outline(self.input_handler.selected_objects, self.camera) | ||||||
|             self.screen.blit(self.sim_view, (self.sim_view_rect.left, self.sim_view_rect.top)) |  | ||||||
| 
 | 
 | ||||||
|         self.hud.manager.draw_ui(self.screen) |         self.hud.render_mouse_position(self.screen, self.camera) | ||||||
|         self.hud.draw_splitters(self.screen)  # <-- Add this line |  | ||||||
| 
 |  | ||||||
|         self.hud.render_mouse_position(self.screen, self.camera, self.sim_view_rect) |  | ||||||
|         self.hud.render_fps(self.screen, self.clock) |         self.hud.render_fps(self.screen, self.clock) | ||||||
|         self.hud.render_tps(self.screen, self.actual_tps) |         self.hud.render_tps(self.screen, self.actual_tps) | ||||||
|         self.hud.render_tick_count(self.screen, self.total_ticks) |         self.hud.render_tick_count(self.screen, self.total_ticks) | ||||||
|         self.hud.render_selected_objects_info(self.screen, self.input_handler.selected_objects) |         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_legend(self.screen, self.input_handler.show_legend) | ||||||
|         self.hud.render_pause_indicator(self.screen, self.input_handler.is_paused) |         self.hud.render_pause_indicator(self.screen, self.input_handler.is_paused) | ||||||
|  | 
 | ||||||
|         if self.input_handler.selected_objects: |         if self.input_handler.selected_objects: | ||||||
|             self.hud.render_neural_network_visualization(self.screen, self.input_handler.selected_objects[0]) |             self.hud.render_neural_network_visualization(self.screen, self.input_handler.selected_objects[0]) | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								main.py
									
									
									
									
									
								
							| @ -1,8 +1,5 @@ | |||||||
| from core.simulation_engine import SimulationEngine | from core.simulation_engine import SimulationEngine | ||||||
| 
 | 
 | ||||||
| def main(): | if __name__ == "__main__": | ||||||
|     engine = SimulationEngine() |     engine = SimulationEngine() | ||||||
|     engine.run() |     engine.run() | ||||||
| 
 |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     main() |  | ||||||
							
								
								
									
										253
									
								
								ui/hud.py
									
									
									
									
									
								
							
							
						
						
									
										253
									
								
								ui/hud.py
									
									
									
									
									
								
							| @ -6,196 +6,19 @@ import pygame_gui | |||||||
| from config.constants import * | from config.constants import * | ||||||
| from world.base.brain import CellBrain, FlexibleNeuralNetwork | from world.base.brain import CellBrain, FlexibleNeuralNetwork | ||||||
| from world.objects import DefaultCell | from world.objects import DefaultCell | ||||||
| from pygame_gui.elements import UIPanel |  | ||||||
| import math | import math | ||||||
| 
 | 
 | ||||||
| DARK_GRAY = (40, 40, 40) |  | ||||||
| DARKER_GRAY = (25, 25, 25) |  | ||||||
| 
 | 
 | ||||||
| class HUD: | class HUD: | ||||||
|     def __init__(self, ui_manager, screen_width=SCREEN_WIDTH, screen_height=SCREEN_HEIGHT): |     def __init__(self): | ||||||
|         self.font = pygame.font.Font("freesansbold.ttf", FONT_SIZE) |         self.font = pygame.font.Font("freesansbold.ttf", FONT_SIZE) | ||||||
|         self.legend_font = pygame.font.Font("freesansbold.ttf", LEGEND_FONT_SIZE) |         self.legend_font = pygame.font.Font("freesansbold.ttf", LEGEND_FONT_SIZE) | ||||||
| 
 | 
 | ||||||
|         self.manager = ui_manager |         self.manager = pygame_gui.UIManager((SCREEN_WIDTH, SCREEN_HEIGHT), "ui/theme.json") | ||||||
|         self.screen_width = screen_width |  | ||||||
|         self.screen_height = screen_height |  | ||||||
| 
 | 
 | ||||||
|         # Panel size defaults |     def render_mouse_position(self, screen, camera): | ||||||
|         self.control_bar_height = 48 |  | ||||||
|         self.inspector_width = 260 |  | ||||||
|         self.properties_width = 320 |  | ||||||
|         self.console_height = 120 |  | ||||||
|         self.splitter_thickness = 6 |  | ||||||
| 
 |  | ||||||
|         self.dragging_splitter = None |  | ||||||
|         self._create_panels() |  | ||||||
| 
 |  | ||||||
|     def _create_panels(self): |  | ||||||
|         # Top control bar |  | ||||||
|         self.control_bar = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect(0, 0, self.screen_width, self.control_bar_height), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#control_bar", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Left inspector |  | ||||||
|         self.inspector_panel = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect( |  | ||||||
|                 0, self.control_bar_height, |  | ||||||
|                 self.inspector_width, |  | ||||||
|                 self.screen_height - self.control_bar_height |  | ||||||
|             ), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#inspector_panel", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Right properties |  | ||||||
|         self.properties_panel = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect( |  | ||||||
|                 self.screen_width - self.properties_width, |  | ||||||
|                 self.control_bar_height, |  | ||||||
|                 self.properties_width, |  | ||||||
|                 self.screen_height - self.control_bar_height |  | ||||||
|             ), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#properties_panel", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Bottom console |  | ||||||
|         self.console_panel = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect( |  | ||||||
|                 self.inspector_width, |  | ||||||
|                 self.screen_height - self.console_height, |  | ||||||
|                 self.screen_width - self.inspector_width - self.properties_width, |  | ||||||
|                 self.console_height |  | ||||||
|             ), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#console_panel", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         self.panels = [ |  | ||||||
|             self.control_bar, |  | ||||||
|             self.inspector_panel, |  | ||||||
|             self.properties_panel, |  | ||||||
|             self.console_panel |  | ||||||
|         ] |  | ||||||
|         self.dragging_splitter = None |  | ||||||
| 
 |  | ||||||
|     def get_viewport_rect(self): |  | ||||||
|         # Returns the rect for the simulation viewport |  | ||||||
|         x = self.inspector_width |  | ||||||
|         y = self.control_bar_height |  | ||||||
|         w = self.screen_width - self.inspector_width - self.properties_width |  | ||||||
|         h = self.screen_height - self.control_bar_height - self.console_height |  | ||||||
|         return pygame.Rect(x, y, w, h) |  | ||||||
| 
 |  | ||||||
|     def update_layout(self, window_width, window_height): |  | ||||||
|         self.screen_width = window_width |  | ||||||
|         self.screen_height = window_height |  | ||||||
| 
 |  | ||||||
|         # Control bar (top) |  | ||||||
|         self.control_bar.set_relative_position((0, 0)) |  | ||||||
|         self.control_bar.set_dimensions((self.screen_width, self.control_bar_height)) |  | ||||||
| 
 |  | ||||||
|         # Inspector panel (left) - goes all the way to the bottom |  | ||||||
|         self.inspector_panel.set_relative_position((0, self.control_bar_height)) |  | ||||||
|         self.inspector_panel.set_dimensions((self.inspector_width, self.screen_height - self.control_bar_height)) |  | ||||||
| 
 |  | ||||||
|         # Properties panel (right) - goes all the way to the bottom |  | ||||||
|         self.properties_panel.set_relative_position( |  | ||||||
|             (self.screen_width - self.properties_width, self.control_bar_height)) |  | ||||||
|         self.properties_panel.set_dimensions((self.properties_width, self.screen_height - self.control_bar_height)) |  | ||||||
| 
 |  | ||||||
|         # Console panel (bottom, spans between inspector and properties) |  | ||||||
|         self.console_panel.set_relative_position((self.inspector_width, self.screen_height - self.console_height)) |  | ||||||
|         self.console_panel.set_dimensions( |  | ||||||
|             (self.screen_width - self.inspector_width - self.properties_width, self.console_height)) |  | ||||||
| 
 |  | ||||||
|     def process_event(self, event): |  | ||||||
|         # Handle splitter dragging for resizing panels |  | ||||||
|         if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: |  | ||||||
|             mx, my = event.pos |  | ||||||
|             # Check if mouse is on a splitter (left/right/bottom) |  | ||||||
|             if abs(mx - self.inspector_width) < self.splitter_thickness and self.control_bar_height < my < self.screen_height - self.console_height: |  | ||||||
|                 self.dragging_splitter = "inspector" |  | ||||||
|             elif abs(mx - (self.screen_width - self.properties_width)) < self.splitter_thickness and self.control_bar_height < my < self.screen_height - self.console_height: |  | ||||||
|                 self.dragging_splitter = "properties" |  | ||||||
|             elif abs(my - (self.screen_height - self.console_height)) < self.splitter_thickness and self.inspector_width < mx < self.screen_width - self.properties_width: |  | ||||||
|                 self.dragging_splitter = "console" |  | ||||||
|             self.update_layout(self.screen_width, self.screen_height) |  | ||||||
|         elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: |  | ||||||
|             self.dragging_splitter = None |  | ||||||
|         elif event.type == pygame.MOUSEMOTION and self.dragging_splitter: |  | ||||||
|             mx, my = event.pos |  | ||||||
|             if self.dragging_splitter == "inspector": |  | ||||||
|                 self.inspector_width = max(100, min(mx, self.screen_width - self.properties_width - 100)) |  | ||||||
|             elif self.dragging_splitter == "properties": |  | ||||||
|                 self.properties_width = max(100, min(self.screen_width - mx, self.screen_width - self.inspector_width - 100)) |  | ||||||
|             elif self.dragging_splitter == "console": |  | ||||||
|                 self.console_height = max(60, min(self.screen_height - my, self.screen_height - self.control_bar_height - 60)) |  | ||||||
|             self.update_layout(self.screen_width, self.screen_height) |  | ||||||
| 
 |  | ||||||
|     def draw_splitters(self, screen): |  | ||||||
|         # Draw draggable splitters for visual feedback |  | ||||||
|         indicator_color = (220, 220, 220) |  | ||||||
|         indicator_size = 6  # Length of indicator line |  | ||||||
|         indicator_gap = 4    # Gap between indicator lines |  | ||||||
|         indicator_count = 3  # Number of indicator lines |  | ||||||
| 
 |  | ||||||
|         # Vertical splitter (inspector/properties) |  | ||||||
|         # Inspector/properties only if wide enough |  | ||||||
|         if self.inspector_width > 0: |  | ||||||
|             x = self.inspector_width - 2 |  | ||||||
|             y1 = self.control_bar_height |  | ||||||
|             y2 = self.screen_height - self.console_height |  | ||||||
|             # Draw indicator (horizontal lines) in the middle |  | ||||||
|             mid_y = (y1 + y2) // 2 |  | ||||||
|             for i in range(indicator_count): |  | ||||||
|                 offset = (i - 1) * (indicator_gap + 1) |  | ||||||
|                 pygame.draw.line( |  | ||||||
|                     screen, indicator_color, |  | ||||||
|                     (x - indicator_size // 2, mid_y + offset), |  | ||||||
|                     (x + indicator_size // 2, mid_y + offset), |  | ||||||
|                     2 |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|         if self.properties_width > 0: |  | ||||||
|             x = self.screen_width - self.properties_width + 2 |  | ||||||
|             y1 = self.control_bar_height |  | ||||||
|             y2 = self.screen_height - self.console_height |  | ||||||
|             mid_y = (y1 + y2) // 2 |  | ||||||
|             for i in range(indicator_count): |  | ||||||
|                 offset = (i - 1) * (indicator_gap + 1) |  | ||||||
|                 pygame.draw.line( |  | ||||||
|                     screen, indicator_color, |  | ||||||
|                     (x - indicator_size // 2, mid_y + offset), |  | ||||||
|                     (x + indicator_size // 2, mid_y + offset), |  | ||||||
|                     2 |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|         # Horizontal splitter (console) |  | ||||||
|         if self.console_height > 0: |  | ||||||
|             y = self.screen_height - self.console_height + 2 |  | ||||||
|             x1 = self.inspector_width |  | ||||||
|             x2 = self.screen_width - self.properties_width |  | ||||||
|             mid_x = (x1 + x2) // 2 |  | ||||||
|             for i in range(indicator_count): |  | ||||||
|                 offset = (i - 1) * (indicator_gap + 1) |  | ||||||
|                 pygame.draw.line( |  | ||||||
|                     screen, indicator_color, |  | ||||||
|                     (mid_x + offset, y - indicator_size // 2), |  | ||||||
|                     (mid_x + offset, y + indicator_size // 2), |  | ||||||
|                     2 |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|     def render_mouse_position(self, screen, camera, sim_view_rect): |  | ||||||
|         """Render mouse position in top left.""" |         """Render mouse position in top left.""" | ||||||
|         mouse_x, mouse_y = pygame.mouse.get_pos() |         mouse_x, mouse_y = camera.get_real_coordinates(*pygame.mouse.get_pos()) | ||||||
|         sim_view_x = mouse_x - sim_view_rect.left |  | ||||||
|         sim_view_y = mouse_y - sim_view_rect.top |  | ||||||
|         world_x, world_y = camera.get_real_coordinates(sim_view_x, sim_view_y) |  | ||||||
| 
 |  | ||||||
|         mouse_text = self.font.render(f"Mouse: ({mouse_x:.2f}, {mouse_y:.2f})", True, WHITE) |         mouse_text = self.font.render(f"Mouse: ({mouse_x:.2f}, {mouse_y:.2f})", True, WHITE) | ||||||
|         text_rect = mouse_text.get_rect() |         text_rect = mouse_text.get_rect() | ||||||
|         text_rect.topleft = (HUD_MARGIN, HUD_MARGIN) |         text_rect.topleft = (HUD_MARGIN, HUD_MARGIN) | ||||||
| @ -205,21 +28,21 @@ class HUD: | |||||||
|         """Render FPS in top right.""" |         """Render FPS in top right.""" | ||||||
|         fps_text = self.font.render(f"FPS: {int(clock.get_fps())}", True, WHITE) |         fps_text = self.font.render(f"FPS: {int(clock.get_fps())}", True, WHITE) | ||||||
|         fps_rect = fps_text.get_rect() |         fps_rect = fps_text.get_rect() | ||||||
|         fps_rect.topright = (self.screen_width - HUD_MARGIN, HUD_MARGIN) |         fps_rect.topright = (SCREEN_WIDTH - HUD_MARGIN, HUD_MARGIN) | ||||||
|         screen.blit(fps_text, fps_rect) |         screen.blit(fps_text, fps_rect) | ||||||
| 
 | 
 | ||||||
|     def render_tps(self, screen, actual_tps): |     def render_tps(self, screen, actual_tps): | ||||||
|         """Render TPS in bottom right.""" |         """Render TPS in bottom right.""" | ||||||
|         tps_text = self.font.render(f"TPS: {actual_tps}", True, WHITE) |         tps_text = self.font.render(f"TPS: {actual_tps}", True, WHITE) | ||||||
|         tps_rect = tps_text.get_rect() |         tps_rect = tps_text.get_rect() | ||||||
|         tps_rect.bottomright = (self.screen_width - HUD_MARGIN, self.screen_height - HUD_MARGIN) |         tps_rect.bottomright = (SCREEN_WIDTH - HUD_MARGIN, SCREEN_HEIGHT - HUD_MARGIN) | ||||||
|         screen.blit(tps_text, tps_rect) |         screen.blit(tps_text, tps_rect) | ||||||
| 
 | 
 | ||||||
|     def render_tick_count(self, screen, total_ticks): |     def render_tick_count(self, screen, total_ticks): | ||||||
|         """Render total tick count in bottom left.""" |         """Render total tick count in bottom left.""" | ||||||
|         tick_text = self.font.render(f"Ticks: {total_ticks}", True, WHITE) |         tick_text = self.font.render(f"Ticks: {total_ticks}", True, WHITE) | ||||||
|         tick_rect = tick_text.get_rect() |         tick_rect = tick_text.get_rect() | ||||||
|         tick_rect.bottomleft = (HUD_MARGIN, self.screen_height - HUD_MARGIN) |         tick_rect.bottomleft = (HUD_MARGIN, SCREEN_HEIGHT - HUD_MARGIN) | ||||||
|         screen.blit(tick_text, tick_rect) |         screen.blit(tick_text, tick_rect) | ||||||
| 
 | 
 | ||||||
|     def render_pause_indicator(self, screen, is_paused): |     def render_pause_indicator(self, screen, is_paused): | ||||||
| @ -227,7 +50,7 @@ class HUD: | |||||||
|         if is_paused: |         if is_paused: | ||||||
|             pause_text = self.font.render("Press 'Space' to unpause", True, WHITE) |             pause_text = self.font.render("Press 'Space' to unpause", True, WHITE) | ||||||
|             pause_rect = pause_text.get_rect() |             pause_rect = pause_text.get_rect() | ||||||
|             pause_rect.center = (self.screen_width // 2, 20) |             pause_rect.center = (SCREEN_WIDTH // 2, 20) | ||||||
|             screen.blit(pause_text, pause_rect) |             screen.blit(pause_text, pause_rect) | ||||||
| 
 | 
 | ||||||
|     def render_selected_objects_info(self, screen, selected_objects): |     def render_selected_objects_info(self, screen, selected_objects): | ||||||
| @ -235,7 +58,7 @@ class HUD: | |||||||
|         if len(selected_objects) < 1: |         if len(selected_objects) < 1: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         max_width = self.screen_width - 20 |         max_width = SCREEN_WIDTH - 20 | ||||||
|         i = 0 |         i = 0 | ||||||
| 
 | 
 | ||||||
|         for obj in selected_objects: |         for obj in selected_objects: | ||||||
| @ -271,7 +94,7 @@ class HUD: | |||||||
|         if not showing_legend: |         if not showing_legend: | ||||||
|             legend_text = self.legend_font.render("Press 'L' to show controls", True, WHITE) |             legend_text = self.legend_font.render("Press 'L' to show controls", True, WHITE) | ||||||
|             legend_rect = legend_text.get_rect() |             legend_rect = legend_text.get_rect() | ||||||
|             legend_rect.center = (self.screen_width // 2, self.screen_height - 20) |             legend_rect.center = (SCREEN_WIDTH // 2, SCREEN_HEIGHT - 20) | ||||||
|             screen.blit(legend_text, legend_rect) |             screen.blit(legend_text, legend_rect) | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
| @ -289,8 +112,8 @@ class HUD: | |||||||
|         legend_width = left_width + right_width + column_gap |         legend_width = left_width + right_width + column_gap | ||||||
|         legend_height = max(len(left_col), len(right_col)) * legend_font_height + 10 |         legend_height = max(len(left_col), len(right_col)) * legend_font_height + 10 | ||||||
| 
 | 
 | ||||||
|         legend_x = (self.screen_width - legend_width) // 2 |         legend_x = (SCREEN_WIDTH - legend_width) // 2 | ||||||
|         legend_y = self.screen_height - legend_height - 10 |         legend_y = SCREEN_HEIGHT - legend_height - 10 | ||||||
| 
 | 
 | ||||||
|         # Draw left column |         # Draw left column | ||||||
|         for i, (key, desc) in enumerate(left_col): |         for i, (key, desc) in enumerate(left_col): | ||||||
| @ -315,7 +138,6 @@ class HUD: | |||||||
|         VIZ_WIDTH = 280  # Width of the neural network visualization area |         VIZ_WIDTH = 280  # Width of the neural network visualization area | ||||||
|         VIZ_HEIGHT = 300  # Height of the neural network visualization area |         VIZ_HEIGHT = 300  # Height of the neural network visualization area | ||||||
|         VIZ_RIGHT_MARGIN = VIZ_WIDTH + 50  # Distance from right edge of screen to visualization |         VIZ_RIGHT_MARGIN = VIZ_WIDTH + 50  # Distance from right edge of screen to visualization | ||||||
|         VIZ_BOTTOM_MARGIN = 50  # Distance from the bottom of the screen |  | ||||||
| 
 | 
 | ||||||
|         # Background styling constants |         # Background styling constants | ||||||
|         BACKGROUND_PADDING = 30  # Padding around the visualization background |         BACKGROUND_PADDING = 30  # Padding around the visualization background | ||||||
| @ -367,9 +189,6 @@ class HUD: | |||||||
|         TOOLTIP_MARGIN = 10 |         TOOLTIP_MARGIN = 10 | ||||||
|         TOOLTIP_LINE_SPACING = 0  # No extra spacing between lines |         TOOLTIP_LINE_SPACING = 0  # No extra spacing between lines | ||||||
| 
 | 
 | ||||||
|         if self.properties_width < VIZ_RIGHT_MARGIN + 50: |  | ||||||
|             self.properties_width = VIZ_RIGHT_MARGIN + 50 # Ensure properties panel is wide enough for tooltip |  | ||||||
| 
 |  | ||||||
|         if not hasattr(cell, 'behavioral_model'): |         if not hasattr(cell, 'behavioral_model'): | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
| @ -380,9 +199,9 @@ class HUD: | |||||||
| 
 | 
 | ||||||
|         network: FlexibleNeuralNetwork = cell_brain.neural_network |         network: FlexibleNeuralNetwork = cell_brain.neural_network | ||||||
| 
 | 
 | ||||||
|         # Calculate visualization position (bottom right) |         # Calculate visualization position | ||||||
|         viz_x = self.screen_width - VIZ_RIGHT_MARGIN  # Right side of screen |         viz_x = SCREEN_WIDTH - VIZ_RIGHT_MARGIN  # Right side of screen | ||||||
|         viz_y = self.screen_height - VIZ_HEIGHT - VIZ_BOTTOM_MARGIN  # Above the bottom margin |         viz_y = (SCREEN_HEIGHT // 2) - (VIZ_HEIGHT // 2)  # Centered vertically | ||||||
| 
 | 
 | ||||||
|         layer_spacing = VIZ_WIDTH // max(1, len(network.layers) - 1) if len(network.layers) > 1 else VIZ_WIDTH |         layer_spacing = VIZ_WIDTH // max(1, len(network.layers) - 1) if len(network.layers) > 1 else VIZ_WIDTH | ||||||
| 
 | 
 | ||||||
| @ -392,8 +211,6 @@ class HUD: | |||||||
|         pygame.draw.rect(screen, BACKGROUND_COLOR, background_rect) |         pygame.draw.rect(screen, BACKGROUND_COLOR, background_rect) | ||||||
|         pygame.draw.rect(screen, WHITE, background_rect, BACKGROUND_BORDER_WIDTH) |         pygame.draw.rect(screen, WHITE, background_rect, BACKGROUND_BORDER_WIDTH) | ||||||
| 
 | 
 | ||||||
|         info = network.get_structure_info() |  | ||||||
| 
 |  | ||||||
|         # Title |         # Title | ||||||
|         title_text = self.font.render("Neural Network", True, WHITE) |         title_text = self.font.render("Neural Network", True, WHITE) | ||||||
|         title_rect = title_text.get_rect() |         title_rect = title_text.get_rect() | ||||||
| @ -401,13 +218,6 @@ class HUD: | |||||||
|         title_rect.top = viz_y - TITLE_TOP_MARGIN |         title_rect.top = viz_y - TITLE_TOP_MARGIN | ||||||
|         screen.blit(title_text, title_rect) |         screen.blit(title_text, title_rect) | ||||||
| 
 | 
 | ||||||
|         # Render network cost under the title |  | ||||||
|         cost_text = self.font.render(f"Cost: {info['network_cost']}", True, WHITE) |  | ||||||
|         cost_rect = cost_text.get_rect() |  | ||||||
|         cost_rect.centerx = title_rect.centerx |  | ||||||
|         cost_rect.top = title_rect.bottom + 4  # Small gap below the title |  | ||||||
|         screen.blit(cost_text, cost_rect) |  | ||||||
| 
 |  | ||||||
|         # Get current activations by running a forward pass with current inputs |         # Get current activations by running a forward pass with current inputs | ||||||
|         input_values = [cell_brain.inputs[key] for key in cell_brain.input_keys] |         input_values = [cell_brain.inputs[key] for key in cell_brain.input_keys] | ||||||
| 
 | 
 | ||||||
| @ -566,6 +376,22 @@ class HUD: | |||||||
|                 label_rect.bottom = viz_y + VIZ_HEIGHT + LAYER_LABEL_BOTTOM_MARGIN |                 label_rect.bottom = viz_y + VIZ_HEIGHT + LAYER_LABEL_BOTTOM_MARGIN | ||||||
|                 screen.blit(label_text, label_rect) |                 screen.blit(label_text, label_rect) | ||||||
| 
 | 
 | ||||||
|  |         # Draw network info | ||||||
|  |         info = network.get_structure_info() | ||||||
|  |         info_lines = [ | ||||||
|  |             f"Layers: {info['total_layers']}", | ||||||
|  |             f"Neurons: {info['total_neurons']}", | ||||||
|  |             f"Connections: {info['total_connections']}", | ||||||
|  |             f"Network Cost: {info['network_cost']}", | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         for i, line in enumerate(info_lines): | ||||||
|  |             info_text = self.legend_font.render(line, True, WHITE) | ||||||
|  |             info_rect = info_text.get_rect() | ||||||
|  |             info_rect.left = viz_x | ||||||
|  |             info_rect.top = viz_y + VIZ_HEIGHT + INFO_TEXT_TOP_MARGIN + i * INFO_TEXT_LINE_SPACING | ||||||
|  |             screen.blit(info_text, info_rect) | ||||||
|  | 
 | ||||||
|         # --- Tooltip logic for neuron hover --- |         # --- Tooltip logic for neuron hover --- | ||||||
|         mouse_x, mouse_y = pygame.mouse.get_pos() |         mouse_x, mouse_y = pygame.mouse.get_pos() | ||||||
|         tooltip_text = None |         tooltip_text = None | ||||||
| @ -627,10 +453,10 @@ class HUD: | |||||||
|             tooltip_y = mouse_y + TOOLTIP_Y_OFFSET |             tooltip_y = mouse_y + TOOLTIP_Y_OFFSET | ||||||
| 
 | 
 | ||||||
|             # Adjust if off right edge |             # Adjust if off right edge | ||||||
|             if tooltip_x + width > self.screen_width: |             if tooltip_x + width > SCREEN_WIDTH: | ||||||
|                 tooltip_x = mouse_x - width - TOOLTIP_X_OFFSET |                 tooltip_x = mouse_x - width - TOOLTIP_X_OFFSET | ||||||
|             # Adjust if off bottom edge |             # Adjust if off bottom edge | ||||||
|             if tooltip_y + height > self.screen_height: |             if tooltip_y + height > SCREEN_HEIGHT: | ||||||
|                 tooltip_y = mouse_y - height - TOOLTIP_Y_OFFSET |                 tooltip_y = mouse_y - height - TOOLTIP_Y_OFFSET | ||||||
| 
 | 
 | ||||||
|             tooltip_rect = pygame.Rect(tooltip_x, tooltip_y, width, height) |             tooltip_rect = pygame.Rect(tooltip_x, tooltip_y, width, height) | ||||||
| @ -641,17 +467,20 @@ class HUD: | |||||||
|                 screen.blit(surf, (tooltip_rect.left + TOOLTIP_PADDING_X, y)) |                 screen.blit(surf, (tooltip_rect.left + TOOLTIP_PADDING_X, y)) | ||||||
|                 y += surf.get_height() + TOOLTIP_LINE_SPACING |                 y += surf.get_height() + TOOLTIP_LINE_SPACING | ||||||
| 
 | 
 | ||||||
|     def render_sprint_debug(self, screen, actual_tps, total_ticks): |     def render_sprint_debug(self, screen, actual_tps, total_ticks, cell_count): | ||||||
|         """Render sprint debug info: header, TPS, and tick count.""" |         """Render sprint debug info: header, TPS, and tick count.""" | ||||||
|         header = self.font.render("Sprinting...", True, (255, 200, 0)) |         header = self.font.render("Sprinting...", True, (255, 200, 0)) | ||||||
|         tps_text = self.font.render(f"TPS: {actual_tps}", True, (255, 255, 255)) |         tps_text = self.font.render(f"TPS: {actual_tps}", True, (255, 255, 255)) | ||||||
|         ticks_text = self.font.render(f"Ticks: {total_ticks}", True, (255, 255, 255)) |         ticks_text = self.font.render(f"Ticks: {total_ticks}", True, (255, 255, 255)) | ||||||
|  |         cell_count = self.font.render(f"Cells: {cell_count}", True, (255, 255, 255)) | ||||||
| 
 | 
 | ||||||
|         y = self.screen_height // 2 - 40 |         y = SCREEN_HEIGHT // 2 - 40 | ||||||
|         header_rect = header.get_rect(center=(self.screen_width // 2, y)) |         header_rect = header.get_rect(center=(SCREEN_WIDTH // 2, y)) | ||||||
|         tps_rect = tps_text.get_rect(center=(self.screen_width // 2, y + 40)) |         tps_rect = tps_text.get_rect(center=(SCREEN_WIDTH // 2, y + 40)) | ||||||
|         ticks_rect = ticks_text.get_rect(center=(self.screen_width // 2, y + 80)) |         ticks_rect = ticks_text.get_rect(center=(SCREEN_WIDTH // 2, y + 80)) | ||||||
|  |         cell_rect = ticks_text.get_rect(center=(SCREEN_WIDTH // 2, y + 120)) | ||||||
| 
 | 
 | ||||||
|         screen.blit(header, header_rect) |         screen.blit(header, header_rect) | ||||||
|         screen.blit(tps_text, tps_rect) |         screen.blit(tps_text, tps_rect) | ||||||
|         screen.blit(ticks_text, ticks_rect) |         screen.blit(ticks_text, ticks_rect) | ||||||
|  |         screen.blit(cell_count, cell_rect) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user