cmuset/pgn-parser
最新稳定版本:v1.1.2
Composer 安装命令:
composer require cmuset/pgn-parser
包简介
A PGN (Portable Game Notation) parser for chess games.
README 文档
README
A lightweight PHP library to parse and export chess game notations:
- PGN (Portable Game Notation) full game parsing (tags, moves, results, comments, NAGs, variations)
- SAN (Standard Algebraic Notation) individual move parsing / exporting
- FEN (Forsyth–Edwards Notation) position parsing / exporting
Built with modern PHP 8.4 features (Enums, typed properties) and released under the MIT license.
Table of Contents
- Installation
- Quick Start
- Features
- Usage Guide
- Data Model Overview
- Enums Reference
- Testing & Development
- License
1. Installation
Requires PHP >= 8.4.
composer require cmuset/pgn-parser
2. Quick Start
use Cmuset\PgnParser\Model\Game; $pgn = file_get_contents('path/to/game.pgn'); $game = Game::fromPGN($pgn); echo "Result: " . ($game->getResult()?->value ?? '*') . PHP_EOL; // e.g. Result: 1-0 foreach ($game->getMainLine() as $key => $node) { $move = $node->getMove(); echo $key . ' ' . $move->getSAN(); // e.g. "1. e4" or "1... e5" if ($comment = $node->getAfterMoveComment()) { echo ' {' . $comment . '}'; // e.g. " {Good central move}" } }
3. Features
- Parse a single PGN string into a structured
Gameobject:- Tag pairs (e.g.
Event,Site,Date, custom tags) - Result (from tag or trailing token)
- Move text with: move numbers, SAN moves, comments
{}, semicolon comments;, NAGs ($1,$2, ...), nested variations( ... )
- Tag pairs (e.g.
- Parse SAN strings (
e4,Nf3,O-O,exd5,c8=Q+,Rxe5#, etc.) into aMoveobject - Parse FEN strings into a
Position(piece placement, side to move, castling rights, en passant target, halfmove and fullmove counters) - Export back to:
- PGN (
GameExporter) - SAN (
MoveExporter) – implicitly viaMove::getSAN() - FEN (
PositionExporter) – implicitly viaPosition::getFEN()
- PGN (
- Access structured comments (before / after a move) and NAGs
- Access variations as arrays of
MoveNodelines - Apply moves to positions with
MoveApplier/Position::applyMove():- Handles castling (incl. rights update), en passant (capture + target square), promotions, capture detection, halfmove/fullmove counters, and side-to-move toggling
- Throws
MoveApplyingExceptionwithMoveViolationEnumand, when relevant, embeddedPositionViolationEnum[]
- Validate positions and games:
PositionValidatorchecks king presence/uniqueness, and whether the side to move is in check, etc.GameValidatorsimulates the main line and variations, returning an array of violations (ViolationEnumInterface[]) for the first failing move encountered
4. Usage Guide
Parsing a PGN into a Game
use Cmuset\PgnParser\Parser\PGNParser; $parser = PGNParser::create(); $game = $parser->parse($rawPgnString);
Or shorthand:
$game = Game::fromPGN($rawPgnString);
Building a Game Programmatically
use Cmuset\PgnParser\Model\Game; use Cmuset\PgnParser\Model\MoveNode; use Cmuset\PgnParser\Model\Move; use Cmuset\PgnParser\Enum\ColorEnum; use Cmuset\PgnParser\Enum\ResultEnum; use Cmuset\PgnParser\Model\Position; use Cmuset\PgnParser\Parser\PGNParser; $game = new Game(); $game->setInitialPosition(Position::fromFEN(PGNParser::INITIAL_FEN)); $game->setTag('Event', 'Casual Game'); $game->setTag('Site', 'Local'); $game->setTag('Result', '1-0'); $game->setResult(ResultEnum::WHITE_WIN); $node1 = new MoveNode(); $node1->setMoveNumber(1); $node1->setColor(ColorEnum::WHITE); $node1->setMove(Move::fromSAN('e4', ColorEnum::WHITE)); $node2 = new MoveNode(); $node2->setMoveNumber(1); $node2->setColor(ColorEnum::BLACK); $node2->setMove(Move::fromSAN('e5', ColorEnum::BLACK)); $game->addMoveNodes($node1, $node2); echo $game->getPGN(); // Example output: // [Event "Casual Game"]\n[Site "Local"]\n[Result "1-0"]\n\n1. e4 e5 1-0
Iterating Moves & Variations
Game::getMainLine() returns an array keyed by move number with . or ... (e.g. "1.", "1..."). Values are MoveNode objects.
/** @var \Cmuset\PgnParser\Model\MoveNode $node */ foreach ($game->getMainLine() as $key => $node) { $san = $node->getMove()->getSAN(); echo $key . ' ' . $san . PHP_EOL; // e.g. "5. Nf3" or "12... Qxd4+" /** @var \Cmuset\PgnParser\Model\MoveNode $variationLine */ foreach ($node->getVariations() as $i => $variationLine) { echo "(Variation #" . $i . ")\n"; // e.g. "(Variation #1)" foreach ($variationLine as $vNode) { echo $vNode->getMoveNumber() . ' ' . $vNode->getColor()->name . ' ' . $vNode->getMove()->getSAN(); // e.g. "6 WHITE d5" } } }
Exporting Back to PGN
$pgnOut = $game->getPGN(); file_put_contents('out.pgn', $pgnOut); // Writes the full PGN text (tags + moves + result) to out.pgn
Parsing SAN Moves
use Cmuset\PgnParser\Model\Move; use Cmuset\PgnParser\Enum\ColorEnum; $move = Move::fromSAN('Nf3', ColorEnum::WHITE); echo $move->getSAN(); // Nf3
Available flags on Move include: piece, origin disambiguation (squareFrom, fileFrom, rowFrom), destination (to), capture, check, checkmate, castling, promotion, annotation (!!, !?, ?!, ??, !, ?).
Parsing / Exporting FEN Positions
use Cmuset\PgnParser\Model\Position; $position = Position::fromFEN('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'); // Read a square use Cmuset\PgnParser\Enum\SquareEnum; use Cmuset\PgnParser\Enum\PieceEnum; $piece = $position->getPieceAt(SquareEnum::e2()); // e.g. white pawn enum instance // Modify then export $position->setPieceAt(SquareEnum::e4(), PieceEnum::from('P')); // places a white pawn on e4 (illustrative) $position->setPieceAt(SquareEnum::e2(), null); // removes piece from e2 echo $position->getFEN(); // e.g. "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1"
Applying Moves (MoveApplier)
Apply a SAN move to a Position, with full rules handling (castling rights, en passant, promotion, counters):
use Cmuset\PgnParser\Model\Position; use Cmuset\PgnParser\Model\Move; use Cmuset\PgnParser\Enum\ColorEnum; use Cmuset\PgnParser\Parser\PGNParser; use Cmuset\PgnParser\Exception\MoveApplyingException; $pos = Position::fromFEN(PGNParser::INITIAL_FEN); try { $pos = $pos->applyMove(Move::fromSAN('e4', ColorEnum::WHITE)); // $pos->getSideToMove() is now BLACK; halfmove/fullmove counters updated; castling rights maintained } catch (MoveApplyingException $e) { // Inspect the violation(s) $moveViolation = $e->getMoveViolation(); // MoveViolationEnum|null $positionViolations = $e->getPositionViolations(); // PositionViolationEnum[] } // Generate legal moves for the side to move $legalMoves = $pos->getLegalMoves(); // array of Move
Validation (PositionValidator & GameValidator)
Validate a single position or a full game (main line + variations):
use Cmuset\PgnParser\Validator\PositionValidator; use Cmuset\PgnParser\Validator\GameValidator; use Cmuset\PgnParser\Model\Game; $posViolations = (new PositionValidator())->validate($pos); // PositionViolationEnum[] $game = Game::fromPGN($somePgn); $violations = (new GameValidator())->validate($game); // ViolationEnumInterface[] (may contain MoveViolationEnum and/or PositionViolationEnum) if (empty($violations)) { // The game main line and its variations are valid }
Common position checks include: presence of both kings, uniqueness (no duplicates), king-in-check for the side to move, and basic legality context for derived positions.
5. Data Model Overview
| Class | Purpose |
|---|---|
Game |
Represents a full PGN game (tags, initial position, main line, result) |
MoveNode |
A node in the move tree (move + comments + NAGs + variations) |
Move |
A parsed SAN move (piece, destination, capture, promotion, etc.) |
Position |
A FEN-described board state with piece placement and metadata |
Square |
A board square container (enum + piece) |
Exporters (GameExporter, MoveExporter, PositionExporter) turn objects back into textual notation.
Parsers (PGNParser, SANParser, FENParser) transform strings into structured objects.
6. Enums Reference
ColorEnum: WHITE / BLACKPieceEnum: Typed by color & pieceSquareEnum: All 64 squares (a1..h8)CastlingEnum: Encodes side + directionResultEnum:1-0,0-1,1/2-1/2,*MoveViolationEnum: Move-level violations (e.g., wrong color to move, no piece to capture, not-a-check, castling not allowed, etc.)PositionViolationEnum: Position-level violations (e.g., missing king, multiple kings, king in check, etc.)
7. Testing & Development
Clone the repository and install dev dependencies:
git clone https://github.com/clemuset/pgn-parser.git
cd pgn-parser
composer install
Run the test suite:
vendor/bin/phpunit
Static analysis & coding style:
vendor/bin/phpstan analyze vendor/bin/php-cs-fixer fix --dry-run
8. License
MIT License. See LICENSE.
Contributions welcome: issues, pull requests, and suggestions. If you add a new feature, please include tests.
统计信息
- 总下载量: 11
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-10-12