152 lines
4.0 KiB
Python
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
|