cmuset/chess-tools

A chess tools library for parsing and exporting chess notations (PGN, SAN, FEN).

Maintainers

Package info

github.com/clemuset/chess-tools

pkg:composer/cmuset/chess-tools

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v2.0.2 2026-03-06 18:40 UTC

This package is auto-updated.

Last update: 2026-04-06 18:52:02 UTC


README

A PHP library for parsing and exporting chess notations: PGN, SAN, and FEN.

Table of Contents

  1. Installation
  2. Quick Start
  3. Tools
  4. Model Layer
  5. Enums Reference
  6. Testing & Development

Installation

Requires PHP >= 8.4.

composer require cmuset/chess-tools

Quick Start

use Cmuset\ChessTools\Model\Game;

// Parse a PGN string
$game = Game::fromPGN($pgn);

echo $game->getTag('White');     // 'Kasparov, Garry'
echo $game->getResult()->value;  // '1-0'

// Iterate moves
foreach ($game->getMainLine() as $key => $node) {
    echo $key . ' ' . $node->getMove()->getSAN(); // '1. e4', '1... e5', ...

    foreach ($node->getVariations() as $variation) {
        // Alternative lines branching from this move
    }
}

// Apply moves to a position
use Cmuset\ChessTools\Model\Position;
use Cmuset\ChessTools\Tool\Parser\PGNParser;

$pos = Position::fromFEN(PGNParser::INITIAL_FEN);
$pos->applyMove('e4');
$pos->applyMove('e5');
echo $pos->getFEN(); // 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1'

// Export
echo $game->getPGN();      // Full PGN with tags, comments, variations
echo $game->getLitePGN();  // Moves only

Tools

Parsers — docs/tools/parser.md

Convert strings into model objects.

Class Input Output
PGNParser PGN string Game or Game[]
SANParser SAN string + color Move
FENParser FEN string Position
use Cmuset\ChessTools\Tool\Parser\PGNParser;

$parser = PGNParser::create();
$game   = $parser->parse($pgn);         // Game | Game[]

Exporters — docs/tools/exporter.md

Serialize model objects back to strings.

Class Input Output
GameExporter Game or Variation PGN string
MoveExporter Move SAN string
PositionExporter Position FEN string
$game->getPGN();        // full PGN
$game->getLitePGN();    // moves only
$game->getVerbosePgn(); // with resolved source squares and check/mate markers
$position->getFEN();
$move->getSAN();

MoveApplier — docs/tools/move-applier.md

Applies a Move to a Position, enforcing all chess rules: castling rights, en passant, promotion, counters, and post-move check validation.

use Cmuset\ChessTools\Tool\MoveApplier\Exception\MoveApplyingException;

try {
    $pos->applyMove('Nf3');
} catch (MoveApplyingException $e) {
    echo $e->getMoveViolation()->value; // e.g. 'No piece found for the move'
}

$pos->getLegalMoves(); // Move[] — all legal moves for the side to move

Validators — docs/tools/validator.md

Check positions and games for illegal states.

Class Validates
PositionValidator A single Position (kings, check, pawns, en passant)
GameValidator A full Game — replays every move including sub-variations
use Cmuset\ChessTools\Tool\Validator\PositionValidator;
use Cmuset\ChessTools\Tool\Validator\GameValidator;

$violations = (new PositionValidator())->validate($pos);   // PositionViolationEnum[]
$violation  = (new GameValidator())->validate($game);      // ?GameViolation

Resolvers — docs/tools/resolver.md

Derive information absent from raw SAN: source squares, capture flags, and check/checkmate markers.

Class Resolves
MoveResolver A single Move against a Position
VariationResolver All moves in a Variation sequentially
GameResolver The full game main line + result detection
use Cmuset\ChessTools\Tool\Resolver\GameResolver;

GameResolver::create()->resolve($game);
// All moves now carry squareFrom, isCapture, isCheck, isCheckmate

VariationSplitter — docs/tools/splitter.md

Extracts all nested variations into a flat list of independent Variation objects, each prefixed with the moves preceding the divergence.

$variations = $game->split(); // Variation[] — first is always the main line

VariationMerger — docs/tools/merger.md

Merges Variation objects back into a main line, inserting diverging moves as nested sub-variations at the correct branching points.

$game->merge($variationA, $variationB);

Model Overview

classDiagram
    direction TB

    class Game {
        tags: array
        result: ResultEnum
        +fromPGN(string) Game
        +getPGN() string
        +split() Variation[]
        +merge(Variation[]) void
    }

    class Position {
        sideToMove: ColorEnum
        castlingRights: CastlingEnum[]
        enPassantTarget: CoordinatesEnum
        halfmoveClock: int
        fullmoveNumber: int
        +fromFEN(string) Position
        +getFEN() string
        +applyMove(Move) void
        +getLegalMoves() Move[]
    }

    class Variation {
        identifier: string
        +fromPGN(string) Variation
        +getPGN() string
        +split() Variation[]
        +merge(Variation[]) void
    }

    class MoveNode {
        moveNumber: int
        nags: int[]
        beforeMoveComment: string
        afterMoveComment: string
        +getKey() string
    }

    class Move {
        isCapture: bool
        isCheck: bool
        isCheckmate: bool
        +fromSAN(string, ColorEnum) Move
        +getSAN() string
    }

    class Square {
        +isEmpty() bool
    }

    Game "1" --> "1" Position : initialPosition
    Game "1" --> "1" Variation : mainLine
    Variation "1" *-- "*" MoveNode : nodes
    MoveNode "1" --> "0..1" Move
    MoveNode "1" *-- "*" Variation : variations
    Position "1" *-- "64" Square : squares
    Move --> PieceEnum
    Move --> CoordinatesEnum : to, squareFrom
    Move --> CastlingEnum
    Square --> PieceEnum
    Square --> CoordinatesEnum
Loading
Class Description Docs
Game Full PGN game: tags, initial position, main line, result
Position Board state: pieces, side to move, castling rights, en passant, counters
Variation Ordered collection of MoveNode instances, keyed by "1." / "1..." notation
MoveNode Node in the move tree: move + move number + comments + NAGs + sub-variations
Move Parsed SAN move: piece, destination, flags (capture, check, castling, promotion)
Square A board square: coordinates + optional piece

Enums Reference

All domain concepts are PHP string-backed enums.

Enum Values Docs
ColorEnum WHITE 'w' · BLACK 'b'
PieceEnum WHITE_KING 'K'BLACK_PAWN 'p' (12 cases)
CoordinatesEnum A1 'a1'H8 'h8' (64 cases)
CastlingEnum WHITE_KINGSIDE 'K' · WHITE_QUEENSIDE 'Q' · BLACK_KINGSIDE 'k' · BLACK_QUEENSIDE 'q'
ResultEnum WHITE_WINS '1-0' · BLACK_WINS '0-1' · DRAW '1/2-1/2' · ONGOING '*'

Testing & Development

git clone https://github.com/clemuset/pgn-parser.git
cd pgn-parser
composer install
vendor/bin/phpunit                        # Run all tests
vendor/bin/phpunit tests/path/to/Test.php # Run a single test file
vendor/bin/phpunit --filter methodName    # Run a single test method

vendor/bin/phpstan analyse                # Static analysis (level 7)
vendor/bin/php-cs-fixer fix               # Fix code style
vendor/bin/php-cs-fixer fix --dry-run     # Check without applying changes

Or with Docker:

make check    # fix + analyse + test
make test
make analyse
make fix

MIT License. See LICENSE.