diff --git a/.gitignore b/.gitignore index 505a3b1..96fcdae 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ wheels/ # Virtual environments .venv + +game_state.jsonl diff --git a/logger.py b/logger.py new file mode 100644 index 0000000..7b24d65 --- /dev/null +++ b/logger.py @@ -0,0 +1,151 @@ +import inspect +import json +import math +from datetime import datetime +from typing import NotRequired, TypedDict + + +class SpriteInfo(TypedDict): + type: str + pos: NotRequired[list[float]] + vel: NotRequired[list[float]] + rad: NotRequired[float] + rot: NotRequired[float] + + +class GroupInfo(TypedDict): + count: int + sprites: list[SpriteInfo] + + +__all__ = ["log_state", "log_event"] + +_FPS = 60 +_MAX_SECONDS = 16 +_SPRITE_SAMPLE_LIMIT = 10 + +_frame_count = 0 +_state_log_initialized = False +_event_log_initialized = False +_start_time = datetime.now() + + +def log_state() -> None: + global _frame_count, _state_log_initialized + + if _frame_count > _FPS * _MAX_SECONDS: + return + + _frame_count += 1 + if _frame_count % _FPS != 0: + return + + now = datetime.now() + + frame = inspect.currentframe() + if frame is None: + return + + frame_back = frame.f_back + if frame_back is None: + return + + local_vars = frame_back.f_locals.copy() + + screen_size: list[int] = [] + game_state: dict[str, object] = {} + sprite_info: SpriteInfo + + for key, value in local_vars.items(): + if "pygame" in str(type(value)) and hasattr(value, "get_size"): + screen_size = list(value.get_size()) + + if hasattr(value, "__class__") and "Group" in value.__class__.__name__: + sprites_data: list[SpriteInfo] = [] + + for i, sprite in enumerate(value): + if i >= _SPRITE_SAMPLE_LIMIT: + break + + sprite_info = {"type": sprite.__class__.__name__} + + if hasattr(sprite, "position"): + sprite_info["pos"] = [ + round(sprite.position.x, 2), + round(sprite.position.y, 2), + ] + + if hasattr(sprite, "velocity"): + sprite_info["vel"] = [ + round(sprite.velocity.x, 2), + round(sprite.velocity.y, 2), + ] + + if hasattr(sprite, "radius"): + sprite_info["rad"] = sprite.radius + + if hasattr(sprite, "rotation"): + sprite_info["rot"] = round(sprite.rotation, 2) + + sprites_data.append(sprite_info) + + group_info: GroupInfo = {"count": len(value), "sprites": sprites_data} + + game_state[key] = group_info + + if len(game_state) == 0 and hasattr(value, "position"): + sprite_info = {"type": value.__class__.__name__} + + sprite_info["pos"] = [ + round(value.position.x, 2), + round(value.position.y, 2), + ] + + if hasattr(value, "velocity"): + sprite_info["vel"] = [ + round(value.velocity.x, 2), + round(value.velocity.y, 2), + ] + + if hasattr(value, "radius"): + sprite_info["rad"] = value.radius + + if hasattr(value, "rotation"): + sprite_info["rot"] = round(value.rotation, 2) + + game_state[key] = sprite_info + + entry: dict[str, object] = { + "timestamp": now.strftime("%H:%M:%S.%f")[:-3], + "elapsed_s": math.floor((now - _start_time).total_seconds()), + "frame": _frame_count, + "screen_size": screen_size, + **game_state, + } + + mode = "w" if not _state_log_initialized else "a" + + with open("game_state.jsonl", mode) as f: + f.write(json.dumps(entry) + "\n") + + _state_log_initialized = True + + +def log_event(event_type: str, **details: object) -> None: + global _event_log_initialized + + now = datetime.now() + + event: dict[str, object] = { + "timestamp": now.strftime("%H:%M:%S.%f")[:-3], + "elapsed_s": math.floor((now - _start_time).total_seconds()), + "frame": _frame_count, + "type": event_type, + **details, + } + + mode = "w" if not _event_log_initialized else "a" + with open("game_events.jsonl", mode) as f: + f.write(json.dumps(event) + "\n") + + _event_log_initialized = True diff --git a/main.py b/main.py index af583b6..808eb72 100644 --- a/main.py +++ b/main.py @@ -3,12 +3,25 @@ import pygame from constants import SCREEN_HEIGHT, SCREEN_WIDTH +from logger import log_state def main(): """Main method for asteroids game""" print(f"Starting Asteroids with pygame version: {pygame.version.ver}") print(f"Screen width: {SCREEN_WIDTH}") print(f"Screen height: {SCREEN_HEIGHT}") + pygame.init() + screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) + + while True: + log_state() + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return + + screen.fill("black") + pygame.display.flip() if __name__ == "__main__": main()