Lese dies auf Deutsch
Zero-dependency Chess960 library in plain ES modules. The project contains:
- a reusable engine in
src/chess960.js - a browser UI in
app.js - a regression suite in
tests/
The engine goes beyond start-position generation. It can play full Chess960 games, track legal moves, export and import notation, detect draws, and manage undo/redo history.
- Generate all 960 legal Chess960 back ranks
- Convert back rank to Scharnagl id and back
- Create and hydrate playable game states
- Generate legal moves and execute moves
- Handle Chess960 castling, en passant, promotion, check, checkmate, and stalemate
- Detect draw rules
- Export and import FEN
- Export and import SAN and PGN
- Track move history and undo/redo snapshots
- Serialize state for
localStorageor other persistence
βββ src/
β βββ chess960.js
βββ tests/
β βββ chess960.core.test.js
β βββ chess960.moves.test.js
β βββ chess960.castling.test.js
β βββ chess960.notation.test.js
β βββ chess960.fen-pgn.test.js
β βββ chess960.draws.test.js
β βββ chess960.history.test.js
β βββ run-tests.js
βββ app.js
βββ index.html
βββ style.css
βββ README.md
βββ README_de.md
import Chess960 from "./src/chess960.js";
const chess960 = new Chess960();const backRank = chess960.generate(518);
// ["R", "N", "B", "Q", "K", "B", "N", "R"]
const id = chess960.getIdFromPosition(backRank);
// 518
const allPositions = chess960.generateAll();
// [{ id, backRank }, ...] with 960 entriesconst game = chess960.createGame(518);
// or:
const customGame = chess960.createGame(["B", "N", "R", "K", "R", "Q", "N", "B"]);createGame(...) accepts either:
- a Chess960 id
- a valid back-rank array
The stable public methods are:
generate(id)generateAll()getIdFromPosition(backRank)isValidBackRank(backRank)createGame(positionInput)hydrateGameState(rawState)serializeGameState(gameState)getPieceAt(gameState, square)getLegalMoves(gameState, fromSquare)selectSquare(gameState, square)performInteraction(gameState, square)movePiece(gameState, fromSquare, toSquare, promotion = "Q")claimDraw(gameState, reason)resetGame(positionInput)undo(gameState)redo(gameState)canUndo(gameState)canRedo(gameState)isKingInCheck(gameState, color)exportFEN(gameState, options)importFEN(fen, options)applySAN(gameState, sanMove)exportPGN(gameState, options)importPGN(pgn, options)
let game = chess960.createGame(518);
game = chess960.movePiece(game, "e2", "e4");
game = chess960.movePiece(game, "e7", "e5");
console.log(game.moveHistory.at(-1).san);
// "e5"
game = chess960.undo(game);
game = chess960.redo(game);If you want click-based UI interaction instead of coordinate-driven moves:
let game = chess960.createGame(518);
game = chess960.selectSquare(game, "e2");
console.log(game.legalTargets);
// ["e3", "e4"]
game = chess960.performInteraction(game, "e4");The engine returns plain serializable objects. Important fields include:
positionIdbackRankboardactiveColorselectedSquarelegalTargetsmoveHistorystatuswinnerisCheckenPassantTargethalfmoveClockfullmoveNumberpositionHistorydrawReasonclaimableDrawscastlingConfigcastlingRightsstateHistoryhistoryIndexcanUndocanRedo
readyactivecheckcheckmatestalematedraw
Claimable draw reasons:
threefoldRepetitionfiftyMoveRule
Automatic draw reasons:
fivefoldRepetitionseventyFiveMoveRuleinsufficientMaterial
Example:
if (game.claimableDraws.includes("threefoldRepetition")) {
game = chess960.claimDraw(game, "threefoldRepetition");
}const fen = chess960.exportFEN(game);
const restored = chess960.importFEN(fen, {
backRank: game.backRank
});The engine uses Chess960-aware castling rights. For imported middle-game FENs, pass backRank or positionId when the original setup cannot be inferred safely.
let game = chess960.createGame(518);
game = chess960.applySAN(game, "e4");
game = chess960.applySAN(game, "e5");
game = chess960.applySAN(game, "Nf3");const pgn = chess960.exportPGN(game, {
headers: {
Event: "Casual Game",
Site: "Berlin",
Date: "2026.04.19",
Round: "1",
White: "Alice",
Black: "Bob"
}
});exportPGN(...) already supports custom headers through options.headers.
Import:
const imported = chess960.importPGN(pgn, {
backRank: game.backRank
});
console.log(imported.headers);
console.log(imported.gameState);Because the game state is plain data, you can store it directly:
const serialized = chess960.serializeGameState(game);
localStorage.setItem("chess960.game", JSON.stringify(serialized));
const restored = chess960.hydrateGameState(
JSON.parse(localStorage.getItem("chess960.game"))
);Undo/redo history is part of the serialized state.
Run the regression suite with:
npm testThe suite covers:
- Chess960 id and back-rank roundtrips
- legal moves and en passant
- Chess960 castling
- SAN and PGN
- FEN roundtrips
- draw detection
- undo/redo history
app.js is intentionally the UI/controller layer. Core rules and notation handling live in src/chess960.js.
That separation is the main project rule:
- UI state and DOM handling in
app.js - chess logic in
src/chess960.js