chess

package module
v2.3.3 Latest Latest
Warning

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

Go to latest
Published: Dec 1, 2025 License: MIT Imports: 19 Imported by: 10

README

Chess Library

GoDoc Go Report Card License codecov CI Go Version

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.

rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1

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()
  • UnsafePushNotationMove() - ~1.1x faster than PushNotationMove()

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: This package is under the /v2 namespace to signify that it might not be backward compatible with the original package. While some parts might work as plug-and-play, others might require changes. Unfortunately, I do not plan to maintain a breaking change list at this time, but I expect in-code comments and the compiler/linter to assist with migration.

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/v2

Usage

Example Random Game
package main

import (
	"fmt"
	"math/rand"

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

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/v2"
	"github.com/corentings/chess/v2/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)
}

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

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

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

game := chess.NewGame()
// Only use when you're certain the move is valid
err := game.UnsafePushNotationMove("e4", chess.AlgebraicNotation{}, 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
  • UnsafePushNotationMove() provides ~1.1x performance improvement over PushNotationMove() 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 Notation

PushNotationMove method accepts string input using any supported notation:

game := chess.NewGame()
if err := game.PushNotationMove("e4", chess.AlgebraicNotation{}, 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.PushNotationMove("e4", chess.AlgebraicNotation{}, nil); err != nil {
fmt.Println("Move failed:", err)
} else {
fmt.Println("e4 move succeeded")
}

// Invalid notation will be caught
if err := game.PushNotationMove("e5", chess.AlgebraicNotation{}, 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.UnsafePushNotationMove("e4", chess.AlgebraicNotation{}, 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.PushNotationMove("f3", chess.AlgebraicNotation{}, nil)
game.PushNotationMove("e6", chess.AlgebraicNotation{}, nil)
game.PushNotationMove("g4", chess.AlgebraicNotation{}, nil)
game.PushNotationMove("Qh4", chess.AlgebraicNotation{}, 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.PushNotationMove("Qb6", chess.AlgebraicNotation{}, 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.PushNotationMove("f3", chess.AlgebraicNotation{}, nil)
game.Resign(chess.Black)
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.PushNotationMove(m, chess.AlgebraicNotation{}, 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.PushNotationMove(m, chess.AlgebraicNotation{}, 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.PushNotationMove("Kf8", chess.AlgebraicNotation{}, 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.PushNotationMove("e4", chess.AlgebraicNotation{}, nil)
game.PushNotationMove("e5", chess.AlgebraicNotation{}, nil)
fmt.Println(game)
/*
[Event "F/S Return Match"]

1.e4 e5  *
*/
Scan PGN

For parsing large PGN database files use Scanner:

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

scanner := chess.NewScanner(f)
// Read all games
for scanner.HasNext() {
game, err := scanner.ParseNext()
if err != nil {
log.Fatal("Failed to parse game: %v", err)
}
fmt.Println(game.GetTagPair("Site"))
// Output &{Site https://lichess.org/8jb5kiqw}
}
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()

scanner := chess.NewScanner(f, chess.WithExpandVariations())
// Read all games
for scanner.HasNext() {
game, err := scanner.ParseNext()
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
Notations

Chess Notation define how moves are encoded in a serialized format. Chess uses a notation 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.PushNotationMove("e4", chess.AlgebraicNotation{}, nil)
game.PushNotationMove("e5", chess.AlgebraicNotation{}, nil)
fmt.Println(game) // 1.e4 e5  *
Long Algebraic Notation

Long Algebraic Notation LongAlgebraicNotation 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.PushNotationMove("f2f3", chess.LongAlgebraicNotation{}, nil)
game.PushNotationMove("e7e5", chess.LongAlgebraicNotation{}, nil)
game.PushNotationMove("g2g4", chess.LongAlgebraicNotation{}, nil)
game.PushNotationMove("Qd8h4", chess.LongAlgebraicNotation{}, 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.PushNotationMove("e2e4", chess.UCINotation{}, nil)
game.PushNotationMove("e7e5", chess.UCINotation{}, nil)
fmt.Println(game) // 1.e2e4 e7e5  *
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♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
*/
Move History

Move History is a convenient API for accessing aligned positions, moves, and comments. Move History is useful when trying to understand detailed information about a game. Below is an example showing how to see which side castled first.

package main

import (
	"fmt"
	"os"

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

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
	for _, mh := range game.MoveHistory() {
		if mh.Move.HasTag(chess.KingSideCastle) || mh.Move.HasTag(chess.QueenSideCastle) {
			color = mh.PrePosition.Turn()
			break
		}
	}
	switch color {
	case chess.White:
		fmt.Println("white castled first")
	case chess.Black:
		fmt.Println("black castled first")
	default:
		fmt.Println("no side castled")
	}
}

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 from the baseline 2015 MBP:

BenchmarkBitboardReverse-4              2000000000               1.01 ns/op
BenchmarkStalemateStatus-4                500000              3116 ns/op
BenchmarkInvalidStalemateStatus-4         500000              2290 ns/op
BenchmarkPositionHash-4                  1000000              1864 ns/op
BenchmarkValidMoves-4                     100000             13445 ns/op
BenchmarkPGN-4                               300           5549192 ns/op

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 / turn management
  • move validation
  • PGN encoding / decoding
  • FEN encoding / decoding

Using Moves

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

Using Algebraic Notation

game := chess.NewGame()
game.MoveStr("e4")

Using PGN

pgn, _ := chess.PGN(pgnReader)
game := chess.NewGame(pgn)

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"

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

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)
    }
    // 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 implements a chess game engine that manages move generation, position analysis, and game state validation. The engine uses bitboard operations and lookup tables for efficient move generation and position analysis. Move generation includes standard piece moves, captures, castling, en passant, and pawn promotions. Example usage:

// Create a position
pos := NewPosition()

// Calculate legal moves for current position
eng := engine{}
moves := eng.CalcMoves(pos, false)

// Check game status
status := eng.Status(pos)
if status == Checkmate {
	fmt.Println("Game Over - Checkmate")
}

Package chess provides a complete chess game implementation with support for move validation, game tree management, and standard chess formats (PGN, FEN). The package manages complete chess games including move history, variations, and game outcomes. It supports standard chess rules including all special moves (castling, en passant, promotion) and automatic draw detection. Example usage:

// Create new game
game := NewGame()

// Make moves
game.PushMove("e4", nil)
game.PushMove("e5", nil)

// Check game status

if game.Outcome() != NoOutcome {
	fmt.Printf("Game ended: %s by %s\n", game.Outcome(), game.Method())
}

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
}

Package chess provides PGN (Portable Game Notation) parsing functionality, supporting standard chess notation including moves, variations, comments, annotations, and game metadata. Example usage:

// Create parser from tokens
tokens := TokenizeGame(game)
parser := NewParser(tokens)

// Parse complete game
game, err := parser.Parse()

Package chess provides position representation and manipulation for chess games. The package implements complete position tracking including piece placement, castling rights, en passant squares, and move counts. It supports standard chess formats (FEN) and provides methods for position analysis and move validation. Example usage:

// Create starting position
pos := StartingPosition()

// Check valid moves
moves := pos.ValidMoves()

// Update position with move
newPos := pos.Update(move)

// Get FEN string
fen := pos.String()

Index

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} }

	ErrNoGameFound = errors.New("no game found in PGN data")
)

Custom error types for different PGN errors

Functions

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 GetPolyglotHashBytes added in v2.0.4

func GetPolyglotHashBytes(index int) []byte

GetPolyglotHashBytes returns a precomputed byte slice for a given index

func GetPolyglotHashes

func GetPolyglotHashes() []string

GetPolyglotHashes returns a reference to the static hash array

func IgnoreFivefoldRepetitionDraw added in v2.3.3

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 added in v2.3.3

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 added in v2.3.3

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 added in v2.0.2

func MoveToPolyglot(m Move) uint16

func PGN

func PGN(r io.Reader) (func(*Game), error)

PGN takes a reader and returns a function that updates the game to reflect the PGN data. The PGN can use any move notation supported by this package. The returned function is designed to be used in the NewGame constructor. An error is returned if there is a problem parsing the PGN data.

func ValidateSAN added in v2.2.0

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#"

func ZobristHashToUint64 added in v2.0.2

func ZobristHashToUint64(hash string) uint64

Types

type AlgebraicNotation

type AlgebraicNotation struct{}

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

func (AlgebraicNotation) Decode

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

Decode implements the Decoder interface.

func (AlgebraicNotation) Encode

func (AlgebraicNotation) Encode(pos *Position, m *Move) string

Encode implements the Encoder interface.

func (AlgebraicNotation) String

func (AlgebraicNotation) String() string

String implements the fmt.Stringer interface and returns the notation's name.

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

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 := NewBoard(squares)

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 added in v2.1.0

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) Flip

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

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) 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) 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) Rotate

func (b *Board) Rotate() *Board

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

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.

type BookSource

type BookSource interface {
	// Read reads exactly len(p) bytes into p or returns an error
	Read(p []byte) (n int, err error)
	// Size returns the total size of the book data
	Size() (int64, error)
}

BookSource defines the interface for reading polyglot book data. This interface allows for different source implementations (file, memory, etc.) while maintaining consistent access patterns.

type BytesBookSource

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

BytesBookSource implements BookSource for byte slices

func NewBytesBookSource

func NewBytesBookSource(data []byte) *BytesBookSource

NewBytesBookSource creates a new memory-based book source

func (*BytesBookSource) Read

func (b *BytesBookSource) Read(p []byte) (n int, err error)

Read implements BookSource for BytesBookSource

func (*BytesBookSource) Size

func (b *BytesBookSource) Size() (int64, error)

Size implements BookSource for BytesBookSource

type CastleRights

type CastleRights string

CastleRights holds the state of both sides castling abilities.

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) String

func (cr CastleRights) String() string

String implements the fmt.Stringer interface and returns a FEN compatible string. Ex. KQq.

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

func ColorFromString(s string) Color

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 Decoder

type Decoder interface {
	Decode(pos *Position, s string) (*Move, error)
}

Decoder is the interface implemented by objects that can decode a string into a move given the position. It is not the decoders responsibility to validate the move. An error is returned if the string could not be decoded.

type Encoder

type Encoder interface {
	Encode(pos *Position, m *Move) string
}

Encoder is the interface implemented by objects that can encode a move into a string given the position. It is not the encoders responsibility to validate the move.

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 FileBookSource

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

FileBookSource implements BookSource for files

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 (*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) AddVariation

func (g *Game) AddVariation(parent *Move, newMove *Move)

AddVariation adds a new variation to the game. The parent move must be a move in the game or nil to add a variation to the root.

func (*Game) Clone

func (g *Game) Clone() *Game

Clone returns a deep copy of the game.

func (*Game) Comments

func (g *Game) Comments() [][]string

Comments returns the comments for the game indexed by moves. Comments returns the comments for the game indexed by moves.

func (*Game) CurrentPosition

func (g *Game) CurrentPosition() *Position

CurrentPosition returns the game's current move position. This is the position at the current pointer in the move tree. This should be used to get the current position of the game instead of Position().

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.

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.

func (*Game) GetRootMove added in v2.0.2

func (g *Game) GetRootMove() *Move

GetRootMove returns the root move of the game.

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) GoBack

func (g *Game) GoBack() bool

GoBack navigates to the previous move in the game. Returns true if the move was successful. Returns false if there are no moves to go back to. If the game is at the start, it will return false.

func (*Game) GoForward

func (g *Game) GoForward() bool

GoForward navigates to the next move in the game. Returns true if the move was successful. Returns false if there are no moves to go forward to. If the game is at the end, it will return false.

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 added in v2.0.7

func (g *Game) Move(move *Move, options *PushMoveOptions) 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.

Example:

possibleMove := game.ValidMoves()[0]

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

func (*Game) Moves

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

Moves returns the move history of the game following the main line.

func (*Game) NavigateToMainLine

func (g *Game) NavigateToMainLine()

NavigateToMainLine navigates to the main line of the game. The main line is the first child of each move.

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.

func (*Game) PushMove deprecated

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

Deprecated: use PushNotationMove instead.

PushMove adds a move in algebraic notation to the game. Returns an error if the move is invalid. This method now validates moves for consistency with other move methods.

Example:

err := game.PushMove("e4", &PushMoveOptions{ForceMainline: true})

func (*Game) PushNotationMove added in v2.0.7

func (g *Game) PushNotationMove(moveStr string, notation Notation, options *PushMoveOptions) error

PushNotationMove adds a move to the game using any supported notation. It validates the move before adding it to ensure game correctness. For high-performance scenarios where moves are pre-validated, use UnsafePushNotationMove.

Example:

err := game.PushNotationMove("e4", chess.AlgebraicNotation{}, &PushMoveOptions{ForceMainline: true})
if err != nil {
  panic(err)
}

game.PushNotationMove("c7c5", chess.UCINotation{}, nil)
game.PushNotationMove("Nc1f3", chess.LongAlgebraicNotation{}, nil)

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)

Resign resigns the game for the given color. If the game has already been completed then the game is not updated.

func (*Game) Split added in v2.1.0

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.

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 added in v2.3.0

func (g *Game) UnsafeMove(move *Move, options *PushMoveOptions) 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) UnsafePushNotationMove added in v2.3.0

func (g *Game) UnsafePushNotationMove(moveStr string, notation Notation, options *PushMoveOptions) error

UnsafePushNotationMove adds a move to the game using any supported notation 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 PushNotationMove which includes validation.

Example:

// Only use when you're certain the move is valid
err := game.UnsafePushNotationMove("e4", chess.AlgebraicNotation{}, nil)
if err != nil {
    panic(err) // Should not happen with valid notation/moves
}

func (*Game) ValidMoves

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

ValidMoves returns all legal moves in the current position.

func (*Game) Variations

func (g *Game) Variations(move *Move) []*Move

Variations returns all alternative moves at the given position.

type GameScanned

type GameScanned struct {
	// Raw contains the complete PGN text of the game
	Raw string
}

GameScanned represents a complete chess game in PGN format.

type Hash

type Hash []byte

Hash represents a Zobrist hash as a byte slice

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 LongAlgebraicNotation

type LongAlgebraicNotation struct{}

LongAlgebraicNotation is a fully expanded version of algebraic notation in which the starting and ending squares are specified. Examples: e2e4, Rd3xd7, O-O (short castling), e7e8=Q (promotion).

func (LongAlgebraicNotation) Decode

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

Decode implements the Decoder interface.

func (LongAlgebraicNotation) Encode

func (LongAlgebraicNotation) Encode(pos *Position, m *Move) string

Encode implements the Encoder interface.

func (LongAlgebraicNotation) String

func (LongAlgebraicNotation) String() string

String implements the fmt.Stringer interface and returns the notation's name.

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 (*Move) AddComment added in v2.0.1

func (m *Move) AddComment(comment string)

func (*Move) AddTag added in v2.0.4

func (m *Move) AddTag(tag MoveTag)

AddTag adds the given MoveTag to the move's tags using a bitwise OR operation. Multiple tags can be combined by calling AddTag multiple times.

func (*Move) Children added in v2.0.1

func (m *Move) Children() []*Move

func (*Move) Clone added in v2.1.0

func (m *Move) Clone() *Move

Clone returns a deep copy of a move.

Per-field exceptions:

parent: not copied; the clone'd move has no parent
children: not copied; the clone'd move has no children

func (*Move) Comments added in v2.0.1

func (m *Move) Comments() string

func (*Move) FullMoveNumber added in v2.0.9

func (m *Move) FullMoveNumber() int

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

func (*Move) GetCommand added in v2.0.1

func (m *Move) GetCommand(key string) (string, bool)

func (*Move) HasTag

func (m *Move) HasTag(tag MoveTag) bool

HasTag returns true if the move contains the MoveTag given.

func (*Move) NAG added in v2.0.1

func (m *Move) NAG() string

func (*Move) Number added in v2.0.1

func (m *Move) Number() int

func (*Move) Parent added in v2.0.1

func (m *Move) Parent() *Move

func (*Move) Ply added in v2.0.9

func (m *Move) Ply() int

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

func (*Move) Position added in v2.0.1

func (m *Move) Position() *Position

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) SetCommand added in v2.0.1

func (m *Move) SetCommand(key, value string)

func (*Move) SetComment added in v2.0.5

func (m *Move) SetComment(comment string)

func (*Move) SetNAG added in v2.0.1

func (m *Move) SetNAG(nag string)

func (*Move) String

func (m *Move) String() string

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

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
)

type MoveWithWeight added in v2.0.2

type MoveWithWeight struct {
	Move   Move
	Weight uint16
}

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

type Notation

type Notation interface {
	Encoder
	Decoder
}

Notation is the interface implemented by objects that can encode and decode moves.

type Outcome

type Outcome string

A Outcome is the result of a game.

const (
	UnknownOutcome Outcome = ""
	// NoOutcome indicates that a game is in progress or ended without a result.
	NoOutcome Outcome = "*"
	// WhiteWon indicates that white won the game.
	WhiteWon Outcome = "1-0"
	// BlackWon indicates that black won the game.
	BlackWon Outcome = "0-1"
	// Draw indicates that game was a draw.
	Draw Outcome = "1/2-1/2"
)

func (Outcome) String

func (o Outcome) String() string

String implements the fmt.Stringer interface.

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 Parser

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

Parser holds the state needed during parsing.

func NewParser

func NewParser(tokens []Token) *Parser

NewParser creates a new parser instance initialized with the given tokens. The parser starts with a root move containing the starting position.

Example:

tokens := TokenizeGame(game)
parser := NewParser(tokens)

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 added in v2.1.0

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 PieceTypeFromByte

func PieceTypeFromByte(b byte) PieceType

func PieceTypeFromString

func PieceTypeFromString(s string) PieceType

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 added in v2.0.2

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.GetRandomMove(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 LoadFromSource

func LoadFromSource(source BookSource) (*PolyglotBook, error)

LoadFromSource loads a polyglot book from any BookSource

func NewPolyglotBookFromMap added in v2.0.2

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 added in v2.0.2

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) DeleteMoves added in v2.0.2

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) GetChessMoves added in v2.0.2

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

func (*PolyglotBook) GetRandomMove

func (book *PolyglotBook) GetRandomMove(positionHash uint64) *PolyglotEntry

GetRandomMove 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 := book.GetRandomMove(hash)
if move != nil {
    decodedMove := DecodeMove(move.Move)
    fmt.Printf("Selected move: %v\n", decodedMove)
}

func (*PolyglotBook) ToMoveMap added in v2.0.2

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

func (*PolyglotBook) UpdateMove added in v2.0.2

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 added in v2.0.2

func (pm PolyglotMove) Encode() uint16

func (PolyglotMove) ToMove added in v2.0.2

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 StartingPosition

func StartingPosition() *Position

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

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) ChangeTurn

func (pos *Position) ChangeTurn() *Position

ChangeTurn returns a new position with the turn changed.

func (*Position) EnPassantSquare

func (pos *Position) EnPassantSquare() Square

EnPassantSquare returns the en-passant square.

func (*Position) HalfMoveClock

func (pos *Position) HalfMoveClock() int

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

func (*Position) Hash

func (pos *Position) Hash() [16]byte

Hash returns a unique hash of the position.

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) Ply added in v2.0.9

func (pos *Position) Ply() int

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

func (*Position) Status

func (pos *Position) Status() Method

Status returns the position's status as one of the outcome methods. Possible returns values include Checkmate, Stalemate, and 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.TextUnarshaler interface and assumes the data is in the FEN format.

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. TODO: Can we make this more efficient? Maybe using an iterator?

func (*Position) XFENString added in v2.1.0

func (pos *Position) XFENString() string

XFENString() is similar to String() except that it returns a string with the X-FEN format

type PushMoveOptions

type PushMoveOptions struct {
	// ForceMainline makes this move the main line if variations exist
	ForceMainline bool
}

PushMoveOptions contains options for pushing a move to the game

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 ReaderBookSource

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

ReaderBookSource implements BookSource for io.Reader

func NewReaderBookSource

func NewReaderBookSource(reader io.Reader) (*ReaderBookSource, error)

NewReaderBookSource creates a new reader-based book source Note: This will read the entire input into memory to support Size() and multiple reads

func (*ReaderBookSource) Read

func (r *ReaderBookSource) Read(p []byte) (n int, err error)

Read implements BookSource for ReaderBookSource

func (*ReaderBookSource) Size

func (r *ReaderBookSource) Size() (int64, error)

Size implements BookSource for ReaderBookSource

type Scanner

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

Scanner provides functionality to read chess games from a PGN source. It supports streaming processing of multiple games and proper handling of PGN syntax.

func NewScanner

func NewScanner(r io.Reader, opts ...ScannerOption) *Scanner

NewScanner creates a new PGN scanner that reads from the provided reader. The scanner is configured to properly split PGN games and handle PGN-specific syntax.

Example:

scanner := NewScanner(strings.NewReader(pgnText))

func (*Scanner) HasNext

func (s *Scanner) HasNext() bool

HasNext returns true if there are more games available to read. This method can be used to iterate over all games in the source.

Example:

for scanner.HasNext() {
    scangame, err := scanner.ScanGame()
    // Process scangame
}

func (*Scanner) ParseNext added in v2.1.0

func (s *Scanner) ParseNext() (*Game, error)

ParseNext is a convenience wrapper combining the functionality of ScanGame(), TokenizeGame(), NewParser(), and Parse() enabling callers to simplify iterating over each Game within a pgn file.

Example:

for scanner.HasNext() {
    game, err := scanner.ParseNext()
    // Process game
}

func (*Scanner) ScanGame

func (s *Scanner) ScanGame() (*GameScanned, error)

ScanGame reads and returns the next game from the source. Returns nil and io.EOF when no more games are available. Returns nil and an error if reading fails.

Example:

game, err := scanner.ScanGame()
if err == io.EOF {
    // No more games
}

type ScannerOption added in v2.1.0

type ScannerOption func(*Scanner)

func WithExpandVariations added in v2.1.0

func WithExpandVariations() ScannerOption

WithExpandVariations() instructs the scanner to expand all variations in a single GameScanned into multiple Game instances (1 per variation) rather than a single Game instance.

type ScannerOpts added in v2.1.0

type ScannerOpts struct {
	ExpandVariations bool // default false
}

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 (Square) Bytes

func (sq Square) Bytes() []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.

func TokenizeGame

func TokenizeGame(game *GameScanned) ([]Token, error)

TokenizeGame converts a PGN game into a sequence of tokens. Returns nil if the game is nil. Returns an error if tokenization fails.

The function handles all PGN elements including moves, comments, annotations, and metadata tags.

Example:

tokens, err := TokenizeGame(game)
if err != nil {
    // Handle error
}

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)
)

func (TokenType) String

func (t TokenType) String() string

type UCINotation

type UCINotation struct{}

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

func (UCINotation) Decode

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

Decode implements the Decoder interface.

func (UCINotation) Encode

func (UCINotation) Encode(_ *Position, m *Move) string

Encode implements the Encoder interface.

func (UCINotation) String

func (UCINotation) String() string

String implements the fmt.Stringer interface and returns the notation's name.

type ZobristHasher added in v2.0.2

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

ZobristHasher provides methods to generate Zobrist hashes for chess positions

func NewChessHasher

func NewChessHasher() *ZobristHasher

NewChessHasher creates a new instance of ChessHasher Deprecated: Use NewZobristHasher instead

func NewZobristHasher added in v2.0.2

func NewZobristHasher() *ZobristHasher

NewZobristHasher creates a new instance of ZobristHasher

func (*ZobristHasher) HashPosition added in v2.0.2

func (ch *ZobristHasher) HashPosition(fen string) (string, error)

HashPosition computes a Zobrist hash for a chess position in FEN notation

Directories

Path Synopsis
Package image is a go library that creates images from board positions
Package image is a go library that creates images from board positions
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