Files
asteroids/logger.py
T

152 lines
4.0 KiB
Python

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