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>
899 lines
20 KiB
Markdown
899 lines
20 KiB
Markdown
# Chess Rules Reference
|
|
|
|
## Table of Contents
|
|
|
|
1. [Game Objective](#game-objective)
|
|
2. [Board Setup](#board-setup)
|
|
3. [Piece Movement](#piece-movement)
|
|
4. [Special Moves](#special-moves)
|
|
5. [Check and Checkmate](#check-and-checkmate)
|
|
6. [Draw Conditions](#draw-conditions)
|
|
7. [Chess Notation](#chess-notation)
|
|
8. [Rule Edge Cases](#rule-edge-cases)
|
|
|
|
---
|
|
|
|
## Game Objective
|
|
|
|
Chess is a two-player strategy game where the objective is to **checkmate** the opponent's king.
|
|
|
|
- **Checkmate**: The king is under attack (in check) and cannot escape
|
|
- **Win Conditions**: Checkmate or opponent resignation
|
|
- **Draw Conditions**: Stalemate, insufficient material, threefold repetition, fifty-move rule, or agreement
|
|
|
|
---
|
|
|
|
## Board Setup
|
|
|
|
### Board Orientation
|
|
|
|
- 8x8 grid with alternating light and dark squares
|
|
- Bottom-right square from each player's perspective is a light square
|
|
- Ranks (rows) numbered 1-8, files (columns) labeled a-h
|
|
- White pieces start on ranks 1-2, black on ranks 7-8
|
|
|
|
### Initial Position
|
|
|
|
```
|
|
a b c d e f g h
|
|
8 ♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ 8
|
|
7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ 7
|
|
6 · · · · · · · · 6
|
|
5 · · · · · · · · 5
|
|
4 · · · · · · · · 4
|
|
3 · · · · · · · · 3
|
|
2 ♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ 2
|
|
1 ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖ 1
|
|
a b c d e f g h
|
|
```
|
|
|
|
**FEN Notation:** `rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1`
|
|
|
|
### Piece Placement
|
|
|
|
**White (bottom):**
|
|
- a1, h1: Rooks (♖)
|
|
- b1, g1: Knights (♘)
|
|
- c1, f1: Bishops (♗)
|
|
- d1: Queen (♕)
|
|
- e1: King (♔)
|
|
- a2-h2: Pawns (♙)
|
|
|
|
**Black (top):**
|
|
- a8, h8: Rooks (♜)
|
|
- b8, g8: Knights (♞)
|
|
- c8, f8: Bishops (♝)
|
|
- d8: Queen (♛)
|
|
- e8: King (♚)
|
|
- a7-h7: Pawns (♟)
|
|
|
|
**Mnemonic:** "Queen on her own color" (white queen on light square, black queen on dark square)
|
|
|
|
---
|
|
|
|
## Piece Movement
|
|
|
|
### Pawn (♙ ♟)
|
|
|
|
**Basic Movement:**
|
|
- Moves forward one square
|
|
- From starting position, can move forward two squares
|
|
- Captures diagonally forward one square
|
|
- Cannot move backward
|
|
|
|
**Special Rules:**
|
|
- Can only capture diagonally (not straight ahead)
|
|
- Blocked if square directly ahead is occupied
|
|
- See [En Passant](#en-passant) and [Promotion](#pawn-promotion)
|
|
|
|
**Diagram:**
|
|
```
|
|
· · · · · · · ·
|
|
· · ♟ · ♟ · · · ← Can capture here
|
|
· · · ♙ · · · · ← Pawn position
|
|
· · · ↑ · · · · ← Can move here
|
|
· · · ↑ · · · · ← Can move here (if first move)
|
|
```
|
|
|
|
**Implementation Note:**
|
|
- White pawns move up (decreasing row), black pawns move down (increasing row)
|
|
- Starting rank: white on row 6, black on row 1
|
|
- Promotion rank: white on row 0, black on row 7
|
|
|
|
---
|
|
|
|
### Knight (♘ ♞)
|
|
|
|
**Movement Pattern:**
|
|
- L-shaped: 2 squares in one direction, 1 square perpendicular
|
|
- Can jump over other pieces
|
|
- 8 possible moves maximum (if not blocked)
|
|
|
|
**Movement Offsets:**
|
|
```
|
|
(-2, -1) (-2, +1)
|
|
↖ ↗
|
|
(-1,-2) ♘ (-1,+2)
|
|
↙ ↘
|
|
(+1,-2) (+1,+2)
|
|
↖ ↗
|
|
(+2, -1) (+2, +1)
|
|
```
|
|
|
|
**Diagram:**
|
|
```
|
|
· · X · X · · ·
|
|
· X · · · X · ·
|
|
· · · ♘ · · · ·
|
|
· X · · · X · ·
|
|
· · X · X · · ·
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
const knightMoves = [
|
|
[-2, -1], [-2, 1], [-1, -2], [-1, 2],
|
|
[1, -2], [1, 2], [2, -1], [2, 1]
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
### Bishop (♗ ♝)
|
|
|
|
**Movement Pattern:**
|
|
- Any number of squares diagonally
|
|
- Cannot jump over pieces
|
|
- Stays on same color squares entire game
|
|
|
|
**Diagram:**
|
|
```
|
|
X · · · · · · ·
|
|
· X · · · · · X
|
|
· · X · · · X ·
|
|
· · · ♗ · X · ·
|
|
· · · · X · · ·
|
|
· · · X · · · ·
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
const bishopDirections = [
|
|
[-1, -1], // up-left
|
|
[-1, +1], // up-right
|
|
[+1, -1], // down-left
|
|
[+1, +1] // down-right
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
### Rook (♖ ♜)
|
|
|
|
**Movement Pattern:**
|
|
- Any number of squares horizontally or vertically
|
|
- Cannot jump over pieces
|
|
- Involved in castling (see [Castling](#castling))
|
|
|
|
**Diagram:**
|
|
```
|
|
· · · X · · · ·
|
|
· · · X · · · ·
|
|
· · · X · · · ·
|
|
X X X ♖ X X X X
|
|
· · · X · · · ·
|
|
· · · X · · · ·
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
const rookDirections = [
|
|
[-1, 0], // up
|
|
[+1, 0], // down
|
|
[0, -1], // left
|
|
[0, +1] // right
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
### Queen (♕ ♛)
|
|
|
|
**Movement Pattern:**
|
|
- Combines rook and bishop movement
|
|
- Any number of squares in any direction
|
|
- Cannot jump over pieces
|
|
- Most powerful piece
|
|
|
|
**Diagram:**
|
|
```
|
|
X · · X · · · X
|
|
· X · X · · X ·
|
|
· · X X · X · ·
|
|
X X X ♕ X X X X
|
|
· · X X · X · ·
|
|
· X · X · · X ·
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
const queenDirections = [
|
|
[-1, -1], [-1, 0], [-1, +1],
|
|
[0, -1], [0, +1],
|
|
[+1, -1], [+1, 0], [+1, +1]
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
### King (♔ ♚)
|
|
|
|
**Movement Pattern:**
|
|
- One square in any direction
|
|
- Cannot move into check
|
|
- Involved in castling (see [Castling](#castling))
|
|
|
|
**Diagram:**
|
|
```
|
|
· · · · · · · ·
|
|
· · X X X · · ·
|
|
· · X ♔ X · · ·
|
|
· · X X X · · ·
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
const kingMoves = [
|
|
[-1, -1], [-1, 0], [-1, +1],
|
|
[0, -1], [0, +1],
|
|
[+1, -1], [+1, 0], [+1, +1]
|
|
];
|
|
```
|
|
|
|
**Critical Rules:**
|
|
- Cannot move into check (square attacked by opponent)
|
|
- Cannot castle out of, through, or into check
|
|
- Most important piece (game ends if checkmated)
|
|
|
|
---
|
|
|
|
## Special Moves
|
|
|
|
### Castling
|
|
|
|
**Purpose:** Protect king and activate rook
|
|
|
|
**Types:**
|
|
1. **Kingside Castling (O-O)**: King moves two squares toward h-file rook
|
|
2. **Queenside Castling (O-O-O)**: King moves two squares toward a-file rook
|
|
|
|
**Conditions (ALL must be met):**
|
|
1. Neither king nor rook has moved previously
|
|
2. No pieces between king and rook
|
|
3. King is not in check
|
|
4. King does not pass through check
|
|
5. King does not end up in check
|
|
|
|
**Kingside Castling (White):**
|
|
```
|
|
Before: · · · · ♔ · · ♖
|
|
After: · · · · · ♖ ♔ ·
|
|
e1 → g1, h1 → f1
|
|
```
|
|
|
|
**Queenside Castling (White):**
|
|
```
|
|
Before: ♖ · · · ♔ · · ·
|
|
After: · · ♔ ♖ · · · ·
|
|
e1 → c1, a1 → d1
|
|
```
|
|
|
|
**Implementation Logic:**
|
|
```javascript
|
|
function canCastle(king, rook, board) {
|
|
// 1. Check if pieces have moved
|
|
if (king.hasMoved || rook.hasMoved) return false;
|
|
|
|
// 2. Check if path is clear
|
|
const [minCol, maxCol] = [
|
|
Math.min(king.col, rook.col),
|
|
Math.max(king.col, rook.col)
|
|
];
|
|
for (let col = minCol + 1; col < maxCol; col++) {
|
|
if (board.getPiece(king.row, col)) return false;
|
|
}
|
|
|
|
// 3. Check if king is in check
|
|
if (isKingInCheck(board, king.color)) return false;
|
|
|
|
// 4. Check if king passes through check
|
|
const direction = rook.col > king.col ? 1 : -1;
|
|
for (let i = 1; i <= 2; i++) {
|
|
const testCol = king.col + (direction * i);
|
|
if (isSquareUnderAttack(board, king.row, testCol, opponentColor)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### En Passant
|
|
|
|
**Purpose:** Prevent pawns from avoiding capture by double-stepping
|
|
|
|
**Condition:**
|
|
- Opponent pawn moves two squares from starting position
|
|
- Lands beside your pawn (same rank)
|
|
- You can capture it as if it moved only one square
|
|
- **Must be done immediately (next move only)**
|
|
|
|
**Diagram:**
|
|
```
|
|
Before: After capture:
|
|
· · · · · · · · · · · · · · · ·
|
|
♟ · ♟ · · · · · · · ♟ · · · · ·
|
|
· ♙ · · · · · · · · ♙ · · · · · ← White pawn captures
|
|
♙ · · · · · · · · · · · · · · ·
|
|
```
|
|
|
|
**FEN Notation:**
|
|
- En passant target square recorded in FEN
|
|
- Example: `...w KQkq e6...` (e6 is en passant target)
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function canEnPassant(pawn, targetCol, gameState) {
|
|
// Must be on correct rank
|
|
const correctRank = pawn.color === 'white' ? 3 : 4;
|
|
if (pawn.row !== correctRank) return false;
|
|
|
|
// Check last move
|
|
const lastMove = gameState.getLastMove();
|
|
if (!lastMove || lastMove.piece.type !== 'pawn') return false;
|
|
|
|
// Must have moved two squares
|
|
const moveDistance = Math.abs(lastMove.to.row - lastMove.from.row);
|
|
if (moveDistance !== 2) return false;
|
|
|
|
// Must be adjacent
|
|
return Math.abs(pawn.col - targetCol) === 1 &&
|
|
lastMove.to.col === targetCol &&
|
|
lastMove.to.row === pawn.row;
|
|
}
|
|
|
|
function executeEnPassant(board, pawn, targetRow, targetCol) {
|
|
// Remove captured pawn
|
|
const capturedPawn = board.getPiece(pawn.row, targetCol);
|
|
board.setPiece(pawn.row, targetCol, null);
|
|
|
|
// Move attacking pawn
|
|
board.movePiece(pawn.row, pawn.col, targetRow, targetCol);
|
|
|
|
return capturedPawn;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Pawn Promotion
|
|
|
|
**Condition:** Pawn reaches the opposite end of the board (rank 8 for white, rank 1 for black)
|
|
|
|
**Options:** Can promote to:
|
|
- Queen (most common)
|
|
- Rook
|
|
- Bishop
|
|
- Knight
|
|
- **Cannot promote to King or Pawn**
|
|
|
|
**Notation:**
|
|
- `e8=Q` - Pawn to e8, promotes to queen
|
|
- `axb8=N+` - Pawn captures on b8, promotes to knight with check
|
|
|
|
**Diagram:**
|
|
```
|
|
♟ · · · · · · · ← Black pawn about to promote
|
|
· · · · · · · ·
|
|
...
|
|
♙ · · · · · · · ← White pawn about to promote
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function canPromote(pawn) {
|
|
const promotionRank = pawn.color === 'white' ? 0 : 7;
|
|
return pawn.row === promotionRank;
|
|
}
|
|
|
|
function promote(board, pawn, pieceType) {
|
|
// pieceType: 'queen', 'rook', 'bishop', 'knight'
|
|
const PieceClass = getPieceClass(pieceType);
|
|
const newPiece = new PieceClass(pawn.color, pawn.position);
|
|
board.setPiece(pawn.row, pawn.col, newPiece);
|
|
return newPiece;
|
|
}
|
|
```
|
|
|
|
**UI Consideration:** Show dialog for player to choose promotion piece
|
|
|
|
---
|
|
|
|
## Check and Checkmate
|
|
|
|
### Check
|
|
|
|
**Definition:** King is under direct attack by opponent piece
|
|
|
|
**Rules:**
|
|
- Player in check **must** get out of check
|
|
- Cannot make any move that leaves king in check
|
|
- Must respond with one of three options:
|
|
1. Move king to safe square
|
|
2. Block the attack
|
|
3. Capture the attacking piece
|
|
|
|
**Notation:** Add `+` to move notation (e.g., `Qh5+`)
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function isKingInCheck(board, color) {
|
|
// Find king position
|
|
const kingPos = findKing(board, color);
|
|
if (!kingPos) return false; // Should never happen
|
|
|
|
// Check if any opponent piece attacks king square
|
|
for (let row = 0; row < 8; row++) {
|
|
for (let col = 0; col < 8; col++) {
|
|
const piece = board.getPiece(row, col);
|
|
if (piece && piece.color !== color) {
|
|
const moves = piece.getValidMoves(board);
|
|
if (moves.some(m => m.row === kingPos.row && m.col === kingPos.col)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Checkmate
|
|
|
|
**Definition:** King is in check and cannot escape
|
|
|
|
**Conditions:**
|
|
1. King is in check
|
|
2. King cannot move to safe square
|
|
3. No piece can block the attack
|
|
4. Attacking piece cannot be captured
|
|
|
|
**Notation:** Add `#` to move notation (e.g., `Qf7#`)
|
|
|
|
**Result:** Game ends, attacking player wins
|
|
|
|
**Famous Checkmate Patterns:**
|
|
|
|
**Scholar's Mate (4 moves):**
|
|
```
|
|
1. e4 e5
|
|
2. Bc4 Nc6
|
|
3. Qh5 Nf6??
|
|
4. Qxf7# (checkmate)
|
|
```
|
|
|
|
**Back Rank Mate:**
|
|
```
|
|
a b c d e f g h
|
|
8 · · · · · ♜ ♚ · 8 ← King trapped by own pawns
|
|
7 · · · · · ♟ ♟ ♟ 7
|
|
```
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function isCheckmate(board, color) {
|
|
// Must be in check
|
|
if (!isKingInCheck(board, color)) {
|
|
return false;
|
|
}
|
|
|
|
// Check if any legal move exists
|
|
for (let row = 0; row < 8; row++) {
|
|
for (let col = 0; col < 8; col++) {
|
|
const piece = board.getPiece(row, col);
|
|
if (piece && piece.color === color) {
|
|
const moves = piece.getValidMoves(board);
|
|
|
|
for (const move of moves) {
|
|
// Simulate move
|
|
const testBoard = board.clone();
|
|
testBoard.movePiece(row, col, move.row, move.col);
|
|
|
|
// If king no longer in check, not checkmate
|
|
if (!isKingInCheck(testBoard, color)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// No legal moves available
|
|
return true;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Draw Conditions
|
|
|
|
### Stalemate
|
|
|
|
**Definition:** Player has no legal moves but is NOT in check
|
|
|
|
**Result:** Game is a draw
|
|
|
|
**Diagram:**
|
|
```
|
|
a b c d e f g h
|
|
8 · · · · · · ♔ · 8
|
|
7 · · · · · ♕ · · 7
|
|
6 · · · · · · · ♚ 6 ← Black king, not in check
|
|
```
|
|
Black to move has no legal moves → **Stalemate**
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function isStalemate(board, color) {
|
|
// Must NOT be in check
|
|
if (isKingInCheck(board, color)) {
|
|
return false;
|
|
}
|
|
|
|
// But has no legal moves
|
|
return !hasAnyLegalMove(board, color);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Insufficient Material
|
|
|
|
**Definition:** Neither player has enough pieces to force checkmate
|
|
|
|
**Automatic Draw Conditions:**
|
|
- King vs King
|
|
- King + Bishop vs King
|
|
- King + Knight vs King
|
|
- King + Bishop vs King + Bishop (same color squares)
|
|
|
|
**Not Automatic Draw (checkmate still possible):**
|
|
- King + Rook vs King
|
|
- King + Queen vs King
|
|
- King + Pawn vs King
|
|
- King + 2 Knights vs King (very rare, but possible)
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function isInsufficientMaterial(board) {
|
|
const pieces = getAllPieces(board);
|
|
|
|
// King vs King
|
|
if (pieces.length === 2) return true;
|
|
|
|
// King + Minor piece vs King
|
|
if (pieces.length === 3) {
|
|
return pieces.some(p => p.type === 'bishop' || p.type === 'knight');
|
|
}
|
|
|
|
// King + Bishop vs King + Bishop (same color)
|
|
if (pieces.length === 4) {
|
|
const bishops = pieces.filter(p => p.type === 'bishop');
|
|
if (bishops.length === 2) {
|
|
return (bishops[0].position.row + bishops[0].position.col) % 2 ===
|
|
(bishops[1].position.row + bishops[1].position.col) % 2;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Threefold Repetition
|
|
|
|
**Definition:** Same position occurs three times (not necessarily consecutive)
|
|
|
|
**Criteria for "same position":**
|
|
- Same pieces on same squares
|
|
- Same player to move
|
|
- Same castling rights
|
|
- Same en passant availability
|
|
|
|
**Claim:** Player can claim draw when position repeats third time
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function isThreefoldRepetition(gameState) {
|
|
const currentFEN = gameState.toFEN();
|
|
const fenHistory = gameState.moveHistory.map(m => m.fen);
|
|
|
|
let count = 0;
|
|
for (const fen of fenHistory) {
|
|
if (fen === currentFEN) {
|
|
count++;
|
|
if (count >= 3) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Fifty-Move Rule
|
|
|
|
**Definition:** 50 consecutive moves by each player with no pawn move or capture
|
|
|
|
**Counter:** Reset to 0 when:
|
|
- Any pawn moves
|
|
- Any piece is captured
|
|
|
|
**Claim:** Player can claim draw after 50 moves
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function isFiftyMoveRule(gameState) {
|
|
return gameState.halfMoveClock >= 100; // 50 moves per player
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Draw by Agreement
|
|
|
|
**Process:**
|
|
1. Player offers draw
|
|
2. Opponent can accept or decline
|
|
3. If accepted, game ends in draw
|
|
|
|
**Notation:** `½-½` in PGN
|
|
|
|
---
|
|
|
|
## Chess Notation
|
|
|
|
### Algebraic Notation (Standard)
|
|
|
|
**Format:** `[Piece][From Square][Capture][To Square][Promotion][Check/Checkmate]`
|
|
|
|
**Pieces:**
|
|
- K = King
|
|
- Q = Queen
|
|
- R = Rook
|
|
- B = Bishop
|
|
- N = Knight
|
|
- (no letter) = Pawn
|
|
|
|
**Examples:**
|
|
- `e4` - Pawn to e4
|
|
- `Nf3` - Knight to f3
|
|
- `Bxc6` - Bishop captures on c6
|
|
- `Qh5+` - Queen to h5, check
|
|
- `e8=Q#` - Pawn to e8, promotes to queen, checkmate
|
|
- `O-O` - Kingside castling
|
|
- `O-O-O` - Queenside castling
|
|
- `Nbd7` - Knight from b-file to d7 (disambiguation)
|
|
|
|
**Disambiguation:**
|
|
When two pieces of same type can move to same square:
|
|
- Add file letter: `Nbd7` (knight from b-file)
|
|
- Add rank number: `R1e2` (rook from rank 1)
|
|
- Add both if needed: `Qh4e1`
|
|
|
|
---
|
|
|
|
### FEN (Forsyth-Edwards Notation)
|
|
|
|
**Purpose:** Represent board position in single string
|
|
|
|
**Format:** `[Position] [Turn] [Castling] [En Passant] [Halfmove] [Fullmove]`
|
|
|
|
**Example:**
|
|
```
|
|
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
|
```
|
|
|
|
**Components:**
|
|
|
|
1. **Position** (ranks 8-1, separated by `/`):
|
|
- Lowercase = black pieces
|
|
- Uppercase = white pieces
|
|
- Numbers = empty squares
|
|
- `/` = next rank
|
|
|
|
2. **Turn**:
|
|
- `w` = white to move
|
|
- `b` = black to move
|
|
|
|
3. **Castling Rights**:
|
|
- `K` = white kingside
|
|
- `Q` = white queenside
|
|
- `k` = black kingside
|
|
- `q` = black queenside
|
|
- `-` = no castling available
|
|
|
|
4. **En Passant**:
|
|
- Target square (e.g., `e3`)
|
|
- `-` if not available
|
|
|
|
5. **Halfmove Clock**:
|
|
- Moves since last capture/pawn move
|
|
|
|
6. **Fullmove Number**:
|
|
- Increments after black's move
|
|
|
|
**Implementation:**
|
|
```javascript
|
|
function toFEN(board, gameState, currentTurn) {
|
|
let fen = '';
|
|
|
|
// Position
|
|
for (let row = 0; row < 8; row++) {
|
|
let emptyCount = 0;
|
|
for (let col = 0; col < 8; col++) {
|
|
const piece = board.getPiece(row, col);
|
|
if (piece) {
|
|
if (emptyCount > 0) {
|
|
fen += emptyCount;
|
|
emptyCount = 0;
|
|
}
|
|
fen += piece.toFENChar();
|
|
} else {
|
|
emptyCount++;
|
|
}
|
|
}
|
|
if (emptyCount > 0) fen += emptyCount;
|
|
if (row < 7) fen += '/';
|
|
}
|
|
|
|
// Turn
|
|
fen += ` ${currentTurn[0]}`;
|
|
|
|
// Castling
|
|
fen += ` ${gameState.castlingRights || '-'}`;
|
|
|
|
// En passant
|
|
fen += ` ${gameState.enPassantTarget || '-'}`;
|
|
|
|
// Clocks
|
|
fen += ` ${gameState.halfMoveClock} ${gameState.fullMoveNumber}`;
|
|
|
|
return fen;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### PGN (Portable Game Notation)
|
|
|
|
**Purpose:** Record complete game with metadata
|
|
|
|
**Format:**
|
|
```
|
|
[Event "Casual Game"]
|
|
[Site "Online"]
|
|
[Date "2025.01.22"]
|
|
[Round "1"]
|
|
[White "Player 1"]
|
|
[Black "Player 2"]
|
|
[Result "1-0"]
|
|
|
|
1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Ba4 Nf6 5. O-O Be7
|
|
6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 1-0
|
|
```
|
|
|
|
**Results:**
|
|
- `1-0` - White wins
|
|
- `0-1` - Black wins
|
|
- `1/2-1/2` - Draw
|
|
- `*` - Game in progress
|
|
|
|
---
|
|
|
|
## Rule Edge Cases
|
|
|
|
### Illegal Positions
|
|
|
|
**Positions that cannot occur:**
|
|
- Both kings in check simultaneously
|
|
- Pawn on rank 1 or 8 (must have promoted)
|
|
- More than 8 pawns of one color
|
|
- More than 16 pieces of one color total
|
|
- Castling when king/rook has moved
|
|
|
|
### Ambiguous Captures
|
|
|
|
When multiple pieces can capture same square:
|
|
```
|
|
Raxe1 // Rook from a-file captures
|
|
R1xe1 // Rook from rank 1 captures
|
|
Qh4e1 // Queen from h4 captures (needs both)
|
|
```
|
|
|
|
### Pawn Captures and Files
|
|
|
|
Pawn captures include file letter:
|
|
```
|
|
exd5 // Pawn from e-file captures on d5
|
|
e4 // Pawn moves to e4 (no capture)
|
|
```
|
|
|
|
### Check and Illegal Moves
|
|
|
|
**Invalid moves:**
|
|
- Moving into check
|
|
- Not responding to check
|
|
- Exposing own king to check
|
|
|
|
All must be prevented in implementation!
|
|
|
|
---
|
|
|
|
## Implementation Checklist
|
|
|
|
Use this checklist to validate your chess implementation:
|
|
|
|
### Basic Movement
|
|
- [ ] All pieces move according to rules
|
|
- [ ] Pieces cannot move through others (except knight)
|
|
- [ ] Pieces cannot capture own color
|
|
- [ ] Pawns move forward only
|
|
- [ ] Pawns capture diagonally
|
|
|
|
### Special Moves
|
|
- [ ] Pawn double-move from start
|
|
- [ ] En passant capture
|
|
- [ ] En passant expires after one turn
|
|
- [ ] Castling kingside
|
|
- [ ] Castling queenside
|
|
- [ ] Castling prevented by moved pieces
|
|
- [ ] Cannot castle through check
|
|
- [ ] Cannot castle in/out of check
|
|
- [ ] Pawn promotion
|
|
- [ ] Pawn promotion piece selection
|
|
|
|
### Check/Checkmate
|
|
- [ ] Detect when king is in check
|
|
- [ ] Prevent moves that leave king in check
|
|
- [ ] Force response to check
|
|
- [ ] Detect checkmate
|
|
- [ ] Detect stalemate
|
|
|
|
### Draw Conditions
|
|
- [ ] Insufficient material
|
|
- [ ] Threefold repetition
|
|
- [ ] Fifty-move rule
|
|
- [ ] Draw by agreement
|
|
|
|
### Notation
|
|
- [ ] FEN export
|
|
- [ ] FEN import
|
|
- [ ] PGN export
|
|
- [ ] Algebraic notation for moves
|
|
- [ ] Move disambiguation
|
|
|
|
---
|
|
|
|
**For implementation details, see [IMPLEMENTATION_GUIDE.md](IMPLEMENTATION_GUIDE.md)**
|
|
|
|
**For API reference, see [API_REFERENCE.md](API_REFERENCE.md)**
|