chessnote

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2025 License: MIT Imports: 6 Imported by: 0

README

ChessNote: High-Performance PGN Parsing in Go

Go Report Card Go Reference CI

ChessNote is a production-grade, high-performance Go library for parsing Portable Game Notation (PGN). It's engineered from the ground up to be a foundational component for ambitious chess applications, from powerful analysis tools and database backends to beautiful game renderers.

We're not just parsing text; we're providing a reliable, rigorously-tested, and developer-friendly toolkit for bringing chess data to life.

Why ChessNote?

In a world of hobbyist libraries, ChessNote is engineered for professional use. It's built on a philosophy of reliability and performance, making it the ideal choice for startups and developers who need a parser that just works.

  • Rock-Solid Reliability: Built with a strict Test-Driven Development (TDD) methodology, ChessNote boasts comprehensive test coverage. Every feature, from simple pawn moves to complex variations, is verified. The parser is designed to handle real-world, messy PGNs without panicking, returning structured errors that make debugging a breeze.

  • Blazing-Fast Performance: Written in pure, idiomatic Go, ChessNote is designed for speed. It minimizes allocations and uses an efficient scanning and parsing model to handle large PGN databases with ease. Its performance is validated by a comprehensive benchmark suite (see the benchmarks/ directory).

  • A Developer-First API: The public API is clean, discoverable, and a joy to use. We believe developers should spend their time building great applications, not fighting with a clunky parser. Our data structures (Game, Move, Square) are intuitive and well-documented.

Feature-Complete Parser

ChessNote implements the complete PGN standard, allowing you to parse virtually any PGN file you encounter.

  • Full Tag Pair Support: Parses the Seven Tag Roster and any other custom tags.
  • Complete Movetext Parsing:
    • Standard pawn and piece moves (e4, Nf3)
    • Captures (exd5, Nxf3)
    • Checks (+) and Checkmates (#)
    • Disambiguation (Rdf8, N1c3)
    • Pawn Promotion (e8=Q)
    • Castling (O-O, O-O-O)
  • Advanced PGN Syntax:
    • Comments ({...} and ;...)
    • Recursive Annotation Variations (RAVs) (...)
    • Numeric Annotation Glyphs (NAGs) ($1, $18)
  • Game Termination Markers: Correctly identifies the game result (1-0, 0-1, 1/2-1/2, *).
  • Robust Error Handling: Returns detailed, structured errors for invalid syntax.

Quick Start

Get the library:

go get github.com/YashBhalodi/chessnote

Parse a complete PGN game in just a few lines of code. Here's an example parsing Paul Morphy's famous "Opera Game":

package main

import (
	"fmt"
	"log"

	"github.com/YashBhalodi/chessnote"
)

func main() {
	// PGN of the "Opera Game" (Paul Morphy vs. Duke Karl / Count Isouard, 1858)
	pgn := `
[Event "A Night at the Opera"]
[Site "Paris, France"]
[Date "1858.??.??"]
[White "Paul Morphy"]
[Black "Duke Karl / Count Isouard"]
[Result "1-0"]

1. e4 e5 2. Nf3 d6 3. d4 Bg4 4. dxe5 Bxf3 5. Qxf3 dxe5 6. Bc4 Nf6 7. Qb3 Qe7
8. Nc3 c6 9. Bg5 b5 10. Nxb5 cxb5 11. Bxb5+ Nbd7 12. O-O-O Rd8
13. Rxd7 Rxd7 14. Rd1 Qe6 15. Bxd7+ Nxd7 16. Qb8+ Nxb8 17. Rd8# 1-0
`
	game, err := chessnote.ParseString(pgn)
	if err != nil {
		log.Fatalf("Failed to parse PGN: %v", err)
	}

	fmt.Printf("Successfully parsed game: %s vs %s\n", game.Tags["White"], game.Tags["Black"])
	fmt.Printf("Result: %s\n", game.Result)
	fmt.Printf("Total moves: %d\n", len(game.Moves))

	// Let's inspect the brilliant final move!
	finalMove := game.Moves[len(game.Moves)-1]
	fmt.Printf("Final move: Rd8#\n")
	fmt.Printf("  - Piece: Rook\n")
	fmt.Printf("  - Destination: d8\n")
	fmt.Printf("  - Is Checkmate: %t\n", finalMove.IsMate)
}

Running the Examples

The examples/ directory contains standalone, runnable programs to showcase the library's features.

To run the basic parsing example:

# Navigate to the example directory
cd examples/basic_parser

# Run the program. It will parse the included 'opera_game.pgn' by default.
go run .

# You can also point it to your own PGN file
go run . --pgn_file=/path/to/your/game.pgn

To run the advanced iterator example, which demonstrates traversing a game's move tree including variations:

# Navigate to the example directory
cd examples/advanced_iterator

# Run the program.
go run .

Project Philosophy

This project adheres to a strict set of engineering guidelines focused on creating professional, enterprise-grade software. We practice Test-Driven Development (TDD), maintain a comprehensive test suite, and prioritize a clean, stable, and well-documented public API.

Technical Documentation

For a deep dive into the parser's architecture, execution flow, and design decisions, please see our comprehensive Technical Documentation. This is the best resource for developers looking to contribute to the project or understand its inner workings.

Contributing & Future Roadmap

ChessNote's parser is now stable and feature-complete. While there is no immediate roadmap for new features, we are always open to suggestions and contributions from the community.

If you have an idea for a new feature or a suggestion for improvement, we encourage you to open a GitHub issue to discuss it. For more detailed guidelines on how to contribute, please see our Contributing Guide.

License

ChessNote is released under the MIT License.

Documentation

Overview

Package chessnote provides a high-performance, production-grade Go library for parsing Portable Game Notation (PGN), the universal standard for chess game data.

Package chessnote provides a high-performance, production-grade Go library for parsing Portable Game Notation (PGN), the universal standard for chess game data.

Index

Constants

This section is empty.

Variables

View Source
var PieceSymbols = map[rune]PieceType{
	'N': Knight,
	'B': Bishop,
	'R': Rook,
	'Q': Queen,
	'K': King,
}

PieceSymbols maps a rune representation of a piece in PGN to a PieceType. Pawns are not represented by a symbol in PGN.

Functions

func SplitMultiGame added in v0.1.1

func SplitMultiGame(pgn string) []string

SplitMultiGame takes a string containing multiple PGN games and splits them into a slice of individual game strings. It normalizes line endings to handle different file formats (e.g., Windows-style \r\n).

This utility is useful for pre-processing PGN files that contain an entire database of games before passing each individual game to the parser.

Types

type Game

type Game struct {
	// Tags is a map of PGN tag key-value pairs.
	Tags map[string]string
	// Moves is a slice of moves made in the game.
	Moves []Move
	// Result is the final result of the game (e.g., "1-0", "0-1").
	Result string
}

Game represents a single parsed PGN game, including its tag pairs, movetext, and final result.

func ParseString

func ParseString(s string, opts ...ParserOption) (*Game, error)

ParseString is a convenience helper that parses a PGN string. It is intended for quickly parsing a complete PGN string already in memory. For parsing from files or network streams, creating a Parser with NewParser is recommended.

type Move

type Move struct {
	// From is the starting square of the move. For many moves, this may be
	// partially or fully zero, as PGN format often omits this information
	// when it's not needed for disambiguation.
	From Square
	// To is the destination square of the move. This is always specified.
	To Square
	// Piece is the type of piece that was moved.
	Piece PieceType
	// Promotion is the piece type a pawn is promoted to. It is zero
	// (Pawn) if there is no promotion.
	Promotion PieceType
	// IsCapture indicates whether the move was a capture.
	IsCapture bool
	// IsCheck indicates whether the move resulted in a check.
	IsCheck bool
	// IsMate indicates whether the move resulted in a checkmate.
	IsMate bool
	// IsKingsideCastle indicates a kingside castling move (O-O).
	IsKingsideCastle bool
	// IsQueensideCastle indicates a queenside castling move (O-O-O).
	IsQueensideCastle bool
	// Variations lists any alternative move sequences that could have been
	// played. This is used for representing Recursive Annotation Variations (RAVs).
	Variations [][]Move
	// NAGs is a slice of Numeric Annotation Glyphs (e.g., $1, $2)
	// associated with the move.
	NAGs []int
}

Move represents a single move made by one player, capturing all details expressed in Standard Algebraic Notation (SAN).

type Parser

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

Parser is a PGN parser that reads from an io.Reader and parses it into a Game. It implements a standard recursive descent parser.

func NewParser

func NewParser(r io.Reader, opts ...ParserOption) *Parser

NewParser creates and returns a new PGN Parser for the given reader. By default, it operates in strict mode. Behavior can be customized with ParserOptions, such as WithLaxParsing().

func (*Parser) Parse

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

Parse reads and parses the entire PGN data from the reader, returning a single Game object. It expects the PGN data to contain exactly one game. The parser stops at the first game-terminating symbol (*, 1-0, etc.).

type ParserConfig added in v0.1.3

type ParserConfig struct {
	// Strict mode requires that a PGN game must end with a valid result token
	// (*, 1-0, 0-1, or 1/2-1/2). When disabled (lax mode), a game can end
	// at the end of the file without a result token.
	// It is enabled by default.
	Strict bool
}

ParserConfig holds configuration settings for the parser.

type ParserOption added in v0.1.3

type ParserOption func(*ParserConfig)

A ParserOption configures a Parser.

func WithLaxParsing added in v0.1.3

func WithLaxParsing() ParserOption

WithLaxParsing returns a ParserOption that disables strict parsing mode. In lax mode, the parser will not require a final game result token and will successfully parse a game that ends abruptly at the end of the file.

type PieceType

type PieceType int

PieceType defines the type of chess piece.

const (
	// Pawn represents a pawn piece. Note that this is the zero value for PieceType.
	Pawn PieceType = iota
	// Knight represents a knight piece.
	Knight
	// Bishop represents a bishop piece.
	Bishop
	// Rook represents a rook piece.
	Rook
	// Queen represents a queen piece.
	Queen
	// King represents a king piece.
	King
)

type Square

type Square struct {
	// File is the file of the square, represented as 0-7 for files a-h.
	File int
	// Rank is the rank of the square, represented as 0-7 for ranks 1-8.
	Rank int
}

Square represents a single square on the board (e.g., e4).

Directories

Path Synopsis
examples
advanced_iterator command
This example demonstrates a more advanced use case: iterating through a game's entire move tree, including all variations (Recursive Annotation Variations).
This example demonstrates a more advanced use case: iterating through a game's entire move tree, including all variations (Recursive Annotation Variations).
basic_parser command
This example demonstrates how to parse a PGN file from your local disk.
This example demonstrates how to parse a PGN file from your local disk.
parser_options command
internal

Jump to

Keyboard shortcuts

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