{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://sashite.dev/schemas/ggn/1.0.0/schema.json",
  "title": "General Gameplay Notation (GGN) v1.0.0",
  "description": "JSON Schema for pseudo-legal moves in abstract board games using GGN format with QPI piece identification and CELL/HAND location support.",
  "type": "object",

  "properties": {
    "$schema": {
      "type": "string",
      "format": "uri"
    }
  },

  "patternProperties": {
    "^([A-Z]:[-+]?[A-Z]|[a-z]:[-+]?[a-z])$": {
      "description": "Piece identifier in QPI format (style:piece)",
      "type": "object",
      "minProperties": 1,
      "patternProperties": {

        "^\\*$": {
          "description": "Source location is HAND '*' (reserve) — destination must NOT be '*'",
          "type": "object",
          "minProperties": 1,
          "patternProperties": {
            "^(?!\\*$)[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?$": {
              "description": "Destination location: CELL coordinate only (HAND→HAND forbidden)",
              "type": "array",
              "minItems": 1,
              "items": {
                "type": "object",
                "properties": {
                  "must": {
                    "type": "object",
                    "default": {},
                    "patternProperties": {
                      "^[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?$": {
                        "description": "Required state: 'empty', 'enemy', or specific QPI piece identifier (CELL coordinates only)",
                        "type": "string",
                        "pattern": "^(?:empty|enemy|([A-Z]:[-+]?[A-Z]|[a-z]:[-+]?[a-z]))$"
                      }
                    },
                    "additionalProperties": false
                  },
                  "deny": {
                    "type": "object",
                    "default": {},
                    "patternProperties": {
                      "^[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?$": {
                        "description": "Forbidden state: 'empty', 'enemy', or specific QPI piece identifier (CELL coordinates only)",
                        "type": "string",
                        "pattern": "^(?:empty|enemy|([A-Z]:[-+]?[A-Z]|[a-z]:[-+]?[a-z]))$"
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "additionalProperties": false
              }
            }
          },
          "additionalProperties": false
        },

        "^(?!\\*$)[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?$": {
          "description": "Source location: CELL coordinate (board)",
          "type": "object",
          "minProperties": 1,
          "patternProperties": {
            "^(\\*|[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?)$": {
              "description": "Destination location: CELL coordinate or HAND '*'",
              "type": "array",
              "minItems": 1,
              "items": {
                "type": "object",
                "properties": {
                  "must": {
                    "type": "object",
                    "default": {},
                    "patternProperties": {
                      "^[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?$": {
                        "description": "Required state: 'empty', 'enemy', or specific QPI piece identifier (CELL coordinates only)",
                        "type": "string",
                        "pattern": "^(?:empty|enemy|([A-Z]:[-+]?[A-Z]|[a-z]:[-+]?[a-z]))$"
                      }
                    },
                    "additionalProperties": false
                  },
                  "deny": {
                    "type": "object",
                    "default": {},
                    "patternProperties": {
                      "^[a-z]+(?:[1-9]\\d*[A-Z]+[a-z]+)*(?:[1-9]\\d*(?:[A-Z]+)?)?$": {
                        "description": "Forbidden state: 'empty', 'enemy', or specific QPI piece identifier (CELL coordinates only)",
                        "type": "string",
                        "pattern": "^(?:empty|enemy|([A-Z]:[-+]?[A-Z]|[a-z]:[-+]?[a-z]))$"
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "additionalProperties": false
              }
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    }
  },

  "additionalProperties": false,

  "examples": [
    {
      "description": "Simple rook movement",
      "value": {
        "C:R": {
          "a1": {
            "a4": [
              {
                "must": { "a2": "empty", "a3": "empty", "a4": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Rook capture",
      "value": {
        "C:R": {
          "a1": {
            "a4": [
              {
                "must": { "a2": "empty", "a3": "empty", "a4": "enemy" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Drop from hand",
      "value": {
        "S:P": {
          "*": {
            "e4": [
              {
                "must": { "e4": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Capture to hand",
      "value": {
        "A:R": {
          "a1": {
            "*": [
              {}
            ]
          }
        }
      }
    },
    {
      "description": "Shōgi pawn drop with file restriction",
      "value": {
        "S:P": {
          "*": {
            "e4": [
              {
                "must": { "e4": "empty" },
                "deny": {
                  "e1": "S:P", "e2": "S:P", "e3": "S:P",
                  "e5": "S:P", "e6": "S:P", "e7": "S:P",
                  "e8": "S:P", "e9": "S:P"
                }
              }
            ]
          }
        }
      }
    },
    {
      "description": "Castling possibility",
      "value": {
        "C:+K": {
          "e1": {
            "g1": [
              {
                "must": { "f1": "empty", "g1": "empty", "h1": "C:+R" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Pawn promotion possibility (movement only, not consequence)",
      "value": {
        "C:P": {
          "e7": {
            "e8": [
              {
                "must": { "e8": "empty" }
              },
              {
                "must": { "e8": "enemy" }
              }
            ]
          }
        }
      }
    },
    {
      "description": "En passant possibility",
      "value": {
        "C:P": {
          "e5": {
            "f6": [
              {
                "must": { "f6": "empty", "f5": "c:-p" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Multi-dimensional movement (3D)",
      "value": {
        "R:B": {
          "a1A": {
            "d4D": [
              {
                "must": { "b2B": "empty", "c3C": "empty", "d4D": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Cross-style game",
      "value": {
        "C:R": {
          "a1": {
            "a4": [
              {
                "must": { "a2": "empty", "a3": "empty", "a4": "enemy" },
                "deny": {}
              }
            ]
          }
        },
        "x:c": {
          "b2": {
            "b8": [
              {
                "must": { "b3": "empty", "b4": "empty", "b5": "empty", "b6": "empty", "b8": "enemy" },
                "deny": { "b7": "empty" }
              }
            ]
          }
        }
      }
    },
    {
      "description": "Unconditional movement (both fields optional)",
      "value": {
        "C:K": {
          "e1": {
            "e2": [
              {}
            ]
          }
        }
      }
    },
    {
      "description": "Multiple move possibilities",
      "value": {
        "C:R": {
          "a1": {
            "a4": [
              {
                "must": { "a2": "empty", "a3": "empty", "a4": "empty" }
              },
              {
                "must": { "a2": "empty", "a3": "empty", "a4": "enemy" }
              }
            ]
          }
        }
      }
    }
  ],

  "counterExamples": [
    {
      "description": "Source and destination both '*' (forbidden: HAND→HAND)",
      "value": {
        "C:P": {
          "*": {
            "*": [
              {
                "must": { "e4": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Invalid QPI format - missing colon",
      "value": {
        "CHESSP": {
          "e2": {
            "e4": [
              {
                "must": { "e3": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Invalid QPI format - multi-character style",
      "value": {
        "CHESS:P": {
          "e2": {
            "e4": [
              {
                "must": { "e3": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Invalid QPI format - case mismatch",
      "value": {
        "C:p": {
          "e2": {
            "e4": [
              {
                "must": { "e3": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Invalid CELL format - uppercase first",
      "value": {
        "C:P": {
          "E2": {
            "e4": [
              {
                "must": { "e3": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Invalid CELL format - number first",
      "value": {
        "C:P": {
          "2e": {
            "e4": [
              {
                "must": { "e3": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Invalid occupation state",
      "value": {
        "C:P": {
          "e2": {
            "e4": [
              {
                "must": { "e3": "invalid_state" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "HAND notation in must/deny keys (not allowed)",
      "value": {
        "C:P": {
          "e2": {
            "e4": [
              {
                "must": { "*": "empty" },
                "deny": {}
              }
            ]
          }
        }
      }
    },
    {
      "description": "Empty array for move possibilities",
      "value": {
        "C:P": {
          "e2": {
            "e4": []
          }
        }
      }
    },
    {
      "description": "Additional field not in spec",
      "value": {
        "C:P": {
          "e2": {
            "e4": [
              {
                "must": { "e3": "empty" },
                "deny": {},
                "diff": {}
              }
            ]
          }
        }
      }
    }
  ]
}
