Code Style - RumenDamyanov/js-chess GitHub Wiki

Code Style Guide

Comprehensive coding standards and conventions for the chess showcase application across all frameworks.

Overview

This guide establishes consistent coding standards for:

  • JavaScript/TypeScript style conventions
  • Framework-specific best practices
  • File and folder naming conventions
  • Documentation standards
  • Git commit conventions
  • Code formatting and linting

General Principles

Code Philosophy

  1. Readability First: Code is written once, read many times
  2. Consistency: Follow established patterns throughout the codebase
  3. Simplicity: Prefer simple, clear solutions over clever ones
  4. Maintainability: Write code that's easy to modify and extend
  5. Performance: Optimize for clarity first, performance second

Project Structure Standards

project-root/
├── shared/                 # Shared code across frameworks
│   ├── components/         # Reusable UI components
│   ├── chess/             # Chess engine and game logic
│   ├── utils/             # Utility functions
│   ├── types/             # TypeScript type definitions
│   └── styles/            # Shared CSS and themes
├── framework-name/        # Framework-specific implementations
│   ├── src/
│   │   ├── components/    # Framework components
│   │   ├── pages/         # Route components
│   │   ├── hooks/         # Custom hooks (React/Vue)
│   │   ├── services/      # Business logic services
│   │   └── utils/         # Framework-specific utilities
│   ├── public/            # Static assets
│   └── tests/             # Test files
└── docs/                  # Documentation

JavaScript/TypeScript Standards

Naming Conventions

// Variables and functions: camelCase
const gameState = {};
const currentPlayer = 'white';
function calculateMoveScore(move) { }

// Constants: SCREAMING_SNAKE_CASE
const MAX_GAME_TIME = 3600000;
const DEFAULT_TIME_CONTROL = {
  initial: 600000,
  increment: 5000
};

// Classes: PascalCase
class ChessEngine {
  constructor() {}
}

class GameStateManager {
  updatePosition(fen) {}
}

// Interfaces and Types: PascalCase
interface ChessPosition {
  rank: number;
  file: number;
}

type GameResult = 'white' | 'black' | 'draw' | 'ongoing';

// Enums: PascalCase with SCREAMING_SNAKE_CASE values
enum PieceType {
  PAWN = 'pawn',
  KNIGHT = 'knight',
  BISHOP = 'bishop',
  ROOK = 'rook',
  QUEEN = 'queen',
  KING = 'king'
}

// Private properties: prefix with underscore
class ChessGame {
  constructor() {
    this._internalState = {};
    this.publicProperty = 'visible';
  }

  _privateMethod() {
    // Internal implementation
  }

  publicMethod() {
    // Public API
  }
}

File Naming Conventions

// JavaScript/TypeScript files: camelCase
chessEngine.js
gameStateManager.ts
moveValidator.js

// React components: PascalCase
ChessBoard.jsx
GameControls.tsx
MoveHistory.jsx

// Vue components: PascalCase
ChessBoard.vue
GameControls.vue
MoveHistory.vue

// Angular components: kebab-case
chess-board.component.ts
game-controls.component.ts
move-history.component.ts

// Test files: match source file + .test or .spec
chessEngine.test.js
ChessBoard.spec.jsx
game-controls.component.spec.ts

// Type definition files: camelCase + .d.ts
chessTypes.d.ts
gameInterfaces.d.ts

// Configuration files: kebab-case
webpack.config.js
jest.config.js
tsconfig.json

Function and Method Standards

// Function documentation with JSDoc
/**
 * Validates a chess move according to game rules
 * @param {ChessMove} move - The move to validate
 * @param {ChessPosition} position - Current board position
 * @param {GameState} gameState - Current game state
 * @returns {MoveValidationResult} Validation result with details
 * @throws {InvalidMoveError} When move format is invalid
 */
function validateMove(move, position, gameState) {
  // Input validation
  if (!move || typeof move !== 'object') {
    throw new InvalidMoveError('Move must be a valid object');
  }

  // Early returns for clarity
  if (!isValidSquare(move.from)) {
    return { valid: false, reason: 'Invalid source square' };
  }

  if (!isValidSquare(move.to)) {
    return { valid: false, reason: 'Invalid destination square' };
  }

  // Main logic
  const piece = position.getPieceAt(move.from);
  if (!piece) {
    return { valid: false, reason: 'No piece at source square' };
  }

  // Validate piece movement
  const isLegal = validatePieceMove(piece, move, position);

  return {
    valid: isLegal,
    reason: isLegal ? null : 'Move violates piece movement rules'
  };
}

// Async function standards
async function fetchGameData(gameId) {
  try {
    // Use descriptive variable names
    const response = await apiClient.get(`/games/${gameId}`);
    const gameData = response.data;

    // Validate response
    if (!gameData || !gameData.id) {
      throw new Error('Invalid game data received');
    }

    return gameData;
  } catch (error) {
    // Enhance error context
    throw new Error(`Failed to fetch game ${gameId}: ${error.message}`);
  }
}

// Pure functions preferred
function calculateMoveTime(startTime, endTime) {
  // No side effects, predictable output
  return endTime - startTime;
}

// Avoid deeply nested functions
function processGameMoves(moves) {
  // Extract complex logic into separate functions
  const validatedMoves = moves.filter(isValidMove);
  const processedMoves = validatedMoves.map(processSingleMove);

  return processedMoves;
}

function processSingleMove(move) {
  // Focused, single-purpose function
  return {
    ...move,
    timestamp: Date.now(),
    processed: true
  };
}

Object and Data Structure Standards

// Use object destructuring for clarity
function createGameState({ fen, timeControl, players }) {
  // Destructure parameters for readability
  const { initial, increment } = timeControl;
  const { white, black } = players;

  return {
    position: new ChessPosition(fen),
    clock: new GameClock(initial, increment),
    players: { white, black },
    status: 'active'
  };
}

// Prefer object spread over Object.assign
const updatedGameState = {
  ...currentGameState,
  position: newPosition,
  moveNumber: currentGameState.moveNumber + 1
};

// Use consistent property ordering
const chessMove = {
  // Identifiers first
  id: generateMoveId(),
  gameId: game.id,

  // Core data
  from: 'e2',
  to: 'e4',
  piece: 'pawn',
  notation: 'e4',

  // Metadata
  timestamp: Date.now(),
  duration: 1500,

  // Optional properties last
  capture: null,
  promotion: null,
  check: false,
  checkmate: false
};

// Use Maps for key-value collections when appropriate
const piecePositions = new Map([
  ['e1', { type: 'king', color: 'white' }],
  ['e8', { type: 'king', color: 'black' }]
]);

// Use Sets for unique collections
const visitedSquares = new Set(['e2', 'e4', 'd4']);

Error Handling Standards

// Custom error classes for better error handling
class ChessError extends Error {
  constructor(message, code = 'CHESS_ERROR') {
    super(message);
    this.name = 'ChessError';
    this.code = code;
  }
}

class InvalidMoveError extends ChessError {
  constructor(message, move = null) {
    super(message, 'INVALID_MOVE');
    this.name = 'InvalidMoveError';
    this.move = move;
  }
}

class GameEndedError extends ChessError {
  constructor(result) {
    super(`Game has ended: ${result}`);
    this.name = 'GameEndedError';
    this.result = result;
  }
}

// Consistent error handling patterns
async function makeMove(gameId, move) {
  try {
    // Validate inputs
    if (!gameId) {
      throw new ChessError('Game ID is required', 'MISSING_GAME_ID');
    }

    // Business logic
    const game = await findGame(gameId);
    if (!game) {
      throw new ChessError(`Game not found: ${gameId}`, 'GAME_NOT_FOUND');
    }

    if (game.status !== 'active') {
      throw new GameEndedError(game.result);
    }

    const validationResult = validateMove(move, game.position);
    if (!validationResult.valid) {
      throw new InvalidMoveError(validationResult.reason, move);
    }

    // Execute move
    return await executeMove(game, move);

  } catch (error) {
    // Add context to errors
    if (error instanceof ChessError) {
      // Re-throw chess-specific errors
      throw error;
    } else {
      // Wrap unexpected errors
      throw new ChessError(`Unexpected error in makeMove: ${error.message}`, 'INTERNAL_ERROR');
    }
  }
}

Framework-Specific Standards

React Standards

// Component structure and organization
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { ChessEngine } from '@shared/chess';
import { useGameState } from '../hooks/useGameState';
import './ChessBoard.css';

/**
 * Interactive chess board component
 * @param {Object} props - Component props
 * @param {string} props.gameId - Unique game identifier
 * @param {Function} props.onMove - Move callback function
 * @param {boolean} props.disabled - Whether board is interactive
 */
function ChessBoard({ gameId, onMove, disabled = false }) {
  // Group related state
  const [selectedSquare, setSelectedSquare] = useState(null);
  const [highlightedSquares, setHighlightedSquares] = useState([]);

  // Custom hooks
  const { gameState, makeMove, error } = useGameState(gameId);

  // Memoized values
  const legalMoves = useMemo(() => {
    if (!selectedSquare || !gameState) return [];
    return gameState.getLegalMoves(selectedSquare);
  }, [selectedSquare, gameState]);

  // Event handlers with useCallback
  const handleSquareClick = useCallback((square) => {
    if (disabled) return;

    if (selectedSquare === square) {
      // Deselect if clicking same square
      setSelectedSquare(null);
      setHighlightedSquares([]);
    } else if (selectedSquare && legalMoves.includes(square)) {
      // Make move
      const move = { from: selectedSquare, to: square };
      makeMove(move);
      setSelectedSquare(null);
      setHighlightedSquares([]);
    } else {
      // Select new square
      setSelectedSquare(square);
      setHighlightedSquares(gameState?.getLegalMoves(square) || []);
    }
  }, [disabled, selectedSquare, legalMoves, makeMove, gameState]);

  // Effects
  useEffect(() => {
    if (error) {
      console.error('Chess board error:', error);
    }
  }, [error]);

  // Early returns
  if (!gameState) {
    return <div className="chess-board loading">Loading game...</div>;
  }

  if (error) {
    return <div className="chess-board error">Error: {error.message}</div>;
  }

  // Render
  return (
    <div className="chess-board">
      <div className="board-grid">
        {gameState.getSquares().map((square) => (
          <ChessSquare
            key={square}
            square={square}
            piece={gameState.getPieceAt(square)}
            selected={selectedSquare === square}
            highlighted={highlightedSquares.includes(square)}
            onClick={handleSquareClick}
            disabled={disabled}
          />
        ))}
      </div>
    </div>
  );
}

// PropTypes for runtime validation
ChessBoard.propTypes = {
  gameId: PropTypes.string.isRequired,
  onMove: PropTypes.func,
  disabled: PropTypes.bool
};

// Default props
ChessBoard.defaultProps = {
  onMove: () => {},
  disabled: false
};

export default ChessBoard;

// Custom hook example
function useGameState(gameId) {
  const [gameState, setGameState] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const makeMove = useCallback(async (move) => {
    try {
      const updatedState = await apiClient.makeMove(gameId, move);
      setGameState(updatedState);
    } catch (err) {
      setError(err);
    }
  }, [gameId]);

  useEffect(() => {
    let cancelled = false;

    const loadGame = async () => {
      try {
        setLoading(true);
        const state = await apiClient.getGame(gameId);

        if (!cancelled) {
          setGameState(state);
          setError(null);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    };

    loadGame();

    return () => {
      cancelled = true;
    };
  }, [gameId]);

  return { gameState, makeMove, loading, error };
}

Vue Standards

<!-- ChessBoard.vue -->
<template>
  <div class="chess-board" :class="{ disabled, loading: !gameState }">
    <div v-if="!gameState" class="loading-state">
      Loading game...
    </div>

    <div v-else-if="error" class="error-state">
      Error: {{ error.message }}
    </div>

    <div v-else class="board-grid">
      <ChessSquare
        v-for="square in squares"
        :key="square"
        :square="square"
        :piece="gameState.getPieceAt(square)"
        :selected="selectedSquare === square"
        :highlighted="highlightedSquares.includes(square)"
        :disabled="disabled"
        @click="handleSquareClick"
      />
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { useGameState } from '@/composables/useGameState';
import ChessSquare from './ChessSquare.vue';

// Props with defaults and validation
const props = defineProps({
  gameId: {
    type: String,
    required: true
  },
  disabled: {
    type: Boolean,
    default: false
  }
});

// Emits declaration
const emit = defineEmits(['move', 'error']);

// Reactive state
const selectedSquare = ref(null);
const highlightedSquares = ref([]);

// Composables
const { gameState, makeMove, loading, error } = useGameState(props.gameId);

// Computed properties
const squares = computed(() => {
  if (!gameState.value) return [];
  return gameState.value.getSquares();
});

const legalMoves = computed(() => {
  if (!selectedSquare.value || !gameState.value) return [];
  return gameState.value.getLegalMoves(selectedSquare.value);
});

// Methods
const handleSquareClick = (square) => {
  if (props.disabled) return;

  if (selectedSquare.value === square) {
    // Deselect
    selectedSquare.value = null;
    highlightedSquares.value = [];
  } else if (selectedSquare.value && legalMoves.value.includes(square)) {
    // Make move
    const move = { from: selectedSquare.value, to: square };
    makeMove(move);
    emit('move', move);

    selectedSquare.value = null;
    highlightedSquares.value = [];
  } else {
    // Select square
    selectedSquare.value = square;
    highlightedSquares.value = gameState.value?.getLegalMoves(square) || [];
  }
};

// Watchers
watch(error, (newError) => {
  if (newError) {
    emit('error', newError);
  }
});

// Lifecycle
onMounted(() => {
  console.log('ChessBoard mounted for game:', props.gameId);
});
</script>

<style scoped>
.chess-board {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}

.chess-board.disabled {
  pointer-events: none;
  opacity: 0.6;
}

.board-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(8, 1fr);
  gap: 1px;
  border: 2px solid var(--border-color);
}

.loading-state,
.error-state {
  padding: 2rem;
  text-align: center;
  color: var(--text-color);
}

.error-state {
  color: var(--error-color);
}
</style>

Angular Standards

// chess-board.component.ts
import {
  Component,
  Input,
  Output,
  EventEmitter,
  OnInit,
  OnDestroy,
  ChangeDetectionStrategy,
  ChangeDetectorRef
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { Subject, takeUntil } from 'rxjs';

import { GameStateService } from '../services/game-state.service';
import { ChessMove, ChessPosition, GameState } from '../types/chess.types';
import { ChessSquareComponent } from './chess-square.component';

@Component({
  selector: 'app-chess-board',
  standalone: true,
  imports: [CommonModule, ChessSquareComponent],
  templateUrl: './chess-board.component.html',
  styleUrls: ['./chess-board.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChessBoardComponent implements OnInit, OnDestroy {
  // Input properties with validation
  @Input({ required: true }) gameId!: string;
  @Input() disabled = false;

  // Output events
  @Output() moveEvent = new EventEmitter<ChessMove>();
  @Output() errorEvent = new EventEmitter<Error>();

  // Component state
  gameState: GameState | null = null;
  selectedSquare: string | null = null;
  highlightedSquares: string[] = [];
  loading = true;
  error: Error | null = null;

  // Lifecycle management
  private readonly destroy$ = new Subject<void>();

  constructor(
    private gameStateService: GameStateService,
    private cdr: ChangeDetectorRef
  ) {}

  ngOnInit(): void {
    this.loadGameState();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  // Public methods
  onSquareClick(square: string): void {
    if (this.disabled) return;

    if (this.selectedSquare === square) {
      this.deselectSquare();
    } else if (this.selectedSquare && this.isLegalMove(square)) {
      this.makeMove(this.selectedSquare, square);
    } else {
      this.selectSquare(square);
    }
  }

  getSquares(): string[] {
    return this.gameState?.getSquares() || [];
  }

  getPieceAt(square: string): any {
    return this.gameState?.getPieceAt(square) || null;
  }

  isSquareSelected(square: string): boolean {
    return this.selectedSquare === square;
  }

  isSquareHighlighted(square: string): boolean {
    return this.highlightedSquares.includes(square);
  }

  // Private methods
  private loadGameState(): void {
    this.gameStateService.getGameState(this.gameId)
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: (state) => {
          this.gameState = state;
          this.loading = false;
          this.error = null;
          this.cdr.detectChanges();
        },
        error: (error) => {
          this.error = error;
          this.loading = false;
          this.errorEvent.emit(error);
          this.cdr.detectChanges();
        }
      });
  }

  private selectSquare(square: string): void {
    this.selectedSquare = square;
    this.highlightedSquares = this.gameState?.getLegalMoves(square) || [];
    this.cdr.detectChanges();
  }

  private deselectSquare(): void {
    this.selectedSquare = null;
    this.highlightedSquares = [];
    this.cdr.detectChanges();
  }

  private isLegalMove(toSquare: string): boolean {
    return this.highlightedSquares.includes(toSquare);
  }

  private makeMove(from: string, to: string): void {
    const move: ChessMove = { from, to };

    this.gameStateService.makeMove(this.gameId, move)
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: (updatedState) => {
          this.gameState = updatedState;
          this.deselectSquare();
          this.moveEvent.emit(move);
          this.cdr.detectChanges();
        },
        error: (error) => {
          this.error = error;
          this.errorEvent.emit(error);
          this.cdr.detectChanges();
        }
      });
  }
}

CSS and Styling Standards

CSS Architecture

/* Base styles - shared/styles/base.css */
:root {
  /* Color palette */
  --color-primary: #646cff;
  --color-secondary: #22c55e;
  --color-background: #ffffff;
  --color-text: #212529;
  --color-border: #dee2e6;

  /* Spacing scale */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;

  /* Typography */
  --font-family-base: -apple-system, BlinkMacSystemFont, sans-serif;
  --font-family-mono: 'JetBrains Mono', monospace;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-normal: 250ms ease;

  /* Z-index scale */
  --z-dropdown: 1000;
  --z-modal: 1050;
  --z-tooltip: 1100;
}

/* Component styles with BEM methodology */
.chess-board {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(8, 1fr);
  border: 2px solid var(--color-border);
  border-radius: 8px;
  overflow: hidden;
  aspect-ratio: 1;
}

.chess-board--disabled {
  pointer-events: none;
  opacity: 0.6;
}

.chess-board__square {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--transition-fast);
  cursor: pointer;
}

.chess-board__square--light {
  background-color: #f0d9b5;
}

.chess-board__square--dark {
  background-color: #b58863;
}

.chess-board__square--selected {
  background-color: var(--color-primary);
}

.chess-board__square--highlighted {
  background-color: rgba(34, 197, 94, 0.3);
}

.chess-board__piece {
  width: 80%;
  height: 80%;
  font-size: 2rem;
  user-select: none;
  transition: transform var(--transition-fast);
}

.chess-board__piece:hover {
  transform: scale(1.1);
}

/* Responsive design */
@media (max-width: 768px) {
  .chess-board {
    max-width: 90vw;
    max-height: 90vw;
  }

  .chess-board__piece {
    font-size: 1.5rem;
  }
}

/* High contrast mode support */
@media (prefers-contrast: high) {
  .chess-board__square--light {
    background-color: #ffffff;
    border: 1px solid #000000;
  }

  .chess-board__square--dark {
    background-color: #000000;
    color: #ffffff;
  }
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  .chess-board__square,
  .chess-board__piece {
    transition: none;
  }
}

SCSS Standards

// Variables and mixins - shared/styles/_variables.scss
$breakpoints: (
  'xs': 0,
  'sm': 576px,
  'md': 768px,
  'lg': 992px,
  'xl': 1200px
);

@mixin respond-to($breakpoint) {
  @if map-has-key($breakpoints, $breakpoint) {
    @media (min-width: map-get($breakpoints, $breakpoint)) {
      @content;
    }
  } @else {
    @warn "Unknown breakpoint: #{$breakpoint}";
  }
}

@mixin hover-effect($property: transform, $value: scale(1.05)) {
  transition: #{$property} var(--transition-fast);

  &:hover:not(:disabled) {
    #{$property}: #{$value};
  }
}

// Component styles
.game-controls {
  display: flex;
  gap: var(--spacing-md);
  padding: var(--spacing-lg);

  @include respond-to('md') {
    flex-direction: row;
    justify-content: center;
  }

  &__button {
    @include hover-effect();

    padding: var(--spacing-sm) var(--spacing-md);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    background: var(--color-background);
    color: var(--color-text);
    cursor: pointer;

    &--primary {
      background: var(--color-primary);
      color: white;
      border-color: var(--color-primary);
    }

    &:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
  }
}

Testing Standards

Unit Test Structure

// chessEngine.test.js
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { ChessEngine } from '../src/chess/ChessEngine';

describe('ChessEngine', () => {
  let engine;

  beforeEach(() => {
    engine = new ChessEngine();
  });

  afterEach(() => {
    engine.cleanup();
  });

  describe('initialization', () => {
    it('should initialize with standard starting position', () => {
      expect(engine.getFEN()).toBe('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
    });

    it('should set current player to white', () => {
      expect(engine.getCurrentPlayer()).toBe('white');
    });
  });

  describe('move validation', () => {
    it('should allow valid pawn moves', () => {
      const move = { from: 'e2', to: 'e4' };
      const result = engine.validateMove(move);

      expect(result.valid).toBe(true);
      expect(result.reason).toBeNull();
    });

    it('should reject invalid moves', () => {
      const move = { from: 'e2', to: 'e5' }; // Two squares from non-starting position
      engine.makeMove({ from: 'e2', to: 'e3' }); // Move pawn first

      const result = engine.validateMove(move);

      expect(result.valid).toBe(false);
      expect(result.reason).toContain('Invalid pawn move');
    });

    it('should handle edge cases gracefully', () => {
      const invalidMoves = [
        null,
        undefined,
        {},
        { from: 'e2' }, // Missing 'to'
        { to: 'e4' }, // Missing 'from'
        { from: 'z9', to: 'e4' }, // Invalid square
      ];

      invalidMoves.forEach((move) => {
        expect(() => engine.validateMove(move)).toThrow();
      });
    });
  });

  describe('game state management', () => {
    it('should track move history', () => {
      engine.makeMove({ from: 'e2', to: 'e4' });
      engine.makeMove({ from: 'e7', to: 'e5' });

      const history = engine.getHistory();

      expect(history).toHaveLength(2);
      expect(history[0]).toMatchObject({
        from: 'e2',
        to: 'e4',
        piece: 'pawn',
        notation: 'e4'
      });
    });
  });
});

// Integration test example
describe('Chess Game Integration', () => {
  it('should play a complete game scenario', async () => {
    const game = new ChessGame();
    await game.initialize();

    // Scholar's mate scenario
    const moves = [
      { from: 'e2', to: 'e4' },
      { from: 'e7', to: 'e5' },
      { from: 'f1', to: 'c4' },
      { from: 'b8', to: 'c6' },
      { from: 'd1', to: 'h5' },
      { from: 'g8', to: 'f6' },
      { from: 'h5', to: 'f7' }
    ];

    for (const move of moves) {
      const result = await game.makeMove(move);
      expect(result.success).toBe(true);
    }

    expect(game.getStatus()).toBe('checkmate');
    expect(game.getWinner()).toBe('white');
  });
});

Documentation Standards

JSDoc Comments

/**
 * @fileoverview Chess engine implementation with move validation and game state management
 * @author Your Name
 * @version 2.1.0
 */

/**
 * Represents a chess game engine with full rule implementation
 * @class
 * @example
 * const engine = new ChessEngine();
 * engine.makeMove({ from: 'e2', to: 'e4' });
 */
class ChessEngine {
  /**
   * Creates a new chess engine instance
   * @param {string} [fen] - Starting position in FEN notation
   * @param {Object} [options] - Configuration options
   * @param {boolean} [options.validateMoves=true] - Whether to validate moves
   * @param {string} [options.variant='standard'] - Chess variant to play
   */
  constructor(fen = STARTING_FEN, options = {}) {
    // Implementation
  }

  /**
   * Validates a chess move according to game rules
   * @param {ChessMove} move - The move to validate
   * @param {string} move.from - Source square (e.g., 'e2')
   * @param {string} move.to - Destination square (e.g., 'e4')
   * @param {string} [move.promotion] - Promotion piece type
   * @returns {MoveValidationResult} Validation result
   * @throws {InvalidMoveError} When move format is invalid
   * @example
   * const result = engine.validateMove({ from: 'e2', to: 'e4' });
   * if (result.valid) {
   *   console.log('Move is legal');
   * }
   */
  validateMove(move) {
    // Implementation
  }

  /**
   * Gets all legal moves for a piece at the given square
   * @param {string} square - Square notation (e.g., 'e2')
   * @returns {string[]} Array of legal destination squares
   * @since 2.0.0
   */
  getLegalMoves(square) {
    // Implementation
  }
}

/**
 * @typedef {Object} ChessMove
 * @property {string} from - Source square
 * @property {string} to - Destination square
 * @property {string} [promotion] - Promotion piece type
 */

/**
 * @typedef {Object} MoveValidationResult
 * @property {boolean} valid - Whether the move is valid
 * @property {string|null} reason - Reason for invalidity
 * @property {ChessMove} [suggestedMove] - Alternative valid move
 */

README Structure

# Chess Showcase

Brief description of the project and its purpose.

## Features

- ✨ Multi-framework implementation (React, Vue, Angular, Vanilla JS)
- 🎮 Complete chess game with all rules
- 🤖 AI opponents with multiple difficulty levels
- 🎨 Customizable themes and responsive design
- 🔄 Real-time multiplayer support

## Quick Start

```bash
# Clone the repository
git clone https://github.com/your-username/chess-showcase.git

# Install dependencies
npm install

# Start development server
npm run dev

Project Structure

chess-showcase/
├── shared/           # Shared code and utilities
├── react-chess/      # React implementation
├── vue-chess/        # Vue implementation
├── angular-chess/    # Angular implementation
└── vanilla-chess/    # Vanilla JavaScript implementation

Development

Prerequisites

  • Node.js 18+
  • npm 8+
  • Git

Setup

  1. Install dependencies: npm install
  2. Copy environment variables: cp .env.example .env
  3. Start development server: npm run dev

Testing

npm run test          # Run all tests
npm run test:unit     # Unit tests only
npm run test:e2e      # End-to-end tests
npm run test:coverage # Coverage report

Contributing

Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.


## Git Standards

### Commit Message Format

type(scope): subject

body

footer


**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `test`: Adding or updating tests
- `chore`: Maintenance tasks

**Examples:**
```bash
feat(chess): add castling move validation

Implement proper castling validation including:
- King and rook haven't moved
- No pieces between king and rook
- King not in check
- King doesn't pass through check

Closes #123

fix(ui): resolve board rendering issue on mobile

The chess board was not displaying correctly on iOS Safari
due to CSS grid compatibility issues.

test(engine): add comprehensive move validation tests

Add test cases for:
- Basic piece movements
- Special moves (castling, en passant)
- Edge cases and error conditions

refactor(api): simplify game state management

Extract game state logic into separate service for better
maintainability and testability.

BREAKING CHANGE: GameState constructor now requires options parameter

Branch Naming

# Feature branches
feature/add-ai-difficulty-levels
feature/implement-chess960

# Bug fix branches
fix/board-rendering-mobile
fix/websocket-connection-issue

# Documentation branches
docs/update-api-reference
docs/add-deployment-guide

# Refactoring branches
refactor/extract-move-validation
refactor/improve-error-handling

Code Quality Tools

ESLint Configuration

{
  "extends": [
    "eslint:recommended",
    "@typescript-eslint/recommended",
    "prettier"
  ],
  "rules": {
    "no-console": "warn",
    "no-debugger": "error",
    "no-unused-vars": "error",
    "prefer-const": "error",
    "no-var": "error",
    "eqeqeq": "error",
    "curly": "error",
    "max-len": ["error", { "code": 100 }],
    "max-depth": ["error", 4],
    "max-params": ["error", 5],
    "complexity": ["error", 10]
  }
}

Prettier Configuration

{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "bracketSpacing": true,
  "arrowParens": "avoid"
}

Next Steps

⚠️ **GitHub.com Fallback** ⚠️