chess/docs/implementation/optimization-checklist.md
Christoph Wagner 5ad0700b41 refactor: Consolidate repository structure - flatten from workspace pattern
Restructured project from nested workspace pattern to flat single-repo layout.
This eliminates redundant nesting and consolidates all project files under version control.

## Migration Summary

**Before:**
```
alex/ (workspace, not versioned)
├── chess-game/ (git repo)
│   ├── js/, css/, tests/
│   └── index.html
└── docs/ (planning, not versioned)
```

**After:**
```
alex/ (git repo, everything versioned)
├── js/, css/, tests/
├── index.html
├── docs/ (project documentation)
├── planning/ (historical planning docs)
├── .gitea/ (CI/CD)
└── CLAUDE.md (configuration)
```

## Changes Made

### Structure Consolidation
- Moved all chess-game/ contents to root level
- Removed redundant chess-game/ subdirectory
- Flattened directory structure (eliminated one nesting level)

### Documentation Organization
- Moved chess-game/docs/ → docs/ (project documentation)
- Moved alex/docs/ → planning/ (historical planning documents)
- Added CLAUDE.md (workspace configuration)
- Added IMPLEMENTATION_PROMPT.md (original project prompt)

### Version Control Improvements
- All project files now under version control
- Planning documents preserved in planning/ folder
- Merged .gitignore files (workspace + project)
- Added .claude/ agent configurations

### File Updates
- Updated .gitignore to include both workspace and project excludes
- Moved README.md to root level
- All import paths remain functional (relative paths unchanged)

## Benefits

 **Simpler Structure** - One level of nesting removed
 **Complete Versioning** - All documentation now in git
 **Standard Layout** - Matches open-source project conventions
 **Easier Navigation** - Direct access to all project files
 **CI/CD Compatible** - All workflows still functional

## Technical Validation

-  Node.js environment verified
-  Dependencies installed successfully
-  Dev server starts and responds
-  All core files present and accessible
-  Git repository functional

## Files Preserved

**Implementation Files:**
- js/ (3,517 lines of code)
- css/ (4 stylesheets)
- tests/ (87 test cases)
- index.html
- package.json

**CI/CD Pipeline:**
- .gitea/workflows/ci.yml
- .gitea/workflows/release.yml

**Documentation:**
- docs/ (12+ documentation files)
- planning/ (historical planning materials)
- README.md

**Configuration:**
- jest.config.js, babel.config.cjs, playwright.config.js
- .gitignore (merged)
- CLAUDE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 10:05:26 +01:00

23 KiB

Performance Optimization Checklist - HTML Chess Game

Overview

This checklist provides a comprehensive guide for implementing performance optimizations throughout the development lifecycle. Use this as a companion to the Performance Budget document.


Pre-Implementation Optimizations

Architecture Planning

  • Choose optimal data structures

    • Use typed arrays for board representation (faster than objects)
    • Implement bitboards for advanced features (optional)
    • Plan for immutable game state (easier undo/redo)
    • Design for minimal object creation in hot paths
  • Plan component interfaces

    • Define clear boundaries between modules
    • Minimize cross-module dependencies
    • Design for lazy loading (AI module separate)
    • Plan event system to reduce coupling
  • Optimize build pipeline

    • Set up code splitting (core vs AI vs sounds)
    • Configure tree shaking (ES6 modules)
    • Enable minification and compression
    • Set up source maps for debugging

Development Environment

  • Performance tooling setup

    • Install Chrome DevTools extensions
    • Set up Lighthouse CI
    • Configure bundle size analyzer
    • Add performance test suite
    • Set up memory profiling
  • Budgets configuration

    • Add webpack-bundle-analyzer
    • Configure size-limit package
    • Set up performance budgets in webpack
    • Create pre-commit hooks for budget checks

Code Quality Standards

  • Establish coding patterns

    • Use const/let (no var) for better optimization
    • Prefer pure functions for testability
    • Avoid premature abstraction
    • Document performance-critical sections
    • Use consistent naming conventions
  • Performance-aware coding

    • Avoid nested loops where possible
    • Use early returns to reduce nesting
    • Cache expensive calculations
    • Minimize DOM access in loops
    • Prefer CSS classes over inline styles

During-Implementation Best Practices

JavaScript Performance

1. General Optimizations

  • Variable usage
    • Use local variables over object properties
    • Cache array/string lengths in loops
    • Minimize global variable access
    • Use destructuring sparingly (creates overhead)
// ❌ Bad - Property access in loop
for (let i = 0; i < pieces.length; i++) {
  if (pieces[i].color === this.currentColor) { ... }
}

// ✅ Good - Cached values
const pieceCount = pieces.length;
const currentColor = this.currentColor;
for (let i = 0; i < pieceCount; i++) {
  if (pieces[i].color === currentColor) { ... }
}
  • Function calls
    • Minimize function call overhead in loops
    • Inline trivial functions (< 3 lines)
    • Use arrow functions for callbacks
    • Avoid creating functions inside loops
// ❌ Bad - Function creation in loop
moves.forEach(function(move) {
  validateMove(move);
});

// ✅ Good - Predefined function
moves.forEach(validateMove);
  • Object operations
    • Use Object.create(null) for dictionaries
    • Prefer Map for key-value pairs with many operations
    • Use WeakMap for memory-sensitive caches
    • Avoid delete operator (use null instead)
// ❌ Bad - delete causes hidden class change
delete obj.property;

// ✅ Good - null maintains hidden class
obj.property = null;

2. Array Operations

  • Efficient array methods
    • Use for loops for performance-critical paths
    • Prefer forEach/map/filter for readability (non-critical)
    • Use Array.from() sparingly (creates new array)
    • Preallocate arrays when size is known
// ❌ Bad - Dynamic growth
const moves = [];
for (let i = 0; i < 64; i++) {
  moves.push(generateMove(i));
}

// ✅ Good - Preallocated
const moves = new Array(64);
for (let i = 0; i < 64; i++) {
  moves[i] = generateMove(i);
}
  • Array searching
    • Use indexOf/includes for small arrays
    • Use Set for large arrays (O(1) lookup)
    • Break early from loops when found
    • Consider binary search for sorted arrays

3. String Operations

  • String concatenation
    • Use template literals for readability
    • Use array.join() for multiple concatenations
    • Avoid + in loops
    • Use String.prototype methods efficiently
// ❌ Bad - Multiple concatenations
let notation = '';
notation += piece;
notation += fromSquare;
notation += toSquare;

// ✅ Good - Template literal
const notation = `${piece}${fromSquare}${toSquare}`;

AI Performance

4. Search Algorithm Optimizations

  • Alpha-Beta Pruning (CRITICAL - 10-100x improvement)
    • Implement fail-soft alpha-beta
    • Use proper window management
    • Track pruning statistics
    • Test with various positions
// ✅ Alpha-Beta Implementation
function alphaBeta(depth, alpha, beta, maximizing, gameState) {
  if (depth === 0 || gameState.isTerminal()) {
    return evaluate(gameState);
  }

  const moves = generateMoves(gameState);

  if (maximizing) {
    let maxEval = -Infinity;
    for (const move of moves) {
      const newState = makeMove(gameState, move);
      const eval = alphaBeta(depth - 1, alpha, beta, false, newState);
      maxEval = Math.max(maxEval, eval);
      alpha = Math.max(alpha, eval);
      if (beta <= alpha) break; // Beta cutoff
    }
    return maxEval;
  } else {
    // Similar for minimizing
  }
}
  • Move Ordering (HIGH - 2-3x improvement on top of alpha-beta)
    • Evaluate captures first (MVV/LVA)
    • Try check-giving moves early
    • Use killer move heuristic
    • Use hash move from transposition table
// ✅ Move Ordering
function orderMoves(moves, gameState) {
  return moves.sort((a, b) => {
    // 1. Hash move first
    if (a === hashMove) return -1;
    if (b === hashMove) return 1;

    // 2. Captures (MVV/LVA - Most Valuable Victim, Least Valuable Attacker)
    const aScore = getCaptureScore(a);
    const bScore = getCaptureScore(b);
    if (aScore !== bScore) return bScore - aScore;

    // 3. Killer moves
    if (isKillerMove(a)) return -1;
    if (isKillerMove(b)) return 1;

    // 4. History heuristic
    return getHistoryScore(b) - getHistoryScore(a);
  });
}
  • Transposition Table (HIGH - 1.5-2x improvement)
    • Use Zobrist hashing for positions
    • Implement replacement strategy (depth-preferred)
    • Store bounds (exact, lower, upper)
    • Size table based on device memory
// ✅ Transposition Table
class TranspositionTable {
  constructor(maxSize = 10_000_000) {
    this.table = new Map();
    this.maxSize = maxSize;
  }

  store(hash, depth, score, flag, bestMove) {
    // Replacement strategy: prefer deeper searches
    const existing = this.table.get(hash);
    if (!existing || depth >= existing.depth) {
      this.table.set(hash, { depth, score, flag, bestMove });
    }

    // Evict oldest entries if too large
    if (this.table.size > this.maxSize) {
      const firstKey = this.table.keys().next().value;
      this.table.delete(firstKey);
    }
  }

  probe(hash, depth, alpha, beta) {
    const entry = this.table.get(hash);
    if (!entry || entry.depth < depth) return null;

    // Check if we can use this score
    if (entry.flag === EXACT) return entry.score;
    if (entry.flag === LOWER && entry.score >= beta) return entry.score;
    if (entry.flag === UPPER && entry.score <= alpha) return entry.score;

    return null;
  }
}
  • Iterative Deepening (MEDIUM - Better UX)
    • Start at depth 1, increment each iteration
    • Use previous iteration for move ordering
    • Support time-based termination
    • Return best move from completed depth
// ✅ Iterative Deepening
function iterativeDeepening(gameState, maxTime = 2000) {
  const startTime = performance.now();
  let bestMove = null;
  let depth = 1;

  while (performance.now() - startTime < maxTime) {
    try {
      const result = alphaBeta(depth, -Infinity, Infinity, true, gameState);
      bestMove = result.move;
      depth++;
    } catch (e) {
      if (e instanceof TimeoutError) break;
      throw e;
    }
  }

  return bestMove;
}

5. Evaluation Function Optimizations

  • Incremental Updates (HIGH - 5x improvement)
    • Track material score incrementally
    • Update positional scores on move/unmove
    • Only recalculate complex metrics when needed
    • Cache king safety calculations
// ✅ Incremental Evaluation
class IncrementalEvaluator {
  constructor() {
    this.materialScore = 0;
    this.positionalScore = 0;
  }

  makeMove(move) {
    // Update material
    if (move.captured) {
      this.materialScore -= PIECE_VALUES[move.captured];
    }

    // Update positional (only changed squares)
    this.positionalScore -= PIECE_SQUARE_TABLES[move.piece][move.from];
    this.positionalScore += PIECE_SQUARE_TABLES[move.piece][move.to];
  }

  unmakeMove(move) {
    // Reverse updates
    if (move.captured) {
      this.materialScore += PIECE_VALUES[move.captured];
    }
    this.positionalScore += PIECE_SQUARE_TABLES[move.piece][move.from];
    this.positionalScore -= PIECE_SQUARE_TABLES[move.piece][move.to];
  }

  getScore() {
    return this.materialScore + this.positionalScore;
  }
}
  • Piece-Square Tables (MEDIUM - 2x improvement)
    • Precompute all positional values
    • Use O(1) array lookups
    • Separate tables for opening/middlegame/endgame
    • Mirror tables for black pieces
// ✅ Piece-Square Tables
const PAWN_TABLE = [
  [  0,  0,  0,  0,  0,  0,  0,  0],
  [ 50, 50, 50, 50, 50, 50, 50, 50],
  [ 10, 10, 20, 30, 30, 20, 10, 10],
  [  5,  5, 10, 25, 25, 10,  5,  5],
  [  0,  0,  0, 20, 20,  0,  0,  0],
  [  5, -5,-10,  0,  0,-10, -5,  5],
  [  5, 10, 10,-20,-20, 10, 10,  5],
  [  0,  0,  0,  0,  0,  0,  0,  0]
];

function getPieceSquareScore(piece, square, color) {
  const [rank, file] = squareToCoords(square);
  const tableRank = color === 'white' ? rank : 7 - rank;
  return PAWN_TABLE[tableRank][file];
}

6. Memory Optimizations

  • Object Pooling (MEDIUM - 20-30% less GC)
    • Pool move objects
    • Pool position objects
    • Reuse evaluation contexts
    • Monitor pool size
// ✅ Object Pool
class ObjectPool {
  constructor(factory, initialSize = 100) {
    this.factory = factory;
    this.pool = [];
    for (let i = 0; i < initialSize; i++) {
      this.pool.push(factory());
    }
  }

  acquire() {
    return this.pool.length > 0 ? this.pool.pop() : this.factory();
  }

  release(obj) {
    obj.reset(); // Clear object state
    this.pool.push(obj);
  }
}

// Usage
const movePool = new ObjectPool(() => new Move(), 1000);
const move = movePool.acquire();
// ... use move ...
movePool.release(move);
  • Garbage Collection Minimization
    • Avoid creating objects in hot loops
    • Reuse arrays instead of creating new ones
    • Use primitive values where possible
    • Clear references when done

DOM Performance

7. Rendering Optimizations

  • Virtual DOM / Diffing (CRITICAL - 5-10x improvement)
    • Track previous board state
    • Only update changed squares
    • Batch DOM updates
    • Use DocumentFragment for batch inserts
// ✅ DOM Diffing
function updateBoard(oldState, newState) {
  const changedSquares = [];

  for (let i = 0; i < 64; i++) {
    if (oldState[i] !== newState[i]) {
      changedSquares.push(i);
    }
  }

  // Only update changed squares
  requestAnimationFrame(() => {
    changedSquares.forEach(index => {
      updateSquare(index, newState[index]);
    });
  });
}
  • CSS Transform Animations (CRITICAL - GPU acceleration)
    • Use transform instead of top/left
    • Use translate3d for GPU acceleration
    • Avoid animating layout properties
    • Use will-change sparingly
/* ❌ Bad - Triggers layout */
.piece {
  transition: top 0.3s, left 0.3s;
}

/* ✅ Good - GPU accelerated */
.piece {
  transition: transform 0.3s;
  will-change: transform;
}
// ✅ Transform Animation
function animatePiece(piece, fromSquare, toSquare) {
  const fromPos = getSquarePosition(fromSquare);
  const toPos = getSquarePosition(toSquare);
  const deltaX = toPos.x - fromPos.x;
  const deltaY = toPos.y - fromPos.y;

  piece.style.transform = `translate3d(${deltaX}px, ${deltaY}px, 0)`;
}
  • CSS Classes over Inline Styles (MEDIUM - 2x improvement)
    • Define CSS classes for all states
    • Use classList API for toggling
    • Avoid style.property = value
    • Batch class changes
// ❌ Bad - Inline styles
square.style.backgroundColor = '#f0d9b5';
square.style.boxShadow = '0 0 10px rgba(0,0,0,0.3)';

// ✅ Good - CSS classes
square.classList.add('highlighted');
  • RequestAnimationFrame (MEDIUM - Smooth animations)
    • Use RAF for all animations
    • Batch reads and writes
    • Avoid layout thrashing
    • Cancel RAF on cleanup
// ✅ RequestAnimationFrame
function smoothUpdate() {
  // Read phase (all DOM reads together)
  const positions = pieces.map(p => p.getBoundingClientRect());

  requestAnimationFrame(() => {
    // Write phase (all DOM writes together)
    positions.forEach((pos, i) => {
      pieces[i].style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
    });
  });
}

8. Event Handling

  • Event Delegation (MEDIUM - Reduces listeners)
    • Use single listener on board container
    • Identify target square from event.target
    • Avoid listeners on every square
    • Clean up listeners on destroy
// ❌ Bad - 64 event listeners
squares.forEach(square => {
  square.addEventListener('click', handleSquareClick);
});

// ✅ Good - 1 event listener
board.addEventListener('click', (event) => {
  const square = event.target.closest('.square');
  if (square) handleSquareClick(square);
});
  • Debouncing and Throttling
    • Debounce window resize handlers
    • Throttle scroll handlers
    • Use passive listeners for scroll/touch
    • Remove listeners when not needed
// ✅ Passive Listeners
board.addEventListener('touchstart', handleTouch, { passive: true });

// ✅ Throttle
function throttle(func, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}

window.addEventListener('resize', throttle(handleResize, 200));

Asset Optimization

9. Image Optimization

  • SVG Sprites (MEDIUM - 50% size reduction)
    • Combine all pieces into single SVG
    • Use tags to reference pieces
    • Optimize SVG with SVGO
    • Inline critical SVG in HTML
<!-- ✅ SVG Sprite -->
<svg style="display: none;">
  <symbol id="piece-king-white" viewBox="0 0 45 45">
    <path d="..."/>
  </symbol>
  <!-- ... other pieces ... -->
</svg>

<!-- Usage -->
<svg class="piece"><use href="#piece-king-white"/></svg>
  • Lazy Loading (MEDIUM - Faster initial load)
    • Load sounds on first interaction
    • Load AI module on game start
    • Use loading="lazy" for images
    • Prefetch critical assets
// ✅ Lazy Load AI
let aiModule = null;

async function loadAI() {
  if (!aiModule) {
    aiModule = await import('./ai-engine.js');
  }
  return aiModule;
}

10. Code Splitting

  • Module Splitting (HIGH - 2x faster initial load)
    • Separate core UI from AI
    • Split by route (if multi-page)
    • Use dynamic imports
    • Analyze bundle with webpack-bundle-analyzer
// ✅ Code Splitting
// main.js - Core UI only (35KB)
import { ChessBoard } from './core/ChessBoard.js';
import { GameController } from './core/GameController.js';

// Lazy load AI when needed
async function startAIGame() {
  const { AIPlayer } = await import('./ai/AIPlayer.js'); // +28KB
  return new AIPlayer();
}
  • Tree Shaking (MEDIUM - 20-30% reduction)
    • Use ES6 modules (not CommonJS)
    • Mark side-effect-free packages
    • Avoid default exports for better shaking
    • Import only what you need
// ❌ Bad - Imports everything
import _ from 'lodash';

// ✅ Good - Imports only needed functions
import { debounce, throttle } from 'lodash-es';

Web Workers (CRITICAL)

11. Background AI Computation

  • Web Worker Setup
    • Move AI calculation to worker
    • Use structured clone for messages
    • Handle worker errors gracefully
    • Terminate worker when done
// ✅ Web Worker for AI
// main.js
const aiWorker = new Worker('ai-worker.js');

function calculateAIMove(gameState) {
  return new Promise((resolve, reject) => {
    aiWorker.onmessage = (e) => resolve(e.data.move);
    aiWorker.onerror = reject;
    aiWorker.postMessage({ type: 'calculate', gameState });
  });
}

// ai-worker.js
self.onmessage = function(e) {
  if (e.data.type === 'calculate') {
    const move = calculateBestMove(e.data.gameState);
    self.postMessage({ move });
  }
};
  • Message Optimization
    • Minimize message size
    • Use Transferable objects for large data
    • Batch messages when possible
    • Use SharedArrayBuffer for shared state (advanced)

Post-Implementation Optimization

🔍 Performance Testing

  • Automated Performance Tests
    • Add performance test suite
    • Test AI calculation time
    • Test rendering frame rate
    • Test memory usage
    • Test bundle size
// ✅ Performance Test Example
describe('Performance', () => {
  it('should render in <16ms', () => {
    const duration = measurePerformance(() => renderBoard());
    expect(duration).toBeLessThan(16);
  });

  it('should calculate AI move in <1s', () => {
    const duration = measurePerformance(() => ai.calculateMove(position));
    expect(duration).toBeLessThan(1000);
  });
});
  • Lighthouse Audits

    • Run Lighthouse on every build
    • Target score > 90
    • Fix all critical issues
    • Document score in README
  • Real Device Testing

    • Test on 3+ different desktop browsers
    • Test on 3+ different mobile devices
    • Test on slow 3G connection
    • Test with throttled CPU (6x slowdown)

🔍 Profiling

  • Chrome DevTools Profiling

    • Record CPU profile during AI calculation
    • Record performance timeline during gameplay
    • Take heap snapshots to find leaks
    • Analyze network waterfall
    • Check for memory leaks with allocation timeline
  • Performance API

    • Add performance.mark() for key operations
    • Measure critical paths
    • Log performance metrics
    • Set up Real User Monitoring (future)
// ✅ Performance Measurement
performance.mark('ai-start');
const move = calculateBestMove(position);
performance.mark('ai-end');
performance.measure('ai-calculation', 'ai-start', 'ai-end');

const measures = performance.getEntriesByName('ai-calculation');
console.log(`AI calculation took ${measures[0].duration}ms`);

🔍 Optimization Opportunities

  • Bottleneck Analysis

    • Identify slowest operations
    • Profile with Chrome DevTools
    • Measure before/after optimization
    • Document improvements
  • Low-Hanging Fruit

    • Cache expensive calculations
    • Reduce unnecessary re-renders
    • Minimize network requests
    • Compress assets
    • Enable gzip/brotli compression

🔍 Long-Term Monitoring

  • Regression Prevention

    • Add performance budgets to CI
    • Fail builds that exceed budgets
    • Track performance over time
    • Create performance dashboard
  • Continuous Improvement

    • Review performance quarterly
    • Update budgets as needed
    • Adopt new browser features
    • Monitor web performance best practices

Platform-Specific Optimizations

📱 Mobile Optimizations

  • Touch Interactions

    • Use touch events (touchstart, touchmove, touchend)
    • Add passive: true to touch listeners
    • Implement touch-action CSS
    • Provide larger touch targets (44x44px minimum)
  • Responsive Performance

    • Use CSS media queries for layout
    • Reduce AI depth on mobile
    • Disable animations on low-end devices
    • Use smaller transposition table
// ✅ Device-Specific Configuration
function getDeviceConfig() {
  const cores = navigator.hardwareConcurrency || 2;
  const memory = navigator.deviceMemory || 2;

  if (cores >= 8 && memory >= 6) {
    return { depth: 6, tableSize: 50_000_000, animations: true };
  } else if (cores >= 4 && memory >= 2) {
    return { depth: 5, tableSize: 10_000_000, animations: true };
  } else {
    return { depth: 4, tableSize: 3_000_000, animations: false };
  }
}

🖥️ Desktop Optimizations

  • Keyboard Shortcuts

    • Implement keyboard navigation
    • Add undo/redo shortcuts (Ctrl+Z, Ctrl+Y)
    • Support arrow keys for piece selection
    • Add accessibility shortcuts
  • Advanced Features

    • Enable deeper AI search
    • Use larger transposition tables
    • Add advanced animations
    • Support multiple game modes

Final Performance Checklist

Before Release

  • Performance Metrics

    • Lighthouse score > 90
    • Bundle size < 150KB gzipped
    • FCP < 500ms
    • TTI < 1s
    • 60fps rendering
    • AI response < 1s (depth 5)
  • Cross-Browser Testing

    • Chrome (latest 2 versions)
    • Firefox (latest 2 versions)
    • Safari (latest 2 versions)
    • Edge (latest 2 versions)
  • Device Testing

    • Desktop (1920x1080, 1366x768)
    • Tablet (iPad, Android tablet)
    • Mobile (iPhone, Android phone)
    • Low-end device (throttled)
  • Network Testing

    • 5G / Fast connection
    • 4G / Regular connection
    • 3G / Slow connection
    • Offline mode (service worker)

Performance Optimization Priority

Critical (Do First)

  1. Alpha-Beta Pruning
  2. Web Workers
  3. DOM Diffing
  4. CSS Transforms
  5. Code Splitting

High (Do Second)

  1. Move Ordering
  2. Transposition Tables
  3. Bundle Optimization
  4. Mobile Optimization

Medium (Do Third)

  1. Object Pooling
  2. Iterative Deepening
  3. SVG Optimization

Resources


Remember: "Premature optimization is the root of all evil, but planning for performance is wisdom."

Document Version: 1.0.0 Last Updated: 2025-11-22 Owner: Performance Optimizer Agent