{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://mjpf.openmahj.org/schema/mjpf-1.3.0.json",
  "title": "Mahjong Portable Format (MJPF) document",
  "description": "A self-describing, platform-neutral document for sharing a Mahjong deal, resume bundle, or result. Spec 1.3.0. See MJPF_SPEC.md. Unknown additional properties are allowed at every level for forward-compatibility.",
  "type": "object",
  "required": ["format", "spec", "kind", "requires", "uses", "game"],
  "additionalProperties": true,
  "properties": {
    "format": {
      "const": "mjpf",
      "description": "Magic discriminator. Must equal \"mjpf\"."
    },
    "spec": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
      "description": "Spec version, MAJOR.MINOR.PATCH (semver)."
    },
    "kind": {
      "enum": ["deal", "resume", "result", "tournament-bundle"],
      "description": "deal = fresh seeded game (portable core); resume = continuable game with event log; result = a played outcome report; tournament-bundle = a whole tournament, i.e. a deterministic STACK of deals derived from one tournament seed (1.2.0)."
    },
    "requires": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Capability flags a reader MUST understand, or refuse the document. Known flags: event-log, branch, result, bonus-tiles (1.0.0); view-only, event-vocab-2 (1.1.0); tournament-bundle (1.2.0)."
    },
    "uses": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Advisory capability flags; a reader MAY ignore unknown entries. Known advisory flags: result (as a `uses` hint on a deal); wall-order (1.3.0) — the document carries the actual ordered wall in `wall`."
    },
    "wall": {
      "type": "array",
      "items": { "type": "integer", "minimum": 0 },
      "description": "The ACTUAL ordered wall as compact WallOrder tokens (1.3.0, advisory `wall-order` capability). Each token is the index of a tile's FACE in the canonical face order (§7.5); a joker's token is the ordinal past the catalogue. When present it is the source of truth for reproduction and `game.seed` becomes optional; a seed-capable reader may ignore it and deal the same wall from the seed."
    },
    "game": {
      "type": "object",
      "required": ["seed", "ruleset", "tier", "mode", "seats", "humanSeat", "recordSeat", "branchPointIndex"],
      "additionalProperties": true,
      "properties": {
        "seed": {
          "type": "string",
          "pattern": "^[0-9]+$",
          "description": "64-bit RNG seed as a DECIMAL STRING (JSON/JS numbers are only safe to 2^53)."
        },
        "ruleset": {
          "type": "string",
          "examples": ["simplified", "hongkong", "riichi-lite", "mcr", "taiwanese16"]
        },
        "tier": {
          "enum": ["basic", "moderate", "veryGood", "wizard"],
          "description": "AI opponent tier."
        },
        "mode": { "enum": ["training", "play"] },
        "seats": { "type": "integer", "minimum": 1, "maximum": 4 },
        "humanSeat": { "type": "integer", "minimum": 0, "maximum": 3 },
        "recordSeat": { "type": "integer", "minimum": 0, "maximum": 3 },
        "branchPointIndex": { "type": "integer", "minimum": 0 }
      }
    },
    "events": {
      "type": "array",
      "description": "Canonical move-by-move event log (present only with the event-log capability). v1 per-event shape follows the reference engine serialization; see example-resume.mjpf.json for the normative shape.",
      "items": { "type": "object" }
    },
    "result": {
      "type": "object",
      "required": ["won", "score"],
      "additionalProperties": true,
      "properties": {
        "won": { "type": "boolean" },
        "score": { "type": "integer", "description": "Points in the rule set's own unit." },
        "bySelfDraw": { "type": "boolean" },
        "turns": { "type": "integer", "minimum": 0 },
        "label": { "type": "string" }
      }
    },
    "tournament": {
      "type": "object",
      "description": "Present IFF kind == \"tournament-bundle\" (1.2.0). The reproducing parameters for a whole tournament: match i's seed derives from tournamentSeed by the derivation in spec 7.4, and each match derives its own per-hand seeds from that.",
      "required": ["tournamentSeed", "ruleSetID", "formatID", "tier", "matchCount"],
      "additionalProperties": true,
      "properties": {
        "tournamentSeed": {
          "type": "string",
          "pattern": "^[0-9]+$",
          "description": "64-bit tournament seed as a DECIMAL STRING (same rule as game.seed)."
        },
        "ruleSetID": { "type": "string", "examples": ["hongkong", "riichi-lite", "mcr"] },
        "formatID": { "type": "string", "examples": ["quick-three", "standard-five"] },
        "tier": { "enum": ["basic", "moderate", "veryGood", "wizard"] },
        "matchCount": { "type": "integer", "minimum": 1, "description": "Denormalised match count, so a reader can show the length without resolving formatID." },
        "title": { "type": "string", "description": "Advisory only; never needed to replay." },
        "readiness": { "type": "integer", "minimum": 0, "maximum": 100, "description": "Advisory: the sender's Readiness score for their own run." },
        "certified": { "type": "boolean", "description": "Advisory: whether the sender's run certified." }
      }
    },
    "meta": {
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "title": { "type": "string" },
        "createdAt": { "type": "string", "description": "ISO-8601, producer-supplied." },
        "app": { "type": "string" },
        "appVersion": { "type": "string" }
      }
    },
    "ext": {
      "type": "object",
      "description": "Vendor-extension namespace. Keys SHOULD be vendor.key-namespaced; values are strings. Readers ignore keys they don't own.",
      "additionalProperties": { "type": "string" }
    }
  },
  "allOf": [
    {
      "if": { "properties": { "kind": { "const": "resume" } } },
      "then": {
        "required": ["events"],
        "properties": { "requires": { "contains": { "const": "event-log" } } }
      }
    },
    {
      "if": { "properties": { "kind": { "const": "result" } } },
      "then": { "required": ["result"] }
    },
    {
      "if": { "properties": { "kind": { "const": "tournament-bundle" } } },
      "then": {
        "required": ["tournament"],
        "properties": { "requires": { "contains": { "const": "tournament-bundle" } } }
      }
    },
    {
      "if": { "required": ["tournament"] },
      "then": { "properties": { "kind": { "const": "tournament-bundle" } } }
    }
  ]
}
