import { Atomic } from 'chessops/variant';// Create a new Atomic positionconst pos = Atomic.default();// Play a normal movepos.play({ from: 12, to: 28 }); // e2-e4// After a capture, pieces explodepos.play({ from: 51, to: 35 }); // d7-d5pos.play({ from: 28, to: 35 }); // e4xd5 - explosion!// Check if game ended via explosionif (pos.isVariantEnd()) { const outcome = pos.variantOutcome(); console.log('Winner:', outcome?.winner);}
The explosion mechanic is implemented in playCaptureAt():
// When a capture occurs:// 1. The captured piece is removed// 2. The capturing piece is removed// 3. All non-pawn pieces in kingAttacks(square) are removedconst pos = Atomic.default();pos.play({ from: 12, to: 28 }); // e2-e4pos.play({ from: 51, to: 35 }); // d7-d5 pos.play({ from: 28, to: 35 }); // e4xd5// After exd5:// - Pawn on e4 is destroyed (capturing piece)// - Pawn on d5 is destroyed (captured piece)// - All non-pawn pieces adjacent to d5 are destroyed// - Pawns in the blast radius survive
import { Atomic } from 'chessops/variant';const pos = Atomic.default();// Check if game ended via king explosionif (pos.isVariantEnd()) { const outcome = pos.variantOutcome(); if (outcome?.winner === 'white') { console.log('Black king exploded!'); } else if (outcome?.winner === 'black') { console.log('White king exploded!'); }}
Variant outcome logic from variant.ts:231-236:
variantOutcome(_ctx?: Context): Outcome | undefined { for (const color of COLORS) { if (this.board.pieces(color, 'king').isEmpty()) { return { winner: opposite(color) }; } } return;}
import { Atomic } from 'chessops/variant';import { parseSquare } from 'chessops';const pos = Atomic.default();const square = parseSquare('e2')!;// Get legal moves (accounts for explosions)const dests = pos.dests(square);// A move is legal if:// 1. After the move, our king exists, AND// 2. Either opponent's king is gone OR our king is not in check
From variant.ts:209-225:
dests(square: Square, ctx?: Context): SquareSet { ctx = ctx || this.ctx(); let dests = SquareSet.empty(); for (const to of pseudoDests(this, square, ctx)) { const after = this.clone(); after.play({ from: square, to }); const ourKing = after.board.kingOf(this.turn); if ( defined(ourKing) && (!defined(after.board.kingOf(after.turn)) || after.kingAttackers(ourKing, after.turn, after.board.occupied).isEmpty()) ) { dests = dests.with(to); } } return dests;}
import { Atomic } from 'chessops/variant';import { parseFen } from 'chessops/fen';const setup = parseFen( 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1').unwrap();const result = Atomic.fromSetup(setup);if (result.isErr) { // Possible errors: // - Empty board // - More than 2 kings // - Opponent's king in check // - Pawns on backrank}
Validation from variant.ts:139-152:
protected validate(): Result<undefined, PositionError> { // Like chess, but allow our king to be missing. if (this.board.occupied.isEmpty()) return Result.err(new PositionError(IllegalSetup.Empty)); if (this.board.king.size() > 2) return Result.err(new PositionError(IllegalSetup.Kings)); const otherKing = this.board.kingOf(opposite(this.turn)); if (!defined(otherKing)) return Result.err(new PositionError(IllegalSetup.Kings)); if (this.kingAttackers(otherKing, this.turn, this.board.occupied).nonEmpty()) { return Result.err(new PositionError(IllegalSetup.OppositeCheck)); } if (SquareSet.backranks().intersects(this.board.pawn)) { return Result.err(new PositionError(IllegalSetup.PawnsOnBackrank)); } return Result.ok(undefined);}
Unlike standard chess, Atomic allows positions where the current player’s king is missing (it was exploded).
const pos = Atomic.default();const isInsufficient = pos.hasInsufficientMaterial('white');// Insufficient material considers:// - If enemy king is already exploded (always sufficient)// - Bare king cannot mate// - Enemy pieces can explode near their own king// - Special cases for bishops on same color squares
Key cases from variant.ts:172-207:
If enemy king is gone, NOT insufficient material
Bare king (only king remaining) IS insufficient
If enemy has pieces near their king, they might self-explode (NOT insufficient)
Special bishop cases: opposite color bishops cannot explode each other
You can move into “check” if it explodes the opponent’s king:
// Normal chess: Cannot move into check// Atomic: Can "move into check" if it captures enemy kingconst pos = Atomic.default();// ... setup position where capture explodes enemy kingconst dests = pos.dests(square);// May include moves that would be illegal in standard chess