From 84ea8df35dc84dbf1a3f39e40b7bb8fe76ccc95d Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 11 Mar 2026 04:41:30 +0000 Subject: [PATCH] feat(03-02): extend Game.ts with match handling and score display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added MatchEngine instantiation in Game constructor - Implemented tilesSelected event handler with match validation - Valid matches: emit tilesMatched event, clear tiles, update score display - Failed matches: emit matchFailed event, deselect after 200ms delay - Added private score property tracking current score - Added updateScoreDisplay method to update HTML overlay - Extended GameEvents interface with tilesMatched and matchFailed events Event flow: 1. Player selects two tiles → tilesSelected event 2. MatchEngine validates match (type → position → path) 3. Success: clear tiles, add score, update display, emit tilesMatched 4. Failure: emit matchFailed, deselect tiles after 200ms --- gsd-framework/src/game/Game.ts | 53 ++++++++++++++++++++++++++++++-- gsd-framework/src/types/index.ts | 2 ++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/gsd-framework/src/game/Game.ts b/gsd-framework/src/game/Game.ts index e6e455d..732acf3 100644 --- a/gsd-framework/src/game/Game.ts +++ b/gsd-framework/src/game/Game.ts @@ -10,6 +10,7 @@ import { GameEvents } from '../types'; import { CONFIG } from '../config'; import { GridManager } from '../managers/GridManager'; import { Renderer } from '../rendering/Renderer'; +import { MatchEngine } from '../matching/MatchEngine'; export class Game { readonly canvas: HTMLCanvasElement; @@ -18,7 +19,9 @@ export class Game { readonly events: TypedEventEmitter; readonly gridManager: GridManager; readonly renderer: Renderer; + readonly matchEngine: MatchEngine; private resizeTimeout: number | undefined; + private score = 0; constructor() { // Get canvas element @@ -40,6 +43,9 @@ export class Game { this.gridManager = new GridManager(this.events); this.gridManager.initializeGrid(); + // Initialize match engine + this.matchEngine = new MatchEngine(this.gridManager, this.events); + // Setup canvas size and scale this.setupCanvas(); @@ -50,15 +56,58 @@ export class Game { // Create game loop with update callback this.loop = new GameLoop(this.update.bind(this)); - // Listen for tilesSelected event (log for now - Phase 3 will handle matching logic) + // Listen for tilesSelected event and validate matches this.events.on('tilesSelected', ({ tile1, tile2 }) => { - console.log('Two tiles selected:', tile1.id, tile2.id); + const result = this.matchEngine.validateMatch(tile1, tile2); + + if (result.valid) { + // Successful match + this.events.emit('tilesMatched', { + tile1, + tile2, + path: result.path!, + turns: result.turns!, + score: result.score! + }); + + // Clear tiles from board + this.gridManager.clearTiles([tile1, tile2]); + + // Update score + this.score += result.score!; + this.updateScoreDisplay(); + + // Emit score update event + this.events.emit('game:score', { points: this.score }); + } else { + // Failed match + this.events.emit('matchFailed', { + tile1, + tile2, + reason: result.reason || 'unknown' + }); + + // Deselect after short delay + setTimeout(() => { + this.gridManager.deselectAll(); + }, 200); + } }); // Setup input listeners this.setupInputListeners(); } + /** + * Update score display in HTML overlay + */ + private updateScoreDisplay(): void { + const scoreDisplay = document.getElementById('score-display'); + if (scoreDisplay) { + scoreDisplay.textContent = `Score: ${this.score}`; + } + } + /** * Sets up canvas dimensions and handles device pixel ratio for sharp rendering */ diff --git a/gsd-framework/src/types/index.ts b/gsd-framework/src/types/index.ts index d602e05..048b1a4 100644 --- a/gsd-framework/src/types/index.ts +++ b/gsd-framework/src/types/index.ts @@ -64,4 +64,6 @@ export interface GameEvents { 'game:score': { points: number }; 'game:over': { won: boolean }; 'error': Error; + 'tilesMatched': { tile1: Tile; tile2: Tile; path: TilePosition[]; turns: number; score: number }; + 'matchFailed': { tile1: Tile; tile2: Tile; reason: string }; }