Skip to main content

Position

Abstract base class representing a chess position. All chess variants extend this class.

Properties

board
Board
The current board state containing piece positions
pockets
Material | undefined
Captured pieces available for dropping (used in Crazyhouse variant)
turn
Color
The side to move ('white' or 'black')
castles
Castles
Castling rights and rook positions for both sides
epSquare
Square | undefined
En passant target square, if available
remainingChecks
RemainingChecks | undefined
Remaining checks for both sides (used in 3check variant)
halfmoves
number
Number of halfmoves since last capture or pawn move (for 50-move rule)
fullmoves
number
Current move number (increments after black’s move)
rules
Rules
required
The variant rules being used ('chess', 'atomic', 'crazyhouse', etc.)

Methods

reset()

Resets the position to the starting position for the variant.
const pos = Chess.default();
pos.play({ from: 12, to: 28 }); // e2-e4
pos.reset(); // Back to starting position

clone()

Creates a deep copy of the position.
clone
Position
A new position instance with identical state
const original = Chess.default();
const copy = original.clone();
copy.play({ from: 12, to: 28 }); // Doesn't affect original

ctx()

Computes the current position context including checks, pins, and variant-specific state.
ctx
Context
Position context containing:
  • king: The current player’s king square
  • blockers: Pieces pinned to the king
  • checkers: Pieces giving check
  • variantEnd: Whether the variant ending condition is met
  • mustCapture: Whether a capture is mandatory (Antichess)
const pos = Chess.default();
const ctx = pos.ctx();
if (ctx.checkers.nonEmpty()) {
  console.log('King is in check!');
}

dests(square, ctx?)

Computes legal destination squares for a piece.
square
Square
required
The square containing the piece to move
ctx
Context
Optional pre-computed context (performance optimization)
dests
SquareSet
Set of legal destination squares
const pos = Chess.default();
const e2Dests = pos.dests(12); // Square 12 is e2
for (const dest of e2Dests) {
  console.log(`Can move to square ${dest}`);
}

dropDests(ctx?)

Computes legal squares for dropping pieces (Crazyhouse variant).
ctx
Context
Optional pre-computed context
dropDests
SquareSet
Set of legal drop squares (empty for standard chess)
const pos = Crazyhouse.default();
if (pos.pockets && pos.pockets[pos.turn].pawn > 0) {
  const dropSquares = pos.dropDests();
  console.log(`Can drop pawn on ${dropSquares.size()} squares`);
}

allDests(ctx?)

Computes legal moves for all pieces of the current player.
ctx
Context
Optional pre-computed context
allDests
Map<Square, SquareSet>
Map from piece squares to their legal destination squares
const pos = Chess.default();
const allMoves = pos.allDests();
for (const [from, dests] of allMoves) {
  console.log(`Piece on ${from} can move to ${dests.size()} squares`);
}

isLegal(move, ctx?)

Checks if a move is legal in the current position.
move
Move
required
The move to validate (either { from, to, promotion? } or { role, to } for drops)
ctx
Context
Optional pre-computed context
true if the move is legal, false otherwise
const pos = Chess.default();
const e4Move = { from: 12, to: 28 };
if (pos.isLegal(e4Move)) {
  pos.play(e4Move);
}

play(move)

Plays a move, updating the position. Does not validate legality.
move
Move
required
The move to play
const pos = Chess.default();
pos.play({ from: 12, to: 28 }); // e2-e4
pos.play({ from: 52, to: 36 }); // e7-e5
console.log(pos.turn); // 'white'
play() does not validate move legality. Use isLegal() first or check with dests() to ensure valid moves.

isCheck()

Checks if the current player is in check.
isCheck
boolean
true if the current player’s king is under attack
const pos = Chess.default();
if (pos.isCheck()) {
  console.log('You must move out of check!');
}

isCheckmate(ctx?)

Checks if the current player is checkmated.
ctx
Context
Optional pre-computed context
isCheckmate
boolean
true if the position is checkmate
const pos = Chess.default();
if (pos.isCheckmate()) {
  console.log(`${opposite(pos.turn)} wins by checkmate!`);
}

isStalemate(ctx?)

Checks if the current player is stalemated.
ctx
Context
Optional pre-computed context
isStalemate
boolean
true if the position is stalemate
const pos = Chess.default();
if (pos.isStalemate()) {
  console.log('Draw by stalemate');
}

isEnd(ctx?)

Checks if the game has ended (checkmate, stalemate, insufficient material, or variant ending).
ctx
Context
Optional pre-computed context
isEnd
boolean
true if the game has ended
const pos = Chess.default();
while (!pos.isEnd()) {
  // Game continues
}

isVariantEnd()

Checks if the variant-specific ending condition is met.
isVariantEnd
boolean
true if variant ending condition is met (always false for standard chess)
const pos = Atomic.default();
if (pos.isVariantEnd()) {
  console.log('A king has been captured!');
}

outcome(ctx?)

Determines the game outcome.
ctx
Context
Optional pre-computed context
outcome
Outcome | undefined
Game outcome: { winner: 'white' | 'black' | undefined } or undefined if game continues
const pos = Chess.default();
const result = pos.outcome();
if (result) {
  if (result.winner) {
    console.log(`${result.winner} wins!`);
  } else {
    console.log('Draw');
  }
}

variantOutcome(ctx?)

Determines variant-specific outcome.
ctx
Context
Optional pre-computed context
variantOutcome
Outcome | undefined
Variant outcome (always undefined for standard chess)

hasInsufficientMaterial(color)

Checks if a side has insufficient mating material.
color
Color
required
The color to check ('white' or 'black')
hasInsufficientMaterial
boolean
true if the side cannot possibly checkmate
const pos = Chess.default();
if (pos.hasInsufficientMaterial('white') && pos.hasInsufficientMaterial('black')) {
  console.log('Draw by insufficient material');
}

isInsufficientMaterial()

Checks if both sides have insufficient mating material.
isInsufficientMaterial
boolean
true if neither side can possibly checkmate
const pos = Chess.default();
if (pos.isInsufficientMaterial()) {
  console.log('Automatic draw');
}

hasDests(ctx?)

Checks if the current player has any legal moves.
ctx
Context
Optional pre-computed context
hasDests
boolean
true if at least one legal move exists
const pos = Chess.default();
if (!pos.hasDests()) {
  console.log(pos.isCheck() ? 'Checkmate!' : 'Stalemate!');
}

toSetup()

Converts the position to a Setup object.
toSetup
Setup
Setup object containing all position data
const pos = Chess.default();
pos.play({ from: 12, to: 28 });
const setup = pos.toSetup();
// Can recreate position later with Chess.fromSetup(setup)

kingAttackers(square, attacker, occupied)

Finds all pieces of a color attacking a square.
square
Square
required
The square being attacked
attacker
Color
required
The attacking color
occupied
SquareSet
required
The set of occupied squares
kingAttackers
SquareSet
Set of squares containing pieces attacking the target square
const pos = Chess.default();
const king = pos.board.kingOf('white');
if (king !== undefined) {
  const attackers = pos.kingAttackers(king, 'black', pos.board.occupied);
  console.log(`${attackers.size()} pieces attacking the king`);
}

Chess

Concrete implementation of standard chess rules.

Static Methods

Chess.default()

Creates a new position with the standard starting position.
default
Chess
A new chess position in the starting state
const pos = Chess.default();
console.log(pos.turn); // 'white'
console.log(pos.fullmoves); // 1

Chess.fromSetup(setup)

Creates a chess position from a Setup object.
setup
Setup
required
The position setup to validate and load
fromSetup
Result<Chess, PositionError>
A Result containing either the valid Chess position or a PositionError
import { Chess } from 'chessops';

const setup = {
  board: Board.default(),
  turn: 'white',
  castlingRights: SquareSet.corners(),
  epSquare: undefined,
  remainingChecks: undefined,
  halfmoves: 0,
  fullmoves: 1
};

const result = Chess.fromSetup(setup);
if (result.isOk) {
  const pos = result.value;
  console.log('Valid position created');
} else {
  console.error('Invalid setup:', result.error.message);
}

Instance Methods

clone()

Creates a deep copy of the chess position.
clone
Chess
A new Chess instance with identical state
const original = Chess.default();
const copy = original.clone();
copy.play({ from: 12, to: 28 });
console.log(original.turn); // 'white' (unchanged)
console.log(copy.turn); // 'black'

Castles

Manages castling rights and paths for both sides.

Properties

castlingRights
SquareSet
Set of squares containing rooks that can castle
rook
ByColor<ByCastlingSide<Square | undefined>>
Rook positions for each side and color: { white: { a: 0, h: 7 }, black: { a: 56, h: 63 } }
path
ByColor<ByCastlingSide<SquareSet>>
Squares that must be empty for castling

Static Methods

Castles.default()

Creates castling state for standard chess starting position.
default
Castles
Castles instance with all castling rights available
const castles = Castles.default();
console.log(castles.rook.white.h); // 7 (h1 rook)

Castles.empty()

Creates castling state with no castling rights.
empty
Castles
Castles instance with no castling available
const castles = Castles.empty();
console.log(castles.castlingRights.isEmpty()); // true

Castles.fromSetup(setup)

Creates castling state from a Setup object.
setup
Setup
required
The setup containing board and castling rights
fromSetup
Castles
Castles instance computed from the setup

Instance Methods

clone()

Creates a copy of the castling state.
clone
Castles
A new Castles instance with identical state

discardRook(square)

Removes castling rights for a rook.
square
Square
required
The square of the rook that moved or was captured
const pos = Chess.default();
pos.castles.discardRook(7); // White loses kingside castling

discardColor(color)

Removes all castling rights for a color (called when king moves).
color
Color
required
The color losing castling rights
const pos = Chess.default();
pos.castles.discardColor('white'); // White loses all castling

Context

Position analysis context computed by ctx().

Properties

king
Square | undefined
The current player’s king square (undefined if no king)
blockers
SquareSet
Pieces pinned to the king (cannot move without exposing check)
checkers
SquareSet
Pieces giving check to the king
variantEnd
boolean
Whether the variant ending condition is met
mustCapture
boolean
Whether a capture is mandatory (Antichess only)
const pos = Chess.default();
const ctx = pos.ctx();

if (ctx.checkers.nonEmpty()) {
  console.log('In check from', ctx.checkers.size(), 'piece(s)');
}

if (ctx.blockers.has(someSquare)) {
  console.log('This piece is pinned to the king');
}

IllegalSetup

Enumeration of position validation errors.

Values

Empty
string
'ERR_EMPTY' - Board has no pieces
OppositeCheck
string
'ERR_OPPOSITE_CHECK' - The side not to move is in check
PawnsOnBackrank
string
'ERR_PAWNS_ON_BACKRANK' - Pawns on the first or eighth rank
Kings
string
'ERR_KINGS' - Wrong number of kings
Variant
string
'ERR_VARIANT' - Variant-specific validation error
import { Chess, IllegalSetup, PositionError } from 'chessops';

const result = Chess.fromSetup(invalidSetup);
if (result.isErr) {
  const error = result.error;
  if (error.message === IllegalSetup.Kings) {
    console.log('Invalid number of kings');
  }
}

PositionError

Error class for invalid positions.

Constructor

message
IllegalSetup
required
The specific validation error
try {
  const result = Chess.fromSetup(setup);
  if (result.isErr) {
    throw result.error;
  }
} catch (error) {
  if (error instanceof PositionError) {
    console.error('Invalid position:', error.message);
  }
}

Utility Functions

pseudoDests(pos, square, ctx)

Computes pseudo-legal moves (ignoring checks and pins).
pos
Position
required
The position to analyze
square
Square
required
The square containing the piece
ctx
Context
required
The position context
pseudoDests
SquareSet
Set of pseudo-legal destination squares
import { pseudoDests } from 'chessops';

const pos = Chess.default();
const ctx = pos.ctx();
const pseudo = pseudoDests(pos, 12, ctx); // e2 pawn moves

equalsIgnoreMoves(left, right)

Compares two positions ignoring move counters.
left
Position
required
First position
right
Position
required
Second position
equalsIgnoreMoves
boolean
true if positions are identical except for halfmoves/fullmoves
import { equalsIgnoreMoves } from 'chessops';

const pos1 = Chess.default();
const pos2 = Chess.default();
pos2.fullmoves = 10;

console.log(equalsIgnoreMoves(pos1, pos2)); // true

castlingSide(pos, move)

Determines which side a move castles to.
pos
Position
required
The position
move
Move
required
The move to check
castlingSide
CastlingSide | undefined
'a' for queenside, 'h' for kingside, or undefined if not castling
import { castlingSide } from 'chessops';

const pos = Chess.default();
const move = { from: 4, to: 6 }; // e1-g1
const side = castlingSide(pos, move);
if (side === 'h') {
  console.log('Kingside castle');
}

normalizeMove(pos, move)

Normalizes a castling move to use the rook square as destination.
pos
Position
required
The position
move
Move
required
The move to normalize
normalizeMove
Move
The normalized move (unchanged if not castling)
import { normalizeMove } from 'chessops';

const pos = Chess.default();
const move = { from: 4, to: 6 }; // e1-g1 (kingside castle)
const normalized = normalizeMove(pos, move);
// normalized.to is now 7 (h1 rook square)

isStandardMaterial(pos)

Checks if a position has standard material (no promoted pieces beyond the starting material).
pos
Chess
required
The chess position to check
isStandardMaterial
boolean
true if the material is standard
import { isStandardMaterial } from 'chessops';

const pos = Chess.default();
console.log(isStandardMaterial(pos)); // true

isImpossibleCheck(pos)

Checks if the current check is impossible (indicates invalid position).
pos
Position
required
The position to validate
isImpossibleCheck
boolean
true if the check is impossible (e.g., triple check, multiple non-aligned sliding checkers)
import { isImpossibleCheck } from 'chessops';

const pos = Chess.fromSetup(suspiciousSetup).value;
if (isImpossibleCheck(pos)) {
  console.log('This position is invalid!');
}

Build docs developers (and LLMs) love