🀄MJPF

Downloads

Everything you need to read, write, and validate MJPF. The example payloads are generated from the reference implementation, so they can never drift from the spec.

White paper

A typeset overview of the format, its transport, governance, attribution, and licence — a readable companion to the normative spec.

MJPF_WhitePaper_v1.2.0.pdf Current · proposed & originated by JSCBIZ · open, royalty-free specification

Earlier editions stay published, never rewritten (additive governance): MJPF_WhitePaper_v1.0.0.pdf

JSON Schema

Validate document structure before acting on it (the four normative checks still apply on top).

mjpf.schema.json Draft 2020-12 · $id: https://mjpf.openmahj.org/schema/mjpf-1.3.0.json

Conformance vectors

Frozen input → output pairs: the exact canonical JSON and MJQR bytes for four documents, plus the first ten tiles dealt from a known seed. An implementation is conformant when it reproduces all of them byte for byte — and if your output differs, the vector is right and your implementation is wrong.

conformance-vectors.json Asserted against the reference implementation on every build, so they can't drift

Example payloads

Each example comes in two forms: the readable .mjpf.json document and its .mjqr.txt transport code (base64 of raw-DEFLATE-compressed canonical JSON).

ExampleKindJSONMJQR code
Deal challengedeal example-deal.mjpf.json .mjqr.txt
Deal + score-to-beatdeal example-challenge.mjpf.json .mjqr.txt
Result reportresult example-result.mjpf.json .mjqr.txt
Full resume bundleresume example-resume.mjpf.json .mjqr.txt

A deal, in full

This is the entire portable core — a seed, a rule set, an opponent tier. It fits in one QR code.

{
  "format": "mjpf",
  "spec": "1.3.0",
  "kind": "deal",
  "requires": [],
  "uses": [],
  "game": {
    "seed": "12648430",
    "ruleset": "riichi-lite",
    "tier": "wizard",
    "mode": "play",
    "seats": 4,
    "humanSeat": 0,
    "recordSeat": 0,
    "branchPointIndex": 0
  },
  "meta": { "app": "Mahjong Coach", "title": "Riichi deal challenge" }
}

…and the same deal as an MJQR transport code (264 bytes — well under one QR's budget):

PY67TgNBDEV/ZeV6iQJZIbQtFQUSghJRODPOjpN54Z0VeSj/jh0kuhmfe318gV2RhA1GSPu6gx4mTATjBbaC2YW3wrm9ZE9HGNc9hCVh/iDL6y8Vr1GoEU9aFHJF/D+UJdJMtliYXeC7yI00NmtghnGwF3nF9w+Pw9OwWStrTKKTHz6jeLj2cOBsEU8YFSdqaKdhrTp8xbAveeqeC7pwK7do57zfdJ11OhcwRsoT2TKh74WFVP75pfZKzuyrzcrUy/wHrr8=

Decode it: base64 → raw inflate (-15) → JSON. See Implement.

A challenge — "beat my score"

A deal plus the challenger's outcome. Note uses: ["result"] (advisory — an old reader still plays the deal even if it ignores the score-to-beat).

{
  "format": "mjpf", "spec": "1.3.0", "kind": "deal",
  "requires": [], "uses": ["result"],
  "game": { "seed": "12648430", "ruleset": "hongkong", "tier": "veryGood",
            "mode": "play", "seats": 4, "humanSeat": 0, "recordSeat": 0, "branchPointIndex": 0 },
  "result": { "won": true, "score": 96, "bySelfDraw": false, "turns": 14, "label": "Hong Kong · 8 faan" },
  "meta": { "app": "Mahjong Coach", "title": "Beat my score" }
}

Reference implementation

The spec's source of truth in Swift, with a full test suite (envelope, semver gating, forward-compat, raw-DEFLATE, large-seed string round-trip, deal→game, resume==live):

Publication of these to a public repository is a project handoff step.