feat: add game loop with logging
This commit is contained in:
@@ -8,3 +8,5 @@ wheels/
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
|
||||
game_state.jsonl
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user