types.ts (5440B)
1 import type { Page } from "@playwright/test"; 2 3 /** A 10x20 boolean grid: true = filled cell, false = empty. Row 0 is the top. */ 4 export type Grid = boolean[][]; 5 6 /** Pixel bounds of the game grid on the page. */ 7 export interface GridBounds { 8 x: number; 9 y: number; 10 width: number; 11 height: number; 12 } 13 14 /** How the game renders its grid. */ 15 export type RendererType = "canvas" | "dom" | "svg" | "unknown"; 16 17 /** Key mappings for game controls. */ 18 export interface Controls { 19 left: string; 20 right: string; 21 down: string; 22 rotate: string; 23 drop: string; 24 } 25 26 /** How the game was started. */ 27 export type StartMechanism = 28 | "auto" 29 | "click_canvas" 30 | "enter" 31 | "space" 32 | "button" 33 | "anykey" 34 | "unknown"; 35 36 /** Pre-test survey data collected before any tests run. */ 37 export interface SurveyData { 38 has_overlay: boolean; 39 has_canvas: boolean; 40 has_dom_grid: boolean; 41 visible_text: string[]; 42 clickable_elements: number; 43 } 44 45 /** Competitive play results (Phase 8). */ 46 export interface CompetitivePlayResult { 47 duration_seconds: number; 48 pieces_placed: number; 49 total_lines_cleared: number; 50 single_clears: number; 51 double_clears: number; 52 triple_clears: number; 53 tetris_clears: number; 54 max_combo: number; 55 score_readings: number[]; 56 score_final: number; 57 score_increases: number[]; 58 level_readings: number[]; 59 level_final: number; 60 game_over_reached: boolean; 61 game_over_text_found: string | null; 62 restart_available: boolean; 63 next_piece_visible: boolean; 64 speed_increased: boolean; 65 bugs_detected: string[]; 66 rendering_trail_detected?: boolean; 67 } 68 69 /** Result of the calibration phase. */ 70 export interface CalibrationResult { 71 renderer: RendererType; 72 gridDetected: boolean; 73 gridBounds: GridBounds | null; 74 cellWidth: number; 75 cellHeight: number; 76 controls: Controls; 77 startMechanism: StartMechanism; 78 scoreElementSelector: string | null; 79 backgroundColor: [number, number, number] | null; 80 consoleErrors: string[]; 81 /** Fraction of grid reads that returned non-null during calibration polling. */ 82 gridConfidence: number; 83 /** Details about the button that started the game, if any. */ 84 startButton?: { 85 selector: string; 86 text: string; 87 disappeared: boolean; 88 position: { x: number; y: number }; 89 }; 90 } 91 92 /** Result of an individual test. */ 93 export interface TestResult { 94 name: string; 95 pass: boolean; 96 detail: string; 97 } 98 99 /** Standard Tetris piece types. */ 100 export type PieceType = "I" | "O" | "T" | "S" | "Z" | "J" | "L" | "unknown"; 101 102 /** 103 * Tetromino definition: cells in a bounding box. 104 * Each rotation is a list of [row, col] offsets relative to the piece origin. 105 */ 106 export interface TetrominoDef { 107 type: PieceType; 108 /** All rotation states. Each is a list of [row, col] cell offsets. */ 109 rotations: [number, number][][]; 110 /** Bounding box dimensions per rotation: [width, height]. */ 111 dimensions: [number, number][]; 112 } 113 114 /** An event observed during continuous grid scanning. */ 115 export type GridEvent = 116 | { type: "piece_spawned"; pieceType: PieceType; frame: number } 117 | { type: "piece_locked"; frame: number; filledDelta: number } 118 | { type: "line_cleared"; count: number; frame: number } 119 | { type: "piece_moved"; direction: "left" | "right" | "down"; frame: number } 120 | { type: "piece_rotated"; frame: number } 121 | { type: "hard_drop"; frame: number } 122 | { type: "game_over"; frame: number } 123 | { type: "grid_read_failed"; frame: number }; 124 125 /** Data collected during one continuous observation session. */ 126 export interface GameSession { 127 started: boolean; 128 startMechanism: string; 129 piecesSpawned: number; 130 piecesLocked: number; 131 linesCleared: number; 132 rotationsObserved: number; 133 movementsObserved: number; 134 hardDropsObserved: number; 135 gameOverDetected: boolean; 136 consoleErrors: string[]; 137 durationSeconds: number; 138 pieceTypes: Set<string>; 139 scoreValues: number[]; 140 gridReadSuccess: number; 141 gridReadFail: number; 142 frames: number; 143 events: GridEvent[]; 144 /** Phases that were skipped and why. */ 145 skippedPhases: string[]; 146 } 147 148 /** Gameplay statistics gathered during the play phase. */ 149 export interface GameplayStats { 150 pieces_placed: number; 151 lines_cleared: number; 152 max_score_observed: number; 153 play_duration_seconds: number; 154 errors_during_play: number; 155 } 156 157 /** The full JSON report written at the end. */ 158 export interface BotReport { 159 implementation: { 160 renderer: string; 161 grid_detected: boolean; 162 grid_bounds: GridBounds | null; 163 controls: Record<string, string>; 164 start_mechanism: string; 165 score_element_found: boolean; 166 grid_confidence: number; 167 survey: SurveyData; 168 }; 169 tests: Array<{ name: string; pass: boolean; detail: string }>; 170 summary: { 171 total: number; 172 passed: number; 173 failed: number; 174 skipped: number; 175 score: number; 176 }; 177 gameplay: GameplayStats; 178 competitive_play: CompetitivePlayResult | null; 179 session: { 180 frames: number; 181 events_count: number; 182 pieces_spawned: number; 183 pieces_locked: number; 184 lines_cleared: number; 185 piece_types_seen: string[]; 186 grid_read_success_rate: number; 187 }; 188 performance?: { 189 load_time_ms: number; 190 }; 191 accessibility?: { 192 issues: string[]; 193 issue_count: number; 194 pass: boolean; 195 }; 196 } 197 198 /** Context passed through calibration, play, and reporting phases. */ 199 export interface BotContext { 200 page: Page; 201 calibration: CalibrationResult; 202 gameplay: GameplayStats; 203 testResults: TestResult[]; 204 }