General Gameplay Notation (GGN) Specification
Version: 1.0.0 Author: Sashité Published: 8 March 2014 License: MIT License
Overview
General Gameplay Notation (GGN) is a rule-agnostic, JSON-based format for describing pseudo-legal moves in abstract strategy board games. GGN expresses whether a move is possible under basic movement constraints, while remaining completely silent about higher-level, game-specific legality questions (e.g., check, ko, repetition, castling paths) and mechanical execution details.
GGN serves as a pure movement possibility oracle: given a piece, source position, and destination position, it determines if the movement is feasible under specified pre-conditions.
Core Philosophy
Movement Possibility Model
A single GGN entry answers the fundamental question:
Can this piece, currently on this square or in hand, reach that square or return to hand?
It encodes:
- Which piece (via arbitrary string identifier)
- From where (source square label or “*” for hand)
- To where (destination square label or “*” for hand)
- Which pre-conditions must hold (
must
) - Which pre-conditions must not hold (
deny
)
Specification Boundaries
GGN is responsible for:
- Defining movement possibilities under basic constraints
- Specifying pre-conditions for move validity
- Providing a query interface for movement feasibility
GGN is NOT responsible for:
- Mechanical execution of moves (delegated to execution layer)
- Turn management or player rights
- Game-specific legality (check, ko, repetition)
- Move validation beyond basic constraints
- Game outcome or victory conditions
- Piece identifier format constraints
GGN delegates to:
- Execution layer: Mechanical decomposition and action sequencing
- Game engines: Full rule validation and legality checking
- Applications: Piece identifier format and naming conventions
JSON Structure
{
"<Source piece>": {
"<Source position>": {
"<Destination position>": [
{
"must": { "<square>": "<required state>", … },
"deny": { "<square>": "<forbidden state>", … }
}
]
}
}
}
Core Fields
Field | Type | Required | Description |
---|---|---|---|
Source piece | string | yes | Current piece identifier (arbitrary string) |
Source position | string | yes | Origin square or “*” for hand |
Destination position | string | yes | Target square or “*” for hand |
must | object | no | Pre-conditions that must be satisfied |
deny | object | no | Pre-conditions that must not be satisfied |
Position Constraints
- Source position: Board square label or “*” (piece from hand)
- Destination position: Board square label or “*” (piece to hand)
- Restriction: Source and destination cannot both be “*” simultaneously
Piece Identifiers
- Format: Arbitrary non-empty strings
- Consistency: Must be consistent within a single GGN document
- Examples:
"K"
,"CHESS:K"
,"WhiteKing"
,"王"
,"♔"
,"king_white"
- Recommendation: Use identifiers that clearly distinguish pieces for your application
Pre-Conditions System
Logical Semantics
- must: All conditions must be satisfied (logical AND)
- deny: Move forbidden if any condition is satisfied (logical OR)
- Consistency: Same state cannot appear in both
must
anddeny
for the same square
Occupation States
State | Meaning |
---|---|
"empty" |
Square must be empty |
"enemy" |
Square must contain an opposing piece |
Piece identifier | Square must contain exactly the specified piece |
Implicit States (via deny
)
Expression | Implicit Meaning |
---|---|
"deny": { "a1": "empty" } |
Square must be occupied (by any piece) |
"deny": { "a1": "enemy" } |
Square must contain a friendly piece |
Constraint Examples
Desired Condition | GGN Expression |
---|---|
Square must be empty | "must": { "a1": "empty" } |
Square must contain enemy | "must": { "a1": "enemy" } |
Square must contain ally | "deny": { "a1": "enemy" } |
Square must be occupied | "deny": { "a1": "empty" } |
Square must contain specific piece | "must": { "a1": "K" } |
Square must not contain specific piece | "deny": { "a1": "K" } |
Move Variants
When a move can result in multiple outcomes, each variant is represented as a separate object in the destination array. Common use cases:
- Promotion choices: Different transformation options
- Optional promotions: Choice between promoting or not
- Conditional transformations: Different outcomes based on game state
All variants share the same source and destination positions but may have different pre-conditions.
Examples
Simple Movement
{
"R": {
"a1": {
"a4": [
{
"must": { "a2": "empty", "a3": "empty", "a4": "empty" }
}
]
}
}
}
Capture
{
"R": {
"a1": {
"a4": [
{
"must": { "a2": "empty", "a3": "empty", "a4": "enemy" }
}
]
}
}
}
Drop from Hand
{
"P": {
"*": {
"5e": [
{
"must": { "5e": "empty" }
}
]
}
}
}
Multiple Promotion Choices
{
"P": {
"e7": {
"e8": [
{
"must": { "e8": "empty" }
}
]
}
}
}
Note: This example shows that promotion is possible. The specific promotion choice (Q, R, N, B) is determined at the execution layer.
Complex Movement (Castling Possibility)
{
"K": {
"e1": {
"g1": [
{
"must": { "f1": "empty", "g1": "empty", "h1": "R" }
}
]
}
}
}
Capture Possibility (Shōgi-style)
{
"S": {
"2c": {
"2b": [
{
"must": { "2b": "enemy" }
}
]
}
}
}
Using Game-Qualified Identifiers
{
"CHESS:R": {
"a1": {
"a4": [
{
"must": { "a2": "empty", "a3": "empty", "a4": "empty" }
}
]
}
}
}
Using Unicode Piece Symbols
{
"♖": {
"a1": {
"a4": [
{
"must": { "a2": "empty", "a3": "empty", "a4": "empty" }
}
]
}
}
}
Using Descriptive Names
{
"white_rook": {
"a1": {
"a4": [
{
"must": { "a2": "empty", "a3": "empty", "a4": "empty" }
}
]
}
}
}
Conditional Movement
{
"P": {
"e5": {
"f6": [
{
"must": { "f6": "empty" },
"deny": { "f5": "empty" }
}
]
}
}
}
This represents an en passant possibility: pawn can move to f6 if f6 is empty AND f5 is occupied.
Validation Rules
Structural Requirements
- Must conform to the JSON Schema (see Schema section)
- All piece identifiers must be non-empty strings
- Source and destination positions cannot both be “*” simultaneously
must
anddeny
fields, if present, must not be empty- No square may have the same state in both
must
anddeny
within the same variant
Pre-Condition Requirements
- All squares in
must
anddeny
must be valid board squares (no “*”) - All occupation states must be valid:
"empty"
,"enemy"
, or valid piece identifiers must
field must not duplicate implicit requirements from query structure
Relationship to Other Notations
Notation | Purpose | Relationship to GGN |
---|---|---|
GAN | Game-qualified piece identifiers | Optional format for piece identification |
FEEN | Complete board state representation | Provides context for move possibility |
PMN | Mechanical move decomposition | Independent execution layer |
PNN | Basic piece naming | Optional format for piece identification |
GGN serves as the movement possibility layer in the Sashité ecosystem, providing a clean interface between static position representation (FEEN) and dynamic move execution (PMN or other execution engines).
Layered Architecture
┌─────────────────────────────────────┐
│ Application Layer │
│ (Game Engines) │
├─────────────────────────────────────┤
│ Execution Layer │
│ (PMN, Custom Formats) │
├─────────────────────────────────────┤
│ Movement Possibility │
│ (GGN) │
├─────────────────────────────────────┤
│ Position Encoding │
│ (FEEN) │
├─────────────────────────────────────┤
│ Piece Identification │
│ (GAN/PNN) │
└─────────────────────────────────────┘
JSON Schema
Schema URL: https://sashite.dev/schemas/ggn/1.0.0/schema.json
The JSON Schema provides structural validation for GGN format compliance without dependencies on external execution formats.
Schema Integration Example
{
"$schema": "https://sashite.dev/schemas/ggn/1.0.0/schema.json",
"P": {
"e7": {
"e8": [
{
"must": { "e8": "empty" }
}
]
}
}
}
Implementations
Ruby
- Ggn.rb - Ruby implementation providing parsing and validation capabilities for standalone GGN documents. Enables pure movement possibility queries without execution dependencies.