電脳麻将の牌譜形式

電脳麻将 ver.0.3 で牌譜再生機能を追加した*1。牌譜再生を実現するにあたり、他のシステム(天鳳とか)の牌譜形式も参照したのだが、どうもしっくりこないので独自のJSON形式である。牌譜形式を定義した目的は2つあって、1つはもちろん牌譜再生だが、より重要な目的としてゲーム進行に使いたいというのがあった。

今回はとりあえず牌譜形式を説明する。ゲーム進行についてはまた別途。

牌譜全体

{
    title:  /* 牌譜のタイトル */ ,
    player: /* 対局者情報 */ ,
    qijia:  /* 起家 */ ,
    log:    /* 半荘全体の記録 */ ,
    defen:  /* 半荘終了時の得点 */ ,
    rank:   /* 順位 */ ,
    point:  /* 獲得ポイント */
}

title
牌譜のタイトル。「第五期 天鳳名人戦 第一節 A卓 第1戦」など。現状は対戦日時を設定している。
player
対局者情報の配列。仮親から順に対局者名を設定する。現在は ['私','下家','対面','上家'] と設定している。
qijia
起家。0: 仮東、1: 仮南、2: 仮西、3: 仮北。電脳麻将では乱数で起家を決定している。
log
半荘全体の記録。後述する。
defen
半荘終了時の得点の配列。仮親からの順に記録する。
rank
その半荘の順位の配列。これも仮親からの順に記録。
point
オカ、ウマを含めたポイントの配列。これも仮親からの順。

半荘全体の記録

[
    [ /* 東一局の記録... */ ],
    [ /* 東二局の記録... */ ],
        ...
    [ /* 最終局の記録... */ ]
]

各局ごとの記録を配列として並べている。

局ごとの記録

[
    /* 摸打情報 */ ,
    /* 摸打情報 */ ,
        ...
    /* 摸打情報 */
]

局の摸打情報を配列内に配牌から和了まで順に並べている。

摸打情報

配牌、自摸、打牌、副露、和了など。各情報の形式は以下の通り。

配牌 (qipai)
{
    qipai: {
        zhuangfeng: /* 場風 */ ,
        jushu:      /* 局数 */ ,
        changbang:  /* 積み棒の数 */ ,
        lizhibang:  /* 立直棒の数 */ ,
        defen:      /* 対局者の得点 */ ,
        baopai:     /* ドラ表示牌 */ ,
        shoupai:    /* 手牌 */
    }
}

zhuangfeng
場風。(0: 東、1: 南、2: 西、3: 北)
jushu
局数。(0: 一局、1: 二局、2: 三局、3: 四局)
changbang
積み棒の数。
lizhibang
その局開始時の立直棒の数。
defen
その局開始時の対局者の持ち点の配列。その局の東家からの順。
baopai
ドラ表示牌。
shoupai
配牌の配列。その局の東家からの順。

対局者ごとの情報は、牌譜全体では仮東からの順だがここではその局の東家からであることに注意。shoupai麻雀の手牌の文字列表現 - koba::blog の手牌の形式にしたがう。ドラは「表示牌」であることに注意。現物としてしまうとドラが六萬の場合に表示牌が赤牌であったかが示せなくなってしまうというのがその理由。東一局の配牌は

{
    qipai: {
        zhuangfeng: 0,
        jushu:      0,
        changbang:  0,
        lizhibang:  0,
        defen:      [ 25000, 25000, 25000, 25000 ],
        baopai:     's7',
        shoupai:    [ 'm178p36s15578z356',
                      'm177p225s069z1247',
                      'm299p1158s288z356',
                      'm44p1234089s3z222'  ]
    }
}

のような形式となる。

自摸 (zimo)
{ zimo: { l: /* 手番 */ , p: /* 牌 */ } }

l
手番。(0: 東家、1: 南家、2: 西家、3: 北家)
p
自摸牌。

東家が一索を自摸したときは {zimo:{l:0, p:'s1'}} となる。

打牌 (dapai)
{ dapai: { l: /* 手番 */ , p: /* 牌 */ } }

l
手番。(0: 東家、1: 南家、2: 西家、3: 北家)
p
打牌。

麻雀の手牌の文字列表現 - koba::blog で書いたように自摸切りの場合は '_'、立直の場合は '*' を付加する。東家が一索を自摸切り立直したときは {dapai:{l:0, p:'s1_*'}} となる。

副露 (fulou)
{ fulou: { l: /* 手番 */ , m: /* 面子 */ } }

l
手番。(0: 東家、1: 南家、2: 西家、3: 北家)
m
副露した面子。

面子も 麻雀の手牌の文字列表現 - koba::blog の形式にしたがう。南家が一索をチーした場合は {fulou:{l:1, m:'s1-23'}} となる。

槓 (gang)
{ gang: { l: /* 手番 */ , m: /* 面子 */ } }

l
手番。(0: 東家、1: 南家、2: 西家、3: 北家)
m
槓した面子。

大明槓は副露として扱うので、ここでの槓は暗槓、加槓を指す*2。西家が赤五筒を自摸って加槓した場合は {gang:{l:2, m:'p555=0'}} のようになる。

自摸 (gangzimo)
{ gangzimo: { l: /* 手番 */ , p: /* 牌 */ } }

l
手番。(0: 東家、1: 南家、2: 西家、3: 北家)
p
自摸牌。

自摸と同じ形式にしてもよかったのだが、槓自摸での和了嶺上開花となるので区別した。

開槓 (kaigang)
{ kaigang: { baopai: /* 牌 */ } }

baopai
槓によって生じた新ドラ表示牌。

「明槓のドラ後乗り」のルールの場合、

暗槓
gang → gamgzimo → kaigang → dapai
大明槓
fulou → gangzimo → dapai → kaigang
加槓
gang → gangzimo → dapai → kaigang

という順序になる。

和了 (hule)
{
    hule: {
        l:         /* 手番 */ ,
        shoupai:   /* 手牌 */ ,
        baojia:    /* 放銃者 */ ,
        fubaopai:  /* 裏ドラ表示牌 */ ,
        fu:        /* 符 */ ,
        fanshu:    /* 翻数 */ ,
        damanguan: /* 役満複合数 */ ,
        defen:     /* 和了打点 */ ,
        hupai: [
            { name: /* 役名 */ , fanshu: /* 翻数 */ },
                    ...
            { name: /* 役名 */ , fanshu: /* 翻数 */ },
        ],
        fenpei:    /* 局収支 */
    }
}

l
和了者。(0: 東家、1: 南家、2: 西家、3: 北家)
shoupai
和了者の手牌の文字列表現。ロン和了の場合、和了牌をツモした状態とする。
baojia
放銃者。ツモ和了の場合は null
fubaopai
裏ドラ表示牌の配列。立直がない場合は null
fu
符。役満の場合このメンバは存在しない。
fanshu
翻数。役満の場合このメンバは存在しない。
damanguan
役満複合数(ダブル役満の場合は2)。役満でない場合このメンバは存在しない。
defen
供託をのぞいた和了点。
hupai
和了役名と翻数のリスト。役満の場合翻数は '*' とする。
fenpei
供託を含めた局収支。その局の東家からの順。

東三局2本場、供託立直棒なし、北家が立直で下記のツモ上がりの場合、
ドラ表示牌: 五萬南、裏ドラ表示牌 二索四萬
七萬八萬四筒赤五筒五筒六筒六筒七筒三索四索五索七索七索九萬

{
    hule: {
        l:         3,
        shoupai:   'm78p405667s34577m9',
        baojia:    null,
        fubaopai:  ['s2','m4'],
        fu:        20,
        fanshu:    5,
        defen:     8000,
        hupai: [
            { name: '立直', fanshu: 1 },
            { name: '門前清自摸和', fanshu: 1 },
            { name: '平和', fanshu: 1 },
            { name: '赤ドラ', fanshu: 1 },
            { name: '裏ドラ', fanshu: 1 },
        ],
        fenpei:    [ -4200, -2200, -2200, 9600 ]
    }
}

となる。
ダブロンの場合は和了のレコードが続くことになる。

流局 (pingju)
{
    pingju: {
        name:    /* 流局理由 */ ,
        shoupai: /* 手牌 */ ,
        fenpei:  /* 局収支 */
    }
}

name
流局理由。電脳麻将では次のいずれか。荒牌平局、流し満貫、九種九牌、四風連打、四家立直、三家和、四開槓。
shoupai
流局時の各々の手牌。ノー聴などの理由で手牌を開示しない場合は '' とする。
fenpei
ノー聴罰符などの局収支。その局の東家からの順。

流局時、西家のみがノー聴の場合は、

{
    pingju: {
        name:    "荒牌平局",
        shoupai: [ 'm678p66s12,s6-78,s555=',
                   'm1177p456s340,z444=',
                   '',
                   'm44p12340,z2222=,p999=' ],
        fenpei:  [ 1000, 1000, -3000, 1000 ]
    }
}

となる。

*1:牌譜の保存自体はver.0.2から実装していたが、ver.0.3で牌譜ビューアの機能を追加したというのが実態

*2:つまり自摸後に槓するケース