SkillAgentSearch skills...

Chess

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.

Install / Use

/learn @CorentinGS/Chess
About this skill

Quality Score

0/100

Supported Platforms

Zed

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 suc

Related Skills

View on GitHub
GitHub Stars73
CategoryContent
Updated2d ago
Forks20

Languages

Go

Security Score

100/100

Audited on Mar 23, 2026

No findings