牌谱数据格式说明
本文档详细说明了牌谱文件(如 data/record/*.json)的数据结构。该说明基于 src/analyzer/simulator.cpp、src/analyzer/action.cpp 和 include/base/tziakcha.h 中的处理逻辑。
概述
牌谱文件是一个 JSON 对象,包含比赛元数据、玩家信息以及详细的游戏动作脚本。
根对象结构
| 字段 |
类型 |
说明 |
id |
String |
牌谱唯一标识符。 |
belongs |
String |
牌谱所属对局(session)。 |
script |
String |
编码的游戏脚本字符串。若脚本已解码并存储在 step 字段中,此字段为 "<Decoded>"。 |
step |
Object |
解码后的游戏数据。包含核心的游戏动作和状态信息。详见 Step 数据。 |
Step 数据
step 对象包含了一场游戏的完整数据。
| 字段 |
类型 |
说明 |
g |
Object |
游戏配置信息(例如 t 字段表示标题)。 |
p |
Array |
玩家信息列表(包含名字、ID等)。 |
w |
String |
牌墙数据(十六进制字符串)。每两个字符表示一张牌的 ID。 |
d |
Integer |
骰子点数。编码为4个4位数值(Nibbles)。 |
i |
Integer |
圈风/局数信息。计算公式 (i / 4) % 4 可得当前的圈风索引(0:东, 1:南, 2:西, 3:北)。 |
a |
Array |
游戏动作序列。详见 动作数据格式。 |
b |
Integer |
结算标志位。 位 0-3:赢家位掩码(Winner Mask)。 位 4-7:放铳者位掩码(Discarder Mask)。 |
y |
Array |
每个玩家的结算详情(如番数、和牌信息等)。 |
a 字段是一个数组,其中每个动作由一个包含 3 个整数的数组表示:[combined, data, time]。
| 索引 |
名称 |
说明 |
| 0 |
combined |
复合字段,编码了玩家索引和动作类型。 |
| 1 |
data |
数据负载。根据动作类型的不同,结构也不同。包含牌ID、吃碰杠详情等。 |
| 2 |
time |
动作发生的时间戳(毫秒,相对于游戏开始)。 |
combined 字段解码
第一个整数通过位运算组合了玩家索引和动作类型。
| 数据项 |
位范围 |
解析逻辑 (C++) |
| 玩家索引 |
Bits 4-5 |
(combined >> 4) & 3 |
| 动作类型 |
Bits 0-3 |
combined & 15 |
动作类型与 Data 结构详情
以下是所有动作类型及其 data 字段的详细位结构说明。
0: 开局/初始手牌 (Start Play)
- 说明: 游戏开始,此时模拟器会从初始状态(配牌)中捕获各家手牌。
- Data:
0 (无数据)
1: 补花 (Flower Replacement)
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 补入牌ID |
Low Byte (0-7) |
data & 0xFF |
补进手中的牌的 ID。 |
| 花牌偏移 |
High Byte (8-11) |
(data >> 8) & 15 |
拔出的花牌基准值。实际花牌 ID = 值 + 136。 |
| 自动补花 |
Bit 12 |
data & 0x1000 |
标志位。若置位,表示为系统自动补花。 |
2: 出牌 (Discard)
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 牌 ID |
Low Byte (0-7) |
data & 0xFF |
打出的牌的 ID。 |
| 手切标识 |
Bit 8 |
(data >> 8) & 1 |
1 = 手切(打出手里的牌);0 = 模切(直接打出刚摸的牌)。 |
3: 吃 (Chi / Chow)
- 说明: 玩家吃上家打出的牌,组成顺子。
- Data 结构:
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 基准牌值 |
Bits 0-5 |
(data & 0x3F) << 2 |
吃牌组合的基准牌值。 |
| 供牌方向 |
Bits 6-9 |
(data >> 6) & 3 |
供牌来源方向(相对)。 |
| 第1张偏移 |
Bits 10-11 |
(data >> 10) & 3 |
顺子第1张牌相对于基准值的偏移。 |
| 第2张偏移 |
Bits 12-13 |
(data >> 12) & 3 |
顺子第2张牌相对于基准值的偏移。 |
| 第3张偏移 |
Bits 14-15 |
(data >> 14) & 3 |
顺子第3张牌相对于基准值的偏移。 |
4: 碰 (Peng / Pung)
- 说明: 玩家碰任意一家打出的牌,组成刻子。
- Data 结构:
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 基准牌值 |
Bits 0-5 |
(data & 0x3F) << 2 |
碰牌组合的基准牌值。 |
| 供牌方向 |
Bits 6-9 |
(data >> 6) & 3 |
供牌来源方向(相对)。 |
| 牌偏移 |
Bits 10-11 |
(data >> 10) & 3 |
实际牌相对于基准值的偏移(用于确定具体是哪几张)。 |
5: 杠 (Gang / Kong)
- 说明: 包含明杠、暗杠和加杠(补杠)。
- Data 结构:
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 基准牌值 |
Bits 0-5 |
(data & 0x3F) << 2 |
杠牌的基准牌值。 |
| 供牌方向 |
Bits 6-9 |
(data >> 6) & 3 |
0 表示暗杠(自己摸4张);非0 表示明杠/加杠供牌方。 |
| 牌偏移 |
Bits 10-11 |
(data >> 10) & 3 |
牌的偏移值。 |
| 加杠标识 |
Bits 8-11 (Mask) |
data & 0x0300 |
若等于 0x0300,表示为加杠(碰牌后补杠)。 |
6: 和牌 (Win / Hu)
- 说明: 玩家和牌(自摸或荣和)。
- Data 结构:
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 自动和牌 |
Bit 0 |
data & 1 |
是否为自动和牌。 |
| 番数 |
Bits 1+ |
data >> 1 |
本次和牌的番数。 |
7: 摸牌 (Draw)
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 牌 ID |
Low Byte (0-7) |
data & 0xFF |
摸到的牌的 ID。 |
| 逆向/补花 |
High Byte (8-15) |
data & 0x0100 |
若置位(非0),表示从牌墙尾部摸牌(如杠上开花、补花时)。 |
8: 过 (Pass)
- 说明: 玩家选择跳过当前可执行的动作(如吃碰杠和)。
- Data 结构:
| 部分 |
位范围 |
掩码/计算 |
说明 |
| 模式 |
Bits 0-1 |
data & 3 |
0: 手动过 (Manual)
1: 自动过 (Auto)
2: 强制过 (Forced) |
9: 弃和 (Abandon)
- 说明: 弃权或无效的和牌操作声明(罕见)。
- Data:
0
示例解析 (基于 0ae10q6N)
动作: [40, 2, 6812]
- Combined:
40 (0x28)
- 动作类型:
40 & 15 = 8 (Pass / 过)
- 玩家索引:
(40 >> 4) & 3 = 2 (Player 2)
- Data:
2
- 累计时间:
6812 ms
动作: [2, 364, 7827]
- Combined:
2 (0x02)
- 动作类型:
2 & 15 = 2 (Discard / 出牌)
- 玩家索引:
(2 >> 4) & 3 = 0 (Player 0)
- Data:
364 (0x16C)
- Low Byte:
364 & 0xFF = 108 (牌 ID 108 -> 红中)
- High Byte:
364 >> 8 = 1 (手切 = True)
- 累计时间:
7827 ms