Blueprint for the Water Path game, as implemented in index.html. All core gameplay logic and UI flows are described for design and development.
DOMContentLoaded, call showMainMenu(). If a saved state exists in localStorage, show Resume Game option. On Start: clearGameState() wipes localStorage, resetGame() initializes level 1 fresh. On Resume: loadGameState() restores grid, lives, level, points from localStorage and the grid renders at the saved level.generateGrid() places well, village, and obstacles. Levels 1-10 use fixed levelLayouts[] arrays. Levels 11+ randomize obstacles validated by BFS to ensure solvability. renderGrid() builds DOM cells with role="grid", role="row", role="gridcell", aria-label, and tabindex for accessibility. updateUI() syncs all dashboard stat elements.tabindex for keyboard access.handleCellClick(). handleGridKeydown() manages arrow-key navigation. Track selected path as an array of {r, c} objects. Prevent selection of obstacles, non-adjacent cells, or wrong start cell. Each dashboard stat has aria-live="polite" for real-time screen reader updates..cell-error CSS class. The dashboard stats (lives, water count, level, points) update live. Submitting validates the path and shows a success popup or error popup. The grid uses role="grid", role="row", and role="gridcell" for screen readers..selected and .cell-error CSS classes. Call updateUI() after any state change. showPopup() renders feedback modals. renderGrid() updates aria-selected and aria-label on each cell to reflect its state. All interactive elements have :focus-visible teal outline for keyboard users.submitPath(): first cell is well, last is village, all steps adjacent (dr+dc===1), no repeats via selectedPath.some(), no obstacles via grid[r][c].type, and length โค maxWater. Show specific error popup if any rule fails. Obstacle validation is redundant since obstacles can't be clicked, but kept as a safeguard.points = 100 + (maxWater-waterUsed)*10 + level*50 - pathObstacles*5. Call showPopup(msg, 'green') with country flag and fact. Set submittedThisAttempt=true, gameActive=false. On failure: showPopup(msg, color) with 'red' or 'orange'. On lives=0: gameOver() shows Game Over modal. On Next Level: nextLevel() increments level, lowers maxWater, raises obstacle count, regenerates grid.100 + (maxWater - waterUsed)*10 + level*50 - obstacleCount*5. Base 100 pts, water bonus (shorter path = more), level bonus (higher level = more), obstacle penalty. Total points accumulates across all levels. High score persists in localStorage. Dashboard shows live: current level, current points, best score. All stat elements have aria-live="polite" so screen readers announce changes immediately.submitPath(): waterBonus = Math.max(0, (maxWater - waterUsed) * 10), levelBonus = level * 50, points = 100 + waterBonus + levelBonus - obstacleBonus. Add to totalPoints. If totalPoints > highscore update highscore. Call saveHighscore() and saveGameState(). All dashboard stat divs have aria-live="polite" aria-atomic="true" attributes..selected class applies teal background + dashed border. .cell-error applies red background for 400ms then removes. showPopup(msg, color) renders modals: 'green' for success, 'red'/'orange' for errors. Popup HTML includes charity: water logo, flag emoji, fact text, and a donate link. Game Over uses role="dialog" aria-modal="true" for accessibility. All animations respect prefers-reduced-motion.lives === 0 after a Reset, call gameOver(). This shows the Game Over modal and calls clearGameState(). Levels beyond 10 fall back to generateGrid() random obstacle placement validated by BFS to remain solvable.clearGameState(), level = 1, totalPoints = 0, lives = 3, showMainMenu(). On Main Menu: saveGameState() then showMainMenu(). Game Over modal uses role="dialog" aria-modal="true" for accessibility.