Bitboard-Based Game Representation in Stockfish

Stockfish represents the chessboard using bitboards: 64-bit unsigned integers where each bit corresponds to a square on the board.

typedef uint64_t Bitboard;
  • Bit 0 (LSB) → A1
  • Bit 63 (MSB) → H8

This representation allows the engine to manipulate entire sets of squares using fast bitwise operations, which is critical for performance.


Piece Encoding

enum Piece {
  NO_PIECE,
  W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
  B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
  PIECE_NB = 16
};

Numeric Structure

PieceValueBinary
W_PAWN10001
W_KING60110
B_PAWN91001
B_KING141110

Key observations:

  • White pieces occupy values 1–6
  • Black pieces occupy values 9–14
  • The 4th bit distinguishes color

This enables a compact and efficient encoding:

Piece = (Color << 3) | PieceType

Extracting Type and Color

inline PieceType type_of(Piece pc) { return PieceType(pc & 7); }
inline Color color_of(Piece pc)    { return Color(pc >> 3); }
  • pc & 7 strips color → piece type
  • pc >> 3 extracts color

Flipping Color

~pc == pc ^ 8

Toggles the color bit while keeping the piece type unchanged.

PIECE_NB

PIECE_NB = 16

Used for array sizing and indexing. It is not the number of actual chess pieces, but the size of the encoding space.


PieceType: Color-Independent Identity

enum PieceType {
  NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
  ALL_PIECES = 0,
  PIECE_TYPE_NB = 8
};

Purpose

  • Represents kind of piece, independent of color

  • Used for:

    • Move generation
    • Attack generation
    • Evaluation
    • Bitboard indexing

This enum deliberately excludes color, which is handled separately.

The encoding aligns perfectly with Piece:

Piece = (Color << 3) | PieceType

Special Values

  • ALL_PIECES = 0 Used as a generic index when aggregating attacks.
  • PIECE_TYPE_NB = 8 Total number of piece types including sentinel values.

Square Representation

enum Square {
  SQ_A1, SQ_B1, ..., SQ_H8,
  SQ_NONE,

  SQUARE_NB = 64,

  NORTH =  8,
  EAST  =  1,
  SOUTH = -8,
  WEST  = -1,

  NORTH_EAST = NORTH + EAST,
  SOUTH_EAST = SOUTH + EAST,
  SOUTH_WEST = SOUTH + WEST,
  NORTH_WEST = NORTH + WEST
};

Numeric Layout

Squares are encoded in rank-major order:

A1 = 0   B1 = 1   ... H1 = 7
A2 = 8   B2 = 9   ... H2 = 15
...
A8 = 56  B8 = 57  ... H8 = 63

Example:

int(SQ_E4) == 4 + 3 * 8 == 28

Why This Layout?

  • Files increment by +1
  • Ranks increment by +8
  • Board geometry becomes simple integer arithmetic

Directions as Integer Offsets

NORTH =  8
EAST  =  1
SOUTH = -8
WEST  = -1

This allows simple move computation:

SQ_E2 + NORTH        == SQ_E3
SQ_E2 + NORTH_EAST   == SQ_F3

Diagonal directions are composed, not duplicated:

NORTH_EAST = NORTH + EAST

File and Rank Enums

enum File : int { FILE_A, FILE_B, ..., FILE_H, FILE_NB };
enum Rank : int { RANK_1, RANK_2, ..., RANK_8, RANK_NB };

These are semantic types, not just integers.

They improve:

  • Readability
  • Type safety
  • Template specialization

Extraction helpers:

inline File file_of(Square s) { return File(s & 7); }
inline Rank rank_of(Square s) { return Rank(s >> 3); }

Construction:

inline Square make_square(File f, Rank r) {
  return Square((r << 3) + f);
}

Everything reduces to:

square = rank * 8 + file

Color-Relative Squares

inline Square relative_square(Color c, Square s) {
  return Square(s ^ (c * 56));
}
  • For WHITE (c = 0): square unchanged
  • For BLACK (c = 1): square vertically flipped

Why 56?

56 = 7 * 8 = A8

Examples:

A1 ^ 56 = A8
C1 ^ 56 = C8

This allows writing color-independent evaluation logic.


Castling Representation

CastlingSide

enum CastlingSide {
  KING_SIDE,
  QUEEN_SIDE,
  CASTLING_SIDE_NB = 2
};
  • Simple selector enum
  • Not a bitmask
  • Used in templates and branching logic

CastlingRight: Bitmask Encoding

enum CastlingRight {
  NO_CASTLING,
  WHITE_OO,
  WHITE_OOO = WHITE_OO << 1,
  BLACK_OO  = WHITE_OO << 2,
  BLACK_OOO = WHITE_OO << 3,
  ANY_CASTLING = WHITE_OO | WHITE_OOO | BLACK_OO | BLACK_OOO,
  CASTLING_RIGHT_NB = 16
};

Numeric Values

WHITE_OO    = 1  // 0001
WHITE_OOO   = 2  // 0010
BLACK_OO    = 4  // 0100
BLACK_OOO   = 8  // 1000

Why Bitmasks?

Castling rights are independent flags:

  • A position may have:

    • Only king-side rights
    • Only queen-side rights
    • Both
    • None

Bitmasks allow all combinations efficiently:

WHITE_OO | WHITE_OOO   // White can castle both sides
BLACK_OO | BLACK_OOO   // Black can castle both sides
WHITE_OO | BLACK_OO    // Both can castle king-side
ANY_CASTLING           // All rights available

Score: Middlegame and Endgame Packed Together

Stockfish does not evaluate a position with a single number. Instead, it evaluates two positions in parallel:

  • Middlegame (MG) score
  • Endgame (EG) score

These two values are packed into a single 32-bit integer called Score.

/// Score enum stores a middlegame and an endgame value in a single integer
/// The upper 16 bits store the middlegame value
/// The lower 16 bits store the endgame value
enum Score : int { SCORE_ZERO };

Bit Layout

32-bit Score integer

|  MG (signed 16 bits) |  EG (signed 16 bits) |
|----------------------|----------------------|
| bits 31 ........ 16 | bits 15 ........ 0  |

This allows Stockfish to accumulate middlegame and endgame evaluations simultaneously, without branching on game phase.


Creating a Score

inline Score make_score(int mg, int eg) {
  return Score((int)((unsigned int)eg << 16) + mg);
}

Conceptually:

Score = (EG << 16) | MG

The implementation uses unsigned arithmetic to avoid undefined behavior when shifting signed integers.


Extracting Values

Endgame value:

inline Value eg_value(Score s) {

  union { uint16_t u; int16_t s; } eg = {
    uint16_t(unsigned(s + 0x8000) >> 16)
  };

  return Value(eg.s);
}

Middlegame value:

inline Value mg_value(Score s) {

  union { uint16_t u; int16_t s; } mg = {
    uint16_t(unsigned(s))
  };

  return Value(mg.s);
}

These functions carefully preserve sign and avoid implementation-defined behavior in C++.


Why Stockfish Uses Score

This design enables:

  • Continuous transition between middlegame and endgame
  • No runtime branching on game phase
  • Extremely cache-friendly evaluation
  • Simple accumulation of evaluation terms

Throughout evaluation, Stockfish accumulates Score values:

Score score = SCORE_ZERO;
score += MobilityBonus;
score += PawnStructure;
score += KingSafety;

Only at the very end is the final value computed by interpolating between MG and EG using the game phase.


Mental Model

Think of Score as a 2-component vector:

Score = ⟨middlegame, endgame⟩

Evaluation is vector addition, and the final numeric score is a weighted projection based on how far the game has progressed.