chess

package module
v3.0.0-beta.4 Latest Latest
Warning

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

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 20 Imported by: 0

README

Chess — Go (Golang) chess library

pkg.go.dev Go Report Card License codecov CI Go Version

A fast, well-tested Go chess library for move generation, PGN/FEN, UCI/Stockfish, opening books, and board image generation.

Features

  • Legal move generation via bitboards
  • PGN encode/decode, plus a streaming PGNDecoder for large game databases
  • FEN encode/decode for board positions
  • UCI client to drive engines like Stockfish
  • Opening book exploration (ECO — Encyclopaedia of Chess Openings)
  • SVG board image generation
  • Notations: Algebraic (SAN), Long Algebraic, and UCI
  • Checkmate, stalemate, repetition, and draw detection
  • Variations support in the move tree
  • Unsafe* fast-path APIs for performance-critical paths

Table of Contents

Introduction

chess is a set of go packages which provide common chess utilities such as move generation, turn management, checkmate detection, PGN encoding, UCI interoperability, image generation, opening book exploration, and others. It is well tested and optimized for performance.

Chess board rendered from the starting position by the Go chess library

Recent Updates

Comprehensive Move Validation: All move methods now properly validate moves according to chess rules, returning descriptive errors for invalid moves. This ensures consistent game correctness across all move APIs.

Performance Options: Added unsafe variants for high-performance scenarios:

  • UnsafeMove() - ~1.5x faster than Move()
  • UnsafeMoveText() - ~1.1x faster than MoveText()

API Consistency: Refactored move methods for clear validation behavior and consistent performance options across all move APIs.

Why I Forked

I forked the original notnil/chess package for several reasons:

  • Update Rate: The original package was not being updated at the pace I needed for my projects.
  • Pending PRs: There were numerous pull requests that needed to be merged to make the package production-ready for my work.
  • Performance and Allocations: I wanted to improve overall performance and reduce memory allocations.
  • Customization: I had specific changes in mind that would not be easily integrated into the original package.

Credits

I want to extend my gratitude to the original author of notnil/chess for their amazing work. This fork is not intended to steal or replace their work but to build upon it, providing an alternative for the open-source community and allowing for faster development.

Disclaimer

Breaking Changes: v3 is a major release with API changes that are not backward compatible with v2. See the MIGRATION.md guide for a detailed list of breaking changes and how to update your code.

Maintenance: This package is primarily maintained for my current work and projects. It is shared as a respect for the original work and to contribute to the community. My main focus is:

  • Rewriting code to reduce allocations
  • Replacing strings with more efficient data structures where possible
  • Improving performance
  • Expanding test coverage and benchmarks
  • Rewriting the parser for better performance and more features
    • Potential major changes to the game representation to support variations

Contributions

I am open to suggestions, pull requests, and contributions from anyone interested in improving this library. If you have ideas or want to help make this package more robust and widely usable, please feel free to:

  • Open issues for bugs or feature requests
  • Submit pull requests with improvements or fixes
  • Contact me directly for discussions or ideas

Repo Structure

Package Docs Link Description
chess corentings/chess Move generation, serialization / deserialization, turn management, checkmate detection
image corentings/chess/image SVG chess board image generation
opening corentings/chess/opening Opening book interactivity
uci corentings/chess/uci Universal Chess Interface client

Installation

chess can be installed using "go get".

go get -u github.com/corentings/chess/v3

Usage

Example Random Game
package main

import (
	"fmt"
	"math/rand"

	"github.com/corentings/chess/v3"
)

func main() {
	game := chess.NewGame()
	// generate moves until game is over
	for game.Outcome() == chess.NoOutcome {
		// select a random move
		moves := game.ValidMoves()
		move := moves[rand.Intn(len(moves))]
		if err := game.Move(move, nil); err != nil {
			panic(err) // Should not happen with valid moves
		}
	}
	// print outcome and game PGN
	fmt.Println(game.Position().Board().Draw())
	fmt.Printf("Game completed. %s by %s.\n", game.Outcome(), game.Method())
	fmt.Println(game.String())
	/*
		Output:

		 A B C D E F G H
		8- - - - - - - -
		7- - - - - - ♚ -
		6- - - - ♗ - - -
		5- - - - - - - -
		4- - - - - - - -
		3♔ - - - - - - -
		2- - - - - - - -
		1- - - - - - - -

		Game completed. 1/2-1/2 by InsufficientMaterial.

		1.Nc3 b6 2.a4 e6 3.d4 Bb7 ...
	*/
}
Example Stockfish v. Stockfish
package main

import (
	"fmt"
	"time"

	"github.com/corentings/chess/v3"
	"github.com/corentings/chess/v3/uci"
)

func main() {
	// set up engine to use stockfish exe
	eng, err := uci.New("stockfish")
	if err != nil {
		panic(err)
	}
	defer eng.Close()
	// initialize uci with new game
	if err := eng.Run(uci.CmdUCI{}, uci.CmdIsReady{}, uci.CmdUCINewGame{}); err != nil {
		panic(err)
	}
	// have stockfish play speed chess against itself (10 msec per move)
	game := chess.NewGame()
	for game.Outcome() == chess.NoOutcome {
		cmdPos := uci.CmdPosition{Position: game.Position()}
		cmdGo := uci.CmdGo{MoveTime: time.Second / 100}
		if err := eng.Run(cmdPos, cmdGo); err != nil {
			panic(err)
		}
		move := eng.SearchResults().BestMove
		if err := game.Move(move, nil); err != nil {
			panic(err)
		}
	}
	fmt.Println(game.String())
	// Output: 
	// 1.c4 c5 2.Nf3 e6 3.Nc3 Nc6 4.d4 cxd4 5.Nxd4 Nf6 6.a3 d5 7.cxd5 exd5 8.Bf4 Bc5 9.Ndb5 O-O 10.Nc7 d4 11.Na4 Be7 12.Nxa8 Bf5 13.g3 Qd5 14.f3 Rxa8 15.Bg2 Rd8 16.b4 Qe6 17.Nc5 Bxc5 18.bxc5 Nd5 19.O-O Nc3 20.Qd2 Nxe2+ 21.Kh1 d3 22.Bd6 Qd7 23.Rab1 h6 24.a4 Re8 25.g4 Bg6 26.a5 Ncd4 27.Qb4 Qe6 28.Qxb7 Nc2 29.Qxa7 Ne3 30.Rb8 Nxf1 31.Qb6 d2 32.Rxe8+ Qxe8 33.Qb3 Ne3 34.h3 Bc2 35.Qxc2 Nxc2 36.Kh2 d1=Q 37.h4 Qg1+ 38.Kh3 Ne1 39.h5 Qxg2+ 40.Kh4 Nxf3#  0-1
}
Movement

Chess provides multiple ways of making moves: direct move execution, valid move generation, and notation parsing. All move methods include proper validation to ensure game correctness.

Move Methods

The library offers two move execution methods to balance safety and performance:

Move() - Validates moves before execution (recommended for general use):

game := chess.NewGame()
moves := game.ValidMoves()
err := game.Move(moves[0], nil)
if err != nil {
// Handle invalid move error
}

UnsafeMove() - High-performance move execution without validation:

game := chess.NewGame()
moves := game.ValidMoves()
// Only use when you're certain the move is valid
err := game.UnsafeMove(moves[0], nil)
if err != nil {
// Handle error (should not occur with valid moves)
}

MoveText() - Validates moves using any notation (recommended for general use):

game := chess.NewGame()
err := game.MoveText("e4", chess.SAN(), nil)
if err != nil {
// Handle invalid move or notation error
}

UnsafeMoveText() - High-performance notation parsing without move validation:

game := chess.NewGame()
// Only use when you're certain the move is valid
err := game.UnsafeMoveText("e2e4", chess.UCI(), nil)
if err != nil {
// Handle notation parsing error (should not occur with valid notation)
}

Performance Note:

  • UnsafeMove() provides ~1.5x performance improvement over Move() by skipping validation
  • UnsafeMoveText() provides ~1.1x performance improvement over MoveText() by skipping move validation
  • Use unsafe variants only when moves are pre-validated or known to be legal
Valid Moves

Valid moves generated from the game's current position:

game := chess.NewGame()
moves := game.ValidMoves()
game.Move(moves[0], nil)
fmt.Println(moves[0]) // b1a3
Parse Move Text

MoveText accepts move text using an explicit codec:

game := chess.NewGame()
if err := game.MoveText("e4", chess.SAN(), nil); err != nil {
// handle error
}
Move Validation

All move methods automatically validate moves according to chess rules. The Move() method validates moves before execution and returns descriptive errors for invalid moves:

game := chess.NewGame()

// Get valid moves from current position
validMoves := game.ValidMoves()
if len(validMoves) > 0 {
// This will succeed - move is known to be valid
if err := game.Move(validMoves[0], nil); err != nil {
fmt.Println("Move failed:", err)
} else {
fmt.Println("Move succeeded")
}
}

// Using notation parsing with validation
if err := game.MoveText("e4", chess.SAN(), nil); err != nil {
fmt.Println("Move failed:", err)
} else {
fmt.Println("e4 move succeeded")
}

// Invalid notation will be caught
if err := game.MoveText("e5", chess.SAN(), nil); err != nil {
fmt.Println("Move failed:", err)
// Output: Move failed: [invalid move error]
}

For scenarios requiring maximum performance where moves are already validated:

game := chess.NewGame()

// Option 1: Using Move structs directly (~1.5x faster)
validMoves := game.ValidMoves()
selectedMove := validMoves[0] // We know this is valid
if err := game.UnsafeMove(selectedMove, nil); err != nil {
panic(err) // Should not happen with pre-validated moves
}

// Option 2: Using notation (~1.1x faster)  
if err := game.UnsafeMoveText("e4", chess.SAN(), nil); err != nil {
panic(err) // Should not happen with valid notation/moves
}
Outcome

The outcome of the match is calculated automatically from the inputted moves if possible. Draw agreements, resignations, and other human initiated outcomes can be inputted as well.

Checkmate

Black wins by checkmate (Fool's Mate):

game := chess.NewGame()
game.MoveText("f3", chess.SAN(), nil)
game.MoveText("e6", chess.SAN(), nil)
game.MoveText("g4", chess.SAN(), nil)
game.MoveText("Qh4", chess.SAN(), nil)
fmt.Println(game.Outcome()) // 0-1
fmt.Println(game.Method()) // Checkmate
/*
 A B C D E F G H
8♜ ♞ ♝ - ♚ ♝ ♞ ♜
7♟ ♟ ♟ ♟ - ♟ ♟ ♟
6- - - - ♟ - - -
5- - - - - - - -
4- - - - - - ♙ ♛
3- - - - - ♙ - -
2♙ ♙ ♙ ♙ ♙ - - ♙
1♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
*/
Stalemate

Black king has no safe move:

fenStr := "k1K5/8/8/8/8/8/8/1Q6 w - - 0 1"
fen, _ := chess.FEN(fenStr)
game := chess.NewGame(fen)
game.MoveText("Qb6", chess.SAN(), nil)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // Stalemate
/*
 A B C D E F G H
8♚ - ♔ - - - - -
7- - - - - - - -
6- ♕ - - - - - -
5- - - - - - - -
4- - - - - - - -
3- - - - - - - -
2- - - - - - - -
1- - - - - - - -
*/
Resignation

Black resigns and white wins:

game := chess.NewGame()
game.MoveText("f3", chess.SAN(), nil)
if err := game.Resign(chess.Black); err != nil {
	panic(err)
}
fmt.Println(game.Outcome()) // 1-0
fmt.Println(game.Method()) // Resignation
Draw Offer

Draw by mutual agreement:

game := chess.NewGame()
game.Draw(chess.DrawOffer)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // DrawOffer
Threefold Repetition

Threefold repetition occurs when the position repeats three times (not necessarily in a row). If this occurs both players have the option of taking a draw, but aren't required until Fivefold Repetition.

game := chess.NewGame()
moves := []string{"Nf3", "Nf6", "Ng1", "Ng8", "Nf3", "Nf6", "Ng1", "Ng8"}
for _, m := range moves {
game.MoveText(m, chess.SAN(), nil)
}
fmt.Println(game.EligibleDraws()) //  [DrawOffer ThreefoldRepetition]
Fivefold Repetition

According to the FIDE Laws of Chess if a position repeats five times then the game is drawn automatically.

game := chess.NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
game.MoveText(m, chess.SAN(), nil)
}
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // FivefoldRepetition
Fifty Move Rule

Fifty-move rule allows either player to claim a draw if no capture has been made and no pawn has been moved in the last fifty moves.

fen, _ := chess.FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 100 23")
game := chess.NewGame(fen)
game.Draw(chess.FiftyMoveRule)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // FiftyMoveRule
Seventy Five Move Rule

According to FIDE Laws of Chess Rule 9.6b if 75 consecutive moves have been made without movement of any pawn or any capture, the game is drawn unless the last move was checkmate.

fen, _ := chess.FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 149 23")
game := chess.NewGame(fen)
game.MoveText("Kf8", chess.SAN(), nil)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // SeventyFiveMoveRule
Insufficient Material

Impossibility of checkmate, or insufficient material, results when neither white or black has the pieces remaining to checkmate the opponent.

fen, _ := chess.FEN("8/2k5/8/8/8/3K4/8/8 w - - 1 1")
game := chess.NewGame(fen)
fmt.Println(game.Outcome()) // 1/2-1/2
fmt.Println(game.Method()) // InsufficientMaterial
PGN

PGN, or Portable Game Notation, is the most common serialization format for chess matches. PGNs include move history and metadata about the match. Chess includes the ability to read and write the PGN format.

Example PGN
[Event "F/S Return Match"]
[Site "Belgrade, Serbia JUG"]
[Date "1992.11.04"]
[Round "29"]
[White "Fischer, Robert J."]
[Black "Spassky, Boris V."]
[Result "1/2-1/2"]

1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 {This opening is called the Ruy Lopez.}
4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 7. Bb3 d6 8. c3 O-O 9. h3 Nb8 10. d4 Nbd7
11. c4 c6 12. cxb5 axb5 13. Nc3 Bb7 14. Bg5 b4 15. Nb1 h6 16. Bh4 c5 17. dxe5
Nxe4 18. Bxe7 Qxe7 19. exd6 Qf6 20. Nbd2 Nxd6 21. Nc4 Nxc4 22. Bxc4 Nb6
23. Ne5 Rae8 24. Bxf7+ Rxf7 25. Nxf7 Rxe1+ 26. Qxe1 Kxf7 27. Qe3 Qg5 28. Qxg5
hxg5 29. b3 Ke6 30. a3 Kd6 31. axb4 cxb4 32. Ra5 Nd5 33. f3 Bc8 34. Kf2 Bf5
35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5 40. Rd6 Kc5 41. Ra6
Nf2 42. g4 Bd3 43. Re6 1/2-1/2
Read PGN

PGN supplied as an optional parameter to the NewGame constructor:

pgn, err := chess.PGN(pgnReader)
if err != nil {
// handle error
}
game := chess.NewGame(pgn)
Write PGN

Moves and tag pairs added to the PGN output:

game := chess.NewGame()
game.AddTagPair("Event", "F/S Return Match")
game.MoveText("e4", chess.SAN(), nil)
game.MoveText("e5", chess.SAN(), nil)
fmt.Println(game)
/*
[Event "F/S Return Match"]

1.e4 e5  *
*/
PGN comment annotations

Parsed PGN comments preserve comment block boundaries, command annotation order, and duplicate command annotations. Use MoveNode.CommentBlocks() when an importer needs structured access to each {...} block and the ordered text or command items inside it. The returned blocks are defensive copies.

game := chess.NewGame(pgn)
node := game.MoveTree().MainLine()[0]

for _, block := range node.CommentBlocks() {
	for _, item := range block.Items {
		switch item.Kind {
		case chess.CommentText:
			fmt.Println(item.Text)
		case chess.CommentCommand:
			fmt.Printf("%s=%s\n", item.Key, item.Value)
		}
	}
}

The helpers MoveNode.Comments(), MoveNode.GetCommand(), MoveNode.SetCommand(), MoveNode.SetComment(), and MoveNode.AddComment() remain available for callers that only need a flattened text comment or single command value.

Scan PGN

For parsing large PGN database files, use PGNGames (Go 1.23+ iterator):

f, err := os.Open("lichess_db_standard_rated_2013-01.pgn")
if err != nil {
    panic(err)
}
defer f.Close()

for game, err := range chess.PGNGames(f) {
    if err != nil {
        log.Fatal("Failed to parse game: %v", err)
    }
    fmt.Println(game.GetTagPair("Site"))
    // Output &{Site https://lichess.org/8jb5kiqw}
}

Or use PGNDecoder directly for index/offset access:

f, err := os.Open("lichess_db_standard_rated_2013-01.pgn")
if err != nil {
    panic(err)
}
defer f.Close()

dec := chess.NewPGNDecoder(f)
for {
    game, err := dec.Decode()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal("Failed to parse game: %v", err)
    }
    fmt.Println(game.GetTagPair("Site"))
}

PGNDecoder fully decodes each record into a Game, including move tree positions, SAN resolution, checks, castling, promotions, en passant, comments, NAGs, and tag pairs. In v3.0.0-beta.2 this path was tuned for large PGN imports: SAN moves are resolved from direct candidate origins, and internal position snapshots avoid a separate heap-allocated board copy while public defensive-copy APIs remain unchanged.

Scan PGN expanding all variations

To expand every variation into a distinct Game:

f, err := os.Open("lichess_db_standard_rated_2013-01.pgn")
if err != nil {
    panic(err)
}
defer f.Close()

for game, err := range chess.PGNGames(f, chess.WithPGNExpandVariations()) {
    if err != nil {
        log.Fatal("Failed to parse game: %v", err)
    }
    fmt.Println(game.GetTagPair("Site"))
    // Output &{Site https://lichess.org/8jb5kiqw}
}
FEN

FEN, or Forsyth–Edwards Notation, is the standard notation for describing a board position. FENs include piece positions, turn, castle rights, en passant square, half move counter ( for 50 move rule), and full move counter.

Read FEN

FEN supplied as an optional parameter to the NewGame constructor:

fen, err := chess.FEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
if err != nil {
// handle error
}
game := chess.NewGame(fen)
Write FEN

Game's current position outputted in FEN notation:

game := chess.NewGame()
pos := game.Position()
fmt.Println(pos.String()) // rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
Move Text Codecs

Chess Notation define how moves are encoded in a serialized format. Chess uses move text codecs when converting to and from PGN and for accepting move text.

Algebraic Notation

Algebraic Notation (or Standard Algebraic Notation) is the official chess notation used by FIDE. Examples: e2, e5, O-O (short castling), e8=Q (promotion)

game := chess.NewGame()
game.MoveText("e4", chess.SAN(), nil)
game.MoveText("e5", chess.SAN(), nil)
fmt.Println(game) // 1.e4 e5  *
Long Algebraic Notation

Long Algebraic Notation LongAlgebraic() is a more beginner friendly alternative to algebraic notation, where the origin of the piece is visible as well as the destination. Examples: Rd1xd8+, Ng8f6.

game := chess.NewGame()
game.MoveText("f2f3", chess.LongAlgebraic(), nil)
game.MoveText("e7e5", chess.LongAlgebraic(), nil)
game.MoveText("g2g4", chess.LongAlgebraic(), nil)
game.MoveText("Qd8h4", chess.LongAlgebraic(), nil)
fmt.Println(game) // 1.f2f3 e7e5 2.g2g4 Qd8h4#  0-1
UCI Notation

UCI notation is a more computer friendly alternative to algebraic notation. This notation is the Universal Chess Interface notation. Examples: e2e4, e7e5, e1g1 (white short castling), e7e8q (for promotion)

game := chess.NewGame()
game.MoveText("e2e4", chess.UCI(), nil)
game.MoveText("e7e5", chess.UCI(), nil)
fmt.Println(game) // 1.e4 e5  *
Text Representation

Board's Draw() method can be used to visualize a position using unicode chess symbols.

game := chess.NewGame()
fmt.Println(game.Position().Board().Draw())
/*
 A B C D E F G H
8♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜
7♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
6- - - - - - - -
5- - - - - - - -
4- - - - - - - -
3- - - - - - - -
2♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙
1♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
*/
Moves

MoveList is a convenient API for accessing the main-line moves and their comments. MoveList is useful when iterating the moves of a game. Below is an example showing how to see which side castled first.

package main

import (
	"fmt"
	"os"

	"github.com/corentings/chess/v3"
)

func main() {
	f, err := os.Open("fixtures/pgns/0001.pgn")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	pgn, err := chess.PGN(f)
	if err != nil {
		panic(err)
	}
	game := chess.NewGame(pgn)
	color := chess.NoColor
	// Replay positions alongside the moves via the tree's cursor.
	tree := game.MoveTree()
	tree.Reset()
	for _, entry := range game.MoveList() {
		preTurn := tree.Peek().Turn()
		if entry.Move.HasTag(chess.KingSideCastle) || entry.Move.HasTag(chess.QueenSideCastle) {
			color = preTurn
			break
		}
		tree.GoForward()
	}
	switch color {
	case chess.White:
		fmt.Println("white castled first")
	case chess.Black:
		fmt.Println("black castled first")
	default:
		fmt.Println("no side castled")
	}
}

Perft

Perft (performance test) is the standard correctness and performance benchmark for a chess move generator: it counts every legal move sequence of a given length reachable from a starting position. This package exposes Perft on *Position; the traversal uses the same legality rules that govern normal play while applying moves through an internal make/unmake path for performance.

Bulk node count
package main

import (
	"fmt"

	"github.com/corentings/chess/v3"
)

func main() {
	pos := chess.StartingPosition()
	fmt.Println("startpos d5:", pos.Perft(5)) // 4 865 609
	fmt.Println("startpos d6:", pos.Perft(6)) // 119 060 324
}

A depth of 0 returns 1 (the position itself counts as a leaf). A position with no legal moves (checkmate or stalemate) returns 0 for any depth greater than 0.

Per-move breakdown (Divide)

Divide returns the per-root-move Perft breakdown as a map from each legal move to its leaf count. It is the standard way to localize a divergence when two move generators disagree at a given depth.

package main

import (
	"fmt"
	"sort"

	"github.com/corentings/chess/v3"
)

func main() {
	pos := chess.StartingPosition()
	results := pos.Divide(1)

	keys := make([]chess.Move, 0, len(results))
	for m := range results {
		keys = append(keys, m)
	}
	sort.Slice(keys, func(i, j int) bool {
		return keys[i].String() < keys[j].String()
	})
	for _, m := range keys {
		fmt.Printf("%s: %d\n", m, results[m])
	}
}

Map iteration order is unspecified; sort by Move.String() for a stable display. Move strings are long-algebraic (s1s2 plus a promotion suffix) and are intended for debugging, not for chess notation.

Canonical positions

The package's Perft tests verify six canonical positions from chessprogramming.org/Perft_Results through depth 4–6. For a side-by-side performance comparison against another Go chess library, see benchcmp/perft/.

Move Tree

A Game owns a MoveTree containing the moves that have been played. The root is a synthetic position node before any move has been played. Each non-root MoveNode holds the Move it represents, its Position after the move, its parent and ordered continuations, plus comments, NAGs, and [%clk ...]-style command annotations. The first continuation is the main line; later continuations are variations.

Use Game.Move, Game.UnsafeMove, Game.MoveText, or Game.UnsafeMoveText to play moves on the active cursor. Those methods keep game legality, terminal outcome guards, and result evaluation in sync with the tree. MoveTree exposes traversal, cursor navigation, and variation editing; it does not expose a public API for advancing the active game state directly.

The older names Game.PushMove, Game.PushMoveText, and Game.UnsafePushMoveText are deprecated but remain functional for this major version.

package main

import (
	"fmt"

	"github.com/corentings/chess/v3"
)

func main() {
	g := chess.NewGame()
	g.MoveText("e4", chess.SAN(), nil)
	g.MoveText("e5", chess.SAN(), nil)

	// Walk the main line.
	for _, n := range g.MoveTree().MainLine() {
		fmt.Printf("%s ", n.Move())
	}
	fmt.Println()
}
package main

import (
	"fmt"
	"strings"

	"github.com/corentings/chess/v3"
)

func main() {
	g, _ := chess.ParsePGN(strings.NewReader(
		`[Result "*"] 1. e4 e5 (1... c5 {Sicilian}) 2. Nf3 *`,
	))

	tree := g.MoveTree()
	e4 := tree.MainChild(tree.Root()) // first move of the main line

	// Walk the main line by following the main continuation at each node.
	fmt.Print("main: ")
	for n := e4; n != nil; n = tree.MainChild(n) {
		fmt.Printf("%s ", n.Move())
	}
	fmt.Println()

	// Enumerate sideline variations at e4.
	fmt.Print("variations at e4: ")
	for _, v := range tree.Variations(e4) {
		fmt.Printf("%s ", v.Move())
	}
	fmt.Println()
}

Useful APIs over the tree:

  • Game.MoveTree() *MoveTree — returns the game tree and active cursor.
  • MoveTree.Root() *MoveNode — root position node.
  • MoveTree.Current() *MoveNode — active cursor node.
  • MoveTree.MainLine() []*MoveNode — main-line nodes, excluding the root.
  • MoveTree.MainChild(node *MoveNode) *MoveNode — main continuation.
  • MoveTree.Continuations(node *MoveNode) []*MoveNode — all continuations.
  • MoveTree.Variations(node *MoveNode) []*MoveNode — sideline continuations.
  • MoveTree.AddVariation(parent *MoveNode, move Move) (*MoveNode, error) — validate and append a sideline.
  • MoveTree.GoBack(), MoveTree.GoForward(), and MoveTree.NavigateToMainLine() — move the active cursor without changing game outcome metadata.
  • node.Move(), node.Position(), node.Comments(), node.NAG(), node.Children(), and node.Parent() — per-node data.

Perft does not store its results in the game tree; it walks a temporary move tree internally, so it does not affect the MoveTree you see from Game.

Performance

Chess has been performance tuned, using pprof, with the goal of being fast enough for use by chess bots. The original map based board representation was replaced by bitboards resulting in a large performance increase.

Benchmarks

The benchmarks can be run with the following command:

go test -bench=.

Results vary by hardware. For comparative benchmarking use benchstat:

go test -bench=. -count=10 > new.txt
benchstat base.txt new.txt

See CONTRIBUTING.md for the full benchmark workflow.

The v3.0.0-beta.2 PGN decode work was measured with:

go test -run '^$' -bench '^BenchmarkPGN_FullGameDecode_(BigBig|Big|CompleteGameDetails|ExpandVariations)$' -benchmem -count=6 ./

On the project big_big.pgn fixture, BenchmarkPGN_FullGameDecode_BigBig improved from roughly 1.48s/op and 6.62M allocs/op to roughly 0.91s/op and 5.55M allocs/op on the maintainer benchmark machine.

Documentation

Overview

Package chess provides a chess engine implementation using bitboard representation.

The package uses bitboards (64-bit integers) to represent the chess board state, where each bit corresponds to a square on the board. The squares are numbered from 0 to 63, starting from the most significant bit (A1) to the least significant bit (H8):

8 | 56 57 58 59 60 61 62 63
7 | 48 49 50 51 52 53 54 55
6 | 40 41 42 43 44 45 46 47
5 | 32 33 34 35 36 37 38 39
4 | 24 25 26 27 28 29 30 31
3 | 16 17 18 19 20 21 22 23
2 | 08 09 10 11 12 13 14 15
1 | 00 01 02 03 04 05 06 07
  -------------------------
    A  B  C  D  E  F  G  H

A bit value of 1 indicates the presence of a piece, while 0 indicates an empty square.

Usage:

// Create a new bitboard with pieces on A1 and E4
squares := map[Square]bool{
    NewSquare(FileA, Rank1): true,
    NewSquare(FileE, Rank4): true,
}
bb := newBitboard(squares)

// Check if E4 is occupied
if bb.Occupied(NewSquare(FileE, Rank4)) {
    fmt.Println("E4 is occupied")
}

// Print board representation
fmt.Println(bb.Draw())

Package chess is a go library designed to accomplish the following:

  • chess game state and move tree management
  • move validation
  • PGN encoding / decoding
  • FEN encoding / decoding

Game values are mutable and are not safe for concurrent use by multiple goroutines. Callers that share a Game between goroutines must provide their own synchronization.

Using Moves

game := chess.NewGame()
moves := game.ValidMoves()
game.Move(moves[0], nil)

Using Strict SAN Move Text

game := chess.NewGame()
game.MoveText("e4", SAN(), nil)

Using PGN

game, _ := chess.ParsePGN(pgnReader)

Using FEN

fen, _ := chess.FEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
game := chess.NewGame(fen)

Random Game

package main

import (
    "fmt"
    "math/rand/v2"

    "github.com/corentings/chess/v3"
)

func main() {
    game := chess.NewGame()
    // generate moves until game is over
    for game.Outcome() == chess.NoOutcome {
        // select a random move
        moves := game.ValidMoves()
        move := moves[rand.IntN(len(moves))]
        game.Move(move, nil)
    }
    // print outcome and game PGN
    fmt.Println(game.Position().Board().Draw())
    fmt.Printf("Game completed. %s by %s.\n", game.Outcome(), game.Method())
    fmt.Println(game.String())
}

Package chess provides PGN lexical analysis through a lexer that converts PGN text into a stream of tokens. The lexer handles all standard PGN notation including moves, annotations, comments, and game metadata. The lexer provides token-by-token processing of PGN content with proper handling of chess-specific notation and PGN syntax rules. Example usage:

// Create new lexer
lexer := NewLexer("[Event \"World Championship\"] 1. e4 e5 {Opening}")

// Process tokens
for {
	token := lexer.NextToken()
	if token.Type == EOF {
		break
	}
	// Process token
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrUnterminatedComment = func(pos int) error { return &PGNError{"unterminated comment", pos} }
	ErrUnterminatedQuote   = func(pos int) error { return &PGNError{"unterminated quote", pos} }
	ErrInvalidCommand      = func(pos int) error { return &PGNError{"invalid command in comment", pos} }
	ErrInvalidPiece        = func(pos int) error { return &PGNError{"invalid piece", pos} }
	ErrInvalidSquare       = func(pos int) error { return &PGNError{"invalid square", pos} }
	ErrInvalidRank         = func(pos int) error { return &PGNError{"invalid rank", pos} }

	// ErrInvalidFEN is the sentinel for any FEN parse failure. Specific
	// causes are wrapped with %w so callers can errors.Is(err, ErrInvalidFEN)
	// without inspecting message text.
	ErrInvalidFEN = errors.New("chess: invalid FEN")

	// ErrInvalidPGN is the sentinel for any PGN parse failure, including
	// empty input. Branch on this for "the PGN did not yield a Game".
	ErrInvalidPGN = errors.New("chess: invalid PGN")

	// ErrNoGameFound is returned when the PGN input contains no game. It
	// wraps ErrInvalidPGN so errors.Is(err, ErrInvalidPGN) also matches.
	ErrNoGameFound = fmt.Errorf("%w: no game found in PGN data", ErrInvalidPGN)

	// ErrGameAlreadyEnded is returned when a move is applied to a Game that
	// already has a terminal Outcome. Callers must ClearOutcome first.
	ErrGameAlreadyEnded = errors.New("chess: game already ended")
)

Package-level error sentinels and constructors used throughout the PGN parser. These are immutable factories, not mutable global state, so the gochecknoglobals warning is a false positive.

View Source
var (
	// ErrInvalidMoveText indicates syntactically invalid or unresolvable move text.
	ErrInvalidMoveText = errors.New("chess: invalid move text")
	// ErrMoveTextMissingPosition indicates that a codec operation needs a position.
	ErrMoveTextMissingPosition = errors.New("chess: move text requires position")
	// ErrMoveTextUnsupportedRawDecode indicates that a format cannot be decoded
	// without a position.
	ErrMoveTextUnsupportedRawDecode = errors.New("chess: move text raw decode unsupported")
	// ErrInvalidMoveTextCodec indicates an impossible codec format/policy pair.
	ErrInvalidMoveTextCodec = errors.New("chess: invalid move text codec")
	// ErrUnsafeMoveTextUnsupported indicates that a codec cannot be used with
	// unsafe move-text insertion.
	ErrUnsafeMoveTextUnsupported = errors.New("chess: unsafe move text unsupported")
)
View Source
var DefaultPGNRenderer = &PGNRenderer{}

DefaultPGNRenderer is the package-level renderer used by Game.String and Game.WritePGN.

View Source
var ErrIllegalMove = errors.New("chess: illegal move")

ErrIllegalMove is returned by resolveCanonicalMove when the supplied coordinates do not match any legal move from the given Position.

Functions

func DecodePGNGamesParallel

func DecodePGNGamesParallel(ctx context.Context, r io.Reader, opts PGNParallelOptions) <-chan PGNResult

DecodePGNGamesParallel decodes PGN records across worker goroutines.

func FEN

func FEN(fen string) (func(*Game), error)

FEN takes a string and returns a function that updates the game to reflect the FEN data. Since FEN doesn't encode prior moves, the move list will be empty. The returned function is designed to be used in the NewGame constructor. An error is returned if there is a problem parsing the FEN data.

func GetPolyglotHashes

func GetPolyglotHashes() []string

GetPolyglotHashes returns a reference to the static hash array.

func HashFromFEN

func HashFromFEN(fen string) (uint64, error)

HashFromFEN computes the Zobrist hash of a chess position from its FEN string.

func IgnoreFivefoldRepetitionDraw

func IgnoreFivefoldRepetitionDraw() func(*Game)

IgnoreFivefoldRepetitionDraw returns a Game option that disables automatic draws caused by the fivefold repetition rule. When applied, the game will not automatically end in a draw if the same position occurs five times.

func IgnoreInsufficientMaterialDraw

func IgnoreInsufficientMaterialDraw() func(*Game)

IgnoreInsufficientMaterialDraw returns a Game option that disables automatic draws caused by insufficient material. When applied, the game will not automatically end in a draw even if checkmate is impossible with the remaining pieces.

func IgnoreSeventyFiveMoveRuleDraw

func IgnoreSeventyFiveMoveRuleDraw() func(*Game)

IgnoreSeventyFiveMoveRuleDraw returns a Game option that disables automatic draws triggered by the seventy-five move rule. When applied, the game will not automatically end in a draw if one hundred fifty half-moves pass without a pawn move or capture.

func MoveToPolyglot

func MoveToPolyglot(m Move) uint16

func PGNEvents

func PGNEvents(r io.Reader, opts ...PGNOption) iter.Seq2[PGNEvent, error]

PGNEvents returns an iterator over semantic PGN events without building Games.

func PGNGames

func PGNGames(r io.Reader, opts ...PGNOption) iter.Seq2[*Game, error]

PGNGames returns an iterator over Games decoded from r.

func PGNRecords

func PGNRecords(ctx context.Context, r io.Reader, opts ...PGNOption) iter.Seq2[PGNRecord, error]

PGNRecords returns an iterator over raw PGN records.

func ParseNAG

func ParseNAG(s string) (string, error)

ParseNAG normalises a NAG spelling to its canonical numeric "$N" form.

It accepts:

  • any numeric NAG written as "$" followed by one or more digits (the six standard codes as well as ChessBase-style extended codes such as "$1406"),
  • the six move-quality symbolic spellings (! ? !! ?? !? ?!), which are mapped to their canonical numeric codes.

Leading zeros in a numeric NAG are stripped ("$01" becomes "$1").

It rejects malformed input such as "", "$", "$x", "$ 1", "!!!", or any other unrecognised spelling.

ParseNAG always returns the canonical numeric "$N" form, never a symbol.

func ValidateSAN

func ValidateSAN(s string) error

ValidateSAN checks if a string is valid Standard Algebraic notation (SAN) syntax. This function only validates the syntax, not whether the move is legal in any position. Examples of valid SAN: "e4", "Nf3", "O-O", "Qxd2+", "e8=Q#".

Types

type Bitboard

type Bitboard uint64

Bitboard is a public 64-bit set of squares. It is backed by a uint64 where the most significant bit represents A1 and the least significant bit represents H8, matching the internal bitboard layout.

func BishopAttacks

func BishopAttacks(sq Square, occupied Bitboard) Bitboard

BishopAttacks returns the set of squares a bishop on sq attacks given the supplied occupied squares.

func KingAttacks

func KingAttacks(sq Square) Bitboard

KingAttacks returns the set of squares a king on sq attacks.

func KnightAttacks

func KnightAttacks(sq Square) Bitboard

KnightAttacks returns the set of squares a knight on sq attacks.

func NewBitboardFromSquares

func NewBitboardFromSquares(squares ...Square) Bitboard

NewBitboardFromSquares returns a Bitboard with the given squares set.

func PawnAttacks

func PawnAttacks(sq Square, c Color) Bitboard

PawnAttacks returns the set of squares a pawn of color c on sq attacks.

func QueenAttacks

func QueenAttacks(sq Square, occupied Bitboard) Bitboard

QueenAttacks returns the set of squares a queen on sq attacks given the supplied occupied squares. It is the union of the bishop and rook attacks.

func RookAttacks

func RookAttacks(sq Square, occupied Bitboard) Bitboard

RookAttacks returns the set of squares a rook on sq attacks given the supplied occupied squares.

func (Bitboard) And

func (bb Bitboard) And(other Bitboard) Bitboard

And returns the intersection of bb and other.

func (Bitboard) Draw

func (bb Bitboard) Draw() string

Draw returns an 8x8 visual representation of the bitboard useful for debugging.

func (Bitboard) FirstSquare

func (bb Bitboard) FirstSquare() Square

FirstSquare returns the most significant set square. It panics if bb is empty.

func (Bitboard) Intersects

func (bb Bitboard) Intersects(other Bitboard) bool

Intersects reports whether bb and other share at least one square.

func (Bitboard) IsEmpty

func (bb Bitboard) IsEmpty() bool

IsEmpty reports whether bb contains no squares.

func (Bitboard) LastSquare

func (bb Bitboard) LastSquare() Square

LastSquare returns the least significant set square. It panics if bb is empty.

func (Bitboard) MarshalText

func (bb Bitboard) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler using the 64-character binary string representation.

func (Bitboard) Not

func (bb Bitboard) Not() Bitboard

Not returns the complement of bb (all 64 squares, flipped).

func (Bitboard) Or

func (bb Bitboard) Or(other Bitboard) Bitboard

Or returns the union of bb and other.

func (Bitboard) Popcount

func (bb Bitboard) Popcount() int

Popcount returns the number of set squares.

func (Bitboard) Squares

func (bb Bitboard) Squares(yield func(Square) bool)

Squares yields every set square using Go 1.23's range-over-func pattern.

Example:

for sq := range bb.Squares {
    // process square
}

func (Bitboard) String

func (bb Bitboard) String() string

String returns a 64-character string of '1's and '0's starting with the most significant bit (A1). This matches the internal bitboard.String() format and round-trips unambiguously.

func (Bitboard) Subtract

func (bb Bitboard) Subtract(other Bitboard) Bitboard

Subtract returns the squares in bb that are not in other.

func (*Bitboard) UnmarshalText

func (bb *Bitboard) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler from the 64-character binary string representation.

func (Bitboard) Xor

func (bb Bitboard) Xor(other Bitboard) Bitboard

Xor returns the symmetric difference of bb and other.

type Board

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

Board represents a chess board and its relationship between squares and pieces. It maintains separate bitboards for each piece type and color, along with convenience bitboards for quick position analysis.

func NewBoard

func NewBoard(m map[Square]Piece) (*Board, error)

NewBoard returns a board from a square to piece mapping. The map should contain only occupied squares.

Example:

squares := map[Square]Piece{
    NewSquare(FileE, Rank1): WhiteKing,
    NewSquare(FileE, Rank8): BlackKing,
}
board, err := NewBoard(squares)

func (*Board) Black

func (b *Board) Black() Bitboard

Black returns the squares occupied by black pieces.

func (*Board) Color

func (b *Board) Color(c Color) Bitboard

Color returns the squares occupied by pieces of the given color.

func (*Board) Draw

func (b *Board) Draw() string

Draw returns a visual ASCII representation of the board. Capital letters represent white pieces, lowercase represent black pieces. Empty squares are shown as "-".

Example output:

  A B C D E F G H
8 r n b q k b n r
7 p p p p p p p p
6 - - - - - - - -
5 - - - - - - - -
4 - - - - - - - -
3 - - - - - - - -
2 P P P P P P P P
1 R N B Q K B N R

func (*Board) Draw2

func (b *Board) Draw2(perspective Color, darkMode bool) string

Draw2 returns visual representation of the board useful for debugging. It is similar to Draw() except allows the caller to specify perspective and dark mode options.

func (*Board) Empty

func (b *Board) Empty() Bitboard

Empty returns all empty squares.

func (*Board) Flip

func (b *Board) Flip(fd FlipDirection) (*Board, error)

Flip returns a new board flipped over the specified axis. For UpDown, pieces are mirrored across the horizontal center line. For LeftRight, pieces are mirrored across the vertical center line.

func (*Board) HasInsufficientMaterial

func (b *Board) HasInsufficientMaterial(c Color) bool

HasInsufficientMaterial reports whether color c cannot force checkmate against any opposing material. It returns true for a lone king, king plus a single minor piece, or king plus bishops that are all confined to one square color. It returns false when c has a queen, rook, pawn, knight, or bishops on both square colors.

func (*Board) MarshalBinary

func (b *Board) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface and returns the bitboard representations as a array of bytes. Bitboads are encoded in the following order: WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight WhitePawn, BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn.

func (*Board) MarshalText

func (b *Board) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface and returns a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR.

func (*Board) Occupied

func (b *Board) Occupied() Bitboard

Occupied returns all occupied squares.

func (*Board) Piece

func (b *Board) Piece(sq Square) Piece

Piece returns the piece for the given square. Returns NoPiece if the square is empty.

func (*Board) Pieces

func (b *Board) Pieces(pt PieceType, c Color) Bitboard

Pieces returns the squares occupied by pieces of the given type and color.

func (*Board) Rotate

func (b *Board) Rotate() (*Board, error)

Rotate rotates the board 90 degrees clockwise.

func (*Board) SquareMap

func (b *Board) SquareMap() map[Square]Piece

SquareMap returns a mapping of squares to pieces. A square is only added to the map if it is occupied.

func (*Board) String

func (b *Board) String() string

String implements the fmt.Stringer interface and returns a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR.

func (*Board) Transpose

func (b *Board) Transpose() (*Board, error)

Transpose flips the board over the A8 to H1 diagonal.

func (*Board) UnmarshalBinary

func (b *Board) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface and parses the bitboard representations as a array of bytes. Bitboads are decoded in the following order: WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight WhitePawn, BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn.

func (*Board) UnmarshalText

func (b *Board) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnarshaler interface and takes a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR.

func (*Board) White

func (b *Board) White() Bitboard

White returns the squares occupied by white pieces.

type CastleRights

type CastleRights struct {
	White castleSideRights
	Black castleSideRights
}

CastleRights holds the state of both sides castling abilities.

func NewCastleRights

func NewCastleRights(whiteKingSide, whiteQueenSide, blackKingSide, blackQueenSide bool) CastleRights

NewCastleRights returns a CastleRights value with the four per-color, per-side flags set explicitly. It is the supported way for callers outside this package to construct a CastleRights value.

func (CastleRights) CanCastle

func (cr CastleRights) CanCastle(c Color, side Side) bool

CanCastle returns true if the given color and side combination can castle.

Example:

if rights.CanCastle(White, KingSide) {
    // White can castle kingside
}

func (CastleRights) MarshalText

func (cr CastleRights) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface using the FEN representation.

func (CastleRights) String

func (cr CastleRights) String() string

String implements the fmt.Stringer interface and returns a FEN compatible string in canonical order (KQkq), or "-" when no side can castle.

func (*CastleRights) UnmarshalText

func (cr *CastleRights) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface using the FEN representation.

type Color

type Color int8

Color represents the color of a chess piece.

const (
	// NoColor represents no color.
	NoColor Color = iota
	// White represents the color white.
	White
	// Black represents the color black.
	Black
)

func ColorFromString deprecated

func ColorFromString(s string) Color

ColorFromString converts a FEN side-to-move character ("w" or "b") to a Color. Returns NoColor for any other input.

Deprecated: use ParseColor for new code; it distinguishes parse failures from the NoColor sentinel.

func ParseColor

func ParseColor(s string) (Color, error)

ParseColor converts a FEN side-to-move character ("w" or "b") to a Color. It returns an error for any other input.

func (Color) Name

func (c Color) Name() string

Name returns a display friendly name.

func (Color) Other

func (c Color) Other() Color

Other returns the opposite color of the receiver.

func (Color) String

func (c Color) String() string

String implements the fmt.Stringer interface and returns. the color's FEN compatible notation.

type CommentBlock

type CommentBlock struct {
	Items []CommentItem
}

CommentBlock represents one PGN {...} block.

type CommentItem

type CommentItem struct {
	Kind  CommentItemKind
	Text  string
	Key   string
	Value string
}

CommentItem is one ordered item inside a PGN comment block.

type CommentItemKind

type CommentItemKind int

CommentItemKind identifies the kind of item inside a PGN comment block.

const (
	// CommentText is plain text inside a PGN comment block.
	CommentText CommentItemKind = iota
	// CommentCommand is a command annotation like [%clk 0:05:00].
	CommentCommand
)

type File

type File int8

A File is the file of a square.

const (
	FileA File = iota
	FileB
	FileC
	FileD
	FileE
	FileF
	FileG
	FileH
)

func (File) Byte

func (f File) Byte() byte

func (File) String

func (f File) String() string

type FlipDirection

type FlipDirection int

FlipDirection is the direction for the Board.Flip method.

const (
	// UpDown flips the board's rank values.
	UpDown FlipDirection = iota
	// LeftRight flips the board's file values.
	LeftRight
)

type Game

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

A Game represents a single chess game.

func NewGame

func NewGame(options ...func(*Game)) *Game

NewGame returns a new game in the standard starting position. Optional functions can be provided to configure the initial game state.

Example:

// Standard game
game := NewGame()

// Game from FEN
game := NewGame(FEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"))

func ParsePGN

func ParsePGN(r io.Reader, opts ...PGNOption) (*Game, error)

ParsePGN parses the first Game from r. Any returned error wraps ErrInvalidPGN so callers can errors.Is(err, ErrInvalidPGN) without inspecting message text; io.EOF is returned unchanged when r yields no game.

func (*Game) AddTagPair

func (g *Game) AddTagPair(k, v string) bool

AddTagPair adds or updates a tag pair with the given key and value and returns true if the value is overwritten.

func (*Game) ClearOutcome

func (g *Game) ClearOutcome()

ClearOutcome resets the game to NoOutcome/NoMethod. It does not modify any tag in tagPairs (including the Result tag): PGN tag metadata is treated as caller-owned data, separate from the live outcome. Callers that want PGN tag parity must update tagPairs["Result"] themselves, or rebuild from the game state via Split which does sync the tag.

func (*Game) Clone

func (g *Game) Clone() *Game

Clone returns a deep copy of the game, including its move tree.

func (*Game) Draw

func (g *Game) Draw(method Method) error

Draw attempts to draw the game by the given method. If the method is valid, then the game is updated to a draw by that method. If the method isn't valid then an error is returned. Returns ErrGameAlreadyEnded if the game is already in a terminal state.

func (*Game) EligibleDraws

func (g *Game) EligibleDraws() []Method

EligibleDraws returns valid inputs for the Draw() method.

func (*Game) FEN

func (g *Game) FEN() string

FEN returns the FEN notation of the current position. Returns "" if the game has no current position.

func (*Game) GetTagPair

func (g *Game) GetTagPair(k string) string

GetTagPair returns the tag pair for the given key or nil if it is not present.

func (*Game) IsAtEnd

func (g *Game) IsAtEnd() bool

IsAtEnd returns true if the game is at the end.

func (*Game) IsAtStart

func (g *Game) IsAtStart() bool

IsAtStart returns true if the game is at the start.

func (*Game) MarshalText

func (g *Game) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface and encodes the game's PGN.

func (*Game) Method

func (g *Game) Method() Method

Method returns the method in which the outcome occurred.

func (*Game) Move

func (g *Game) Move(move Move, options *MoveInsertOptions) (*MoveNode, error)

Move method adds a move to the game using a Move struct. It returns an error if the move is invalid. This method validates the move before adding it to ensure game correctness. For high-performance scenarios where moves are pre-validated, use UnsafeMove.

Null moves are rejected here: they are never part of ValidMovesUnsafe, so callers wanting to pass the side must use Game.NullMove or Game.UnsafeMove explicitly.

Example:

possibleMove := game.ValidMoves()[0]

err := game.Move(possibleMove, nil)
if err != nil {
    panic(err)
}

func (*Game) MoveList

func (g *Game) MoveList() MoveList

MoveList returns the main-line moves in order with any comments. Variations are not included. The returned slice is safe to retain: it holds no references to the game's live cursor state. Returns an empty slice for games with no moves.

func (*Game) MoveText

func (g *Game) MoveText(moveText string, codec MoveTextCodec, options *MoveInsertOptions) (*MoveNode, error)

MoveText adds a move to the game using an explicit move text codec. It decodes against the current position so the inserted move carries position-derived tags. The decoded Move is canonical (codec.Decode resolves legal moves), so it crosses the trusted fast path: no extra legal-move lookup is needed before insertion.

func (*Game) MoveTree

func (g *Game) MoveTree() *MoveTree

MoveTree returns the game's move tree.

func (*Game) Moves

func (g *Game) Moves() []Move

Moves returns the main-line move values of the game.

func (*Game) NullMove

func (g *Game) NullMove(options ...*MoveInsertOptions) (*MoveNode, error)

NullMove appends a null move (a side-to-move flip with no piece movement) to the current position's main line. Null moves are never part of the legal moves returned by ValidMoves, so they cannot be inserted via Game.Move. Use this method or Game.UnsafeMove(NullMove()) to add one explicitly.

Pass nil (or no argument) to use default options.

Example
package main

import (
	"fmt"

	"github.com/corentings/chess/v3"
)

func main() {
	g := chess.NewGame()
	if _, err := g.NullMove(); err != nil {
		fmt.Println("Error:", err)
		return
	}
	// A null move flips the side to move without moving any piece.
	fmt.Println(g.Position().Turn())
}
Output:
b

func (*Game) Outcome

func (g *Game) Outcome() Outcome

Outcome returns the game outcome.

func (*Game) Position

func (g *Game) Position() *Position

Position returns the game's current position.

func (*Game) Positions

func (g *Game) Positions() []*Position

Positions returns all positions in the game in the main line. This includes the starting position and all positions after each move. The cursor is saved and restored so the caller's active position is unchanged.

func (*Game) PushMove deprecated

func (g *Game) PushMove(algebraicMove string, options *MoveInsertOptions) (*MoveNode, error)

PushMove adds a move in algebraic notation to the game. Returns an error if the move is invalid.

Deprecated: use MoveText(algebraicMove, SAN(), options) instead.

func (*Game) PushMoveText deprecated

func (g *Game) PushMoveText(moveText string, codec MoveTextCodec, options *MoveInsertOptions) (*MoveNode, error)

PushMoveText adds a move to the game using an explicit move text codec.

Deprecated: use MoveText instead.

func (*Game) RemoveTagPair

func (g *Game) RemoveTagPair(k string) bool

RemoveTagPair removes the tag pair for the given key and returns true if a tag pair was removed.

func (*Game) Resign

func (g *Game) Resign(color Color) error

Resign resigns the game for the given color. If the game has already been completed or the color is invalid, Resign returns an error and does not update the game.

func (*Game) SetOutcomeMethod

func (g *Game) SetOutcomeMethod(pair OutcomeMethodPair) error

SetOutcomeMethod sets the game's outcome and method together after validating that the pair is internally consistent. It returns an error for invalid pairs such as WhiteWon with Stalemate or Draw with Checkmate. It does not modify any Result tag in tagPairs.

Example
package main

import (
	"fmt"

	"github.com/corentings/chess/v3"
)

func main() {
	g := chess.NewGame()
	if err := g.SetOutcomeMethod(chess.OutcomeMethodPair{
		Outcome: chess.WhiteWon,
		Method:  chess.Resignation,
	}); err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println(g.Outcome(), g.Method())
}
Output:
1-0 Resignation

func (*Game) Split

func (g *Game) Split() []*Game

Split takes a Game with a main line and 0 or more variations and returns a slice of Games (one for each variation), each containing exactly only a main line and 0 variations.

func (*Game) String

func (g *Game) String() string

String implements the fmt.Stringer interface and returns the game's PGN. It delegates to DefaultPGNRenderer.

func (*Game) UnmarshalText

func (g *Game) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface and assumes the data is in the PGN format.

func (*Game) UnsafeMove

func (g *Game) UnsafeMove(move Move, options *MoveInsertOptions) (*MoveNode, error)

UnsafeMove adds a move to the game without validation. This method is intended for high-performance scenarios where moves are known to be valid. Use this method only when you have already validated the move or are certain it's legal. For general use, prefer the Move method which includes validation.

Example:

// Only use when you're certain the move is valid
validMoves := game.ValidMoves()
move := validMoves[0] // We know this is valid
err := game.UnsafeMove(move, nil)
if err != nil {
    panic(err) // Should not happen with valid moves
}

func (*Game) UnsafeMoveText

func (g *Game) UnsafeMoveText(moveText string, codec MoveTextCodec, options *MoveInsertOptions) (*MoveNode, error)

UnsafeMoveText adds fully specified move text without legal move verification. It supports only codecs that can raw-decode a move without SAN resolution.

func (*Game) UnsafeMoves

func (g *Game) UnsafeMoves() []Move

UnsafeMoves returns all pseudo-legal moves that leave the moving side's king in check. These moves are valid piece movements but illegal because they expose the king.

func (*Game) UnsafePushMoveText deprecated

func (g *Game) UnsafePushMoveText(moveText string, codec MoveTextCodec, options *MoveInsertOptions) (*MoveNode, error)

UnsafePushMoveText adds fully specified move text without legal move verification.

Deprecated: use UnsafeMoveText instead.

func (*Game) ValidMoves

func (g *Game) ValidMoves() []Move

ValidMoves returns all legal moves in the current position.

func (*Game) Variant

func (g *Game) Variant() Variant

Variant returns the starting-position variant of the game (Standard or Chess960), derived from the game's root position.

func (*Game) WritePGN

func (g *Game) WritePGN(w io.Writer) error

WritePGN writes the game's PGN to w. It delegates to DefaultPGNRenderer and returns any write error.

type Lexer

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

Lexer provides lexical analysis of PGN text.

func NewLexer

func NewLexer(input string) *Lexer

NewLexer creates a new Lexer for the provided input text. The lexer is initialized and ready to produce tokens through calls to NextToken().

Example:

lexer := NewLexer("1. e4 e5")

func (*Lexer) NextToken

func (l *Lexer) NextToken() Token

NextToken reads the next token from the input stream. Returns an EOF token when the input is exhausted. Returns an ILLEGAL token for invalid input.

The method handles all standard PGN notation including: - Move notation (e4, Nf3, O-O) - Comments ({comment} or ; comment) - Tags ([Event "World Championship"]) - Move numbers and variations - Annotations ($1, !!, ?!)

Example:

lexer := NewLexer("1. e4 {Strong move}")
token := lexer.NextToken() // NUMBER: "1"
token = lexer.NextToken()  // DOT: "."
token = lexer.NextToken()  // NOTATION: "e4"
token = lexer.NextToken()  // COMMENT: "Strong move"
token = lexer.NextToken()  // EOF

type Method

type Method uint8

A Method is the method that generated the outcome.

const (
	// NoMethod indicates that an outcome hasn't occurred or that the method can't be determined.
	NoMethod Method = iota
	// Checkmate indicates that the game was won checkmate.
	Checkmate
	// Resignation indicates that the game was won by resignation.
	Resignation
	// DrawOffer indicates that the game was drawn by a draw offer.
	DrawOffer
	// Stalemate indicates that the game was drawn by stalemate.
	Stalemate
	// ThreefoldRepetition indicates that the game was drawn when the game
	// state was repeated three times and a player requested a draw.
	ThreefoldRepetition
	// FivefoldRepetition indicates that the game was automatically drawn
	// by the game state being repeated five times.
	FivefoldRepetition
	// FiftyMoveRule indicates that the game was drawn by the half
	// move clock being one hundred or greater when a player requested a draw.
	FiftyMoveRule
	// SeventyFiveMoveRule indicates that the game was automatically drawn
	// when the half move clock was one hundred and fifty or greater.
	SeventyFiveMoveRule
	// InsufficientMaterial indicates that the game was automatically drawn
	// because there was insufficient material for checkmate.
	InsufficientMaterial
)

func (Method) String

func (i Method) String() string

type Move

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

A Move is the movement of a piece from one square to another.

func NewMove

func NewMove(s1, s2 Square, promo ...PieceType) Move

NewMove returns a Move from origin square s1 to destination square s2. The optional promo argument supplies a promotion piece type; when omitted the move has no promotion. Tags default to none.

The constructor does not attempt to infer Capture or Check tags because those depend on the position context (the piece on s1, the occupant of s2, and whether the move leaves the opponent in check). Use Position.IsLegal or Game.Move to obtain a fully tagged canonical Move when the position is known.

func NewNullMove

func NewNullMove() Move

NewNullMove returns a Move that flips the side to move without moving any piece. The returned move carries the Null MoveTag and has no origin, destination, or promotion. Null moves must be inserted via Game.NullMove or Game.UnsafeMove; they are rejected by Game.Move because they are never part of a position's legal moves.

func (Move) HasTag

func (m Move) HasTag(tag MoveTag) bool

HasTag returns true if the move contains the MoveTag given.

func (Move) Promo

func (m Move) Promo() PieceType

Promo returns promotion piece type of the move.

func (Move) S1

func (m Move) S1() Square

S1 returns the origin square of the move.

func (Move) S2

func (m Move) S2() Square

S2 returns the destination square of the move.

func (Move) String

func (m Move) String() string

String returns a string useful for debugging. String doesn't return algebraic notation.

func (Move) WithTag

func (m Move) WithTag(tag MoveTag) Move

WithTag returns a copy of the move with the given MoveTag added.

type MoveInsertOptions

type MoveInsertOptions struct {
	// PromoteToMainLine makes the inserted or selected continuation the main line.
	PromoteToMainLine bool
}

MoveInsertOptions contains options for inserting a move into a MoveTree.

type MoveList

type MoveList []MoveListEntry

MoveList is the main-line move sequence of a game. Variations are not included. The zero value is an empty list; use Game.MoveList to obtain one.

type MoveListEntry

type MoveListEntry struct {
	Move     Move
	Comments []string
}

MoveListEntry is a single move in a MoveList: the move itself plus any comments attached to it. Positions are not cached here — callers that need the pre/post position replay it via Game.MoveTree and the cursor API.

type MoveNode

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

MoveNode is one occurrence of a move in a game's move tree.

func (*MoveNode) AddComment

func (n *MoveNode) AddComment(comment string)

func (*MoveNode) AddNAG

func (n *MoveNode) AddNAG(nag string) error

AddNAG normalises and appends a single NAG to the move node. Order is preserved. A malformed value leaves the node unchanged.

func (*MoveNode) Children

func (n *MoveNode) Children() []*MoveNode

func (*MoveNode) ChildrenIter

func (n *MoveNode) ChildrenIter(yield func(*MoveNode) bool)

ChildrenIter returns an allocation-free iterator over the continuations.

func (*MoveNode) CommentBlocks

func (n *MoveNode) CommentBlocks() []CommentBlock

CommentBlocks returns a defensive copy of the move node's structured PGN comment blocks.

func (*MoveNode) Comments

func (n *MoveNode) Comments() string

func (*MoveNode) FullMoveNumber

func (n *MoveNode) FullMoveNumber() int

FullMoveNumber returns the full move number (increments after Black's move).

func (*MoveNode) GetCommand

func (n *MoveNode) GetCommand(key string) (string, bool)

func (*MoveNode) Move

func (n *MoveNode) Move() Move

Move returns the move value for this move node.

func (*MoveNode) NAGs

func (n *MoveNode) NAGs() []string

NAGs returns a defensive copy of the move node's numeric annotation glyphs in their canonical "$N" form, in the order they were added.

func (*MoveNode) Number

func (n *MoveNode) Number() int

func (*MoveNode) Parent

func (n *MoveNode) Parent() *MoveNode

func (*MoveNode) Ply

func (n *MoveNode) Ply() int

Ply returns the half-move number (increments every move). For the synthetic root it returns 0; otherwise it equals the depth of the parent chain from n up to (not including) the synthetic root.

func (*MoveNode) Position

func (n *MoveNode) Position() *Position

Position returns the position reached by playing this node's move from its parent's position. It saves and restores the tree's active cursor so the read is observationally pure under ADR-016's single-active-cursor invariant. See ADR-018 for the lazy-position design.

func (*MoveNode) SetCommand

func (n *MoveNode) SetCommand(key, value string)

func (*MoveNode) SetComment

func (n *MoveNode) SetComment(comment string)

func (*MoveNode) SetNAGs

func (n *MoveNode) SetNAGs(nags []string) error

SetNAGs replaces all of the move node's NAGs. Each value is normalised via ParseNAG. The replacement is all-or-nothing: if any value is malformed the node is left unchanged and the error is returned.

type MoveTag

type MoveTag uint16

A MoveTag represents a notable consequence of a move.

const (
	// KingSideCastle indicates that the move is a king side castle.
	KingSideCastle MoveTag = 1 << iota
	// QueenSideCastle indicates that the move is a queen side castle.
	QueenSideCastle
	// Capture indicates that the move captures a piece.
	Capture
	// EnPassant indicates that the move captures via en passant.
	EnPassant
	// Check indicates that the move puts the opposing player in check.
	Check
	// Null indicates that the move flips the side to move without moving
	// any piece. Null moves are encoded as "Z0" in PGN and "0000" in UCI.
	// They are never generated by ValidMoves and must be inserted
	// explicitly via Game.NullMove or Game.UnsafeMove.
	Null
)

type MoveTextCodec

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

MoveTextCodec parses, resolves, validates, and formats move text.

The zero value is the strict SAN codec returned by SAN().

func LongAlgebraic

func LongAlgebraic() MoveTextCodec

LongAlgebraic returns the coordinate-expanded algebraic notation codec.

func PGNImportSAN

func PGNImportSAN() MoveTextCodec

PGNImportSAN returns the permissive SAN codec used by PGN import.

func SAN

func SAN() MoveTextCodec

SAN returns the strict Standard Algebraic notation codec.

func UCI

func UCI() MoveTextCodec

UCI returns the Universal Chess Interface coordinate notation codec.

func (MoveTextCodec) Decode

func (c MoveTextCodec) Decode(pos *Position, s string) (Move, error)

Decode resolves move text against the supplied position.

func (MoveTextCodec) DecodeRaw

func (c MoveTextCodec) DecodeRaw(s string) (RawMoveText, error)

DecodeRaw decodes fully specified move text without a position.

func (MoveTextCodec) Encode

func (c MoveTextCodec) Encode(pos *Position, m Move) (string, error)

Encode formats a move from the supplied position. Tag-dependent suffixes (check, capture, disambiguation) are recomputed from the resulting position rather than trusted from the input move; Decode returns already-tagged moves from the legal-move generator and does not recompute.

func (MoveTextCodec) Format

func (c MoveTextCodec) Format() MoveTextFormat

Format returns the codec's move text format.

func (MoveTextCodec) Policy

func (c MoveTextCodec) Policy() MoveTextPolicy

Policy returns the codec's parsing policy.

func (MoveTextCodec) String

func (c MoveTextCodec) String() string

String implements fmt.Stringer.

func (MoveTextCodec) ValidateSyntax

func (c MoveTextCodec) ValidateSyntax(s string) error

ValidateSyntax reports whether text matches the codec grammar without checking move legality.

type MoveTextFormat

type MoveTextFormat uint8

MoveTextFormat identifies the concrete move text grammar handled by a codec.

const (
	// MoveTextFormatSAN is strict Standard Algebraic notation.
	MoveTextFormatSAN MoveTextFormat = iota
	// MoveTextFormatLongAlgebraic is coordinate-expanded algebraic notation.
	MoveTextFormatLongAlgebraic
	// MoveTextFormatUCI is Universal Chess Interface coordinate notation.
	MoveTextFormatUCI
)

type MoveTextPolicy

type MoveTextPolicy uint8

MoveTextPolicy identifies how permissive a codec is while reading move text.

const (
	// MoveTextPolicyStrict accepts only generated canonical text.
	MoveTextPolicyStrict MoveTextPolicy = iota
	// MoveTextPolicyPGNImport accepts documented PGN import spellings and
	// canonicalises them when writing.
	MoveTextPolicyPGNImport
)

type MoveTree

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

MoveTree owns the move topology and active cursor for a game.

The root is a synthetic position node, not a move occurrence. The cursor's current position is tracked in pos alongside current: every navigation (addMove, GoForward, GoBack, Forward, Goto, Reset, setCurrent) advances pos via the in-place makeMoveCursor / unmakeMoveCursor pair (move_applier.go), with one entry in undos per level between root and current. rootPos holds the starting position so the cursor can be reset without re-deriving it from the synthetic root MoveNode.

func (*MoveTree) AddVariation

func (t *MoveTree) AddVariation(parent *MoveNode, move Move) (*MoveNode, error)

AddVariation validates and appends move as a variation from parent. The cursor is saved and restored so the caller's active position is unchanged.

func (*MoveTree) Clone

func (t *MoveTree) Clone() *MoveTree

Clone returns a deep copy of the move tree with the cursor preserved.

func (*MoveTree) Continuations

func (t *MoveTree) Continuations(parent *MoveNode) []*MoveNode

Continuations returns all continuations from parent.

func (*MoveTree) Current

func (t *MoveTree) Current() *MoveNode

Current returns the active cursor node.

func (*MoveTree) Forward

func (t *MoveTree) Forward(idx int) bool

Forward advances the cursor to the idx-th child of the current node. Forward(0) is equivalent to MoveTree.GoForward (main-line continuation). Returns true on success; false if t is nil, the current node has no children, or idx is out of range.

func (*MoveTree) GoBack

func (t *MoveTree) GoBack() bool

GoBack moves the active cursor to its parent.

func (*MoveTree) GoForward

func (t *MoveTree) GoForward() bool

GoForward moves the active cursor to the main continuation.

func (*MoveTree) Goto

func (t *MoveTree) Goto(node *MoveNode) bool

Goto jumps the cursor to the given node. Returns true if the cursor moved. Returns false if t is nil, node is nil, or node belongs to a different tree. Goto uses the same fast paths as internal cursor navigation: direct child, LCA walk, and full replay-from-root (see [MoveTree.setCurrent]).

func (*MoveTree) Lines

func (t *MoveTree) Lines() [][]*MoveNode

Lines returns every root-to-leaf line in the tree.

func (*MoveTree) MainChild

func (t *MoveTree) MainChild(parent *MoveNode) *MoveNode

MainChild returns the main-line continuation from parent.

func (*MoveTree) MainLine

func (t *MoveTree) MainLine() []*MoveNode

MainLine returns the main-line move nodes, excluding the root position node.

func (*MoveTree) NavigateToMainLine

func (t *MoveTree) NavigateToMainLine()

NavigateToMainLine moves the active cursor to the first main-line move.

func (*MoveTree) Peek

func (t *MoveTree) Peek() *Position

Peek returns the live Position at the tree's active cursor without copying. The returned pointer aliases the tree's internal state and is valid only until the next cursor move (GoForward, GoBack, Forward, Goto, Reset, AddVariation, or any move pushed onto the tree). Callers MUST NOT mutate it; mutation corrupts the cursor and every subsequent read in the library, including legal-move generation and repetition detection.

For a snapshot you can retain or mutate, use MoveNode.Position (a defensive copy at a node) or Game.Position (a defensive copy of the current position). Both copy.

Returns nil if t is nil or has no position.

func (*MoveTree) Reset

func (t *MoveTree) Reset()

Reset returns the cursor to the synthetic root, restoring the starting position. Safe to call on a nil tree.

func (*MoveTree) Root

func (t *MoveTree) Root() *MoveNode

Root returns the root position node.

func (*MoveTree) Variations

func (t *MoveTree) Variations(parent *MoveNode) []*MoveNode

Variations returns all non-main-line continuations from parent.

type MoveWithWeight

type MoveWithWeight struct {
	Move   Move
	Weight uint16
}

MoveWithWeight is a helper struct that couples a chess.Move with a weight.

type Outcome

type Outcome int8

A Outcome is the result of a game.

const (
	// NoOutcome indicates that a game is in progress or ended without a result.
	NoOutcome Outcome = iota
	// WhiteWon indicates that white won the game.
	WhiteWon
	// BlackWon indicates that black won the game.
	BlackWon
	// Draw indicates that game was a draw.
	Draw
)

func ParseOutcome

func ParseOutcome(s string) (Outcome, error)

ParseOutcome converts a PGN result token into a typed Outcome. Unknown tokens return an error.

func (Outcome) MarshalText

func (o Outcome) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface using the PGN result token.

func (Outcome) String

func (o Outcome) String() string

String implements the fmt.Stringer interface and returns the PGN result token.

func (*Outcome) UnmarshalText

func (o *Outcome) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface from a PGN result token.

type OutcomeMethodPair

type OutcomeMethodPair struct {
	Outcome Outcome
	Method  Method
}

OutcomeMethodPair pairs an Outcome with the Method that produced it.

type PGNDecoder

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

PGNDecoder streams Games from a PGN reader.

func NewPGNDecoder

func NewPGNDecoder(r io.Reader, opts ...PGNOption) *PGNDecoder

NewPGNDecoder creates a decoder that reads Games from r.

func (*PGNDecoder) Decode

func (d *PGNDecoder) Decode() (*Game, error)

Decode returns the next Game from the PGN stream.

func (*PGNDecoder) Index

func (d *PGNDecoder) Index() int64

Index returns the number of Games successfully decoded by this decoder.

func (*PGNDecoder) Offset

func (d *PGNDecoder) Offset() int64

Offset returns the byte offset of the current PGN record when available.

type PGNError

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

PGNError custom error types for different PGN errors.

func (*PGNError) Error

func (e *PGNError) Error() string

func (*PGNError) Is

func (e *PGNError) Is(target error) bool

type PGNEvent

type PGNEvent struct {
	Kind   PGNEventKind
	Index  int64
	Offset int64
	Name   string
	Value  string
	Move   string
	NAG    string
}

PGNEvent is one semantic event from a PGN stream.

type PGNEventKind

type PGNEventKind uint8

PGNEventKind identifies a semantic PGN event.

const (
	PGNEventUnknown PGNEventKind = iota
	PGNTag
	PGNMove
	PGNComment
	PGNNAG
	PGNVariationStart
	PGNVariationEnd
	PGNGameEnd
)

type PGNOption

type PGNOption func(*pgnOptions)

PGNOption configures PGN decoding.

func WithImportSAN

func WithImportSAN() PGNOption

WithImportSAN accepts documented PGN import SAN spellings.

func WithPGNBufferSize

func WithPGNBufferSize(n int) PGNOption

WithPGNBufferSize configures the reader buffer size used for PGN record framing.

func WithPGNExpandVariations

func WithPGNExpandVariations() PGNOption

WithPGNExpandVariations expands each Game's Variations into separate Games.

func WithStrictSAN

func WithStrictSAN() PGNOption

WithStrictSAN requires PGN movetext to use strict canonical SAN.

type PGNParallelOptions

type PGNParallelOptions struct {
	Workers int
	Buffer  int
	Ordered bool
	Options []PGNOption
}

PGNParallelOptions configures parallel PGN Game decoding.

type PGNRecord

type PGNRecord struct {
	Index  int64
	Offset int64
	Raw    string
}

PGNRecord is one complete PGN game record from a stream.

func (PGNRecord) Decode

func (r PGNRecord) Decode(opts ...PGNOption) (*Game, error)

Decode parses this record into a Game.

func (PGNRecord) Tags

func (r PGNRecord) Tags() (map[string]string, error)

Tags returns the PGN tag pairs in this record without building a Game.

type PGNRenderer

type PGNRenderer struct{}

PGNRenderer renders a Game into PGN text. It is a stateless function object that reads Game state and writes formatted PGN to a string or io.Writer without mutating the Game.

The renderer takes a pointer so callers may extend it in the future (custom tag ordering, value escaping toggles, optional result token, etc.). All such configuration fields are deferred to v3.1; this initial type is a pure extraction with no knobs.

func (*PGNRenderer) Render

func (r *PGNRenderer) Render(g *Game) string

Render returns the PGN text for g.

func (*PGNRenderer) RenderGameTo

func (r *PGNRenderer) RenderGameTo(g *Game, w io.Writer) error

RenderGameTo writes the PGN text for g to w. It returns any write error from w.

type PGNResult

type PGNResult struct {
	Game   *Game
	Err    error
	Index  int64
	Offset int64
}

PGNResult is the result of decoding one PGN record.

type Parser

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

Parser holds the state needed during parsing.

func (*Parser) Parse

func (p *Parser) Parse() (*Game, error)

Parse processes all tokens and returns the complete game. This includes parsing header information (tags), moves, variations, comments, and the game result.

Returns an error if the PGN is malformed or contains illegal moves.

Example:

game, err := parser.Parse()
if err != nil {
    log.Fatal("Error parsing game:", err)
}
fmt.Printf("Event: %s\n", game.GetTagPair("Event"))

type ParserError

type ParserError struct {
	Message    string
	TokenValue string
	TokenType  TokenType
	Position   int
}

func (*ParserError) Error

func (e *ParserError) Error() string

type Piece

type Piece int8

Piece is a piece type with a color.

const (
	// NoPiece represents no piece.
	NoPiece Piece = iota
	// WhiteKing is a white king.
	WhiteKing
	// WhiteQueen is a white queen.
	WhiteQueen
	// WhiteRook is a white rook.
	WhiteRook
	// WhiteBishop is a white bishop.
	WhiteBishop
	// WhiteKnight is a white knight.
	WhiteKnight
	// WhitePawn is a white pawn.
	WhitePawn
	// BlackKing is a black king.
	BlackKing
	// BlackQueen is a black queen.
	BlackQueen
	// BlackRook is a black rook.
	BlackRook
	// BlackBishop is a black bishop.
	BlackBishop
	// BlackKnight is a black knight.
	BlackKnight
	// BlackPawn is a black pawn.
	BlackPawn
)

func NewPiece

func NewPiece(t PieceType, c Color) Piece

NewPiece returns the piece matching the PieceType and Color. NoPiece is returned if the PieceType or Color isn't valid.

func (Piece) Color

func (p Piece) Color() Color

Color returns the color of the piece.

func (Piece) DarkString

func (p Piece) DarkString() string

DarkString is equivalent to String() except colors reversed for terminal windows in dark mode.

func (Piece) String

func (p Piece) String() string

String implements the fmt.Stringer interface.

func (Piece) Type

func (p Piece) Type() PieceType

Type returns the type of the piece.

type PieceType

type PieceType int8

PieceType is the type of a piece.

const (
	// NoPieceType represents a lack of piece type.
	NoPieceType PieceType = iota
	// King represents a king.
	King
	// Queen represents a queen.
	Queen
	// Rook represents a rook.
	Rook
	// Bishop represents a bishop.
	Bishop
	// Knight represents a knight.
	Knight
	// Pawn represents a pawn.
	Pawn
)

func ParsePieceType

func ParsePieceType(s string) (PieceType, error)

ParsePieceType parses a single-character piece notation into a PieceType. It returns an error if the input is empty or longer than one character, or if the character is not a valid piece type.

func ParsePieceTypeFromByte

func ParsePieceTypeFromByte(b byte) (PieceType, error)

ParsePieceTypeFromByte parses a FEN piece-type character (case-insensitive) into a PieceType. It returns an error for any other byte.

func PieceTypeFromByte deprecated

func PieceTypeFromByte(b byte) PieceType

PieceTypeFromByte parses a FEN piece-type character (case-insensitive) into a PieceType. Returns NoPieceType for any other byte.

Deprecated: use ParsePieceTypeFromByte for new code; it distinguishes parse failures from the NoPieceType sentinel.

func PieceTypeFromString deprecated

func PieceTypeFromString(s string) PieceType

PieceTypeFromString parses a single-character piece notation into a PieceType. Returns NoPieceType for invalid input.

Deprecated: use ParsePieceType for new code; it distinguishes parse failures from the NoPieceType sentinel.

func PieceTypes

func PieceTypes() [6]PieceType

PieceTypes returns a slice of all piece types.

func (PieceType) Bytes

func (p PieceType) Bytes() []byte

func (PieceType) String

func (p PieceType) String() string

func (PieceType) ToPolyglotPromotionValue

func (p PieceType) ToPolyglotPromotionValue() int

type PolyglotBook

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

PolyglotBook represents a polyglot opening book with optimized lookup capabilities. A polyglot book is a binary file format widely used in chess engines to store opening moves. Each entry in the book contains a position hash, a move, and additional metadata.

Example usage:

// Load from file
book, err := LoadBookFromReader(fileReader)
if err != nil {
    log.Fatal(err)
}

// Find moves for a position
hash := uint64(0x463b96181691fc9c) // Starting position hash
moves := book.FindMoves(hash)

// Get a random move weighted by the stored weights
randomMove := book.RandomMove(hash)

func LoadFromBytes

func LoadFromBytes(data []byte) (*PolyglotBook, error)

LoadFromBytes loads a polyglot book from a byte slice. This is useful when the book data is already in memory.

Example:

data := // ... your book data ...
book, err := LoadFromBytes(data)
if err != nil {
    log.Fatal(err)
}

func LoadFromReader

func LoadFromReader(reader io.Reader) (*PolyglotBook, error)

LoadFromReader loads a polyglot book from an io.Reader. Note that this will read the entire input into memory.

Example:

file, err := os.Open("openings.bin")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

book, err := LoadFromReader(file)
if err != nil {
    log.Fatal(err)
}

func NewPolyglotBookFromMap

func NewPolyglotBookFromMap(m map[uint64][]MoveWithWeight) *PolyglotBook

NewPolyglotBookFromMap creates a PolyglotBook from a map where the key is the zobrist hash (uint64) and the value is a slice of MoveWithWeight.

func (*PolyglotBook) AddMove

func (book *PolyglotBook) AddMove(positionHash uint64, move Move, weight uint16)

AddMove adds a new move (with its weight) to a given position hash in the book.

func (*PolyglotBook) ChessMoves

func (book *PolyglotBook) ChessMoves(positionHash uint64) ([]Move, error)

func (*PolyglotBook) DeleteMoves

func (book *PolyglotBook) DeleteMoves(positionHash uint64)

DeleteMoves removes all moves for a given position hash from the book.

func (*PolyglotBook) FindMoves

func (book *PolyglotBook) FindMoves(positionHash uint64) []PolyglotEntry

FindMoves looks up all moves for a given position hash. Returns moves sorted by weight (highest weight first). Returns nil if no moves are found.

Example:

hash := uint64(0x463b96181691fc9c) // Starting position
moves := book.FindMoves(hash)
if moves != nil {
    for _, move := range moves {
        decodedMove := DecodeMove(move.Move)
        fmt.Printf("Move: %v, Weight: %d\n", decodedMove, move.Weight)
    }
}

func (*PolyglotBook) RandomMove

func (book *PolyglotBook) RandomMove(positionHash uint64) (*PolyglotEntry, error)

RandomMove returns a weighted random move from the available moves for a position. The probability of selecting a move is proportional to its weight. Returns nil if no moves are available.

Example:

hash := uint64(0x463b96181691fc9c) // Starting position
move, err := book.RandomMove(hash)
if err != nil {
    log.Fatal(err)
}
if move != nil {
    decodedMove := DecodeMove(move.Move)
    fmt.Printf("Selected move: %v\n", decodedMove)
}

func (*PolyglotBook) ToMoveMap

func (book *PolyglotBook) ToMoveMap() map[uint64][]MoveWithWeight

func (*PolyglotBook) UpdateMove

func (book *PolyglotBook) UpdateMove(positionHash uint64, move Move, newWeight uint16) error

UpdateMove searches for an existing move at the given position and updates its weight.

type PolyglotEntry

type PolyglotEntry struct {
	Key    uint64 // Zobrist hash of the chess position
	Move   uint16 // Encoded move (see DecodeMove for format)
	Weight uint16 // Relative weight for move selection
	Learn  uint32 // Learning data (usually 0)
}

PolyglotEntry represents a single entry in a polyglot opening book. Each entry is exactly 16 bytes and contains information about a chess position and a recommended move.

type PolyglotMove

type PolyglotMove struct {
	FromFile     int  // Source file (0-7)
	FromRank     int  // Source rank (0-7)
	ToFile       int  // Target file (0-7)
	ToRank       int  // Target rank (0-7)
	Promotion    int  // Promotion piece type (0=none, 1=knight, 2=bishop, 3=rook, 4=queen)
	CastlingMove bool // True if this is a castling move
}

PolyglotMove represents a decoded chess move from a polyglot entry. The coordinates use 0-based indices where: - Files go from 0 (a-file) to 7 (h-file) - Ranks go from 0 (1st rank) to 7 (8th rank).

func DecodeMove

func DecodeMove(move uint16) PolyglotMove

DecodeMove converts a polyglot move encoding into a more usable format. The move encoding uses bit fields as follows:

  • bits 0-2: to file
  • bits 3-5: to rank
  • bits 6-8: from file
  • bits 9-11: from rank
  • bits 12-14: promotion piece

Promotion pieces are encoded as:

  • 0: none
  • 1: knight
  • 2: bishop
  • 3: rook
  • 4: queen

Example:

move := uint16(0x1234) // Some move from the book
decoded := DecodeMove(move)
fmt.Printf("From: %c%d, To: %c%d\n",
    'a'+decoded.FromFile, decoded.FromRank+1,
    'a'+decoded.ToFile, decoded.ToRank+1)

func (PolyglotMove) Encode

func (pm PolyglotMove) Encode() uint16

func (PolyglotMove) ToMove

func (pm PolyglotMove) ToMove() Move

type Position

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

Position represents a complete chess position state. It includes piece placement, castling rights, en passant squares, move counts, and side to move.

func NewPosition

func NewPosition(s Setup) (*Position, error)

NewPosition validates s and constructs a playable Position from it.

Validation covers:

  • exactly one king of each color
  • no pawns on the first or eighth rank
  • castling rights that match the king and rook placement on their starting squares
  • an en passant square that is consistent with a pawn that just moved two squares
  • non-negative halfmove clock and positive fullmove number

func StartingPosition

func StartingPosition() *Position

StartingPosition returns the starting position rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1.

func (*Position) AnyLegalMove

func (pos *Position) AnyLegalMove() bool

AnyLegalMove reports whether the position has at least one legal move.

func (*Position) Board

func (pos *Position) Board() *Board

Board returns the position's board.

func (*Position) CastleRights

func (pos *Position) CastleRights() CastleRights

CastleRights returns the castling rights of the position.

func (*Position) Checkers

func (pos *Position) Checkers() []Square

Checkers returns the squares of the pieces currently giving check to the side to move. The returned slice is empty when the position is not in check. It allocates only when called.

func (*Position) Divide

func (pos *Position) Divide(depth int) map[Move]uint64

Divide returns the per-root-move Perft breakdown at the given depth: a map from each legal move in the current position to the number of leaf positions reachable in exactly depth-1 further plies after that move. It is the per-move form of Perft and is typically used to localize a divergence between two move generators.

Like Perft, a depth of 0 returns a single-entry map for the current position (the move is the zero Move, count 1). A position with no legal moves returns an empty map for any depth greater than 0.

The map iteration order is unspecified; sort by key if a stable display is required.

Example:

pos := chess.StartingPosition()
results := pos.Divide(1)
for _, m := range sortedMoves(results) {
    fmt.Printf("%s: %d\n", m, results[m])
}

func (*Position) EnPassantSquare

func (pos *Position) EnPassantSquare() Square

EnPassantSquare returns the raw en-passant target square set after any double pawn push, even when no enemy pawn can capture it. This is the FEN en-passant field.

func (*Position) HalfMoveClock

func (pos *Position) HalfMoveClock() int

HalfMoveClock returns the half-move clock (50-rule).

func (*Position) HasInsufficientMaterial

func (pos *Position) HasInsufficientMaterial(c Color) bool

HasInsufficientMaterial reports whether color c cannot force checkmate with its remaining material against any opposing material.

func (*Position) IsCheck

func (pos *Position) IsCheck() bool

IsCheck reports whether the side to move is in check.

func (*Position) IsLegal

func (pos *Position) IsLegal(m Move) bool

IsLegal reports whether m is a legal move in the current position. It returns false for null moves and for moves whose origin square belongs to the side not to move. The check is a linear scan of the legal move list; callers who need the canonical Move with position-derived tags should use Game.Move or resolveCanonicalMove.

func (*Position) LegalEnPassantSquare

func (pos *Position) LegalEnPassantSquare() Square

LegalEnPassantSquare returns the en-passant target square only if an enemy pawn can actually capture en passant; otherwise it returns NoSquare. This is the value that feeds the Zobrist hash.

func (*Position) LegalMovesFast

func (pos *Position) LegalMovesFast() []Move

LegalMovesFast returns legal moves in the same stable generation order as ValidMovesUnsafe without computing display-only check annotations. The returned moves remain valid inputs to Position.Update. This is intended for replay/index codecs that need move identity and legality but not SAN tags.

func (*Position) MarshalBinary

func (pos *Position) MarshalBinary() ([]byte, error)

MarshalBinary implements the encoding.BinaryMarshaler interface.

func (*Position) MarshalText

func (pos *Position) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface and encodes the position's FEN.

func (*Position) Outcome

func (pos *Position) Outcome() Outcome

Outcome returns the decisive or draw outcome implied by the position, or NoOutcome if play continues. It considers board-state terminal conditions only: checkmate, stalemate, insufficient material, and the seventy-five move rule. Game-level outcomes such as resignation, draw offer, and the claimable fifty-move rule are not included and must be queried on Game.

func (*Position) Perft

func (pos *Position) Perft(depth int) uint64

Perft (performance test) counts the number of legal move sequences of the given length reachable from the receiver. It is a standard correctness and performance test for chess move generators.

A depth of 0 counts the current position itself. A depth of 1 counts the number of legal moves. Otherwise each legal move is applied in turn and the counts are summed recursively.

Perft is purely a node counter; it does not collect, store, or annotate the move sequences. For a per-root-move breakdown use Divide.

Example:

pos := chess.StartingPosition()
nodes := pos.Perft(5) // 4 865 609 for the start position

The traversal uses the same legality rules that govern normal play, but it applies moves through an internal make/unmake path to avoid allocating a new Position at each ply. A position that is checkmate or stalemate returns 0 for any depth greater than 0 (no legal moves to count).

Example
package main

import (
	"fmt"

	"github.com/corentings/chess/v3"
)

func main() {
	pos := chess.StartingPosition()
	fmt.Println(pos.Perft(3)) // 8,902 leaf positions at depth 3
}
Output:
8902

func (*Position) Ply

func (pos *Position) Ply() int

Ply returns the half-move number (increments every move).

func (*Position) PositionKey

func (pos *Position) PositionKey() string

PositionKey returns the four FEN fields that identify a position, without the half-move clock and full-move number.

func (*Position) SamePosition

func (pos *Position) SamePosition(pos2 *Position) bool

SamePosition returns true if the two positions are the same according to FIDE Article 9.2.3. Uses Zobrist hash as a fast-path, falling back to full field comparison on hash collision.

func (*Position) Status

func (pos *Position) Status() Method

Status returns the position's outcome Method (e.g. Checkmate, Stalemate, or NoMethod).

func (*Position) String

func (pos *Position) String() string

String implements the fmt.Stringer interface and returns a string with the FEN format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1.

func (*Position) Turn

func (pos *Position) Turn() Color

Turn returns the color to move next.

func (*Position) UnmarshalBinary

func (pos *Position) UnmarshalBinary(data []byte) error

UnmarshalBinary implements the encoding.BinaryMarshaler interface.

func (*Position) UnmarshalText

func (pos *Position) UnmarshalText(text []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface and assumes the data is in the FEN format.

func (*Position) UnsafeMoves

func (pos *Position) UnsafeMoves() []Move

UnsafeMoves returns all pseudo-legal moves that are illegal because they leave the moving side's king in check. These moves should not be played via Move().

func (*Position) Update

func (pos *Position) Update(m Move) *Position

Update returns a new position resulting from the given move. The move isn't validated - use Game.Move() for validation. This method is optimized for move generation where validation is handled separately.

Example:

newPos := pos.Update(move)

func (*Position) ValidMoves

func (pos *Position) ValidMoves() []Move

ValidMoves returns all legal moves in the current position. The moves are cached for performance. The returned slice is a defensive copy safe for modification by the caller.

func (*Position) ValidMovesIter

func (pos *Position) ValidMovesIter(yield func(Move) bool)

ValidMovesIter yields all legal moves in the current position. It uses Go 1.23's range-over-func pattern for zero-allocation iteration once the move cache is warm. The first call may allocate if moves have not been computed yet.

Example:

for move := range pos.ValidMovesIter {
    // process move
}

func (*Position) ValidMovesUnsafe

func (pos *Position) ValidMovesUnsafe() []Move

ValidMovesUnsafe returns all legal moves in the current position without copying. The caller must not modify the returned slice. This is a zero-allocation alternative to ValidMoves() for hot paths.

func (*Position) Variant

func (pos *Position) Variant() Variant

Variant returns the starting-position variant of the position. The zero value is Standard.

func (*Position) XFENString

func (pos *Position) XFENString() string

XFENString is similar to String() except it uses the legally-relevant en passant square (the square only when an enemy pawn can actually capture) instead of the raw FEN en passant square. For Chess960 positions the castling-rights field is emitted in Shredder-FEN file-letter form. The name is historical: this is the FEN key with clocks, not a separate notation.

func (*Position) ZobristHash

func (pos *Position) ZobristHash() uint64

ZobristHash returns the Zobrist hash of the position. This is a fast, collision-resistant hash suitable for transposition tables and position comparison. Two positions that are identical by FIDE rules will have the same hash value.

type Rank

type Rank int8

A Rank is the rank of a square.

const (
	Rank1 Rank = iota
	Rank2
	Rank3
	Rank4
	Rank5
	Rank6
	Rank7
	Rank8
)

func (Rank) Byte

func (r Rank) Byte() byte

func (Rank) String

func (r Rank) String() string

type RawMoveText

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

RawMoveText is unclassified move data decoded without a board position.

func (RawMoveText) From

func (m RawMoveText) From() Square

From returns the raw origin square.

func (RawMoveText) Move

func (m RawMoveText) Move() Move

Move returns the raw move as a Move without legality-derived tags.

func (RawMoveText) Null

func (m RawMoveText) Null() bool

Null reports whether the raw move is a null move.

func (RawMoveText) Promotion

func (m RawMoveText) Promotion() PieceType

Promotion returns the raw promotion piece type.

func (RawMoveText) To

func (m RawMoveText) To() Square

To returns the raw destination square.

type Setup

type Setup struct {
	Board         Board
	Turn          Color
	CastleRights  CastleRights
	EnPassant     Square
	HalfMoveClock int
	FullMoveNo    int
	Variant       Variant
}

Setup is an unvalidated description of a chess position. It separates "describing a position" from "playing a position" by letting callers build a complete position representation and then asking NewPosition to validate it.

func Chess960Setup

func Chess960Setup(index int) (Setup, error)

Chess960Setup returns the Setup for the Chess960 starting position with the given Scharnagl index (0-959). The index selects one of 960 initial back-rank arrangements satisfying the Chess960 constraints: bishops on opposite-colored squares and the king placed between the two rooks. Index 518 is the standard starting position. The returned Setup has Variant Chess960 and full castling rights for both sides.

func EmptySetup

func EmptySetup() Setup

EmptySetup returns a Setup representing an empty board: no pieces, White to move, no castling rights, no en passant square, halfmove clock 0, fullmove 1. It is the zero position used as a starting point for construction.

func InitialSetup

func InitialSetup() Setup

InitialSetup returns a Setup representing the standard chess starting position. The returned value is a copy and safe for callers to mutate.

func (Setup) Mirror

func (s Setup) Mirror() Setup

Mirror returns a vertically mirrored copy of s: every piece swaps color and moves to the square on the same file but the opposite rank (rank 1 ↔ rank 8); the side to move flips; castling rights swap color; the en passant square moves to the opposite rank. Halfmove clock and fullmove number are preserved.

Useful for opening-book authors who want to reuse one side's analysis for the other, and for symmetric position testing. Mirroring a legal standard position always produces a legal standard position.

func (Setup) SwapTurn

func (s Setup) SwapTurn() Setup

SwapTurn returns a copy of s with the side to move flipped and the en passant square cleared. Halfmove clock and fullmove number are preserved. This is the FIDE "pass" semantic used for repetition detection: only the side to move and en passant rights change, not the move counters.

type Side

type Side int

Side represents a side of the board.

const (
	// KingSide is the right side of the board from white's perspective.
	KingSide Side = iota + 1
	// QueenSide is the left side of the board from white's perspective.
	QueenSide
)

type Square

type Square int8

A Square is one of the 64 rank and file combinations that make up a chess board.

const (
	NoSquare Square = iota - 1
	A1
	B1
	C1
	D1
	E1
	F1
	G1
	H1
	A2
	B2
	C2
	D2
	E2
	F2
	G2
	H2
	A3
	B3
	C3
	D3
	E3
	F3
	G3
	H3
	A4
	B4
	C4
	D4
	E4
	F4
	G4
	H4
	A5
	B5
	C5
	D5
	E5
	F5
	G5
	H5
	A6
	B6
	C6
	D6
	E6
	F6
	G6
	H6
	A7
	B7
	C7
	D7
	E7
	F7
	G7
	H7
	A8
	B8
	C8
	D8
	E8
	F8
	G8
	H8
)

func NewSquare

func NewSquare(f File, r Rank) Square

NewSquare creates a new Square from a File and a Rank.

func ParseSquare

func ParseSquare(s string) (Square, error)

ParseSquare converts a 2-character square notation (e.g., "e4") to a Square. It returns an error if the input is not a valid square.

func SquareFromString deprecated

func SquareFromString(s string) Square

SquareFromString converts a 2-character square notation (e.g., "e4") to a Square. Returns NoSquare if the string is not a valid square.

Deprecated: use ParseSquare for new code; it distinguishes parse failures from the NoSquare sentinel.

func (Square) Bytes

func (sq Square) Bytes() [2]byte

func (Square) File

func (sq Square) File() File

File returns the square's file.

func (Square) Rank

func (sq Square) Rank() Rank

Rank returns the square's rank.

func (Square) String

func (sq Square) String() string

type TagPairs

type TagPairs map[string]string

TagPairs represents a collection of PGN tag pairs.

type Token

type Token struct {
	Error error
	Value string
	Type  TokenType
}

Token represents a lexical token from PGN text.

type TokenType

type TokenType int

TokenType represents the type of token in PGN text.

const (
	EOF TokenType = iota
	Undefined
	TagStart            // [
	TagEnd              // ]
	TagKey              // The key part of a tag (e.g., "Site")
	TagValue            // The value part of a tag (e.g., "Internet")
	MoveNumber          // 1, 2, 3, etc.
	DOT                 // .
	ELLIPSIS            // ...
	PIECE               // N, B, R, Q, K
	SQUARE              // e4, e5, etc.
	CommentStart        // {
	CommentEnd          // }
	COMMENT             // The comment text
	RESULT              // 1-0, 0-1, 1/2-1/2, *
	CAPTURE             // 'x' in moves
	FILE                // a-h in moves when used as disambiguation
	RANK                // 1-8 in moves when used as disambiguation
	KingsideCastle      // 0-0
	QueensideCastle     // 0-0-0
	PROMOTION           // = in moves
	PromotionPiece      // The piece being promoted to (Q, R, B, N)
	CHECK               // + in moves
	CHECKMATE           // # in moves
	NAG                 // Numeric Annotation Glyph (e.g., $1, $2, etc.)
	VariationStart      // ( for starting a variation
	VariationEnd        // ) for ending a variation
	CommandStart        // [%
	CommandName         // The command name (e.g., clk, eval)
	CommandParam        // Command parameter
	CommandEnd          // ]
	DeambiguationSquare // Full square disambiguation (e.g., e8 in Qe8f7)
	NullMove            // A null move: Z0, Z1, --, or @@
)

func (TokenType) String

func (t TokenType) String() string

type Variant

type Variant int8

Variant identifies the starting-position variant of a position. The zero value is Standard. Chess960 (Fischer Random Chess) selects one of 960 indexed initial back-rank arrangements; its castling rights and rook origins differ from standard chess but its castling destinations do not.

const (
	// Standard is the standard chess starting position.
	Standard Variant = iota
	// Chess960 is the Fischer Random Chess variant (960 indexed starts).
	Chess960
)

func (Variant) String

func (v Variant) String() string

String returns the variant's canonical name.

Directories

Path Synopsis
Package image renders chess positions as SVG images.
Package image renders chess positions as SVG images.
Package opening implements chess opening determination and exploration.
Package opening implements chess opening determination and exploration.

Jump to

Keyboard shortcuts

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