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>
528 lines
11 KiB
Markdown
528 lines
11 KiB
Markdown
# Data Models and Structures
|
||
|
||
## Core Data Structures
|
||
|
||
### 1. Square
|
||
|
||
Represents a single square on the chess board.
|
||
|
||
```javascript
|
||
class Square {
|
||
file: number; // 0-7 (a-h)
|
||
rank: number; // 0-7 (1-8)
|
||
color: 'light' | 'dark';
|
||
piece: ChessPiece | null;
|
||
|
||
// Helper methods
|
||
toAlgebraic(): string; // "e4"
|
||
fromAlgebraic(notation: string): Square;
|
||
equals(other: Square): boolean;
|
||
clone(): Square;
|
||
}
|
||
|
||
// Examples
|
||
{ file: 4, rank: 3, color: 'light', piece: null } // e4
|
||
{ file: 0, rank: 0, color: 'dark', piece: WhiteRook } // a1
|
||
```
|
||
|
||
**Algebraic Notation Mapping**:
|
||
- Files: a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7
|
||
- Ranks: 1=0, 2=1, 3=2, 4=3, 5=4, 6=5, 7=6, 8=7
|
||
|
||
---
|
||
|
||
### 2. BoardState
|
||
|
||
Represents the complete chess board configuration.
|
||
|
||
```javascript
|
||
class BoardState {
|
||
// Array representation (primary)
|
||
squares: Square[64];
|
||
|
||
// Alternative: 2D array
|
||
// grid: Square[8][8];
|
||
|
||
// Bitboard representation (optional, for performance)
|
||
bitboards: {
|
||
white: {
|
||
pawns: BigInt,
|
||
knights: BigInt,
|
||
bishops: BigInt,
|
||
rooks: BigInt,
|
||
queens: BigInt,
|
||
king: BigInt,
|
||
all: BigInt
|
||
},
|
||
black: { /* same structure */ },
|
||
occupied: BigInt,
|
||
empty: BigInt
|
||
};
|
||
|
||
// Helper methods
|
||
getSquare(file: number, rank: number): Square;
|
||
getSquareByIndex(index: number): Square;
|
||
getPieceAt(square: Square): ChessPiece | null;
|
||
setPieceAt(square: Square, piece: ChessPiece): void;
|
||
removePieceAt(square: Square): void;
|
||
clone(): BoardState;
|
||
toFEN(): string;
|
||
fromFEN(fen: string): BoardState;
|
||
}
|
||
```
|
||
|
||
**Index Calculation**:
|
||
```javascript
|
||
// Square to index: rank * 8 + file
|
||
// Index to square: { file: index % 8, rank: Math.floor(index / 8) }
|
||
```
|
||
|
||
**Starting Position FEN**:
|
||
```
|
||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
||
```
|
||
|
||
---
|
||
|
||
### 3. PieceType Enumeration
|
||
|
||
```javascript
|
||
const PieceType = {
|
||
PAWN: 'pawn',
|
||
KNIGHT: 'knight',
|
||
BISHOP: 'bishop',
|
||
ROOK: 'rook',
|
||
QUEEN: 'queen',
|
||
KING: 'king'
|
||
};
|
||
|
||
const PieceValue = {
|
||
pawn: 1,
|
||
knight: 3,
|
||
bishop: 3,
|
||
rook: 5,
|
||
queen: 9,
|
||
king: Infinity
|
||
};
|
||
|
||
const PieceNotation = {
|
||
pawn: '', // No letter for pawns
|
||
knight: 'N',
|
||
bishop: 'B',
|
||
rook: 'R',
|
||
queen: 'Q',
|
||
king: 'K'
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 4. Move
|
||
|
||
Represents a single chess move with all metadata.
|
||
|
||
```javascript
|
||
class Move {
|
||
from: Square;
|
||
to: Square;
|
||
piece: PieceType;
|
||
color: 'white' | 'black';
|
||
captured: PieceType | null;
|
||
promotion: PieceType | null;
|
||
|
||
// Special move flags
|
||
isCastling: boolean;
|
||
isEnPassant: boolean;
|
||
isCheck: boolean;
|
||
isCheckmate: boolean;
|
||
|
||
// Metadata
|
||
notation: string; // "Nf3", "exd5", "O-O"
|
||
algebraicNotation: string; // "e2e4"
|
||
timestamp: number;
|
||
moveNumber: number;
|
||
|
||
// Methods
|
||
toSAN(): string; // Standard Algebraic Notation
|
||
toLAN(): string; // Long Algebraic Notation
|
||
toUCI(): string; // Universal Chess Interface
|
||
equals(other: Move): boolean;
|
||
clone(): Move;
|
||
}
|
||
```
|
||
|
||
**Notation Examples**:
|
||
- **SAN**: "Nf3", "e4", "O-O", "Qxe5+", "e8=Q#"
|
||
- **LAN**: "Ng1-f3", "e2-e4", "Qd1xe5+"
|
||
- **UCI**: "e2e4", "e7e5", "e1g1" (castling), "e7e8q" (promotion)
|
||
|
||
---
|
||
|
||
### 5. GameState
|
||
|
||
Complete game state snapshot for state management.
|
||
|
||
```javascript
|
||
class GameState {
|
||
board: BoardState;
|
||
currentPlayer: 'white' | 'black';
|
||
moveNumber: number;
|
||
halfMoveClock: number; // For 50-move rule
|
||
|
||
// Special move tracking
|
||
enPassantSquare: Square | null;
|
||
castlingRights: {
|
||
whiteKingSide: boolean,
|
||
whiteQueenSide: boolean,
|
||
blackKingSide: boolean,
|
||
blackQueenSide: boolean
|
||
};
|
||
|
||
// Game status
|
||
status: GameStatus;
|
||
lastMove: Move | null;
|
||
|
||
// Captured pieces
|
||
capturedPieces: {
|
||
white: PieceType[],
|
||
black: PieceType[]
|
||
};
|
||
|
||
// Methods
|
||
toFEN(): string;
|
||
fromFEN(fen: string): GameState;
|
||
clone(): GameState;
|
||
hash(): string; // For position repetition
|
||
equals(other: GameState): boolean;
|
||
}
|
||
```
|
||
|
||
**FEN Format**:
|
||
```
|
||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
||
│ │ │ │ │ │
|
||
│ │ │ │ │ └─ Full move number
|
||
│ │ │ │ └─── Halfmove clock
|
||
│ │ │ └───── En passant square
|
||
│ │ └────────── Castling rights
|
||
│ └──────────── Active player
|
||
└──────────────────────────────────────────────────────── Board position
|
||
```
|
||
|
||
---
|
||
|
||
### 6. GameStatus Enumeration
|
||
|
||
```javascript
|
||
const GameStatus = {
|
||
ACTIVE: 'active',
|
||
CHECK: 'check',
|
||
CHECKMATE: 'checkmate',
|
||
STALEMATE: 'stalemate',
|
||
DRAW_50_MOVE: 'draw-50-move',
|
||
DRAW_REPETITION: 'draw-repetition',
|
||
DRAW_INSUFFICIENT: 'draw-insufficient-material',
|
||
DRAW_AGREEMENT: 'draw-agreement',
|
||
RESIGNATION: 'resignation'
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 7. GameConfiguration
|
||
|
||
Settings and options for game initialization.
|
||
|
||
```javascript
|
||
class GameConfig {
|
||
mode: 'pvp' | 'pva' | 'ava';
|
||
timeControl: TimeControl | null;
|
||
playerWhite: Player;
|
||
playerBlack: Player;
|
||
aiDifficulty: number; // 1-10 for AI opponent
|
||
theme: string;
|
||
soundEnabled: boolean;
|
||
animationSpeed: number; // ms for animations
|
||
autoSave: boolean;
|
||
legalMovesHighlight: boolean;
|
||
dragAndDrop: boolean;
|
||
}
|
||
|
||
class TimeControl {
|
||
type: 'none' | 'classical' | 'rapid' | 'blitz' | 'bullet';
|
||
initialTime: number; // seconds
|
||
increment: number; // seconds per move
|
||
whiteTime: number;
|
||
blackTime: number;
|
||
}
|
||
|
||
class Player {
|
||
name: string;
|
||
type: 'human' | 'ai';
|
||
color: 'white' | 'black';
|
||
elo: number | null;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 8. MoveHistory
|
||
|
||
Structure for tracking complete game history.
|
||
|
||
```javascript
|
||
class MoveHistory {
|
||
moves: Move[];
|
||
positions: string[]; // FEN strings for repetition detection
|
||
currentIndex: number;
|
||
startingPosition: string; // Initial FEN
|
||
|
||
// PGN metadata
|
||
metadata: {
|
||
event: string,
|
||
site: string,
|
||
date: string,
|
||
round: string,
|
||
white: string,
|
||
black: string,
|
||
result: string
|
||
};
|
||
|
||
// Methods
|
||
addMove(move: Move, position: string): void;
|
||
getMove(index: number): Move;
|
||
getAllMoves(): Move[];
|
||
undo(): Move | null;
|
||
redo(): Move | null;
|
||
canUndo(): boolean;
|
||
canRedo(): boolean;
|
||
clear(): void;
|
||
toPGN(): string;
|
||
fromPGN(pgn: string): MoveHistory;
|
||
toJSON(): string;
|
||
fromJSON(json: string): MoveHistory;
|
||
}
|
||
```
|
||
|
||
**PGN Format Example**:
|
||
```
|
||
[Event "Casual Game"]
|
||
[Site "Chess App"]
|
||
[Date "2025.11.22"]
|
||
[Round "1"]
|
||
[White "Player 1"]
|
||
[Black "Player 2"]
|
||
[Result "1-0"]
|
||
|
||
1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 1-0
|
||
```
|
||
|
||
---
|
||
|
||
### 9. Event System
|
||
|
||
Event definitions for component communication.
|
||
|
||
```javascript
|
||
class GameEvent {
|
||
type: EventType;
|
||
payload: any;
|
||
timestamp: number;
|
||
source: string;
|
||
}
|
||
|
||
const EventType = {
|
||
// Board events
|
||
SQUARE_CLICKED: 'square-clicked',
|
||
PIECE_SELECTED: 'piece-selected',
|
||
PIECE_MOVED: 'piece-moved',
|
||
PIECE_CAPTURED: 'piece-captured',
|
||
PIECE_PROMOTED: 'piece-promoted',
|
||
|
||
// Game events
|
||
GAME_STARTED: 'game-started',
|
||
GAME_OVER: 'game-over',
|
||
TURN_CHANGED: 'turn-changed',
|
||
CHECK_DETECTED: 'check-detected',
|
||
MOVE_EXECUTED: 'move-executed',
|
||
MOVE_UNDONE: 'move-undone',
|
||
MOVE_REDONE: 'move-redone',
|
||
|
||
// UI events
|
||
THEME_CHANGED: 'theme-changed',
|
||
SETTINGS_UPDATED: 'settings-updated',
|
||
|
||
// AI events
|
||
AI_THINKING: 'ai-thinking',
|
||
AI_MOVE_READY: 'ai-move-ready',
|
||
|
||
// Error events
|
||
INVALID_MOVE: 'invalid-move',
|
||
VALIDATION_FAILED: 'validation-failed'
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 10. Bitboard Representation (Advanced)
|
||
|
||
For performance-critical operations and AI.
|
||
|
||
```javascript
|
||
class Bitboard {
|
||
value: BigInt; // 64-bit integer representing board
|
||
|
||
// Bitwise operations
|
||
setBit(square: Square): void;
|
||
clearBit(square: Square): void;
|
||
toggleBit(square: Square): void;
|
||
testBit(square: Square): boolean;
|
||
popCount(): number; // Count set bits
|
||
|
||
// Board operations
|
||
and(other: Bitboard): Bitboard;
|
||
or(other: Bitboard): Bitboard;
|
||
xor(other: Bitboard): Bitboard;
|
||
not(): Bitboard;
|
||
shift(direction: number): Bitboard;
|
||
|
||
// Move generation helpers
|
||
northOne(): Bitboard;
|
||
southOne(): Bitboard;
|
||
eastOne(): Bitboard;
|
||
westOne(): Bitboard;
|
||
getSetSquares(): Square[];
|
||
}
|
||
```
|
||
|
||
**Bitboard Example**:
|
||
```
|
||
Bit 0 = a1, Bit 1 = b1, ..., Bit 7 = h1
|
||
Bit 8 = a2, Bit 9 = b2, ..., Bit 63 = h8
|
||
|
||
White pawns starting position:
|
||
0x000000000000FF00 (bits 8-15 set)
|
||
|
||
Black pawns starting position:
|
||
0x00FF000000000000 (bits 48-55 set)
|
||
```
|
||
|
||
---
|
||
|
||
## Data Flow Diagrams
|
||
|
||
### Move Execution Flow
|
||
|
||
```
|
||
User Input
|
||
↓
|
||
UI captures click/drag
|
||
↓
|
||
GameController validates selection
|
||
↓
|
||
MoveValidator checks legality
|
||
↓
|
||
GameEngine executes move
|
||
↓
|
||
BoardState updated
|
||
↓
|
||
GameHistory records move
|
||
↓
|
||
UI renders new state
|
||
↓
|
||
Turn switches
|
||
```
|
||
|
||
### State Update Flow
|
||
|
||
```
|
||
Move → GameState (immutable) → New GameState
|
||
↓ ↓
|
||
└─────── History records both ──────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Serialization Formats
|
||
|
||
### JSON Save Format
|
||
|
||
```json
|
||
{
|
||
"version": "1.0.0",
|
||
"timestamp": 1700000000000,
|
||
"gameState": {
|
||
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||
"moveHistory": [
|
||
{"from": "e2", "to": "e4", "notation": "e4"},
|
||
{"from": "e7", "to": "e5", "notation": "e5"}
|
||
],
|
||
"capturedPieces": {"white": [], "black": []},
|
||
"timeControl": {
|
||
"whiteTime": 600,
|
||
"blackTime": 595
|
||
}
|
||
},
|
||
"config": {
|
||
"mode": "pvp",
|
||
"theme": "classic"
|
||
}
|
||
}
|
||
```
|
||
|
||
### Local Storage Keys
|
||
|
||
```javascript
|
||
const StorageKeys = {
|
||
CURRENT_GAME: 'chess-current-game',
|
||
SAVED_GAMES: 'chess-saved-games',
|
||
SETTINGS: 'chess-settings',
|
||
THEME: 'chess-theme',
|
||
GAME_HISTORY: 'chess-game-history'
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## Performance Considerations
|
||
|
||
### Memory Optimization
|
||
|
||
- **Board Representation**: Array[64] uses ~2KB per position
|
||
- **Move History**: Average game ~80 moves = ~40KB
|
||
- **Bitboards**: Enable compact representation (8 bytes per piece type)
|
||
|
||
### Caching Strategy
|
||
|
||
```javascript
|
||
class CacheManager {
|
||
moveValidationCache: Map<string, boolean>;
|
||
moveGenerationCache: Map<string, Move[]>;
|
||
evaluationCache: Map<string, number>;
|
||
|
||
maxSize: number = 10000;
|
||
|
||
set(key: string, value: any): void;
|
||
get(key: string): any | null;
|
||
clear(): void;
|
||
prune(): void; // Remove old entries
|
||
}
|
||
```
|
||
|
||
### Position Hashing
|
||
|
||
```javascript
|
||
// Zobrist hashing for position identification
|
||
class ZobristHash {
|
||
pieceKeys: BigInt[64][12]; // Square × PieceType
|
||
castlingKeys: BigInt[4];
|
||
enPassantKeys: BigInt[8];
|
||
sideToMoveKey: BigInt;
|
||
|
||
hash(gameState: GameState): BigInt;
|
||
updateHash(hash: BigInt, move: Move): BigInt;
|
||
}
|
||
```
|
||
|
||
This data model design ensures efficient state management, easy serialization, and optimal performance for both UI rendering and AI computation.
|