Skip to content

eilievska/ai_arch_assignment

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

PuzzleEngine — Technical Audit

Overview

PuzzleEngine is a single-HTML game framework that powers three distinct puzzle games — Match-3, Sudoku, and Falling Blocks (Tetris-style) — from a unified core architecture. The games feel like different "skins" or "modules" running on the same engine, sharing identical lifecycle methods, telemetry, and level progression systems.


How the "Single Source of Truth" Is Maintained

1. Unified Game API (The Contract)

Every game module implements the exact same interface:

init(levelConfig)  → Set up the game board and state from a config object
start()            → Begin gameplay (bind input, start timers)
reset()            → Handled by engine: destroy + init + start
destroy()          → Tear down DOM, unbind events, stop loops

The GameEngine class is the only entity that calls these methods. Games never self-start or self-reset — they respond to the engine's orchestration. This inversion of control means adding a 4th game requires zero changes to the engine; you simply implement the four methods and call engine.registerGame('newGame', NewGameModule).

2. Centralized Telemetry Class

A single Telemetry class handles all event tracking. Every game reports the same three events with an identical data structure:

Event When Fired
Tutorial_Complete Player dismisses the first-time tutorial
Level_Start Engine starts a new level
Level_Fail Player loses (out of moves/mistakes/space)

Event schema (universal):

{
  "eventName":  "Level_Start",
  "gameId":     "match3",
  "level":      1,
  "score":      0,
  "timestamp":  "2025-01-15T12:00:00.000Z",
  "metadata":   { "description": "Tutorial" }
}

Games don't instantiate their own trackers or define custom events — they call this.engine.failLevel() or this.engine.winLevel() and the engine handles telemetry dispatch. The tutorial flow is also engine-managed: it shows a tutorial on first play and fires Tutorial_Complete through the same telemetry pipeline.

3. Declarative Level Progression (JSON Config)

All difficulty curves live in a single LEVEL_DATA object at the top of the file:

{
  "match3": [
    { "level": 1, "gridSize": 6, "moves": 25, "targetScore": 400, "colors": 4 },
    ...
  ],
  "sudoku": [
    { "level": 1, "clues": 45, "maxMistakes": 5 },
    ...
  ],
  "tetris": [
    { "level": 1, "speed": 800, "linesTarget": 5, "width": 10, "height": 20 },
    ...
  ]
}

Each game's init() receives the current level's config object. The game module doesn't know what level it's on or how progression works — the engine manages level indexing, advancement, and retry. A designer can tweak difficulty by editing this JSON without touching any game logic.

4. Engine-Managed Lifecycle

The GameEngine class owns:

  • Level state (currentLevels — tracks each game's progress)
  • Tutorial state (tutorialShown — one-time per game)
  • Transitions (win → next level, fail → retry, reset)
  • UI overlays (tutorial dialogs, game-over screens)

Games are stateless modules in the sense that they don't manage their own lifecycle. They hold gameplay state (board, score, etc.) but the engine tells them when to exist.


Architecture Diagram

┌─────────────────────────────────────────────────┐
│                  GameEngine                      │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ Telemetry│  │Level Data│  │ UI/Views │      │
│  │ (shared) │  │  (JSON)  │  │ (shared) │      │
│  └──────────┘  └──────────┘  └──────────┘      │
│                                                  │
│  registerGame()  initGame()  startGame()         │
│  resetGame()     nextLevel() failLevel()         │
│  winLevel()                                      │
├──────────┬──────────┬───────────────────────────┤
│ Match-3  │  Sudoku  │  Falling Blocks           │
│ Module   │  Module  │  Module                    │
│          │          │                            │
│ init()   │ init()   │ init()                     │
│ start()  │ start()  │ start()                    │
│ destroy()│ destroy()│ destroy()                  │
└──────────┴──────────┴───────────────────────────┘

Scalability Notes

  • Adding a new game: Create a module object with init/start/destroy, add level data to LEVEL_DATA, register it. ~0 lines of engine code change.
  • Adding a new telemetry event: Add one method to Telemetry, call it from the engine. All games get it automatically.
  • Changing difficulty: Edit the JSON. No code changes.
  • Shared UI: Game-over screens, tutorials, the telemetry panel, and navigation are all engine-level — games don't reimplement them.

File Structure

This is a single-file project by design (per the assignment spec). In a production version, you would split:

/engine/       GameEngine.js, Telemetry.js
/games/        match3.js, sudoku.js, tetris.js
/config/       levels.json
/ui/           views, overlays, components
index.html     Shell / entry point

The single-file approach demonstrates that the architecture holds even without a build system.

About

testing

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages