| from __future__ import annotations |
|
|
| from enum import Enum |
| from typing import Annotated |
|
|
| from pydantic import BaseModel, Field |
|
|
|
|
| class Shape(str, Enum): |
| CIRCLE = "circle" |
| RECTANGLE = "rectangle" |
| TRIANGLE = "triangle" |
| STAR = "star" |
|
|
|
|
| class MovementPattern(str, Enum): |
| STATIC = "static" |
| KEYBOARD_HORIZONTAL = "keyboard_horizontal" |
| KEYBOARD_ALL = "keyboard_all" |
| RAIN_DOWN = "rain_down" |
| BOUNCE = "bounce" |
| CHASE_PLAYER = "chase_player" |
| ZIGZAG = "zigzag" |
| CIRCULAR = "circular" |
|
|
|
|
| class CollisionAction(str, Enum): |
| GAME_OVER = "game_over" |
| SCORE_PLUS = "score_plus" |
| SCORE_MINUS = "score_minus" |
| DESTROY_TARGET = "destroy_target" |
| BOUNCE_BACK = "bounce_back" |
|
|
|
|
| class InputKey(str, Enum): |
| ARROW_LEFT = "arrow_left" |
| ARROW_RIGHT = "arrow_right" |
| ARROW_UP = "arrow_up" |
| ARROW_DOWN = "arrow_down" |
| SPACE = "space" |
|
|
|
|
| class InputAction(str, Enum): |
| MOVE_LEFT = "move_left" |
| MOVE_RIGHT = "move_right" |
| MOVE_UP = "move_up" |
| MOVE_DOWN = "move_down" |
|
|
|
|
| class ScorePosition(str, Enum): |
| TOP_LEFT = "top_left" |
| TOP_RIGHT = "top_right" |
| TOP_CENTER = "top_center" |
|
|
|
|
| class SpriteComponent(BaseModel): |
| id: str = Field(description="Unique sprite type identifier (e.g. 'player', 'enemy', 'coin')") |
| shape: Shape |
| color: str = Field(default="#ffffff", description="CSS hex color, e.g. '#00ffcc'") |
| x: float = Field(default=300, ge=-300, le=1100, description="Initial X position; can be negative for off-screen spawning") |
| y: float = Field(default=400, ge=-700, le=1350, description="Initial Y position; can be negative for off-screen spawning") |
| width: float = Field(default=30, ge=5, le=150) |
| height: float = Field(default=30, ge=5, le=150) |
| movement_pattern: MovementPattern = MovementPattern.STATIC |
| speed: float = Field(default=3.0, ge=0.5, le=15.0) |
| is_player: bool = Field(default=False, description="True for the player-controlled sprite") |
| count: int = Field(default=1, ge=1, le=20, description="Number of instances to spawn (for enemies/collectibles)") |
|
|
|
|
| class CollisionRule(BaseModel): |
| between: Annotated[list[str], Field(min_length=2, max_length=2, description="Exactly two sprite IDs")] |
| action: CollisionAction |
| value: int = Field(default=1, ge=1, description="Points added/removed for score actions") |
|
|
|
|
| class InputBinding(BaseModel): |
| key: InputKey |
| action: InputAction |
| target: str = Field(description="ID of the sprite to control") |
|
|
|
|
| class ScoreDisplay(BaseModel): |
| position: ScorePosition = ScorePosition.TOP_RIGHT |
| font_size: int = Field(default=22, ge=12, le=40) |
| color: str = Field(default="#00ffcc") |
| label: str = Field(default="SCORE") |
|
|
|
|
| class CanvasGame(BaseModel): |
| title: str = Field(description="Short evocative game title (2-4 words)") |
| width: int = Field(default=600, ge=300, le=800) |
| height: int = Field(default=500, ge=300, le=650) |
| background_color: str = Field(default="#0a0a0a") |
| sprites: list[SpriteComponent] = Field(default_factory=list) |
| collision_rules: list[CollisionRule] = Field(default_factory=list) |
| input_bindings: list[InputBinding] = Field(default_factory=list) |
| score_display: ScoreDisplay | None = Field(default=None) |
| lives: int = Field(default=3, ge=1, le=10) |
|
|