Bitboards
A bitboard is a compact way to encode a grid as a single integer, using one bit per cell (1
= filled, 0
= empty). In Quadrille, bitboards are represented with JavaScript BigInt and, by default, arranged in row-major, big-endian order: the most significant bit (MSB) maps to the top-left cell, indices decrease leftward across each row, and continue row by row until the least significant bit (LSB) at the bottom-right.
On an 8×8 chessboard, filling the main diagonal encodes: 2⁶³ + 2⁵⁴ + 2⁴⁵ + 2³⁶ + 2²⁷ + 2¹⁸ + 2⁹ + 2⁰ = 9241421688590303745n
. This is the set of cells a bishop standing on that diagonal may reach. The value is symmetric, so it remains unchanged under either endianness.
(press cells to toggle bits and watch the BigInt
update)
This chapter teaches
- How a bitboard encodes a grid as a
BigInt
- How to convert grids to and from bitboards with
toBigInt()
and related helpers - How to apply mutators like
shift(dRow, dCol, wrap?)
to move patterns around the board - How a bitboard-backed Quadrille works as an aggregated layer where every
1
-bit becomes a filled cell with the same valid JavaScriptvalue
Knight Jumps
Place a knight anywhere on an 8×8 board and instantly see every square it can reach in one move. Click a cell to position the knight; pulsing dots show the targets. Toggle Wrap to switch between edge clipping and toroidal wrap-around.
code
const COLS = 8;
const ROWS = 8;
Quadrille.cellLength = 50;
Quadrille.tileDisplay = undefined;
let wrap;
let board, knight, jumps;
// Fixed reference cell (origin of all shifts)
const refRow = 4, refCol = 4;
// Reference masks (anchored at 4,4)
let knightMask, jumpsMask;
// Current shift offset relative to reference
let dRow = 0, dCol = 0;
function setup() {
// Initialize immutable reference masks
knightMask = createQuadrille(COLS, ROWS, 134217728n, N);
jumpsMask = createQuadrille(COLS, ROWS, 22136263676928n, bit);
// Empty chessboard for background
board = createQuadrille();
// Initialize shifted layers (start aligned with masks)
knight = knightMask.clone();
jumps = jumpsMask.clone();
createCanvas(COLS * Quadrille.cellLength, ROWS * Quadrille.cellLength);
// Wrap toggle: toroidal vs. clipped shifting
wrap = createCheckbox('Wrap');
wrap.position(3, height + 10);
wrap.changed(update);
}
function draw() {
drawQuadrille(board); // chessboard
drawQuadrille(knight); // Knight (shifted)
drawQuadrille(jumps); // Knight jumps (shifted)
}
function mousePressed() {
const row = board.mouseRow;
const col = board.mouseCol;
if (board.isValid(row, col)) {
// Update shift offsets
dRow = row - refRow;
dCol = col - refCol;
// Update the displayed knight and jumps quadrille layers
update();
}
}
function update() {
// Non-destructive shift from reference masks
knight = Quadrille.shift(knightMask, dRow, dCol, wrap.checked());
jumps = Quadrille.shift(jumpsMask, dRow, dCol, wrap.checked());
}
// Chess knight symbol (for display)
const N = Quadrille.chessSymbols.get('N');
// 1-bit cell display for jumps
const bit = function ({ row, col }) {
const l = Quadrille.cellLength;
const r = (l * 0.6) + (l * 0.1 * sin(millis() / 100));
push();
noStroke();
colorMode(HSB, 360, 100, 100, 255);
const hue = (row * 45 + col * 25) % 360;
fill(hue, 85, 95, 220);
ellipse(l / 2, l / 2, r, r);
pop();
}
Strategy
- Define two immutable reference masks at the reference cell
(4, 4)
:knightMask
(the piece) andjumpsMask
(its possible targets). - On mouse clicks, compute the offset from
(4, 4)
and shift the masks to update the displayedknight
andjumps
quadrille layers.
Try this
- Compute masks for other chess pieces (rook, bishop, queen, king, pawn) anchored at
(4,4)
, then shift them on clicks. - Swap the target display (
bit
) for other effects (glow, pulse, wiggle) to compare feedback. - Ghost overlay. While hovering, color the AND (overlap) in red to preview invalid landings before clicking.
- Export & replay. After each click, append
jumps.toBigInt()
to a Timeline and add simple undo/redo keys. - Beyond 8×8. Rebuild the same demo on
5×5
or10×8
; confirm that only the stride (board width) changes in your mask math.
Anchoring Masks
The reference masks knightMask
and jumpsMask
are defined once at the reference cell (4, 4)
(the board’s center).
- A single knight there sets bit 27, corresponding to
2²⁷ = 134217728n
. - The JUMPS mask marks the eight legal destinations from that square (excluding the knight’s own cell), encoding:
2¹⁰ + 2¹² + 2¹⁷ + 2²¹ + 2³³ + 2³⁷ + 2⁴² + 2⁴⁴ = 22136263676928n
setup() excerpt
// Reference masks (anchored at 4,4)
let knightMask, jumpsMask;
function setup() {
// Initialize immutable reference masks
knightMask = createQuadrille(COLS, ROWS, 134217728n, N);
jumpsMask = createQuadrille(COLS, ROWS, 22136263676928n, bit);
// ...
}
n
. For example, 134217728n
is a BigInt, while 134217728
is a regular Number.Creating the Quadrille Layers
You may think of a Quadrille built from a bitboard as an aggregated layer where every 1
-bit becomes the same value—an emoji, a color, a cell effect, or even an object.
Here the three layers are:
board
— empty 8×8 backgroundknight
— shifted copy ofknightMask
, initially cloned from itjumps
— shifted copy ofjumpsMask
, initially cloned from it
setup() excerpt
let board, knight, jumps;
function setup() {
// ...
// Empty chessboard for background
board = createQuadrille();
// Initialize shifted layers (start aligned with masks)
knight = knightMask.clone();
jumps = jumpsMask.clone();
// ...
}
// Chess knight symbol (for display)
const N = Quadrille.chessSymbols.get('N');
// 1-bit cell display for jump targets (animated dots)
const bit = function ({ row, col }) {
const l = Quadrille.cellLength;
const r = (l * 0.6) + (l * 0.1 * sin(millis() / 100)); // pulsing radius
push();
noStroke();
colorMode(HSB, 360, 100, 100, 255);
const hue = (row * 45 + col * 25) % 360; // vary hue by position
fill(hue, 85, 95, 220);
ellipse(l / 2, l / 2, r, r);
pop();
}
The three layers are then rendered in order:
draw()
function draw() {
drawQuadrille(board); // chessboard
drawQuadrille(knight); // Knight (shifted)
drawQuadrille(jumps); // Knight jumps (shifted)
}
Interaction: Shifting by Offset
Click any cell to compute the offset from (4, 4)
, then shift both masks by (dRow, dCol)
.
mousePressed()
function mousePressed() {
const row = board.mouseRow;
const col = board.mouseCol;
if (board.isValid(row, col)) {
// Update shift offsets
dRow = row - refRow;
dCol = col - refCol;
// Update the displayed knight and jumps quadrille layers
update();
}
}
The update()
function shifts the reference masks to update the displayed knight
and jumps
quadrille layers.
update()
function update() {
// Non-destructive shift from reference masks
knight = Quadrille.shift(knightMask, dRow, dCol, wrap.checked());
jumps = Quadrille.shift(jumpsMask, dRow, dCol, wrap.checked());
}
From Bits to Quadrilles
Think in layers: precompute masks as BigInt
bitboards, then turn them into Quadrilles by assigning each 1
-bit a single valid JavaScript value (emoji, color, display function, or object), e.g., createQuadrille(8, 8, mask, '♘')
. Once in grid form, combine them with algebra—and
, or
, xor
, not
, diff
—and transform them with mutators such as shift(dRow, dCol, wrap?)
. The API provides:
toBigInt()
— Export a Quadrille as abitboard
;1
-bits mark all non-null cellsbitIndex
— Map(row, col)
→ bit position; build single-bit masks with1n << index
bitCell
— Recover{ row, col }
from a bit index (inverse ofbitIndex
)createQuadrille(w, h, bitboard, value)
— Import a bitboard as a grid; every1
becomes the same valueclear(bitboard)
— Clear cells where the mask has1
sfill(bitboard, value)
— Fill cells where the mask has1
s withvalue
Together these methods make bitboards fully interchangeable with Quadrilles, letting you switch seamlessly between binary masks and rich quadrilles.
BigInt
, which supports arbitrary sizes. Within sketches, Quadrille’s own operations (clone
, fill
, search
, shift
) are ergonomic and efficient; use toBigInt()
and related methods when you need to import or export bitboards for interoperability with external tools.References
- Bitboard (Wikipedia) — compact encoding used in many chess engines.
- Related chapters — Aggregated States · Layered Boards · Cell Effects · Timelines.
Further Reading
Bitwise toolkit — Operators
&
(AND),|
(OR),^
(XOR),~
(NOT),<<
/>>
for shifting.Board Representation (Computer Chess, Wikipedia) — Overview of board data structures including bitboards, rotated bitboards, and magic bitboards.
Avoiding Rotated Bitboards with Direct Lookup (Sam Tannous, 2007, arXiv) — Efficient sliding move generation without rotated boards.
Rotated Bitboards in FUSc# and Reinforcement Learning in Computer Chess and Beyond (Johannes Buchner, 2025, arXiv) — Contemporary analysis of bitboard techniques in a modern engine.
Bitboard Methods for Games (Cameron Browne, 2025, ResearchGate) — Survey of bitboard applications across many game types.
Parallel Chess Searching and Bitboards (D. R. Rasmussen, 2004, CMU) — Benefits of bitboards in parallel chess engines.
Magic Move-Bitboard Generation in Computer Chess (P. Kannan, 2007) — Introduction to magic bitboards for fast move generation.
Beyond 8×8:
- Shogi —
9×9
- Xiangqi —
9×10
- Capablanca Chess —
10×8
- Mini-chess variants — small boards like
5×5
or6×6
- Shogi —
Bitboards aren’t tied to chess: any w × h
board works as long as masks stay consistent with row-major ordering.
Patterns, shifts, and searches work the same—only the stride (board width) changes.