Code Style - RumenDamyanov/js-chess GitHub Wiki
Comprehensive coding standards and conventions for the chess showcase application across all frameworks.
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
- Readability First: Code is written once, read many times
- Consistency: Follow established patterns throughout the codebase
- Simplicity: Prefer simple, clear solutions over clever ones
- Maintainability: Write code that's easy to modify and extend
- Performance: Optimize for clarity first, performance second
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
// 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
}
}
// 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 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
};
}
// 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']);
// 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');
}
}
}
// 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 };
}
<!-- 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>
// 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();
}
});
}
}
/* 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;
}
}
// 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;
}
}
}
// 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');
});
});
/**
* @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
*/
# 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
chess-showcase/
├── shared/ # Shared code and utilities
├── react-chess/ # React implementation
├── vue-chess/ # Vue implementation
├── angular-chess/ # Angular implementation
└── vanilla-chess/ # Vanilla JavaScript implementation
- Node.js 18+
- npm 8+
- Git
- Install dependencies:
npm install
- Copy environment variables:
cp .env.example .env
- Start development server:
npm run dev
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
Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
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
# 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
{
"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]
}
}
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "avoid"
}
- Adding Frameworks - How to add new framework implementations
- Contributing - Contribution guidelines and process
- Testing Guide - Comprehensive testing strategies