pgn

package module
v3.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 10, 2026 License: MIT Imports: 16 Imported by: 0

README

pgn

A high-performance PGN (Portable Game Notation) parser for Go.

v2.0 - Complete rewrite with bitboard engine, parallel parsing, and zstd support.

Go Reference

Installation

go get github.com/freeeve/pgn/v3

Quick Start

package main

import (
    "fmt"
    "log"

    "github.com/freeeve/pgn/v3"
)

func main() {
    // Parse any PGN file (handles .zst compression automatically)
    for game := range pgn.Games("games.pgn").Games {
        fmt.Printf("%s vs %s: %s\n",
            game.Tags["White"],
            game.Tags["Black"],
            game.Tags["Result"])
        
        // Replay the game
        gs := pgn.NewStartingPosition()
        for _, move := range game.Moves {
            pgn.MakeMove(gs, move)
        }
        fmt.Println(gs.ToFEN())
    }
}

Features

  • Fast parallel parsing - 475+ MB/s, 620K games/sec on Apple M3 Max
  • Streaming - Parse files of any size with constant memory
  • Zstd support - Automatic compression/decompression of .zst files
  • Bitboard engine - Efficient move generation and validation
  • FEN support - Parse and generate FEN strings
  • Packed positions - 34-byte compact position encoding with base64
  • Position indexing - Enumerate all positions with deterministic indices
  • Game state queries - IsCheckmate, IsStalemate, IsSquareAttacked, KingSquare

API

Parsing PGN Files
// Parse from file (parallel, handles .zst)
parser := pgn.Games("lichess_games.pgn.zst")
for game := range parser.Games {
    // game.Tags is map[string]string
    // game.Moves is []pgn.Mv
}
if err := parser.Err(); err != nil {
    log.Fatal(err)
}

// Parse from reader
parser := pgn.GamesFromReader(reader)
for game := range parser.Games {
    // ...
}

// Stop early
parser := pgn.Games("huge.pgn")
count := 0
for game := range parser.Games {
    count++
    if count >= 1000 {
        parser.Stop()
        break
    }
}
Working with Positions
// Create starting position
gs := pgn.NewStartingPosition()

// Parse from FEN
gs, err := pgn.NewGame("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")

// Generate FEN
fen := gs.ToFEN()

// Get piece at square
piece := gs.PieceAt(pgn.SqE4) // Returns 'P', 'n', etc. or 0 for empty

// Position status
inCheck := gs.IsInCheck()
isMate := gs.IsCheckmate()
isStalemate := gs.IsStalemate()

// Find king location
whiteKingSq := gs.KingSquare(pgn.White)

// Check if square is attacked
attacked := gs.IsSquareAttacked(pgn.SqE4, pgn.Black)

// Generate legal moves
moves := pgn.GenerateLegalMoves(gs)
Making Moves
gs := pgn.NewStartingPosition()

// Make move (modifies gs in place, returns undo info)
undo := pgn.MakeMove(gs, move)

// Unmake move (restore previous position)
pgn.UnmakeMove(gs, move, undo)

// Parse SAN notation
move, err := pgn.ParseSAN(gs, "e4")
move, err := pgn.ParseSAN(gs, "Nxf7+")
move, err := pgn.ParseSAN(gs, "O-O-O")

// Parse UCI notation
move, err := pgn.ParseUCI("e2e4")
move, err := pgn.ParseUCI("e7e8q") // promotion
Packed Positions

Compact 34-byte encoding for storage/transmission:

// Pack a position
gs := pgn.NewStartingPosition()
packed := gs.Pack()

// Get base64 string (46 chars, URL-safe)
key := packed.String() // "JFM2QhEREREAAAAAAAAAAAAAAAAAAAAAd3d3d4q5nKge_w"

// Parse from base64
packed, err := pgn.ParsePackedPosition(key)

// Unpack to GameState
gs := packed.Unpack()

// Convert to FEN
fen := packed.ToFEN()
Position Indexing

Enumerate all legal chess positions up to a given depth with deterministic indexing:

// Create enumerator from starting position
start := pgn.NewStartingPosition()
enum := pgn.NewPositionEnumeratorDFS(start)

// Enumerate all positions up to depth 5 (~5M positions)
enum.EnumerateDFS(5, func(index uint64, pos *pgn.GameState, depth int) bool {
    // Each position has a unique, deterministic index
    fmt.Printf("Index %d (depth %d): %s\n", index, depth, pos.ToFEN())
    return true // continue enumeration
})

// Save checkpoints for fast position lookup (supports .zst compression)
enum.SaveCheckpointsCSV("checkpoints_depth5.csv.zst", 5)

// Load checkpoints and lookup positions by index
enum2 := pgn.NewPositionEnumeratorDFS(start)
enum2.LoadCheckpointsCSV("checkpoints_depth5.csv.zst")
pos, found := enum2.PositionAtIndexDFS(1000000, 5)

// Find index of a position
idx, found := enum2.IndexOfPositionDFS(somePosition, 5)

Build checkpoint files with the included tool:

# Build and run (outputs checkpoints_depth7.csv.zst by default)
go run ./cmd/build_checkpoints -depth 7

# Custom output, more cores
go run ./cmd/build_checkpoints -depth 8 -cores 16 -output my_checkpoints.csv.zst

Benchmarks

Test Machine: Apple M3 Max (16 cores: 12P + 4E), 128 GB RAM, macOS

PGN Parsing

Benchmark file: Lichess January 2013 rated games (89 MB, ~121K games)

$ go test -bench=BenchmarkParsePGN -benchmem -count=3 -benchtime=5s

goos: darwin
goarch: arm64
pkg: github.com/freeeve/pgn/v3/bench
cpu: Apple M3 Max
BenchmarkParsePGN-16    46    190885760 ns/op    486.2 MB/sec    635626 games/sec
BenchmarkParsePGN-16    28    192204878 ns/op    482.9 MB/sec    631264 games/sec
BenchmarkParsePGN-16    30    204266194 ns/op    454.4 MB/sec    593990 games/sec
Metric Result
Throughput ~475 MB/s
Games/sec ~620K games/sec
Parallelism 16 workers (auto-detected)
Perft (Move Generation)
$ go test -bench='BenchmarkPerft_Startpos_D6' -benchtime=5s -count=3

BenchmarkPerft_Startpos_D6-16    7    815897476 ns/op    145925631 nodes/sec
BenchmarkPerft_Startpos_D6-16    7    794158440 ns/op    149920157 nodes/sec
BenchmarkPerft_Startpos_D6-16    7    763260548 ns/op    155989135 nodes/sec
Depth Nodes Throughput
6 119,060,324 ~150M nodes/sec
Move Parsing & Application

20-move Ruy Lopez opening line (ParseSAN + MakeMove):

$ go test -bench=BenchmarkMakeMovesRuyLopez -benchmem -count=3

BenchmarkMakeMovesRuyLopez-16    1241637    951.5 ns/op    0 B/op    0 allocs/op
BenchmarkMakeMovesRuyLopez-16    1261483    988.5 ns/op    0 B/op    0 allocs/op
BenchmarkMakeMovesRuyLopez-16    1205192    987.6 ns/op    0 B/op    0 allocs/op
Metric Result
20 moves (parse + apply) ~0.98 µs
Per move ~49 ns
Allocations 0

Migration from v1

Key Changes
v1 v2
pgn.NewPGNScanner(r) pgn.Games(path) or pgn.GamesFromReader(r)
scanner.Next() / scanner.Scan() for game := range parser.Games
game.Moves is []Move game.Moves is []Mv
pgn.NewBoard() pgn.NewStartingPosition()
board.MakeMove(move) pgn.MakeMove(gs, move)
board.String() (FEN) gs.ToFEN()
move.From, move.To (Position bitmask) move.From, move.To (Square 0-63)
Migration Examples

v1 - Sequential scanning:

f, _ := os.Open("games.pgn")
ps := pgn.NewPGNScanner(f)
for ps.Next() {
    game, _ := ps.Scan()
    b := pgn.NewBoard()
    for _, move := range game.Moves {
        b.MakeMove(move)
    }
    fmt.Println(b) // FEN
}

v2 - Parallel streaming:

for game := range pgn.Games("games.pgn").Games {
    gs := pgn.NewStartingPosition()
    for _, move := range game.Moves {
        pgn.MakeMove(gs, move)
    }
    fmt.Println(gs.ToFEN())
}
Square Utilities
// Parse algebraic notation
sq, err := pgn.ParseSquare("e4") // returns SqE4

// Create from file/rank (0-indexed)
sq := pgn.MakeSquare(4, 3) // file=e (4), rank=4 (3) = e4

// Get file and rank
file := sq.File() // 0-7 for a-h
rank := sq.Rank() // 0-7 for 1-8

// String representation
str := sq.String() // "e4"

License

MIT License - see LICENSE file.

Documentation

Overview

Package pgn provides PGN parsing and chess position handling using bitboards.

The core types are:

  • GameState: bitboard-based position representation
  • Mv: compact move encoding
  • PGNScanner: streaming PGN parser
  • ParsePGNParallel: high-performance parallel parser

Index

Constants

View Source
const (
	CheckpointIntervalDFS = 1 << 20 // 1,048,576

	// Max depth for terminal detection (prevent infinite loops)
	MaxDepthDFS = 500 // ~250 moves, well beyond normal games
)

Variables

This section is empty.

Functions

func AppendCheckpointCSV

func AppendCheckpointCSV(filename string, index uint64, fen string) error

AppendCheckpointCSV appends a single checkpoint to a CSV file. This is useful for incremental saves during long enumerations.

func ApplyMove

func ApplyMove(pos *GameState, m Mv) error

ApplyMove applies a move to the position without undo capability. This is simpler but slower than MakeMove/UnmakeMove for search.

func PackedFENFromFEN

func PackedFENFromFEN(fen string) (string, error)

PackedFENFromFEN is a convenience function to get a base64 string from FEN. Returns the full FEN encoding (with metadata).

func PackedPositionFromFEN

func PackedPositionFromFEN(fen string) (string, error)

PackedPositionFromFEN is a convenience function to get a base64 string from FEN. Returns just the board encoding (no metadata).

func Perft

func Perft(pos *GameState, depth int) int64

Perft counts all legal move sequences to a given depth from a position. It uses parallel processing and pooled allocations for best performance. Returns the number of leaf nodes (positions) at the given depth.

func UnmakeMove

func UnmakeMove(pos *GameState, m Mv, undo UndoInfo)

UnmakeMove reverses a move using the undo info from MakeMove.

func ValidateParentMove added in v3.2.0

func ValidateParentMove(parent *GameState, move Mv) bool

ValidateParentMove checks if the forward move from parent to current is legal. Use this after looking up a parent position in your store.

func ValidateParentMoveQuick added in v3.2.0

func ValidateParentMoveQuick(parent *GameState, move Mv) bool

ValidateParentMoveQuick is like ValidateParentMove but only checks from/to/promo. This is sufficient for most cases since flags are mainly internal.

func WithLegalMoves

func WithLegalMoves(pos *GameState, fn func([]Mv))

WithLegalMoves generates legal moves using pooled slices and calls fn with them. The moves slice is automatically released after fn returns. This is more efficient than GenerateLegalMoves for repeated calls.

Types

type Bitboard

type Bitboard uint64

Bitboard represents a set of squares as a 64-bit integer where each bit corresponds to a square (bit 0 = a1, bit 63 = h8).

type CheckpointDFS

type CheckpointDFS struct {
	Index uint64    // Position index
	Depth int       // Current ply depth
	State GameState // Position state (for quick restart)
	Stack []Mv      // Move stack to reach this position (enables DFS resumption)
}

CheckpointDFS stores minimal info for DFS restart.

type Color

type Color int

Color represents the side to move (White or Black).

const (
	White Color = iota
	Black
)

func (Color) String

func (c Color) String() string

String returns "w" for White, "b" for Black.

type Game

type Game struct {
	Tags  map[string]string
	Moves []Mv
}

Game represents a parsed PGN game with tags and moves.

type GameParser

type GameParser struct {
	// Games yields parsed games. Closed when parsing completes.
	Games <-chan *Game
	// contains filtered or unexported fields
}

GameParser streams parsed games from a PGN source.

func Games

func Games(path string, workers ...int) *GameParser

Games returns a parser that yields games from a PGN file. Handles .zst compressed files automatically.

Workers defaults to NumCPU-1 (leaving one core for decompression).

Example:

for game := range pgn.Games("huge.pgn.zst").Games {
    fmt.Println(game.Tags["Event"])
}

func GamesFromReader

func GamesFromReader(r io.Reader, workers ...int) *GameParser

GamesFromReader returns a channel that yields parsed games from a reader. Workers defaults to NumCPU-1 (leaving one core for I/O).

func (*GameParser) Err

func (gp *GameParser) Err() error

Err returns any error that occurred during parsing. Only valid after Games channel is closed.

func (*GameParser) Stop

func (gp *GameParser) Stop()

Stop cancels parsing early. Safe to call multiple times.

type GameState

type GameState struct {
	SideToMove Color
	Castle     uint8
	EP         Square
	Halfmove   int
	Fullmove   int
	FEN        string
	// contains filtered or unexported fields
}

GameState represents a chess position using bitboards.

func NewGame

func NewGame(fen string) (*GameState, error)

NewGame parses a FEN string into a GameState.

func NewStartingPosition

func NewStartingPosition() *GameState

NewStartingPosition creates a new game at the standard starting position.

func (*GameState) BoardEquals

func (gs *GameState) BoardEquals(other *GameState) bool

BoardEquals compares board state (ignores halfmove/fullmove counters). Returns true if piece positions, side to move, castling rights, and EP square match.

func (*GameState) Copy

func (p *GameState) Copy() *GameState

Copy returns a deep copy of the game state.

func (*GameState) Equals

func (gs *GameState) Equals(other *GameState) bool

Equals compares positions exactly (including halfmove/fullmove counters).

func (*GameState) IsCheckmate

func (p *GameState) IsCheckmate() bool

IsCheckmate returns true if the side to move is in checkmate.

func (*GameState) IsInCheck

func (p *GameState) IsInCheck() bool

IsInCheck returns true if the side to move is in check.

func (*GameState) IsSquareAttacked

func (p *GameState) IsSquareAttacked(sq Square, byColor Color) bool

IsSquareAttacked returns true if the given square is attacked by the specified color.

func (*GameState) IsStalemate

func (p *GameState) IsStalemate() bool

IsStalemate returns true if the side to move is in stalemate (not in check but has no legal moves).

func (*GameState) KingSquare

func (p *GameState) KingSquare(color Color) Square

KingSquare returns the square of the king for the given color. Returns -1 if no king is found.

func (*GameState) Pack

func (gs *GameState) Pack() PackedPosition

Pack encodes a GameState into a PackedPosition (26 bytes with metadata).

func (*GameState) PackFEN

func (gs *GameState) PackFEN() PackedFEN

PackFEN encodes a GameState into a PackedFEN (29 bytes with all metadata).

func (*GameState) PieceAt

func (p *GameState) PieceAt(sq Square) byte

PieceAt returns the piece character at the given square. Returns 0 for empty, uppercase for white, lowercase for black.

func (*GameState) String

func (p *GameState) String() string

String returns a human-readable board diagram.

func (*GameState) ToFEN

func (p *GameState) ToFEN() string

ToFEN returns the FEN string for the position. Always computes from piece positions (cached FEN is only for initial position).

type Mv

type Mv struct {
	From  Square
	To    Square
	Promo PromoPiece
	Flags uint16
}

Mv represents a chess move with from/to squares and optional promotion.

func GenerateLegalMoves

func GenerateLegalMoves(pos *GameState) []Mv

GenerateLegalMoves returns all legal moves from the position.

func ParseSAN

func ParseSAN(gs *GameState, san string) (Mv, error)

ParseSAN parses a SAN move string and returns the matching move.

func ParseSANBytes

func ParseSANBytes(gs *GameState, san []byte) (Mv, error)

ParseSANBytes parses a SAN move from a byte slice without allocations.

func ParseUCI

func ParseUCI(uci string) (Mv, error)

ParseUCI parses a move in UCI notation (e.g., "e2e4", "e7e8q"). Returns an error if the string is not a valid UCI move format. Note: This only parses the format; it does not validate legality.

func (Mv) String

func (m Mv) String() string

String returns the move in UCI notation (e.g., "e2e4", "e7e8q").

type PGNScanner

type PGNScanner struct {
	// contains filtered or unexported fields
}

PGNScanner scans PGN games from a reader one at a time.

func NewPGNScanner

func NewPGNScanner(r io.Reader) *PGNScanner

NewPGNScanner creates a new PGN scanner that reads from r.

func (*PGNScanner) Next

func (ps *PGNScanner) Next() bool

Next returns true if there are more games to scan.

func (*PGNScanner) Scan

func (ps *PGNScanner) Scan() (*Game, error)

Scan parses and returns the next game, or an error if parsing fails.

type PackedFEN

type PackedFEN [29]byte

PackedFEN is a compact 29-byte representation containing all FEN fields. Layout:

  • Bytes 0-25: PackedPosition (board + metadata)
  • Byte 26: Halfmove clock (0-255)
  • Bytes 27-28: Fullmove number (little-endian, 1-65535)

func PackFEN

func PackFEN(fen string) (PackedFEN, error)

PackFEN encodes a FEN string into a PackedFEN.

func ParsePackedFEN

func ParsePackedFEN(s string) (PackedFEN, error)

ParsePackedFEN decodes a base64 URL-safe encoded packed FEN.

func ParsePackedFENBytes added in v3.1.0

func ParsePackedFENBytes(src []byte) (PackedFEN, error)

ParsePackedFENBytes decodes a base64 URL-safe encoded packed FEN from bytes. This can avoid allocation when the input is already a byte slice.

func (PackedFEN) AppendString added in v3.1.0

func (pf PackedFEN) AppendString(dst []byte) []byte

AppendString appends the base64 URL-safe encoding to dst and returns the extended buffer. This is the zero-allocation version of String() when dst has sufficient capacity.

func (PackedFEN) CastlingRights

func (pf PackedFEN) CastlingRights() byte

CastlingRights returns the castling rights byte. Bit 0: White kingside, Bit 1: White queenside, Bit 2: Black kingside, Bit 3: Black queenside.

func (PackedFEN) EPFile

func (pf PackedFEN) EPFile() int

EPFile returns the en passant file (0-7), or -1 if none.

func (PackedFEN) EPSquare

func (pf PackedFEN) EPSquare() Square

EPSquare returns the en passant target square, or -1 if none.

func (PackedFEN) Fullmove

func (pf PackedFEN) Fullmove() int

Fullmove returns the fullmove number (1-65535).

func (PackedFEN) Halfmove

func (pf PackedFEN) Halfmove() int

Halfmove returns the halfmove clock (0-255).

func (PackedFEN) Occupancy

func (pf PackedFEN) Occupancy() uint64

Occupancy returns the 64-bit bitmap of occupied squares.

func (PackedFEN) PieceAt

func (pf PackedFEN) PieceAt(sq Square) byte

PieceAt returns the piece at the given square, or 0 if empty.

func (PackedFEN) PieceCount

func (pf PackedFEN) PieceCount() int

PieceCount returns the number of pieces on the board.

func (*PackedFEN) SetCastlingRights

func (pf *PackedFEN) SetCastlingRights(castle byte)

SetCastlingRights sets the castling rights.

func (*PackedFEN) SetEPFile

func (pf *PackedFEN) SetEPFile(file int)

SetEPFile sets the en passant file (0-7), or -1 for none.

func (*PackedFEN) SetFullmove

func (pf *PackedFEN) SetFullmove(fm int)

SetFullmove sets the fullmove number (capped at 65535, minimum 1).

func (*PackedFEN) SetHalfmove

func (pf *PackedFEN) SetHalfmove(hm int)

SetHalfmove sets the halfmove clock (capped at 255).

func (*PackedFEN) SetSideToMove

func (pf *PackedFEN) SetSideToMove(c Color)

SetSideToMove sets the side to move.

func (PackedFEN) SideToMove

func (pf PackedFEN) SideToMove() Color

SideToMove returns the side to move (White or Black).

func (PackedFEN) String

func (pf PackedFEN) String() string

String returns the base64 URL-safe encoding of the packed FEN.

func (PackedFEN) ToFEN

func (pf PackedFEN) ToFEN() string

ToFEN converts the packed FEN to a FEN string.

func (PackedFEN) ToPackedPosition

func (pf PackedFEN) ToPackedPosition() PackedPosition

ToPackedPosition extracts the position (without move counters).

func (PackedFEN) Unpack

func (pf PackedFEN) Unpack() *GameState

Unpack decodes a PackedFEN into a GameState.

func (PackedFEN) UnpackInto added in v3.1.0

func (pf PackedFEN) UnpackInto(gs *GameState)

UnpackInto decodes a PackedFEN into an existing GameState (zero allocation). The GameState is fully reset before unpacking.

type PackedPosition

type PackedPosition [26]byte

PackedPosition is a compact 26-byte representation of a chess position. Layout:

  • Bytes 0-7: Occupancy bitmap (bit i set means square i has a piece)
  • Bytes 8-23: Piece codes (4 bits each, up to 32 pieces, in occupancy bit order)
  • Byte 24: Flags (side to move, castling rights)
  • Byte 25: EP file (0-7, or 0xFF for none)

func ParsePackedPosition

func ParsePackedPosition(s string) (PackedPosition, error)

ParsePackedPosition decodes a base64 URL-safe encoded packed position.

func ParsePackedPositionBytes added in v3.1.0

func ParsePackedPositionBytes(src []byte) (PackedPosition, error)

ParsePackedPositionBytes decodes a base64 URL-safe encoded packed position from bytes. This can avoid allocation when the input is already a byte slice.

func (PackedPosition) AppendString added in v3.1.0

func (pp PackedPosition) AppendString(dst []byte) []byte

AppendString appends the base64 URL-safe encoding to dst and returns the extended buffer. This is the zero-allocation version of String() when dst has sufficient capacity.

func (PackedPosition) CastlingRights

func (pp PackedPosition) CastlingRights() byte

CastlingRights returns the castling rights byte. Bit 0: White kingside, Bit 1: White queenside, Bit 2: Black kingside, Bit 3: Black queenside.

func (PackedPosition) EPFile

func (pp PackedPosition) EPFile() int

EPFile returns the en passant file (0-7), or -1 if none.

func (PackedPosition) EPSquare

func (pp PackedPosition) EPSquare() Square

EPSquare returns the en passant target square, or -1 if none.

func (PackedPosition) Occupancy

func (pp PackedPosition) Occupancy() uint64

Occupancy returns the 64-bit bitmap of occupied squares.

func (PackedPosition) PieceAt

func (pp PackedPosition) PieceAt(sq Square) byte

PieceAt returns the piece at the given square, or 0 if empty. Returns piece character: P, N, B, R, Q, K (white) or p, n, b, r, q, k (black).

func (PackedPosition) PieceCount

func (pp PackedPosition) PieceCount() int

PieceCount returns the number of pieces on the board.

func (PackedPosition) Pieces

func (pp PackedPosition) Pieces(fn func(sq Square, piece byte))

Pieces iterates over all pieces on the board. Calls fn with (square, piece character) for each piece.

func (*PackedPosition) SetCastlingRights

func (pp *PackedPosition) SetCastlingRights(castle byte)

SetCastlingRights sets the castling rights.

func (*PackedPosition) SetEPFile

func (pp *PackedPosition) SetEPFile(file int)

SetEPFile sets the en passant file (0-7), or -1 for none.

func (*PackedPosition) SetSideToMove

func (pp *PackedPosition) SetSideToMove(c Color)

SetSideToMove sets the side to move.

func (PackedPosition) SideToMove

func (pp PackedPosition) SideToMove() Color

SideToMove returns the side to move (White or Black).

func (PackedPosition) String

func (pp PackedPosition) String() string

String returns the base64 URL-safe encoding of the packed position.

func (PackedPosition) ToFEN

func (pp PackedPosition) ToFEN() string

ToFEN converts the packed position to a FEN string (with default metadata).

func (PackedPosition) Unpack

func (pp PackedPosition) Unpack() *GameState

Unpack decodes a PackedPosition into a GameState.

func (PackedPosition) UnpackInto added in v3.1.0

func (pp PackedPosition) UnpackInto(gs *GameState)

UnpackInto decodes a PackedPosition into an existing GameState (zero allocation). The GameState is fully reset before unpacking.

type ParentCandidate added in v3.2.0

type ParentCandidate struct {
	Position PackedPosition
	Move     Mv // The forward move from parent to current position
}

ParentCandidate represents a candidate parent position with the connecting move.

func GenerateParentPositions added in v3.2.0

func GenerateParentPositions(pos *GameState) []ParentCandidate

GenerateParentPositions generates all candidate parent positions. For each candidate, Move indicates the forward move from parent to current position.

This generates pseudo-legal retro-moves and should be validated by: 1. Looking up Position in your store 2. If found, verifying Move is in GenerateLegalMoves(parent)

type PositionEnumeratorDFS

type PositionEnumeratorDFS struct {
	// contains filtered or unexported fields
}

PositionEnumeratorDFS performs depth-first enumeration.

func NewPositionEnumeratorDFS

func NewPositionEnumeratorDFS(startPos *GameState) *PositionEnumeratorDFS

NewPositionEnumeratorDFS creates a DFS enumerator.

func (*PositionEnumeratorDFS) ContinueFromCheckpoint

func (e *PositionEnumeratorDFS) ContinueFromCheckpoint(
	ckptIdx int,
	maxDepth int,
	callback func(uint64, *GameState, int) bool,
) error

ContinueFromCheckpoint continues enumeration from a checkpoint. This allows extending enumeration depth or resuming after interruption.

func (*PositionEnumeratorDFS) CurrentIndexDFS

func (e *PositionEnumeratorDFS) CurrentIndexDFS() uint64

CurrentIndexDFS returns the current enumeration index.

func (*PositionEnumeratorDFS) EnumerateDFS

func (e *PositionEnumeratorDFS) EnumerateDFS(
	maxDepth int,
	callback func(index uint64, pos *GameState, depth int) bool,
)

EnumerateDFS performs depth-first enumeration with make/unmake. This is MUCH more memory efficient than BFS - only O(depth) memory.

The enumeration order is deterministic based on move generation order. Terminal conditions:

  • Checkmate/Stalemate (no legal moves)
  • 50-move rule (halfmove >= 100)
  • Threefold repetition (same position 3x on path)
  • Max depth reached (safety limit)
  • Callback returns false (early stop)

The callback should return true to continue, false to stop enumeration.

func (*PositionEnumeratorDFS) EnumerateDFSParallel

func (e *PositionEnumeratorDFS) EnumerateDFSParallel(
	maxDepth int,
	callback func(index uint64, pos *GameState, depth int) bool,
)

EnumerateDFSParallel performs two-pass parallel enumeration with deterministic ordering.

Pass 1: Count subtree sizes for each root move (parallel) Pass 2: Enumerate each subtree with correct index offsets (parallel)

This maintains deterministic position-to-index mapping while using all CPU cores.

func (*PositionEnumeratorDFS) GetCheckpointForIndexDFS

func (e *PositionEnumeratorDFS) GetCheckpointForIndexDFS(targetIdx uint64) *CheckpointDFS

GetCheckpointForIndexDFS finds the checkpoint at or before the given index.

func (*PositionEnumeratorDFS) GetCheckpointsDFS

func (e *PositionEnumeratorDFS) GetCheckpointsDFS() []*CheckpointDFS

GetCheckpointsDFS returns all stored checkpoints.

func (*PositionEnumeratorDFS) IndexOfBoardDFS

func (e *PositionEnumeratorDFS) IndexOfBoardDFS(target *GameState, maxDepth int) (uint64, bool)

IndexOfBoardDFS searches for a board state (ignoring move counters) and returns its first index. This is faster than IndexOfPositionDFS when you only care about the board configuration.

func (*PositionEnumeratorDFS) IndexOfPositionDFS

func (e *PositionEnumeratorDFS) IndexOfPositionDFS(target *GameState, maxDepth int) (uint64, bool)

IndexOfPositionDFS searches for a position and returns its index. Requires exact match including halfmove and fullmove counters.

func (*PositionEnumeratorDFS) LoadCheckpointsCSV

func (e *PositionEnumeratorDFS) LoadCheckpointsCSV(filename string) (int, error)

LoadCheckpointsCSV loads checkpoints from a CSV file. Returns the number of checkpoints loaded. If filename ends with .zstd or .zst, the input is decompressed.

func (*PositionEnumeratorDFS) PositionAtIndexDFS

func (e *PositionEnumeratorDFS) PositionAtIndexDFS(targetIdx uint64, maxDepth int) (*GameState, bool)

PositionAtIndexDFS returns the position at a specific index. This replays the DFS from the start until reaching the target index.

func (*PositionEnumeratorDFS) SaveCheckpointsCSV

func (e *PositionEnumeratorDFS) SaveCheckpointsCSV(filename string, maxDepth int) error

SaveCheckpointsCSV writes checkpoints to a CSV file with metadata. Format: index,depth,fen,stack with maxDepth metadata header. The stack field contains comma-separated UCI moves. If filename ends with .zstd or .zst, the output is compressed.

type PromoPiece

type PromoPiece int

PromoPiece represents the piece type for pawn promotion.

const (
	NoPromo PromoPiece = iota
	PromoQueen
	PromoRook
	PromoBishop
	PromoKnight
)

type Square

type Square int

Square is a board square index from 0-63, where a1=0, b1=1, ..., h8=63.

const (
	SqA1 Square = iota
	SqB1
	SqC1
	SqD1
	SqE1
	SqF1
	SqG1
	SqH1
	SqA2
	SqB2
	SqC2
	SqD2
	SqE2
	SqF2
	SqG2
	SqH2
	SqA3
	SqB3
	SqC3
	SqD3
	SqE3
	SqF3
	SqG3
	SqH3
	SqA4
	SqB4
	SqC4
	SqD4
	SqE4
	SqF4
	SqG4
	SqH4
	SqA5
	SqB5
	SqC5
	SqD5
	SqE5
	SqF5
	SqG5
	SqH5
	SqA6
	SqB6
	SqC6
	SqD6
	SqE6
	SqF6
	SqG6
	SqH6
	SqA7
	SqB7
	SqC7
	SqD7
	SqE7
	SqF7
	SqG7
	SqH7
	SqA8
	SqB8
	SqC8
	SqD8
	SqE8
	SqF8
	SqG8
	SqH8
	SqNone Square = -1
)

Square constants for convenient reference.

func MakeSquare

func MakeSquare(file, rank int) Square

MakeSquare creates a Square from file (0-7) and rank (0-7).

func ParseSquare

func ParseSquare(s string) (Square, error)

ParseSquare parses algebraic notation (e.g., "e4") to a Square.

func (Square) File

func (sq Square) File() int

File returns the file (0-7 for a-h) of the square.

func (Square) Rank

func (sq Square) Rank() int

Rank returns the rank (0-7 for 1-8) of the square.

func (Square) String

func (sq Square) String() string

String returns the algebraic notation of the square (e.g., "e4").

type UndoInfo

type UndoInfo struct {
	// contains filtered or unexported fields
}

UndoInfo stores the state needed to unmake a move.

func MakeMove

func MakeMove(pos *GameState, m Mv) UndoInfo

MakeMove applies a move in-place and returns undo info for UnmakeMove. This is the fast path for perft and search where we need to undo moves.

Directories

Path Synopsis
cmd
build_checkpoints command
build_checkpoints is a tool for building position index checkpoint files.
build_checkpoints is a tool for building position index checkpoint files.
zsttest command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL