diff --git a/CLAUDE.md b/CLAUDE.md
index 574eeb6..3ecb5cc 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -74,4 +74,6 @@ git config core.hooksPath .githooks
## 测试与 Git
- **测试纪律**:细则见 client 05 §10 / server 04 §10。要点——测试代码可用现代语法(只跑 Node,不上线);**正式代码禁止为测试而加逻辑/放宽校验**,禁止为过测而改正式代码或降低测试标准(skip、软化断言、吞异常等);失败先用证据裁定「业务缺陷 vs 脚本缺陷」;正面/反面/边界用例都要覆盖。
+- **新增断言要反向验证**:写完一条守卫断言,**故意破坏它守的那处逻辑,确认它真会变红**——否则你加的可能是一条空转断言(「全绿但什么都没验」比红更有害)。
+- ⚠️ **本仓库文件是 CRLF 行尾**:任何基于行尾的脚本化改写(`sed`/正则批量删改)必须按 `\r?\n` 处理。写 `\n` 会**一行都匹配不上却静默成功**——曾据此得出「测试抓不住缺陷」的错误结论。脚本改完先 `git diff --stat` 确认文件真的变了,再据其结果下判断。
- **Git 提交**(细则见 server 04 §11):完成一个可独立成立的逻辑改动即可**自动提交、无需逐次确认**(`push` 按需);提交信息用中文、聚焦一件事、结尾保留 `Co-Authored-By` 署名;不用 `git add -A`/`git add .`、不跳过 hooks、不做 `reset --hard`/`push --force`。
diff --git a/client/index.html b/client/index.html
index c7c9747..49d1687 100644
--- a/client/index.html
+++ b/client/index.html
@@ -236,7 +236,24 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/js/01_SubGame/codes/SubGameHooks.js b/client/js/01_SubGame/codes/SubGameHooks.js
index ccdcb22..a4f1134 100644
--- a/client/js/01_SubGame/codes/SubGameHooks.js
+++ b/client/js/01_SubGame/codes/SubGameHooks.js
@@ -122,8 +122,7 @@ SubGameHooks.gamemydraw = function (gameid, spid, times, timelong, no2, no3, no4
// [A] Free(_msg):投票解散确认回调。
// SubGameHooks.Free = function (_msg) { /* ... */ };
-// [A] DeskInfo(_msg):未开战状态下加入牌桌的数据同步。
-// SubGameHooks.DeskInfo = function (_msg) { /* ... */ };
+// [A] DeskInfo(_msg):牌局已开始时加入牌桌的数据同步(实现见文末「网络与对局」一段)。
// [A] breakRoom():开局后自己退出房间触发。
// SubGameHooks.breakRoom = function () { /* ... */ };
@@ -246,5 +245,105 @@ SubGameHooks.gamemydraw = function (gameid, spid, times, timelong, no2, no3, no4
// [D] gameui_stop_voice(uiIndex):停止桌面语音气泡。
// SubGameHooks.gameui_stop_voice = function (uiIndex) { /* ... */ };
+//—— 网络与对局(B 阶段接线)——
+//平台入口只解包转交,业务全在 net/ 的 handler 里。
+
+// ---------------------------------------------------------------------------
+// 【room.mySeat 的唯一写入处】(SSOT)
+//
+// 为什么不能只在 appStart 里取一次:
+// 平台 12_Logic.js:389 调 Game_Modify.appStart() 时,C_Player 还只是一个
+// `var C_Player;`——`C_Player = new Player(-1)` 要到【同一个函数】的第 480 行才执行。
+// 此刻 typeof C_Player === 'undefined',守卫为假、整个 appStart 空转;
+// 之后玩家登录、07_Desk.js 里 C_Player.SetSeat() 拿到真座位,但 appStart 再也不会被调用,
+// 于是 mySeat 永远停在 -1:发包带 seat:-1(服务端 check_player 必然拒绝)、
+// StartWar 差异化下发找不到本座位而整包丢弃、SeatMap.toDisplay(-1) 抛错。
+// ——而且【全程无报错】,这正是它能潜伏下来的原因。
+//
+// 因此改为:单一写入处 + 多个调用点。凡是平台已经确定座位的入口都同步一次,
+// 不赌「所有路径都会经过某一个入口」。
+//
+// 幂等:可重复调用;C_Player 不可用或座位非法时【保持原值不写】——
+// 绝不用 -1 覆盖已同步好的正确座位。
+// ---------------------------------------------------------------------------
+SubGameHooks._syncMySeat = function () {
+ if (typeof C_Player === 'undefined' || !C_Player) { return; }
+ var seat = C_Player.seat;
+ if (seat !== 0 && seat !== 1 && seat !== 2) { return; }
+ EQW_GameState.room.mySeat = seat;
+};
+
+// 对局推送。_msg 是完整包 {app, route, rpc, data}
+// (平台 12_Logic.js 按 route 分流,只有子游戏路由 erqiwang 才到这里)
+SubGameHooks._ReceiveData = function (_msg) {
+ if (!_msg) { return; }
+ EQW_Dispatcher.dispatch({ rpc: _msg.rpc, data: _msg.data });
+};
+
+// 开局。可能差异化下发(sendtype:1 + seatlist[])
+// 【必须先同步座位】handleStartWar 要按 mySeat 在 seatlist[] 里认领自己那份
+SubGameHooks.StartWar = function (_msg) {
+ SubGameHooks._syncMySeat();
+ EQW_ResyncHandler.handleStartWar(_msg);
+};
+
+// 断线重连:拿到 get_deskinfo 的全量快照
+// (handleDeskinfo 内的 GameState.reset() 不清 room.mySeat,此处同步的值不会被冲掉)
+SubGameHooks.Reconnect = function (_deskinfo) {
+ SubGameHooks._syncMySeat();
+ EQW_ResyncHandler.handleDeskinfo(_deskinfo);
+};
+
+// 中途进桌:牌局【已经开始】,但自己走的是「加入房间」而非「重连」这条路径。
+// 平台 07_Desk.js 在 self_join_room 且 battlestate==1 时走 Game_Modify.DeskInfo(_msg.data.deskinfo),
+// 与 self_login 的 Game_Modify.Reconnect(_msg.data.deskinfo) 是两条并列分支——
+// 不接这个 hook,整份 deskinfo 会被 02 转发壳静默丢弃(无 hook 即 no-op),界面全空且不报错。
+//
+// 【入参与 Reconnect 完全同源、且已按【自己的】座位裁剪】:
+// 两条路径的 deskinfo 都出自服务端 server/youle/server_room/class.return.js —— 进房走
+// self_join_room 的 get_deskinfo(o_room, o_player.gameinfo.seat),登录走 self_login 的
+// get_deskinfo(o_player.gameinfo.o_room, o_player.gameinfo.seat),同一个函数、同一个
+// 「本人座位」入参。get_deskinfo 内部的可见性裁剪(底牌/埋牌底牌只给庄家、亮牌只给闲家、
+// mustcard 只给轮到的那家)因此对中途进桌一样生效,不会把别人的视角发过来。
+// 故直接复用同一条重画路径(前端 04 §3「重连 = 重画」),不为它单写一套渲染。
+SubGameHooks.DeskInfo = function (_deskinfo) {
+ SubGameHooks._syncMySeat();
+ EQW_ResyncHandler.handleDeskinfo(_deskinfo);
+};
+
+// 重连但未开战:没有牌局数据,无需重画对局
+SubGameHooks.ReconnectNoMakewar = function () {
+ SubGameHooks._syncMySeat();
+ console.log('[EQW] 重连(未开战)');
+};
+
+// 解散。_msg 即 Desk.deskfree,可能为 null(首局发牌前解散)
+SubGameHooks.Free = function (_msg) {
+ EQW_ResultHandler.handleFree(_msg);
+};
+
+// 房间信息(房号 / 总局数 / roomtype 位串)
+// 平台三条进桌路径(07_Desk.js:358 登录 / 510 建房 / 631 进房)都在 C_Player.SetSeat() 之后、
+// Reconnect / StartWar / DeskInfo 之前调它——是覆盖面最广的一个同步点
+SubGameHooks.setRoomDes = function (roomcode, asetcount, roomtype) {
+ SubGameHooks._syncMySeat();
+ EQW_ResyncHandler.handleSetRoomDes(roomcode, asetcount, roomtype);
+};
+
+// 换座(平台 07_Desk.js:186-191 已先把 C_Player.seat 改成新座位再回调这里)。
+// 这是唯一「座位会中途变化、且不重走 setRoomDes」的路径,漏掉这里 mySeat 会静默过期
+SubGameHooks.changeSeat = function (seat1, seat2) {
+ SubGameHooks._syncMySeat();
+};
+
+// 启动编排。B 阶段只记下自己的座位;UI 初始化留给 C 阶段
+// 注意:此刻 C_Player 通常还没创建(见 _syncMySeat 上方注释),座位靠后续入口补齐
+SubGameHooks.appStart = function () {
+ SubGameHooks._syncMySeat();
+ if (typeof Desk !== 'undefined' && Desk.roomtype) {
+ EQW_GameState.room.options = EQW_RoomOptions_Parse.parse(Desk.roomtype);
+ }
+};
+
window.SubGameHooks = SubGameHooks;
if (typeof module !== 'undefined' && module.exports) { module.exports = SubGameHooks; }
diff --git a/client/js/01_SubGame/codes/net/Dispatcher.js b/client/js/01_SubGame/codes/net/Dispatcher.js
new file mode 100644
index 0000000..2f8c38c
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/Dispatcher.js
@@ -0,0 +1,66 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_Dispatcher: 唯一收包入口 + 纯路由表 ////////////
+///////////////////////////////////////////////////////////////
+// 前端 04 §2:收包统一分发,一 rpc 一处理器。
+// 【本文件只分发,不写业务】——新增一种服务端推送 = 注册一条 rpc → handler。
+//
+// 判定顺序(关键):先看 data.success,为 false 一律交 FailHandler、不进业务 handler。
+// 因为协议 §0.2 规定失败回包的 rpc 与【请求】同名(chupai 失败回 chupai,
+// 而成功走 chupai1/2/3),不先分流的话每个业务 handler 都得自己判一遍。
+var EQW_Dispatcher = EQW_Dispatcher || {
+
+ _handlers: {},
+
+ //注册一条 rpc → handler
+ register: function (rpc, handler) {
+ if (typeof rpc !== 'string' || typeof handler !== 'function') {
+ throw new Error('[EQW_Dispatcher] register 参数非法: ' + rpc);
+ }
+ if (this._handlers.hasOwnProperty(rpc)) {
+ throw new Error('[EQW_Dispatcher] rpc 重复注册: ' + rpc);
+ }
+ this._handlers[rpc] = handler;
+ },
+
+ hasHandler: function (rpc) {
+ return this._handlers.hasOwnProperty(rpc);
+ },
+
+ //唯一收包入口。msg = { rpc, data }
+ dispatch: function (msg) {
+ if (!msg || typeof msg.rpc !== 'string') {
+ console.warn('[EQW_Dispatcher] 畸形数据包,已忽略');
+ return;
+ }
+ var data = msg.data || {};
+
+ //成败只认 data.success(前端 04 §4):失败包一律先分流
+ if (data.success === false) {
+ EQW_FailHandler.handle(msg.rpc, data);
+ return;
+ }
+
+ var h = this._handlers[msg.rpc];
+ if (h) {
+ h(data);
+ } else {
+ //平台日后新增推送不该让前端崩
+ console.warn('[EQW_Dispatcher] 未处理的 rpc: ' + msg.rpc);
+ }
+ }
+};
+
+//—— 路由表:一 rpc 一处理器(协议里每个服务端推送都要在这里注册)——
+//新增一种推送 = 加一条注册 + 写对应 handler;本文件不写业务。
+EQW_Dispatcher.register('fapai', function (d) { EQW_DealHandler.handle(d); });
+EQW_Dispatcher.register('jiaofen', function (d) { EQW_CallHandler.handleJiaofen(d); });
+EQW_Dispatcher.register('shangzhuang', function (d) { EQW_CallHandler.handleShangzhuang(d); });
+EQW_Dispatcher.register('xuanzhu', function (d) { EQW_MainHandler.handleXuanzhu(d); });
+EQW_Dispatcher.register('maipai', function (d) { EQW_BuryHandler.handle(d); });
+EQW_Dispatcher.register('chupai1', function (d) { EQW_PlayHandler.handle(d, 1); });
+EQW_Dispatcher.register('chupai2', function (d) { EQW_PlayHandler.handle(d, 2); });
+EQW_Dispatcher.register('chupai3', function (d) { EQW_PlayHandler.handle(d, 3); });
+EQW_Dispatcher.register('mingpai', function (d) { EQW_QueryHandler.handleMingpai(d); });
+EQW_Dispatcher.register('tishi', function (d) { EQW_QueryHandler.handleTishi(d); });
+EQW_Dispatcher.register('jiesuan', function (d) { EQW_ResultHandler.handleJiesuan(d); });
+EQW_Dispatcher.register('zhunbei', function (d) { EQW_ReadyHandler.handle(d); });
diff --git a/client/js/01_SubGame/codes/net/Rpc.js b/client/js/01_SubGame/codes/net/Rpc.js
new file mode 100644
index 0000000..1de1348
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/Rpc.js
@@ -0,0 +1,123 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_Rpc: 语义化发包(一操作一方法)/////////////////
+///////////////////////////////////////////////////////////////
+// 前端 04 §1:业务不直接拼包,走两层封装——
+// 业务 → EQW_Rpc.xxx(意图) → RpcHelper(注入平台字段)→ 引擎发送
+//
+// 【route 必须是 erqiwang】RpcHelper.sendGameRpc 预设 route="room"(平台房间模块),
+// 用它包会被路由到平台、永远到不了子游戏 mod.js,而且不会有任何报错。
+//
+// 【请求包只带意图,不带结论】(前端 04 §1 / 服务端 04 §8)
+// 可以带:操作类型、目标标识(牌 id / 花色 / 提示类型)、座位号(仅供一致性校验)。
+// 禁止带:分数、番数、判定结果、阶段推进指令、nextSeat、handCards。
+// 前端 shared/ 的计算只用于本地提示与预校验,【结果不回传】。
+// 一句话:前端能自己推出来的东西,就是玩家能改的东西——这是防作弊的根本。
+//
+// 本文件只做【形状】校验(牌 id 范围/去重/张数),不做【规则】校验
+// (这手牌能不能出、叫分够不够低由服务端裁定)。形状不合法就 fail-fast 抛错,
+// 不发出残缺包——服务端会按 PARAM 拒绝,但让错误暴露在最近处更省事。
+var EQW_Rpc = EQW_Rpc || {
+
+ APP: 'youle',
+ ROUTE: 'erqiwang',
+
+ //—— 内部:取自己的座位(fail-fast)——
+ //room.mySeat 由 SubGameHooks._syncMySeat 从平台 C_Player.seat 同步,初始值是 -1。
+ //【为什么不能透传】seat:-1 发出去,服务端 check_player 必然返回 falsy → 每个操作都回 ERR.PLAYER,
+ //叫分/选主/埋牌/出牌全部发不出去,而前端一声不吭。无声透传正是这类缺陷能长期潜伏的原因,
+ //故与本文件其余形状校验一样在最近处显式失败。
+ _seat: function () {
+ var seat = EQW_GameState.room.mySeat;
+ if (seat !== 0 && seat !== 1 && seat !== 2) {
+ throw new Error('[EQW_Rpc] 座位未同步: ' + seat +
+ '(须为 0/1/2;检查 SubGameHooks._syncMySeat 是否已由平台入口调用)');
+ }
+ return seat;
+ },
+
+ //—— 内部:发送 ——
+ _send: function (rpc, data) {
+ var pack = { seat: this._seat() };
+ var key;
+ for (key in data) {
+ if (data.hasOwnProperty(key)) {
+ pack[key] = data[key];
+ }
+ }
+ RpcHelper.sendRpc(this.APP, this.ROUTE, rpc, pack);
+ },
+
+ //—— 内部:牌 id 列表形状校验(协议 §0.2)——
+ //必须是非空数组,元素是 0–107 的整数,互不重复
+ _checkCards: function (cards, expectCount) {
+ if (Object.prototype.toString.call(cards) !== '[object Array]' || cards.length === 0) {
+ throw new Error('[EQW_Rpc] cards 必须是非空数组');
+ }
+ if (typeof expectCount === 'number' && cards.length !== expectCount) {
+ throw new Error('[EQW_Rpc] cards 张数应为 ' + expectCount + ',实际 ' + cards.length);
+ }
+ var seen = {};
+ for (var i = 0; i < cards.length; i++) {
+ var c = cards[i];
+ if (typeof c !== 'number' || c !== Math.floor(c) || c < 0 || c > 107) {
+ throw new Error('[EQW_Rpc] 非法牌 id: ' + c + '(须为 0–107 的整数)');
+ }
+ if (seen[c]) { throw new Error('[EQW_Rpc] 牌 id 重复: ' + c); }
+ seen[c] = true;
+ }
+ },
+
+ //—— 叫分(call: 5–70 的 5 的倍数;0 = 不叫)——
+ jiaofen: function (call) {
+ if (typeof call !== 'number' || call !== Math.floor(call)) {
+ throw new Error('[EQW_Rpc] call 必须是整数');
+ }
+ if (call !== 0 && (call < 5 || call > 70 || call % 5 !== 0)) {
+ throw new Error('[EQW_Rpc] 非法叫分: ' + call + '(须为 0 或 5–70 的 5 的倍数)');
+ }
+ this._send('jiaofen', { call: call });
+ },
+
+ //—— 投降(仅 70 分坐庄、选主阶段;合法性由服务端裁定)——
+ touxiang: function () {
+ this._send('touxiang', {});
+ },
+
+ //—— 选主(flower: 1方块 2梅花 3红心 4黑桃)——
+ xuanzhu: function (flower) {
+ if (typeof flower !== 'number' || flower < 1 || flower > 4) {
+ throw new Error('[EQW_Rpc] 非法花色: ' + flower + '(须为 1–4)');
+ }
+ this._send('xuanzhu', { flower: flower });
+ },
+
+ //—— 埋牌(恒 8 张)——
+ maipai: function (cards) {
+ this._checkCards(cards, 8);
+ this._send('maipai', { cards: cards });
+ },
+
+ //—— 出牌 ——
+ chupai: function (cards) {
+ this._checkCards(cards);
+ this._send('chupai', { cards: cards });
+ },
+
+ //—— 明牌(查看他家未出主牌)——
+ mingpai: function () {
+ this._send('mingpai', {});
+ },
+
+ //—— 出牌提示(tip: 1踩 2没分 3有分)——
+ tishi: function (tip) {
+ if (tip !== 1 && tip !== 2 && tip !== 3) {
+ throw new Error('[EQW_Rpc] 非法提示类型: ' + tip + '(须为 1踩 2没分 3有分)');
+ }
+ this._send('tishi', { tip: tip });
+ },
+
+ //—— 准备 ——
+ zhunbei: function () {
+ this._send('zhunbei', {});
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/BuryHandler.js b/client/js/01_SubGame/codes/net/handlers/BuryHandler.js
new file mode 100644
index 0000000..d7afcb6
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/BuryHandler.js
@@ -0,0 +1,56 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_BuryHandler: 埋牌(maipai)////////////////////
+///////////////////////////////////////////////////////////////
+// 协议 §9:cards 埋牌后手牌(仅庄家) / burycards 埋牌底牌(仅庄家) /
+// step 阶段(=5 出牌) / seat 控制权=首出者(必为庄家) / countdown 出牌倒计时 /
+// seatlist 三家牌况(仅可查牌模式) /
+// liangpai 亮牌(仅闲家 + 可查牌模式 + 庄家固定主牌达门槛)
+//
+// 注意 burycards(埋牌底牌,庄家埋下的 8 张)与 bottomcards(底牌,发牌留桌的 8 张)
+// 是两批不同的牌,协议 §0.0 与验收清单专门警告过别写反。
+//
+// 【seatlist 此刻是全初值,但必须接】:埋牌完成时这张表刚初始化
+// (每家 [[0,0],[0,0],[0,0],[0,0],[-1,-1]]),界面上看不出差别——但「埋牌完成 → 庄家首出」
+// 这段窗口内重连拿到的 PushCards.seatlist 就是它,漏接会让增量路径此刻为空、重连路径却有表。
+//
+// 【bottomcards 到此失效,要清空】:底牌在上庄时已翻给玩家看并并入手牌,埋牌完成后
+// 只剩「埋牌底牌 burycards」有意义;deskinfo 的 PushCards 不带 bottomcards,重连重建恒为 []。
+//
+// 【playproc 是服务端的「本轮进行态」,原样拷贝】:埋牌完成时服务端已就地开好 round-1
+// 的进行态(庄家首出、桌面全空),恒有、三家同值,与 chupai1/2/3.playproc / 重连包
+// PushCards.playproc 同源同结构(服务端同一个 get_playproc())。没有它,「埋牌完成 →
+// 庄家首出」这段窗口增量路径 table.playproc 会停在上一阶段的 null,与重连路径不一致。
+var EQW_BuryHandler = EQW_BuryHandler || {
+
+ handle: function (data) {
+ if (!data.hasOwnProperty('seat')) {
+ console.error('[EQW_BuryHandler] maipai 缺 seat,已跳过该包');
+ return;
+ }
+
+ //庄家才有的两项,闲家缺字段时保持原值、不抹手牌
+ EQW_GameState._apply(EQW_GameState.my, data, {
+ cards: 'cards',
+ buryCards: 'burycards'
+ });
+ //底牌已并入手牌、埋牌完成即失效(重连的 PushCards 不带它),显式清空保持两条路径一致
+ EQW_GameState.my.bottomCards = [];
+ //三家牌况(仅可查牌模式)、闲家才可能有的亮牌、本轮进行态(恒有,见上方注释)
+ EQW_GameState._apply(EQW_GameState.table, data, {
+ seatlist: 'seatlist',
+ liangpai: 'liangpai',
+ playproc: 'playproc'
+ });
+ //阶段只读包字段(服务端权威),不按 rpc 名硬编码 step=5
+ EQW_GameState._apply(EQW_GameState.aset, data, { step: 'step' });
+ //控制权:本包的 seat 就是「轮到谁」(即将首出的庄家,服务端取自 playproc.currseat)
+ EQW_GameState._apply(EQW_GameState.turn, data, {
+ seat: 'seat',
+ countdown: 'countdown'
+ });
+
+ EventBus.emit(EQW_Events.EQW_BURY_DONE);
+ if (data.hasOwnProperty('cards')) { EventBus.emit(EQW_Events.EQW_HAND_CHANGED); }
+ EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/CallHandler.js b/client/js/01_SubGame/codes/net/handlers/CallHandler.js
new file mode 100644
index 0000000..6ae54e2
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/CallHandler.js
@@ -0,0 +1,78 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_CallHandler: 叫分(jiaofen)与上庄(shangzhuang)
+///////////////////////////////////////////////////////////////
+// 协议 §3 jiaofen:seat 叫分者 / call 分数(0=不叫) / currcall 当前叫到的分 /
+// multiple 该叫分对应基础子数 / step 阶段(仍=1) /
+// nextseat 下一个叫分者(控制权) / countdown
+// 协议 §4 shangzhuang:seat / call / banker / grade 庄家叫分 / multiple /
+// step 阶段(=2 选主/投降) / nextseat 控制权(=庄家) /
+// bottomcards 底牌(庄家恒有,闲家仅 70 分时有) / ancard3s 开底标志 /
+// cards 摸底后的 36 张(仅庄家) / countdown 选主倒计时 /
+// touxiang 是否允许投降 / curmultiple 当前抓分倍数(带符号)
+//
+// 【shangzhuang 的 seat 与 nextseat 别混用】seat 是【最后一个叫分者】,nextseat 才是
+// 「轮到谁」(选主/投降只由庄家做,服务端 mod.xuanzhu/mod.touxiang 以 banker 为准)。
+// 曾经这里写 turn.seat = aset.banker 自己推——那是把「选主 = 庄家」这条规则搬到了前端;
+// 同一条规则在 ResyncHandler 里还有一份,同一份数据两个写入处(SSOT)。现一律读包字段。
+var EQW_CallHandler = EQW_CallHandler || {
+
+ handleJiaofen: function (data) {
+ if (!data.hasOwnProperty('seat')) {
+ console.error('[EQW_CallHandler] jiaofen 缺 seat,已跳过该包');
+ return;
+ }
+
+ //记录这一家叫了多少:0 = 不叫,与「还没叫」(null) 区分
+ if (data.hasOwnProperty('call') && data.seat >= 0 && data.seat < 3) {
+ EQW_GameState.call.calls[data.seat] = data.call;
+ }
+ EQW_GameState._apply(EQW_GameState.call, data, { currcall: 'currcall' });
+ EQW_GameState._apply(EQW_GameState.aset, data, {
+ multiple: 'multiple',
+ step: 'step'
+ });
+ EQW_GameState._apply(EQW_GameState.turn, data, {
+ seat: 'nextseat',
+ countdown: 'countdown'
+ });
+
+ EventBus.emit(EQW_Events.EQW_CALL_CHANGED);
+ EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
+ },
+
+ handleShangzhuang: function (data) {
+ if (!data.hasOwnProperty('banker')) {
+ console.error('[EQW_CallHandler] shangzhuang 缺 banker,已跳过该包');
+ return;
+ }
+
+ EQW_GameState._apply(EQW_GameState.aset, data, {
+ banker: 'banker',
+ call: 'grade', //协议里庄家叫分叫 grade
+ multiple: 'multiple',
+ touxiang: 'touxiang',
+ curmultiple:'curmultiple',
+ step: 'step' //阶段服务端权威给出,不按 rpc 名硬编码
+ });
+ EQW_GameState._apply(EQW_GameState.table, data, { ancard3s: 'ancard3s' });
+ //底牌与摸底后的手牌只有庄家(及 70 分时的闲家)才有,缺就不写、不抹已有值
+ EQW_GameState._apply(EQW_GameState.my, data, {
+ bottomCards: 'bottomcards',
+ cards: 'cards'
+ });
+ //控制权:读包里的 nextseat(服务端权威),不用本地的 banker 反推
+ EQW_GameState._apply(EQW_GameState.turn, data, {
+ seat: 'nextseat',
+ countdown: 'countdown'
+ });
+
+ //叫分过程量(currcall/calls)step2 起无语义:deskinfo 从 ChooseMain 起就不再带 CallRun,
+ //这里清成 reset() 的默认值,避免前端留着一份 deskinfo 不会再给的数据(视图=f(快照))
+ EQW_GameState.call.currcall = 0;
+ EQW_GameState.call.calls = [null, null, null];
+
+ EventBus.emit(EQW_Events.EQW_BANKER_SET);
+ if (data.hasOwnProperty('cards')) { EventBus.emit(EQW_Events.EQW_HAND_CHANGED); }
+ EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/DealHandler.js b/client/js/01_SubGame/codes/net/handlers/DealHandler.js
new file mode 100644
index 0000000..2aa334c
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/DealHandler.js
@@ -0,0 +1,37 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_DealHandler: 发牌(fapai)//////////////////////
+///////////////////////////////////////////////////////////////
+// 协议 §1:asetidx 当前局数 / asetcount 总局数 / cards 自己的 28 张 /
+// step 阶段(=1 叫分) / seat 当前等待叫分者 / countdown 叫分倒计时
+//
+// 【step 只读包字段,不按 rpc 硬编码】阶段与控制权由服务端唯一维护、逐包显式下发
+//(前端红线「数据驱动、前端无对局状态机」/ 服务端红线「任何状态变更都要有包承载」)。
+// 曾经这里写死 aset.step = 1:服务端一旦插入中间阶段,前端不会红、只会静默画错。
+//
+// fapai 是一局的起点,先 reset 清掉上一局的残留(reset 保留 mySeat 与 options)。
+var EQW_DealHandler = EQW_DealHandler || {
+
+ handle: function (data) {
+ if (!data.hasOwnProperty('cards')) {
+ console.error('[EQW_DealHandler] fapai 缺 cards,已跳过该包');
+ return;
+ }
+
+ EQW_GameState.reset();
+
+ EQW_GameState._apply(EQW_GameState.room, data, {
+ asetIdx: 'asetidx',
+ asetCount: 'asetcount'
+ });
+ EQW_GameState._apply(EQW_GameState.my, data, { cards: 'cards' });
+ EQW_GameState._apply(EQW_GameState.aset, data, { step: 'step' });
+ EQW_GameState._apply(EQW_GameState.turn, data, {
+ seat: 'seat',
+ countdown: 'countdown'
+ });
+
+ EventBus.emit(EQW_Events.EQW_RESET);
+ EventBus.emit(EQW_Events.EQW_HAND_CHANGED);
+ EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/FailHandler.js b/client/js/01_SubGame/codes/net/handlers/FailHandler.js
new file mode 100644
index 0000000..634225d
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/FailHandler.js
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_FailHandler: 失败回包 //////////////////////////
+///////////////////////////////////////////////////////////////
+// 协议 §0.2:服务端受理请求时任一校验不通过,回一个【rpc 与请求同名】的失败包,
+// data 只含 success:false 与 errcode,且只回给请求者。
+//
+// errcode(服务端 youle_erqiwang.ERR):
+// 1 PLAYER 玩家/房间/座位校验不通过 2 NODESK 牌桌或牌局不存在
+// 3 STEP 当前阶段不允许该操作 4 SEAT 位置不符或还没轮到
+// 5 PARAM 参数非法 6 RULE 规则不允许
+//
+// 失败信息是【一次性】的,不属于对局状态,故不进 GameState,只随事件带出去。
+var EQW_FailHandler = EQW_FailHandler || {
+
+ ERR: {
+ PLAYER: 1, NODESK: 2, STEP: 3, SEAT: 4, PARAM: 5, RULE: 6
+ },
+
+ handle: function (rpc, data) {
+ var errcode = (data && typeof data.errcode === 'number') ? data.errcode : 0;
+ console.warn('[EQW] 请求失败: rpc=' + rpc + ' errcode=' + errcode);
+ EventBus.emit(EQW_Events.EQW_RPC_FAILED, { rpc: rpc, errcode: errcode });
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/MainHandler.js b/client/js/01_SubGame/codes/net/handlers/MainHandler.js
new file mode 100644
index 0000000..445c468
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/MainHandler.js
@@ -0,0 +1,39 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_MainHandler: 选主(xuanzhu)///////////////////
+///////////////////////////////////////////////////////////////
+// 协议 §7:banker 庄家 / flower 主牌花色(1方块 2梅花 3红心 4黑桃) /
+// step 阶段(=3 埋牌) / nextseat 控制权(=庄家,埋牌由庄家做) /
+// countdown 埋牌倒计时 / cards 选主后自己手上的牌
+//
+// 【step 与 nextseat 只读包字段】曾经这里写死 step=3 且 turn.seat = aset.banker:
+// 阶段与「埋牌 = 庄家」两条规则都被搬到了前端(前端红线「数据驱动、前端无对局状态机」)。
+//
+// 选主后正 2 / 正 7 升格、主花色普通牌并入主牌段(design §3),手牌需整体重排——
+// 但【重排是渲染时的事】,由 core/CardOrder 现算,不在这里落地成状态。
+//
+// 投降(touxiang)没有成功推送:点投降后服务端直接结算,前端收到的是 jiesuan。
+// 失败时才回一个同名的 touxiang 失败包,由 FailHandler 统一处理。
+var EQW_MainHandler = EQW_MainHandler || {
+
+ handleXuanzhu: function (data) {
+ if (!data.hasOwnProperty('flower')) {
+ console.error('[EQW_MainHandler] xuanzhu 缺 flower,已跳过该包');
+ return;
+ }
+
+ EQW_GameState._apply(EQW_GameState.aset, data, {
+ banker: 'banker',
+ flower: 'flower',
+ step: 'step'
+ });
+ EQW_GameState._apply(EQW_GameState.my, data, { cards: 'cards' });
+ EQW_GameState._apply(EQW_GameState.turn, data, {
+ seat: 'nextseat',
+ countdown: 'countdown'
+ });
+
+ EventBus.emit(EQW_Events.EQW_MAIN_SET);
+ if (data.hasOwnProperty('cards')) { EventBus.emit(EQW_Events.EQW_HAND_CHANGED); }
+ EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/PlayHandler.js b/client/js/01_SubGame/codes/net/handlers/PlayHandler.js
new file mode 100644
index 0000000..41aa649
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/PlayHandler.js
@@ -0,0 +1,109 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_PlayHandler: 出牌(chupai1 / 2 / 3)///////////
+///////////////////////////////////////////////////////////////
+// 协议 §11–§13。三个包字段不完全相同:
+// chupai1 首家独有:shuaicuo 甩错标志 / count 出牌数量 / flower 出牌花色 /
+// cardtype 牌型 / shuai 甩牌分量构成
+// chupai3 末家独有:maxseat 本轮谁最大 / grade 本轮闲家得分
+// 共有:seat / cards / seatlist(仅可查牌) / countdown / baozhu / nextseat /
+// cardsinhand(仅出牌者自己有) / curmultiple(1 和 3 有)
+// mustcard:三个包都可能带,但只发给 nextseat 那一家(其余两家无此属性)
+//
+// 【cardsinhand 只有出牌者自己有】:收到就更新自己的手牌;别人出牌时没这个字段,
+// 绝不能抹掉自己的手牌——这正是「缺字段不兜底」要防的。
+//
+// 【mustcard 没有就要清空】:它是「本轮跟牌的必出牌」,只对当前这一轮有效。
+// 上一轮的建议残留下来会让界面自动选中错误的牌。
+//
+// 【playproc 是服务端的「本轮进行态」,原样拷贝】:它的结构是
+// {round, start, currseat, startcount, startflower, starttype, maxseat, maxcard, cards, shuai_demand}
+// (协议 §11),与重连包 PushCards.playproc 同源同结构(服务端同一个 get_playproc())。
+// 注意它的 cards 是【本轮三家各自出的牌、下标 = 座位】,与本包的 cards(这一手出的牌)
+// 语义相反,绝不能混写。轮次/最大者由服务端给,前端不累积、不推导。
+//
+// 【这一手的信息走事件载荷,不进 GameState】:谁出了什么、几张、什么牌型是【一次性表现】
+// (落牌动画),不是对局状态;deskinfo 也不下发它。放进 GameState 会立刻制造一处
+// 增量/全量不一致。故随 EQW_CARD_PLAYED 事件下发给 UI。
+// 【本墩结果同理】:chupai3 的 maxseat / grade(谁最大、这墩闲家得几分)也是一次性表现,
+// 随 EQW_TRICK_END 事件载荷下发,同样不进 GameState。
+var EQW_PlayHandler = EQW_PlayHandler || {
+
+ //order: 1/2/3,对应 chupai1/2/3
+ handle: function (data, order) {
+ if (!data.hasOwnProperty('seat') || !data.hasOwnProperty('cards')) {
+ console.error('[EQW_PlayHandler] chupai' + order + ' 缺 seat 或 cards,已跳过该包');
+ return;
+ }
+
+ //—— 本轮进行态:服务端权威快照,原样拷贝,前端零推导 ——
+ EQW_GameState._apply(EQW_GameState.table, data, { playproc: 'playproc' });
+
+ //—— 出牌历史:把这一手记进本轮(chupai1 = 一轮的第一手,开新的一轮)——
+ EQW_GameState.pushPlay(data.seat, data.cards, order === 1);
+
+ //—— 自己的手牌:只有出牌者本人才收到 cardsinhand ——
+ EQW_GameState._apply(EQW_GameState.my, data, { cards: 'cardsinhand' });
+
+ //—— 必出牌:本轮有效,没给就清空 ——
+ if (data.hasOwnProperty('mustcard')) {
+ EQW_GameState.my.mustCard = data.mustcard;
+ } else {
+ EQW_GameState.my.mustCard = [];
+ }
+
+ //—— 牌况与报无主(仅可查牌模式下发)——
+ EQW_GameState._apply(EQW_GameState.table, data, { seatlist: 'seatlist' });
+ EQW_GameState._apply(EQW_GameState.aset, data, {
+ baozhu: 'baozhu',
+ curmultiple: 'curmultiple'
+ });
+
+ //—— 控制权与倒计时 ——
+ EQW_GameState._apply(EQW_GameState.turn, data, {
+ seat: 'nextseat',
+ countdown: 'countdown'
+ });
+
+ EventBus.emit(EQW_Events.EQW_CARD_PLAYED, this._playPayload(data, order));
+ if (data.hasOwnProperty('cardsinhand')) { EventBus.emit(EQW_Events.EQW_HAND_CHANGED); }
+
+ //—— 末家出完:本轮结束 ——
+ //【本墩结果走事件载荷,不进 GameState】谁最大、这墩闲家得了几分,是【一次性表现】
+ //(收牌动画、得分飘字),与 EQW_CARD_PLAYED 同类;任何阶段的 deskinfo 都不下发它,
+ //写进 GameState 就多出一处「只活在前端、服务端不知道」的对局态——出牌中途断线重连
+ //即归零,违反前端红线「丢弃 this.data、仅凭最近一次服务端快照重画,界面必须一致」。
+ //小局累计捡分 aset.grade 不同:它有服务端来源(PushCards.grade / chupai3.grade),
+ //属于镜像量,仍留在 GameState。
+ if (order === 3) {
+ //本轮闲家得分累加进小局捡分;没得分时不带 grade,累计值保持不变
+ if (data.hasOwnProperty('grade')) {
+ EQW_GameState.aset.grade = EQW_GameState.aset.grade + data.grade;
+ }
+ EventBus.emit(EQW_Events.EQW_TRICK_END, this._trickPayload(data));
+ }
+
+ EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
+ },
+
+ //EQW_CARD_PLAYED 的事件载荷:这一手的一次性信息(落牌动画用),不落 GameState。
+ //【缺字段不兜底】:包里没有的键就不带(count/flower/cardtype 只有 chupai1 有,
+ //shuai 仅合法甩牌时有,shuaicuo 仅甩错时有),订阅方按 hasOwnProperty 判断。
+ _playPayload: function (data, order) {
+ var payload = { order: order, seat: data.seat, cards: data.cards };
+ var keys = ['count', 'flower', 'cardtype', 'shuai', 'shuaicuo'];
+ for (var i = 0; i < keys.length; i++) {
+ if (data.hasOwnProperty(keys[i])) { payload[keys[i]] = data[keys[i]]; }
+ }
+ return payload;
+ },
+
+ //EQW_TRICK_END 的事件载荷:这一墩的一次性结果(收牌动画、得分飘字用),不落 GameState。
+ //【缺字段不兜底】grade 只在这墩被闲家收走且确实有分时才有,包里没有就不带这个键。
+ //与 ResultHandler 为收尾墩发的那份载荷字段一致(收尾墩不发 chupai3,整包换成 jiesuan)。
+ _trickPayload: function (data) {
+ var payload = { seat: data.seat, cards: data.cards };
+ if (data.hasOwnProperty('maxseat')) { payload.maxseat = data.maxseat; }
+ if (data.hasOwnProperty('grade')) { payload.grade = data.grade; }
+ return payload;
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/QueryHandler.js b/client/js/01_SubGame/codes/net/handlers/QueryHandler.js
new file mode 100644
index 0000000..7f1a2ed
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/QueryHandler.js
@@ -0,0 +1,33 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_QueryHandler: 明牌(mingpai)与提示(tishi)///
+///////////////////////////////////////////////////////////////
+// 协议 §13.6 mingpai(只回给请求者):
+// seat 请求者 / others 另外两家各自未出的全部主牌 [{seat, zhucards}]
+// 受理条件:可查牌模式 + 出牌阶段 + 已有【任一】玩家报无主(design §9.3)
+//
+// 协议 §13.8 tishi(只转发给对家,即另一闲家):
+// seat 发出提示的闲家 / tip 1=踩 2=没分 3=有分
+// 注意:成功时【不给发送者回执】,只转发给对家;只有失败才回给发送者。
+//
+// 明牌数据进 GameState(面板可反复开关查看),提示不进——
+// 提示是一次性的通知、不是对局状态,重连也不重放,存下来只会变成幽灵数据。
+var EQW_QueryHandler = EQW_QueryHandler || {
+
+ handleMingpai: function (data) {
+ if (!data.hasOwnProperty('others')) {
+ console.error('[EQW_QueryHandler] mingpai 缺 others,已跳过该包');
+ return;
+ }
+ EQW_GameState.table.mingpai = data.others;
+ EventBus.emit(EQW_Events.EQW_MINGPAI);
+ },
+
+ handleTishi: function (data) {
+ if (!data.hasOwnProperty('seat') || !data.hasOwnProperty('tip')) {
+ console.error('[EQW_QueryHandler] tishi 缺 seat 或 tip,已跳过该包');
+ return;
+ }
+ //一次性通知:不进 GameState,随事件把内容带给订阅者
+ EventBus.emit(EQW_Events.EQW_TIP, { seat: data.seat, tip: data.tip });
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/ReadyHandler.js b/client/js/01_SubGame/codes/net/handlers/ReadyHandler.js
new file mode 100644
index 0000000..d67c8dc
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/ReadyHandler.js
@@ -0,0 +1,16 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_ReadyHandler: 准备(zhunbei)//////////////////
+///////////////////////////////////////////////////////////////
+// 协议 §16:seat 位置序号。
+// 准备状态由平台的玩家信息 UI 呈现(清单 T-15:准备标识用平台的),
+// 子游戏只需把事件发出去,让关心的组件(如结算面板的「下一局」按钮)响应。
+var EQW_ReadyHandler = EQW_ReadyHandler || {
+
+ handle: function (data) {
+ if (!data.hasOwnProperty('seat')) {
+ console.error('[EQW_ReadyHandler] zhunbei 缺 seat,已跳过该包');
+ return;
+ }
+ EventBus.emit(EQW_Events.EQW_READY_CHANGED, { seat: data.seat });
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/ResultHandler.js b/client/js/01_SubGame/codes/net/handlers/ResultHandler.js
new file mode 100644
index 0000000..5a989e8
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/ResultHandler.js
@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_ResultHandler: 结算(jiesuan)与解散(Free)///
+///////////////////////////////////////////////////////////////
+// 协议 §14:结算包由 chupai + bottom + aset 拼装,末局再加 account。
+// 三种来源的组成不同:
+// 正常出牌结算:chupai + bottom + aset(末局加 account)
+// 投降结算: 只有 aset(末局加 account),无 chupai、无 bottom
+// 解散结算: 只有 aset + account,且【走另一个入口】,见下
+//
+// 【解散的特殊性】(协议 §14.1,已对平台代码核实):
+// 解散包的 route 是平台的 "room",平台 12_Logic.js 按 route 分流时把它交给
+// Net[rpc] 而【不是】Game_Modify._ReceiveData——所以它根本进不了子游戏的分发表。
+// 平台另有专门入口:07_Desk.js 的 Game_Modify.Free(Desk.deskfree)。
+// 两个后果:
+// 1. 取值路径比协议文档少一层——平台已把 deskfree 取出来传进来,
+// 所以是 deskfree.data.aset,不是 data.deskfree.data.aset;
+// 2. 参数可能是 null——解散若发生在开战后、首局发牌前,服务端 get_disbandRoom
+// 返回 null,平台走「不带 deskfree」分支。这时只做房间收尾、不弹结算。
+var EQW_ResultHandler = EQW_ResultHandler || {
+
+ handleJiesuan: function (data) {
+ if (!data.hasOwnProperty('aset')) {
+ console.error('[EQW_ResultHandler] jiesuan 缺 aset,已跳过该包');
+ return;
+ }
+ this._applyResult(data);
+ },
+
+ //平台解散入口。deskfree = { rpc:"jiesuan", data:{ success, aset, account } },可能为 null
+ handleFree: function (deskfree) {
+ if (!deskfree || !deskfree.data) {
+ //首局发牌前解散:没有结算数据,只做房间收尾,不弹结算面板
+ console.warn('[EQW_ResultHandler] 解散包无 deskfree 数据(首局发牌前解散)');
+ return;
+ }
+ var d = deskfree.data;
+ if (!d.hasOwnProperty('aset')) {
+ console.error('[EQW_ResultHandler] 解散包缺 aset,已跳过');
+ return;
+ }
+ this._applyResult(d);
+ },
+
+ //三种来源共用的落地逻辑:有哪组就写哪组,缺的保持 null
+ _applyResult: function (d) {
+ //—— 收尾轮的第三手:牌局在最后一手打完时【不发 chupai3】,整包换成 jiesuan(协议 §13/§14)——
+ //夹具实跑一局的包数就是证据:chupai1×28 / chupai2×28 / chupai3×27。
+ //只在 chupai3 归档会永远丢掉最后一轮,故在这里补齐——它必是本轮第三手,落进当前这一轮。
+ //jiesuan.chupai 的字段与 chupai3 不同:只有 seat / cards / maxseat(+ 有分时的 grade),
+ //没有 order / playproc / cardsinhand。
+ //归档随后会被下面的结算收敛清掉(deskinfo step6 不带 PushCards),但 emit 是同步的:
+ //落牌与收牌表现在清空之前就已取到完整的收尾轮。
+ //【本墩结果只走事件载荷、不进 GameState】:谁最大、这墩得几分是一次性表现,
+ //deskinfo 的 Balance 不带它(它属于「出牌过程」而非结算数据),写进 GameState
+ //就会多出一处服务端不知道的对局态。收尾墩与 chupai3 走同一对事件、同一份载荷字段。
+ if (d.chupai && d.chupai.hasOwnProperty('seat') && d.chupai.hasOwnProperty('cards')) {
+ EQW_GameState.pushPlay(d.chupai.seat, d.chupai.cards, false);
+ var lastPlay = { seat: d.chupai.seat, cards: d.chupai.cards };
+ if (d.chupai.hasOwnProperty('maxseat')) { lastPlay.maxseat = d.chupai.maxseat; }
+ if (d.chupai.hasOwnProperty('grade')) { lastPlay.grade = d.chupai.grade; }
+ EventBus.emit(EQW_Events.EQW_CARD_PLAYED, lastPlay);
+ EventBus.emit(EQW_Events.EQW_TRICK_END, lastPlay);
+ }
+
+ //结算是"清空过程量、只留结算数据"的快照点:deskinfo step6 只带 Balance(=aset),
+ //其余分组一律回到 reset() 默认值——jiesuan 也要收敛到同一状态,否则重连前后画面不一致
+ //(清单以 ResyncHandler.handleDeskinfo 在 step6 时实际重建出的字段集为准,不多清不少清)。
+ EQW_GameState.aset.banker = -1;
+ EQW_GameState.aset.call = -1;
+ EQW_GameState.aset.multiple = 0;
+ EQW_GameState.aset.flower = 0;
+ EQW_GameState.aset.curmultiple = 0;
+ EQW_GameState.aset.grade = 0;
+ EQW_GameState.aset.baozhu = 0;
+ EQW_GameState.aset.touxiang = 0;
+ EQW_GameState.turn.seat = -1;
+ EQW_GameState.turn.countdown = 0;
+ EQW_GameState.call.currcall = 0;
+ EQW_GameState.call.calls = [null, null, null];
+ //末轮若由 jiesuan.chupai 带出(无 cardsinhand),出牌者自己的手牌不会被清空,这里显式清掉,
+ //不留幽灵牌——deskinfo 在结算阶段的 MyCards 恒为 []
+ EQW_GameState.my.cards = [];
+ EQW_GameState.my.mustCard = [];
+ EQW_GameState.my.bottomCards = [];
+ EQW_GameState.my.buryCards = [];
+ EQW_GameState.table.ancard3s = 0;
+ EQW_GameState.table.playproc = null;
+ EQW_GameState.table.pushlist = [];
+ EQW_GameState.table.seatlist = [];
+ EQW_GameState.table.liangpai = null;
+ EQW_GameState.table.mingpai = null;
+
+ //【不写 chupai】收尾墩已在上面随事件载荷发走;deskinfo step6 的 Balance 只有
+ //aset / bottom / account 三份,多存一个 chupai 就是增量路径独有的私有对局态
+ EQW_GameState._apply(EQW_GameState.result, d, {
+ bottom: 'bottom',
+ aset: 'aset',
+ account: 'account'
+ });
+ //阶段只读包字段:jiesuan 的 step 由服务端 get_paiju_account 统一给出(三种结算来源恒为 6),
+ //前端不按 rpc 名硬编码——解散结算走的是另一条投递路径,同样带着这个字段
+ EQW_GameState._apply(EQW_GameState.aset, d, { step: 'step' });
+
+ //三家总积分:jiesuan 的 aset.seatlist[i].score 就是"累积得分"(协议 §14),
+ //与 deskinfo.PlayerInfo 同源同值——deskinfo 恒有的 PlayerInfo 在增量路径上没有对应来源,
+ //只能从这里回填,否则一局中段(fapai 后至 jiesuan 前)room.playerScores 永远追不上服务端
+ if (d.aset && Object.prototype.toString.call(d.aset.seatlist) === '[object Array]') {
+ var scores = [];
+ for (var i = 0; i < d.aset.seatlist.length; i++) {
+ scores.push(d.aset.seatlist[i] && d.aset.seatlist[i].score);
+ }
+ EQW_GameState.room.playerScores = scores;
+ }
+
+ EventBus.emit(EQW_Events.EQW_ASET_RESULT);
+ //account 只在末局或解散时才有
+ if (d.hasOwnProperty('account')) {
+ EventBus.emit(EQW_Events.EQW_ACCOUNT_RESULT);
+ }
+ }
+};
diff --git a/client/js/01_SubGame/codes/net/handlers/ResyncHandler.js b/client/js/01_SubGame/codes/net/handlers/ResyncHandler.js
new file mode 100644
index 0000000..a6f1291
--- /dev/null
+++ b/client/js/01_SubGame/codes/net/handlers/ResyncHandler.js
@@ -0,0 +1,159 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_ResyncHandler: 重连(deskinfo)与开局(StartWar)
+///////////////////////////////////////////////////////////////
+// 前端 04 §3:重连 = 重画。断线重连与硬刷新本质相同,复用同一条路径:
+// 填 GameState → emit EQW_RESYNC_ALL → 各组件据数据重建界面
+// 【绝不为重连单写一套渲染】。
+//
+// deskinfo 按 step 只带对应阶段的分组(协议文末):
+// 恒有 count / idx / PlayerInfo / step / MyCards
+// CallRun(step1) ChooseMain(step2) BuryCards(step3) PushCards(step5) Balance(step6)
+//
+// 【重连不重放一次性事件】:70 分坐庄的 3 秒开底是上庄时的一次性事件,
+// deskinfo 里没有 ancard3s,前端也不得补播。
+var EQW_ResyncHandler = EQW_ResyncHandler || {
+
+ //平台房间信息(roomcode / 总局数 / roomtype 位串)
+ handleSetRoomDes: function (roomcode, asetcount, roomtype) {
+ if (typeof asetcount === 'number') { EQW_GameState.room.asetCount = asetcount; }
+ EQW_GameState.room.options = EQW_RoomOptions_Parse.parse(roomtype);
+ },
+
+ //开局:makewar 可能差异化下发(sendtype:1 + seatlist[]),先取自己那份
+ handleStartWar: function (msg) {
+ if (!msg || !msg.data) {
+ console.warn('[EQW_ResyncHandler] StartWar 无数据,已忽略');
+ return;
+ }
+ var raw = msg.data.deskwar || msg.data;
+ var mine = raw;
+ if (raw && raw.sendtype === 1 && Object.prototype.toString.call(raw.seatlist) === '[object Array]') {
+ mine = null;
+ for (var i = 0; i < raw.seatlist.length; i++) {
+ if (raw.seatlist[i] && raw.seatlist[i].seat === EQW_GameState.room.mySeat) {
+ mine = raw.seatlist[i].data;
+ break;
+ }
+ }
+ if (!mine) {
+ console.error('[EQW_ResyncHandler] StartWar 差异化下发里没有本座位的数据');
+ return;
+ }
+ }
+ this.handleDeskinfo(mine);
+ },
+
+ //重连:deskinfo 全量快照
+ handleDeskinfo: function (info) {
+ if (!info) {
+ console.warn('[EQW_ResyncHandler] deskinfo 为空,已忽略');
+ return;
+ }
+
+ EQW_GameState.reset();
+
+ //—— 恒有的部分 ——
+ EQW_GameState._apply(EQW_GameState.room, info, {
+ asetCount: 'count',
+ asetIdx: 'idx',
+ playerScores: 'PlayerInfo'
+ });
+ EQW_GameState._apply(EQW_GameState.aset, info, { step: 'step' });
+ EQW_GameState._apply(EQW_GameState.my, info, { cards: 'MyCards' });
+
+ //—— 按阶段分组 ——
+ if (info.CallRun) { this._applyCallRun(info.CallRun); }
+ if (info.ChooseMain) { this._applyChooseMain(info.ChooseMain); }
+ if (info.BuryCards) { this._applyBuryCards(info.BuryCards); }
+ if (info.PushCards) { this._applyPushCards(info.PushCards); }
+ if (info.Balance) { this._applyBalance(info.Balance); }
+
+ EventBus.emit(EQW_Events.EQW_RESYNC_ALL);
+ },
+
+ _applyCallRun: function (g) {
+ EQW_GameState._apply(EQW_GameState.call, g, {
+ currcall: 'nowcall',
+ calls: 'call'
+ });
+ EQW_GameState._apply(EQW_GameState.aset, g, { multiple: 'multiple' });
+ EQW_GameState._apply(EQW_GameState.turn, g, {
+ seat: 'seat',
+ countdown: 'countdown'
+ });
+ },
+
+ _applyChooseMain: function (g) {
+ EQW_GameState._apply(EQW_GameState.aset, g, {
+ banker: 'banker',
+ call: 'call',
+ multiple: 'multiple',
+ touxiang: 'touxiang',
+ curmultiple: 'curmultiple' //与 shangzhuang 推送同源同值,漏读会让「抓分」角标掉档
+ });
+ EQW_GameState._apply(EQW_GameState.my, g, { bottomCards: 'bottomcards' });
+ //控制权由服务端在分组里显式给出(与 shangzhuang.nextseat 同源同值);
+ //曾经这里写 turn.seat = aset.banker 自推,与 CallHandler 里那份是同一条规则的第二个写入处
+ EQW_GameState._apply(EQW_GameState.turn, g, {
+ seat: 'seat',
+ countdown: 'countdown'
+ });
+ },
+
+ _applyBuryCards: function (g) {
+ EQW_GameState._apply(EQW_GameState.aset, g, {
+ banker: 'banker',
+ call: 'call',
+ multiple: 'multiple',
+ flower: 'flower',
+ curmultiple: 'curmultiple' //同上:一张牌未出时恒为 +3(大光),也必须重建
+ });
+ EQW_GameState._apply(EQW_GameState.my, g, { bottomCards: 'bottomcards' });
+ //控制权同上,读服务端给的 seat(与 xuanzhu.nextseat 同源同值)
+ EQW_GameState._apply(EQW_GameState.turn, g, {
+ seat: 'seat',
+ countdown: 'countdown'
+ });
+ },
+
+ _applyPushCards: function (g) {
+ EQW_GameState._apply(EQW_GameState.aset, g, {
+ banker: 'banker',
+ call: 'call',
+ multiple: 'multiple',
+ flower: 'flower',
+ grade: 'grade',
+ curmultiple: 'curmultiple',
+ //报无主:明牌按钮与余主公示的开关,增量路径由 chupai1/2/3 的 baozhu 维护,
+ //重连路径必须从 PushCards.baozhu 一起恢复,否则重连后按钮凭空消失
+ baozhu: 'baozhu'
+ });
+ EQW_GameState._apply(EQW_GameState.my, g, {
+ buryCards: 'burycards',
+ mustCard: 'mustcard'
+ });
+ EQW_GameState._apply(EQW_GameState.table, g, {
+ playproc: 'playproc',
+ pushlist: 'pushlist',
+ seatlist: 'seatlist',
+ liangpai: 'liangpai'
+ });
+ EQW_GameState._apply(EQW_GameState.turn, g, { countdown: 'countdown' });
+ //当前该谁出牌由 playproc.currseat 给出(服务端权威,前端不推导)
+ if (g.playproc && typeof g.playproc.currseat === 'number') {
+ EQW_GameState.turn.seat = g.playproc.currseat;
+ }
+ },
+
+ //结算阶段(step6):Balance 与 jiesuan 推送给的是同一份结算数据(服务端同源快照)。
+ //bottom(抠底明细)只有正常出牌结算才有、account(大局结算)只有末局/解散才有——
+ //缺就不写、保持 reset() 的 null,与 jiesuan 那条路径的取舍完全一致。
+ //【为什么必须接】结算面板还开着时断线重连/硬刷新,只恢复 aset 会让抠底明细与末局大结算空白。
+ _applyBalance: function (g) {
+ EQW_GameState._apply(EQW_GameState.result, g, {
+ aset: 'aset',
+ bottom: 'bottom',
+ account: 'account'
+ });
+ }
+};
diff --git a/client/js/01_SubGame/codes/state/Events.js b/client/js/01_SubGame/codes/state/Events.js
new file mode 100644
index 0000000..93235d3
--- /dev/null
+++ b/client/js/01_SubGame/codes/state/Events.js
@@ -0,0 +1,41 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_Events: 玩法事件常量 ///////////////////////////
+///////////////////////////////////////////////////////////////
+// 前端红线:框架保持游戏中立,玩法专属事件由子游戏追加到 EventBus.Events。
+//
+// 【对局状态不进事件载荷】——凡是 GameState 里有的,订阅者一律从 EQW_GameState 读,
+// 同一份数据不在事件载荷与状态里各存一份(SSOT)。
+// 例外只有「一次性、不属于对局状态、deskinfo 也不下发」的信息,它们不该进 GameState
+// (进了就会立刻制造一处增量/全量不一致),只能随事件载荷走:
+// EQW_RPC_FAILED {rpc, errcode} 一次性错误信息
+// EQW_CARD_PLAYED {order, seat, cards, count?, flower?, cardtype?, shuai?, shuaicuo?}
+// 这一手的落牌信息(一次性表现)。带 ? 的键【缺字段不兜底】,
+// 包里没有就不带;收尾轮由 jiesuan.chupai 带出时只有 {seat, cards, maxseat?}
+var EQW_Events = EQW_Events || {
+
+ EQW_RESET: 'eqw.reset', //新一局发牌,全部清场
+ EQW_HAND_CHANGED: 'eqw.hand_changed', //自己手牌变化
+ EQW_CALL_CHANGED: 'eqw.call_changed', //叫分推进
+ EQW_BANKER_SET: 'eqw.banker_set', //上庄
+ EQW_MAIN_SET: 'eqw.main_set', //选主
+ EQW_BURY_DONE: 'eqw.bury_done', //埋牌完成
+ EQW_CARD_PLAYED: 'eqw.card_played', //有人出牌
+ EQW_TRICK_END: 'eqw.trick_end', //一墩结束;载荷 {seat,cards,maxseat[,grade]},不落 GameState
+ EQW_TURN_CHANGED: 'eqw.turn_changed', //控制权或倒计时变化
+ EQW_MINGPAI: 'eqw.mingpai', //收到明牌数据
+ EQW_TIP: 'eqw.tip', //收到对家提示
+ EQW_ASET_RESULT: 'eqw.aset_result', //小局结算
+ EQW_ACCOUNT_RESULT: 'eqw.account_result', //大局/解散结算
+ EQW_READY_CHANGED: 'eqw.ready_changed', //有人准备
+ EQW_RESYNC_ALL: 'eqw.resync_all', //重连/开局全量重画
+ EQW_RPC_FAILED: 'eqw.rpc_failed' //失败回包 {rpc, errcode}
+};
+
+//追加到框架事件总线的事件表(框架不认识玩法事件,由子游戏注册)
+if (typeof EventBus !== 'undefined' && EventBus.Events) {
+ for (var _eqwEvtKey in EQW_Events) {
+ if (EQW_Events.hasOwnProperty(_eqwEvtKey)) {
+ EventBus.Events[_eqwEvtKey] = EQW_Events[_eqwEvtKey];
+ }
+ }
+}
diff --git a/client/js/01_SubGame/codes/state/GameState.js b/client/js/01_SubGame/codes/state/GameState.js
new file mode 100644
index 0000000..1523e9e
--- /dev/null
+++ b/client/js/01_SubGame/codes/state/GameState.js
@@ -0,0 +1,136 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_GameState: 服务端快照的镜像(前端 SSOT)////////
+///////////////////////////////////////////////////////////////
+// 前端红线「视图 = f(服务端快照)」的落点:本对象只存【服务端下发过的字段】,
+// 不存任何前端算出来的结论。手牌排序、牌面标记、花色统计等由 core/ 的纯函数
+// 在渲染时现算,不落地成状态。
+//
+// 三条硬约束:
+// 1. 只镜像不派生——存进来的必须是服务端给过的
+// 2. 字段名对齐协议——协议叫 currcall 就叫 currcall,便于逐条核对
+// 3. 缺字段不兜底——包里没有的保持原值,不填默认值掩盖;必需字段缺失
+// 则 console.error 并跳过该包(服务端漏发,修在服务端)
+var EQW_GameState = EQW_GameState || {
+
+ //数据分组名(snapshot / reset 按此遍历;新增分组要同步加进来)
+ _GROUPS: ['room', 'aset', 'turn', 'call', 'my', 'table', 'result'],
+
+ //—— 房间级(跨小局)——
+ room: {
+ asetCount: 0, //总局数 ← fapai.asetcount / deskinfo.count / setRoomDes
+ asetIdx: 0, //当前第几局 ← fapai.asetidx / deskinfo.idx
+ mySeat: -1, //自己的座位 ← 平台 C_Player.seat
+ playerScores: [], //三家总积分 ← deskinfo.PlayerInfo / jiesuan.aset.seatlist[].score,跨局不清
+ options: null //roomtype 解析结果
+ },
+
+ //—— 小局级 ——
+ aset: {
+ step: 0, //1叫分 2选主/投降 3埋牌 5出牌 6结算
+ banker: -1,
+ call: -1, //庄家叫分
+ multiple: 0, //基础子数
+ flower: 0, //主牌花色(0 = 未选主)
+ curmultiple: 0, //当前抓分倍数(带符号,服务端权威,前端不自算)
+ grade: 0, //闲家已捡分
+ baozhu: 0, //是否已有人报无主
+ touxiang: 0 //是否允许投降
+ },
+
+ //—— 当前控制权与倒计时(任何阶段都读这里)——
+ turn: {
+ seat: -1, //该谁操作
+ countdown: 0 //展示用秒数;服务端无对应定时器,归零【不做任何界面推进】
+ },
+
+ //—— 叫分过程 ——
+ call: {
+ currcall: 0,
+ calls: [null, null, null] //null 未叫 / 0 不叫 / >0 叫了多少
+ },
+
+ //—— 自己 ——
+ my: {
+ cards: [],
+ mustCard: [], //本轮必出牌(服务端建议,只发给轮到的那家)
+ bottomCards: [], //底牌(庄家恒有;闲家仅 70 分坐庄时有)
+ buryCards: [] //埋牌底牌(仅庄家)
+ },
+
+ //—— 桌面 ——
+ table: {
+ ancard3s: 0, //开底标志(70 分坐庄)
+ playproc: null,
+ pushlist: [],
+ seatlist: [],
+ liangpai: null,
+ mingpai: null
+ },
+
+ //—— 结算 ——
+ //字段集与 deskinfo step6 的 Balance 严格对齐(aset / bottom / account)。
+ //【不存 chupai】收尾墩的「谁最大、得几分」是一次性表现,随 EQW_TRICK_END 事件载荷走,
+ //任何 deskinfo 都不下发它——存进来就是一处服务端不知道的私有对局态(见 ResultHandler / PlayHandler)
+ result: {
+ bottom: null,
+ aset: null,
+ account: null
+ },
+
+ //新一局开局时清空对局态;room.mySeat / room.options / room.playerScores 跨局不变,不清
+ //(playerScores 由 deskinfo.PlayerInfo 或 jiesuan.aset.seatlist[].score 增量更新,见 ResyncHandler / ResultHandler)
+ reset: function () {
+ this.room.asetIdx = 0;
+
+ this.aset = { step: 0, banker: -1, call: -1, multiple: 0, flower: 0,
+ curmultiple: 0, grade: 0, baozhu: 0, touxiang: 0 };
+ this.turn = { seat: -1, countdown: 0 };
+ this.call = { currcall: 0, calls: [null, null, null] };
+ this.my = { cards: [], mustCard: [], bottomCards: [], buryCards: [] };
+ this.table = { ancard3s: 0, playproc: null, pushlist: [], seatlist: [],
+ liangpai: null, mingpai: null };
+ this.result = { bottom: null, aset: null, account: null };
+ },
+
+ //深拷贝快照(只含数据分组,不含方法),供测试做「增量 vs 全量」比对
+ snapshot: function () {
+ var out = {};
+ for (var i = 0; i < this._GROUPS.length; i++) {
+ var g = this._GROUPS[i];
+ out[g] = JSON.parse(JSON.stringify(this[g]));
+ }
+ return out;
+ },
+
+ //把「一手牌」记进出牌历史 table.pushlist。
+ //结构与 deskinfo.PushCards.pushlist 完全一致:外层下标 = 轮次-1,内层恒 3 个数组、
+ //下标 = 座位序号(协议 §断线重连 pushlist)。
+ //
+ //【只记录服务端发来的事实】:谁、出了哪几张。轮次的推进不在这里推导——
+ //startRound 由调用方按【包类型】给出:chupai1 就是「一轮的第一手」(服务端定义的包,
+ //不是前端算出来的),其余各手落进当前这一轮。
+ //
+ //【为什么不能读 chupai3.playproc.cards 直接归档】:服务端 do_playcard 在第三家出完后
+ //就地调用 new_playround,chupai3 带的是【下一轮】的进行态(round+1、cards 全空,
+ //协议 §13 明标),本轮那三手只在 chupai1/2/3 各自的 seat + cards 里。
+ //
+ //【仅可查牌模式累积】:pushlist 是「查牌」历史,不查牌模式下服务端的 deskinfo 压根
+ //不下发它(design §9),前端也不能自己攒一份——否则增量路径会凭空多出重连路径没有的历史。
+ pushPlay: function (seat, cards, startRound) {
+ var opts = this.room.options;
+ if (!opts || opts.nocheck) { return; }
+ var list = this.table.pushlist;
+ if (startRound || list.length === 0) { list.push([[], [], []]); }
+ list[list.length - 1][seat] = cards;
+ },
+
+ //把包里的字段写进目标分组。map = { 目标键: 源键 }。
+ //【缺字段不兜底】源键在包里不存在就不写,保持原值——不填默认值掩盖漏发。
+ _apply: function (target, src, map) {
+ for (var dst in map) {
+ if (!map.hasOwnProperty(dst)) { continue; }
+ var srcKey = map[dst];
+ if (src.hasOwnProperty(srcKey)) { target[dst] = src[srcKey]; }
+ }
+ }
+};
diff --git a/client/js/01_SubGame/codes/state/RoomOptions.js b/client/js/01_SubGame/codes/state/RoomOptions.js
new file mode 100644
index 0000000..99a64df
--- /dev/null
+++ b/client/js/01_SubGame/codes/state/RoomOptions.js
@@ -0,0 +1,34 @@
+///////////////////////////////////////////////////////////////
+////////// EQW_RoomOptions_Parse: roomtype 位串解析 ///////////
+///////////////////////////////////////////////////////////////
+// 协议 §0.5:roomtype 是建房时拼好的定长数字位串(字符串),每一位 '0'/'1'。
+// 服务端解析入口是 class.config.js 的 parse(),本文件必须与之【同规则】。
+//
+// 位0 局数 '0'=6局(缺省) '1'=12局
+// 位1 扣卡 '0'=房主扣卡 '1'=AA 每人扣卡
+// 位2 傍王 '0'=关 '1'=开
+// 位3 爬坡 '0'=常规算子 '1'=爬坡
+// 位4 查牌 '0'=可查牌 '1'=不查牌
+//
+// 对【缺失、非字符串、过短】的 roomtype 一律按 '0' 处理(与服务端一致),
+// 这是协议明文规定的行为,不是隐式兜底。
+var EQW_RoomOptions_Parse = (function() {
+ //取第 idx 位,越界或非字符串时按 '0'
+ function _bit(roomtype, idx) {
+ if (typeof roomtype !== 'string') { return 0; }
+ if (idx >= roomtype.length) { return 0; }
+ return roomtype.charAt(idx) === '1' ? 1 : 0;
+ }
+
+ return {
+ parse: function (roomtype) {
+ return {
+ asetCount: _bit(roomtype, 0) ? 12 : 6,
+ deductAA: _bit(roomtype, 1),
+ bangwang: _bit(roomtype, 2),
+ climb: _bit(roomtype, 3),
+ nocheck: _bit(roomtype, 4)
+ };
+ }
+ };
+})();
diff --git a/client/tests/fixtures/export_packets.js b/client/tests/fixtures/export_packets.js
new file mode 100644
index 0000000..52f7922
--- /dev/null
+++ b/client/tests/fixtures/export_packets.js
@@ -0,0 +1,319 @@
+// 导出脚本(测试代码,跑在 Node,非正式代码):
+// 用服务端 test/_rpc.js + class.desk.js 的既有测试脚手架跑一局完整流程
+//(发牌 fapai → 叫分 jiaofen → 上庄 shangzhuang → 选主 xuanzhu → 埋牌 maipai
+// → 出牌 chupai1/2/3 若干轮 → 结算 jiesuan),把真实下发包按座位快照导出成
+// client/tests/fixtures/packets.json,供前端测试回放(不手写假包,测的是真实契约)。
+//
+// 用法:node client/tests/fixtures/export_packets.js
+//
+// 装配方式参照服务端既有测试的两种建立法:
+// - _rpc.js 的 setup() 只接受"已构造好的 o_paiju",不走 class.desk.js,
+// 因此不会产生 fapai(发牌)包——它是给"单个 RPC 处理器"用的轻量桩。
+// - test_flow.js / test_endgame.js 的 mkRoom() 模式改用真实 class.desk.js
+// 的 D.new()/D.do_new_paiju(),才会真的下发 fapai;本脚本需要完整流程
+// (含 fapai),故照抄这一种既有用法,而不是 setup()。
+// 两条路径殊途同归:都是把 mod.app / mod.import 指向同一个 sent 捕获数组,
+// 区别只在"谁负责建立 o_room/o_desk/o_paiju"。
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+
+// 装配 mod.js 及其依赖类,把关键类挂到 global(与 _rpc.js 内部做法一致)
+const R = require('../../../server/games/erqiwang/test/_rpc.js');
+const mod = R.mod;
+const P = global.cls_youle_erqiwang_paiju;
+const A = global.cls_youle_erqiwang_arith;
+const D = require('../../../server/games/erqiwang/class.desk.js');
+const E = require('../../../server/games/erqiwang/class.export.js').new();
+
+const ROOT = path.resolve(__dirname, '..', '..', '..');
+const OUT = path.join(ROOT, 'client/tests/fixtures/packets.json');
+
+// 常规房:可查牌、不爬坡、不傍王(roomtype 位串全 0,见 class.config.js)
+const ROOMTYPE = '00000';
+
+// 固定种子:只在本导出脚本进程内替换 global.min_random(不改 server/_shim.js),
+// 让「同一份脚本重复运行」产出逐字节相同的 packets.json,便于 review diff 与回归。
+// 写法与服务端既有测试(test_flow.js/test_fuzz.js/test_leak.js)的 xorshift 种子一致。
+// 改这个值会重新生成整局牌(发牌结果、叫分走向、出牌过程全变),产物 diff 会很大。
+const DEAL_SEED = 0x5A5A2026;
+(function seedDeal(seed) {
+ let state = seed >>> 0;
+ const rnd = function (n) {
+ state ^= state << 13; state >>>= 0;
+ state ^= state >>> 17;
+ state ^= state << 5; state >>>= 0;
+ return state % n;
+ };
+ global.min_random = function (min, max) { return min + rnd(max - min + 1); };
+})(DEAL_SEED);
+
+const clone = function (m) { return JSON.parse(JSON.stringify(m)); };
+
+// 装配一个"真实牌桌":D.new() 建牌桌,global.youle_erqiwang.app/import 与
+// mod.app/mod.import 同时指向同一个 sent 捕获数组——class.desk.js 的 fapai/zhunbei
+// 走前者(bare 全局引用),mod.js 的 RPC handler 走后者(mod 自身的模块变量),
+// 两条路径必须都接上,否则 fapai 会走到 _shim.js 的默认空实现,捕获不到。
+function makeRoom(roomtype) {
+ const sent = [];
+ const o_room = {
+ roomtype: roomtype, asetcount: 6, roomcode: 1, createtime: 'T', makewartime: 'T',
+ seatlist: [0, 1, 2].map(function (i) {
+ return { conmode: 0, fromid: i, playerid: 100 + i, nickname: 'P' + i, avatar: '', gameinfo: {} };
+ }),
+ method: { sendpack_toother: function (m) { sent.push(clone(m)); } }
+ };
+ const desk = D.new(o_room);
+ o_room.o_desk = desk;
+ global.youle_erqiwang.app = { SendPack: function (m) { sent.push(clone(m)); } };
+ global.youle_erqiwang.import = { check_player: function () { return o_room; }, deduct_roomcard: function () { }, save_grade: function () { } };
+ mod.import = global.youle_erqiwang.import;
+ mod.app = global.youle_erqiwang.app;
+ return { o_room: o_room, desk: desk, sent: sent };
+}
+
+const pk = function (seat, extra) {
+ return {
+ conmode: 0, fromid: seat,
+ data: Object.assign({ agentid: 'a', playerid: seat, gameid: 'g', roomcode: 1, seat: seat }, extra || {})
+ };
+};
+
+const room = makeRoom(ROOMTYPE);
+const o_room = room.o_room;
+const desk = room.desk;
+const sent = room.sent;
+
+// ---- 按座位收集下发包(保留差异化下发:谁收到什么、谁没收到) ----
+// sent 里每一项要么带明确的 fromid(0/1/2,逐座位分别下发,内容可能互不相同),
+// 要么是 sendpack_toother(msg, -1) 的整体广播(未逐座位改写 conmode/fromid,
+// fromid 为 undefined)——广播内容三家相同,按"三家都收到"处理。
+const seatPackets = [[], [], []];
+const deskinfo = [];
+let drained = 0;
+function drain() {
+ for (; drained < sent.length; drained++) {
+ const raw = sent[drained];
+ const packet = { rpc: raw.rpc, data: raw.data };
+ if (raw.fromid === 0 || raw.fromid === 1 || raw.fromid === 2) {
+ seatPackets[raw.fromid].push(packet);
+ } else {
+ seatPackets[0].push(packet);
+ seatPackets[1].push(packet);
+ seatPackets[2].push(packet);
+ }
+ }
+}
+// packetIndex 是关键对齐锚点:取快照时先 drain,记下该座位此刻已收到的包数,
+// 与 Task 12 一致性测试"喂完 seats[seat] 的前 packetIndex 个包"对齐。
+//
+// 【必须 clone】get_deskinfo 的返回值里仍有若干【无拷贝的活引用赋值】——
+// 例如 Balance.readystate = o_desk.prepare、Balance.aset/bottom/account 三份结算快照。
+// (PushCards.playproc / PushCards.seatlist / pushlist / burycards 已各自走深拷贝快照函数,
+// 这段注释早先把它们也算在内,已过时,现更正。)
+// 真实链路里这份返回值被平台立刻序列化下发,活引用不会被观测到;但本脚本要跑完整局之后才
+// 统一 JSON.stringify 写盘,若不在这里当场拷贝一份,写盘时这些字段会被后续操作篡改成终局
+// 状态,导致同一座位不同 packetIndex 下的快照"逐字节相同"——这是本脚本自身的缺陷,与服务端
+// 无关(服务端没有"延后序列化"这个用法)。
+function snap(step, seat) {
+ drain();
+ deskinfo.push({
+ step: step,
+ seat: seat,
+ packetIndex: seatPackets[seat].length,
+ info: clone(E.get_deskinfo(o_room, seat))
+ });
+}
+
+// ================= 跑一局 =================
+
+// 发牌(fapai):class.desk.js 内部随机发牌 + 广播
+D.do_new_paiju(desk, 0);
+const pj = desk.method.curr_paiju();
+
+// StartWar(开局握手,前端 04/05 讲的"增量路径真实起点"):真实链路里客户端开局收到的
+// 第一包,结构就是 deskwar —— 即 get_deskinfo() 的返回值(见 server/youle/server_room/
+// rpc.js 里 pack.data.deskwar = youle_room.import.makewar_deskwar(o_room) 一段)。
+// 当前 erqiwang 的 exp.makewar(class.export.js)尚未把 get_deskinfo 接成返回值,这是
+// 另一项任务;本脚本独立调用 get_deskinfo 反映"接线后客户端此刻会收到什么",让前端测试
+// 能提前对齐这条起点(尤其是 room.playerScores 这类 fapai 包不带、只有 StartWar 能给的初值)。
+// 与紧随其后的 step1 快照取同一时刻(do_new_paiju 之后、任何 jiaofen 之前),按座位各存
+// 一份(三家 MyCards 视角不同),套一层 { deskwar: ... } 外壳与生产环境 pack.data.deskwar
+// 的包裹方式对齐,供 EQW_ResyncHandler.handleStartWar 直接消费。
+const startwar = [0, 1, 2].map(function (seat) {
+ return { deskwar: clone(E.get_deskinfo(o_room, seat)) };
+});
+
+snap(1, 0); snap(1, 1); snap(1, 2); // 叫分阶段(step1):三家快照,验证 MyCards 各不相同
+
+// 叫分:0 号位叫 65,其余不叫 → 0 号位上庄(触发 jiaofen 广播 + shangzhuang 差异化下发)
+mod.jiaofen(pk(pj.method.get_callgrade_seat(), { call: 65 }));
+mod.jiaofen(pk(pj.method.get_callgrade_seat(), { call: 0 }));
+mod.jiaofen(pk(pj.method.get_callgrade_seat(), { call: 0 }));
+snap(2, 0); snap(2, 1); snap(2, 2); // 选主阶段(step2):庄家有 cards/bottomcards,闲家没有
+
+// 选主:庄家选方块为主
+mod.xuanzhu(pk(pj.banker, { flower: 1 }));
+snap(3, pj.banker); // 埋牌阶段(step3):庄家视角
+
+// 埋牌:庄家埋掉当前手上最后 8 张(get_seat_cards 已按主牌排序,任取8张即可)
+mod.maipai(pk(pj.banker, { cards: P.get_seat_cards(pj, pj.banker).slice(-8) }));
+snap(5, 0); snap(5, 1); snap(5, 2); // 出牌阶段开局(step5):出牌者 cardsinhand、下一位 mustcard 差异
+
+// ================= 出牌策略 =================
+// 【为什么不再是"全出单张"】:单张一手无所谓顺序,服务端「这一手打出的牌」的两个顺序口径
+// (chupai/playproc.cards 与 deskinfo.PushCards.pushlist)在单张局面下永远相等,夹具测不出
+// 分岔。甩牌(design §5.4)与领对子会产生"一手多张",才真正压到这条契约。
+//
+// 策略(确定性,不含随机):
+// 首家:优先甩牌(多分量主牌,且服务端 can_playcard 判定合法)→ 其次领最小的一对
+// → 否则最小单张
+// 跟家:先取 get_followcard 的必出牌,再从可出牌里补足张数,逐个组合试到
+// can_followcard(+ 甩牌时的 flush_follow_ok)通过为止
+//
+// 【提交顺序刻意用升序】:真实客户端提交的是玩家的点击顺序,服务端不得依赖它
+//(server 04 §8「前端不是数据源」;class.arith.js can_followcard 里也有同样的告诫)。
+// 服务端的权威展示顺序是 order_cards 的【降序】,所以这里一律把选好的一手【反转成升序】
+// 再提交,模拟"玩家从小到大点选"。这不是伪造数据——包仍然是服务端真实产出的,
+// 只是让请求侧的顺序与权威顺序刻意不同,把"下发顺序是否被请求顺序污染"暴露出来。
+const asc = function (cards) {
+ return A.order_cards(pj.flower, cards.concat()).reverse();
+};
+
+// 两名对手未出的主牌(供甩牌最大性判定),与 class.paiju.js do_playcard 的取法一致
+function oppZhuList(seat) {
+ const list = [];
+ for (let os = 0; os < 3; os++) {
+ if (os !== seat) { list.push(P.get_seat_zhucards(pj, os)); }
+ }
+ return list;
+}
+
+// 首家:尝试甩牌——把手上主牌分解成分量,按分量牌力从大到小取前 k 个(k 从多到少),
+// 交给服务端 can_playcard 判定;只接受 result===true 的(合法甩牌),
+// 不提交会被判"甩错"的组合(那会被服务端收回成单张,反而回到单张局面)。
+// 注意 can_playcard 会【就地排序】入参数组,故一律传副本。
+function tryShuai(seat, hand) {
+ const trumps = hand.filter(function (c) { return A.id_to_code(pj.flower, c) > 1000; });
+ if (trumps.length < 2) { return null; }
+ const comps = A.decompose_trump(pj.flower, trumps).slice()
+ .sort(function (a, b) { return b.value - a.value; });
+ const opp = oppZhuList(seat);
+ for (let k = Math.min(comps.length, 3); k >= 2; k--) {
+ let pick = [];
+ for (let i = 0; i < k; i++) { pick = pick.concat(comps[i].cards); }
+ if (A.can_playcard(pj.flower, pick.concat(), seat, pj.seatlist, opp).result) {
+ return pick;
+ }
+ }
+ return null;
+}
+
+// 首家:尝试领一对。取【最大的一对】——甩牌合法的前提是两名对手都压不过任一分量
+//(§5.4.2 opp_can_beat_flush),大牌先走能更快拔掉对手的主牌,后续局面才会出现合法甩牌。
+// 这不是"为了凑测试"的取巧:本夹具的目的就是覆盖甩牌,而甩牌本来就只在这种局面下成立。
+// 同一颗种子下:领最小的一对 → 全局只有 1 手合法甩牌;领最大的一对 → 4 手。
+function tryLeadPair(seat, hand) {
+ const pairs = A.get_pairlist(pj.flower, hand.concat());
+ if (!pairs.length) { return null; }
+ const pick = pairs[0].concat();
+ if (A.can_playcard(pj.flower, pick.concat(), seat, pj.seatlist, oppZhuList(seat)).result) {
+ return pick;
+ }
+ return null;
+}
+
+// 跟家:这一手是否被服务端接受(与 class.paiju.js do_playcard 的跟牌校验同一组判定)
+function followOk(hand, cand) {
+ const proc = pj.playproc;
+ if (!A.can_followcard(pj.flower, hand, cand, proc.startcount, proc.startflower, proc.starttype).result) {
+ return false;
+ }
+ if (proc.shuai_demand && !A.flush_follow_ok(pj.flower, hand, cand, proc.shuai_demand)) {
+ return false;
+ }
+ return true;
+}
+
+// 跟家:凑出 startcount 张的合法跟牌
+function pickFollow(seat, hand) {
+ const proc = pj.playproc;
+ const n = proc.startcount;
+ const get = A.get_followcard(pj.flower, hand.concat(), n, proc.startflower, proc.starttype);
+ const base = (get && get.mustcard) ? get.mustcard.concat() : [];
+ if (base.length === n) {
+ return followOk(hand, base) ? base : null;
+ }
+ const need = n - base.length;
+ if (need < 0) { return null; }
+ // 候选池:优先服务端给出的"可出的牌",为空则退回整手牌
+ let pool = (get && get.cancard && get.cancard.length) ? get.cancard.concat() : hand.concat();
+ pool = pool.filter(function (c) { return base.indexOf(c) < 0; });
+ let found = null;
+ const combo = [];
+ (function walk(start) {
+ if (found) { return; }
+ if (combo.length === need) {
+ const cand = base.concat(combo);
+ if (followOk(hand, cand)) { found = cand; }
+ return;
+ }
+ for (let i = start; i < pool.length && !found; i++) {
+ combo.push(pool[i]);
+ walk(i + 1);
+ combo.pop();
+ }
+ })(0);
+ return found;
+}
+
+// 打到一半时的 step5 快照锚点(按已出手数计)。
+// 【为什么要不止一个】:出牌阶段开局那三张 step5 快照的 pushlist 都还是空的,
+// 只有"打到一半"的快照才真的比对到出牌历史(一致性测试里唯一压到 pushlist 的点)。
+// 一个锚点=一个比对点,太薄;多取几个覆盖不同轮次/不同座位/轮中不同位次。
+const MID_SNAP_AT = [4, 9, 16, 25, 34];
+let guard = 0;
+let shuaiHands = 0; // 统计:合法甩牌手数
+let multiHands = 0; // 统计:一手多张的手数(含跟牌)
+while (pj.step === 5 && guard < 400) {
+ guard++;
+ const seat = pj.playproc.currseat;
+ const hand = P.get_seat_cards(pj, seat);
+ let pick = null;
+ if (seat === pj.playproc.start) {
+ pick = tryShuai(seat, hand);
+ if (pick) { shuaiHands++; }
+ if (!pick) { pick = tryLeadPair(seat, hand); }
+ if (!pick) { pick = [hand[hand.length - 1]]; }
+ } else {
+ pick = pickFollow(seat, hand);
+ }
+ // 找不到合法出牌属脚本缺陷(服务端保证当前座位总有牌可出),显式失败、不静默跳过
+ if (!pick) {
+ throw new Error('export_packets: 座位 ' + seat + ' 第 ' + guard + ' 手凑不出合法出牌');
+ }
+ if (pick.length > 1) { multiHands++; }
+ mod.chupai(pk(seat, { cards: asc(pick) }));
+ // 出牌进行到中段时额外拍 step5 快照,覆盖"打到一半"的重连场景
+ if (MID_SNAP_AT.indexOf(guard) >= 0 && pj.step === 5) {
+ snap(5, pj.playproc.currseat);
+ }
+}
+snap(6, 0); snap(6, 1); snap(6, 2); // 结算阶段(step6):jiesuan 广播后
+
+drain(); // 保险:吸收循环末尾可能残留的未 drain 包
+
+const fixture = {
+ roomtype: ROOMTYPE,
+ startwar: startwar,
+ seats: seatPackets,
+ deskinfo: deskinfo
+};
+
+fs.writeFileSync(OUT, JSON.stringify(fixture, null, 2));
+console.log('已写入 ' + OUT);
+console.log('座位包数: ' + seatPackets.map(function (a) { return a.length; }).join(' / '));
+console.log('deskinfo 快照数: ' + deskinfo.length);
+console.log('出牌手数: ' + guard + '(其中一手多张 ' + multiHands + ' 手,合法甩牌 ' + shuaiHands + ' 手)');
+console.log('paiju.step(结束时)=' + pj.step + ' result=' + pj.result);
diff --git a/client/tests/fixtures/packets.json b/client/tests/fixtures/packets.json
new file mode 100644
index 0000000..4a47914
--- /dev/null
+++ b/client/tests/fixtures/packets.json
@@ -0,0 +1,19669 @@
+{
+ "roomtype": "00000",
+ "startwar": [
+ {
+ "deskwar": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 1,
+ "MyCards": [
+ 52,
+ 19,
+ 94,
+ 68,
+ 14,
+ 55,
+ 93,
+ 105,
+ 103,
+ 102,
+ 46,
+ 92,
+ 37,
+ 36,
+ 90,
+ 67,
+ 75,
+ 18,
+ 72,
+ 71,
+ 54,
+ 0,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59
+ ],
+ "CallRun": {
+ "seat": 0,
+ "countdown": 15,
+ "nowcall": 0,
+ "multiple": 0,
+ "call": [
+ null,
+ null,
+ null
+ ]
+ }
+ }
+ },
+ {
+ "deskwar": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 1,
+ "MyCards": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17,
+ 12,
+ 11,
+ 65,
+ 9,
+ 8,
+ 5,
+ 58,
+ 4
+ ],
+ "CallRun": {
+ "seat": 0,
+ "countdown": 15,
+ "nowcall": 0,
+ "multiple": 0,
+ "call": [
+ null,
+ null,
+ null
+ ]
+ }
+ }
+ },
+ {
+ "deskwar": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 1,
+ "MyCards": [
+ 53,
+ 107,
+ 45,
+ 86,
+ 32,
+ 6,
+ 60,
+ 40,
+ 27,
+ 1,
+ 39,
+ 104,
+ 44,
+ 98,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74,
+ 61
+ ],
+ "CallRun": {
+ "seat": 0,
+ "countdown": 15,
+ "nowcall": 0,
+ "multiple": 0,
+ "call": [
+ null,
+ null,
+ null
+ ]
+ }
+ }
+ }
+ ],
+ "seats": [
+ [
+ {
+ "rpc": "fapai",
+ "data": {
+ "success": true,
+ "asetidx": 1,
+ "asetcount": 6,
+ "step": 1,
+ "seat": 0,
+ "countdown": 15,
+ "cards": [
+ 52,
+ 19,
+ 94,
+ 68,
+ 14,
+ 55,
+ 93,
+ 105,
+ 103,
+ 102,
+ 46,
+ 92,
+ 37,
+ 36,
+ 90,
+ 67,
+ 75,
+ 18,
+ 72,
+ 71,
+ 54,
+ 0,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59
+ ]
+ }
+ },
+ {
+ "rpc": "jiaofen",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "call": 65,
+ "currcall": 65,
+ "multiple": 2,
+ "step": 1,
+ "nextseat": 1,
+ "countdown": 15
+ }
+ },
+ {
+ "rpc": "jiaofen",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "call": 0,
+ "currcall": 65,
+ "multiple": 2,
+ "step": 1,
+ "nextseat": 2,
+ "countdown": 15
+ }
+ },
+ {
+ "rpc": "shangzhuang",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "call": 0,
+ "banker": 0,
+ "grade": 65,
+ "multiple": 2,
+ "step": 2,
+ "nextseat": 0,
+ "countdown": 20,
+ "touxiang": 0,
+ "curmultiple": 3,
+ "cards": [
+ 52,
+ 19,
+ 94,
+ 68,
+ 14,
+ 55,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33,
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 72,
+ 18,
+ 71,
+ 0,
+ 54,
+ 66,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59
+ ],
+ "bottomcards": [
+ 51,
+ 101,
+ 100,
+ 33,
+ 79,
+ 23,
+ 20,
+ 66
+ ]
+ }
+ },
+ {
+ "rpc": "xuanzhu",
+ "data": {
+ "success": true,
+ "banker": 0,
+ "flower": 1,
+ "step": 3,
+ "nextseat": 0,
+ "countdown": 25,
+ "cards": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 68,
+ 14,
+ 54,
+ 0,
+ 66,
+ 64,
+ 10,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33,
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 72,
+ 18,
+ 71
+ ]
+ }
+ },
+ {
+ "rpc": "maipai",
+ "data": {
+ "success": true,
+ "step": 5,
+ "seat": 0,
+ "countdown": 30,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "cards": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 68,
+ 14,
+ 54,
+ 0,
+ 66,
+ 64,
+ 10,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ],
+ "burycards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 14,
+ 68
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 2202,
+ "cards": [
+ [
+ 14,
+ 68
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201,
+ "cardsinhand": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 0,
+ 54,
+ 66,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 65,
+ 11
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 2202,
+ "cards": [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 107,
+ 53
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 60,
+ 6
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ null,
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 54,
+ 0
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ [
+ 54,
+ 0
+ ],
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "cardsinhand": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 66,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 58,
+ 4
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2,
+ "grade": 10
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 86,
+ 32
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 7307,
+ "cards": [
+ null,
+ null,
+ [
+ 86,
+ 32
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201,
+ "mustcard": [
+ 10,
+ 64
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 64,
+ 10
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 7307,
+ "cards": [
+ [
+ 64,
+ 10
+ ],
+ null,
+ [
+ 86,
+ 32
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "cardsinhand": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 66,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 106,
+ 99
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 44,
+ 98
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 406,
+ "cards": [
+ null,
+ null,
+ [
+ 44,
+ 98
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 4,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 105,
+ 51
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 413,
+ "cards": [
+ [
+ 105,
+ 51
+ ],
+ null,
+ [
+ 44,
+ 98
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "cardsinhand": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 66,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 103,
+ 102,
+ 101,
+ 100,
+ 46,
+ 92,
+ 37,
+ 90,
+ 36,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 50,
+ 49
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 52,
+ 19
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 7207,
+ "cards": [
+ [
+ 52,
+ 19
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ },
+ "cardsinhand": [
+ 55,
+ 94,
+ 66,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 103,
+ 102,
+ 101,
+ 100,
+ 46,
+ 92,
+ 37,
+ 90,
+ 36,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 73,
+ 81
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 7207,
+ "cards": [
+ [
+ 52,
+ 19
+ ],
+ [
+ 73,
+ 81
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 45,
+ 1
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 55,
+ 94
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ },
+ "cardsinhand": [
+ 66,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 103,
+ 102,
+ 101,
+ 100,
+ 46,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 12,
+ 9
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ [
+ 12,
+ 9
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 40,
+ 27
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 66,
+ 63,
+ 62
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 3,
+ "startflower": 1,
+ "starttype": 103,
+ "maxseat": 0,
+ "maxcard": 1109,
+ "cards": [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ },
+ "baozhu": 0,
+ "count": 3,
+ "flower": 1,
+ "cardtype": 103,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ },
+ "cardsinhand": [
+ 7,
+ 59,
+ 93,
+ 103,
+ 102,
+ 101,
+ 100,
+ 46,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 8,
+ 5,
+ 48
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 3,
+ "startflower": 1,
+ "starttype": 103,
+ "maxseat": 0,
+ "maxcard": 1109,
+ "cards": [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ [
+ 8,
+ 5,
+ 48
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 61,
+ 39,
+ 104
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 7,
+ 59
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 1106,
+ "cards": [
+ [
+ 7,
+ 59
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ },
+ "cardsinhand": [
+ 93,
+ 103,
+ 102,
+ 101,
+ 100,
+ 46,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 47,
+ 80
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 1106,
+ "cards": [
+ [
+ 7,
+ 59
+ ],
+ [
+ 47,
+ 80
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 97,
+ 43
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 46,
+ 100
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 4,
+ "cardtype": 201,
+ "cardsinhand": [
+ 93,
+ 103,
+ 102,
+ 101,
+ 92,
+ 37,
+ 90,
+ 36,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 88,
+ 34
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ [
+ 88,
+ 34
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 26,
+ 38
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 36,
+ 90
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 3,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 311,
+ "cards": [
+ [
+ 36,
+ 90
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 3,
+ "cardtype": 201,
+ "cardsinhand": [
+ 93,
+ 103,
+ 102,
+ 101,
+ 92,
+ 37,
+ 33
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 87,
+ 31
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 3,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 311,
+ "cards": [
+ [
+ 36,
+ 90
+ ],
+ [
+ 87,
+ 31
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 35,
+ 89
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 33
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 308,
+ "cards": [
+ [
+ 33
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101,
+ "cardsinhand": [
+ 93,
+ 103,
+ 102,
+ 101,
+ 92,
+ 37
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 84
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 308,
+ "cards": [
+ [
+ 33
+ ],
+ [
+ 84
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 91
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 5
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 74
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ null,
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 93
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ [
+ 93
+ ],
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 103,
+ 102,
+ 101,
+ 92,
+ 37
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 24
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 1,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 1
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 17
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 1,
+ "maxcard": 205,
+ "cards": [
+ null,
+ [
+ 17
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 13
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 214,
+ "cards": [
+ null,
+ [
+ 17
+ ],
+ [
+ 13
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 103
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 5,
+ "cardsinhand": [
+ 102,
+ 101,
+ 92,
+ 37
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 76
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 210,
+ "cards": [
+ null,
+ null,
+ [
+ 76
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 102
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 210,
+ "cards": [
+ [
+ 102
+ ],
+ null,
+ [
+ 76
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 101,
+ 92,
+ 37
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 78
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 1,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 1,
+ "grade": 20
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 21
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 1,
+ "maxcard": 209,
+ "cards": [
+ null,
+ [
+ 21
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 25
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 213,
+ "cards": [
+ null,
+ [
+ 21
+ ],
+ [
+ 25
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 101
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 10,
+ "cardsinhand": [
+ 92,
+ 37
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 30
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 305,
+ "cards": [
+ null,
+ null,
+ [
+ 30
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 92
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 313,
+ "cards": [
+ [
+ 92
+ ],
+ null,
+ [
+ 30
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 37
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 77
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 37
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 312,
+ "cards": [
+ [
+ 37
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101,
+ "cardsinhand": []
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 22
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 312,
+ "cards": [
+ [
+ 37
+ ],
+ [
+ 22
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "jiesuan",
+ "data": {
+ "success": true,
+ "curmultiple": 1,
+ "chupai": {
+ "seat": 2,
+ "cards": [
+ 85
+ ],
+ "maxseat": 0
+ },
+ "bottom": {
+ "cards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ },
+ "step": 6,
+ "aset": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "flower": 1,
+ "grade": 50,
+ "upgrade": 1,
+ "bangwang": 0,
+ "climb": 0,
+ "seatlist": [
+ {
+ "cards": [
+ 52
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": 4,
+ "grade": 4,
+ "score": 4
+ },
+ {
+ "cards": [
+ 106
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ },
+ {
+ "cards": [
+ 53,
+ 107
+ ],
+ "chongguan": 0,
+ "wang": 2,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ }
+ ]
+ }
+ }
+ }
+ ],
+ [
+ {
+ "rpc": "fapai",
+ "data": {
+ "success": true,
+ "asetidx": 1,
+ "asetcount": 6,
+ "step": 1,
+ "seat": 0,
+ "countdown": 15,
+ "cards": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17,
+ 12,
+ 11,
+ 65,
+ 9,
+ 8,
+ 5,
+ 58,
+ 4
+ ]
+ }
+ },
+ {
+ "rpc": "jiaofen",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "call": 65,
+ "currcall": 65,
+ "multiple": 2,
+ "step": 1,
+ "nextseat": 1,
+ "countdown": 15
+ }
+ },
+ {
+ "rpc": "jiaofen",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "call": 0,
+ "currcall": 65,
+ "multiple": 2,
+ "step": 1,
+ "nextseat": 2,
+ "countdown": 15
+ }
+ },
+ {
+ "rpc": "shangzhuang",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "call": 0,
+ "banker": 0,
+ "grade": 65,
+ "multiple": 2,
+ "step": 2,
+ "nextseat": 0,
+ "countdown": 20,
+ "touxiang": 0,
+ "curmultiple": 3
+ }
+ },
+ {
+ "rpc": "xuanzhu",
+ "data": {
+ "success": true,
+ "banker": 0,
+ "flower": 1,
+ "step": 3,
+ "nextseat": 0,
+ "countdown": 25,
+ "cards": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 12,
+ 11,
+ 65,
+ 9,
+ 8,
+ 5,
+ 58,
+ 4,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "maipai",
+ "data": {
+ "success": true,
+ "step": 5,
+ "seat": 0,
+ "countdown": 30,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ }
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 14,
+ 68
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 2202,
+ "cards": [
+ [
+ 14,
+ 68
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 65,
+ 11
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 2202,
+ "cards": [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "cardsinhand": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 12,
+ 9,
+ 8,
+ 5,
+ 4,
+ 58,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 107,
+ 53
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 60,
+ 6
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ null,
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 54,
+ 0
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ [
+ 54,
+ 0
+ ],
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "mustcard": [
+ 4,
+ 58
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 58,
+ 4
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2,
+ "grade": 10,
+ "cardsinhand": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 12,
+ 9,
+ 8,
+ 5,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 86,
+ 32
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 7307,
+ "cards": [
+ null,
+ null,
+ [
+ 86,
+ 32
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 64,
+ 10
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 7307,
+ "cards": [
+ [
+ 64,
+ 10
+ ],
+ null,
+ [
+ 86,
+ 32
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 106,
+ 99
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2,
+ "cardsinhand": [
+ 73,
+ 81,
+ 12,
+ 9,
+ 8,
+ 5,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 44,
+ 98
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 406,
+ "cards": [
+ null,
+ null,
+ [
+ 44,
+ 98
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 4,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 105,
+ 51
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 413,
+ "cards": [
+ [
+ 105,
+ 51
+ ],
+ null,
+ [
+ 44,
+ 98
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 50,
+ 49
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0,
+ "cardsinhand": [
+ 73,
+ 81,
+ 12,
+ 9,
+ 8,
+ 5,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 52,
+ 19
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 7207,
+ "cards": [
+ [
+ 52,
+ 19
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 73,
+ 81
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 7207,
+ "cards": [
+ [
+ 52,
+ 19
+ ],
+ [
+ 73,
+ 81
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "cardsinhand": [
+ 12,
+ 9,
+ 8,
+ 5,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 24,
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 45,
+ 1
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 55,
+ 94
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 12,
+ 9
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ [
+ 12,
+ 9
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "cardsinhand": [
+ 8,
+ 5,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 24,
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 40,
+ 27
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 66,
+ 63,
+ 62
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 3,
+ "startflower": 1,
+ "starttype": 103,
+ "maxseat": 0,
+ "maxcard": 1109,
+ "cards": [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ },
+ "baozhu": 0,
+ "count": 3,
+ "flower": 1,
+ "cardtype": 103,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 8,
+ 5,
+ 48
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 3,
+ "startflower": 1,
+ "starttype": 103,
+ "maxseat": 0,
+ "maxcard": 1109,
+ "cards": [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ [
+ 8,
+ 5,
+ 48
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 24,
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 61,
+ 39,
+ 104
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 7,
+ 59
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 1106,
+ "cards": [
+ [
+ 7,
+ 59
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 47,
+ 80
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 1106,
+ "cards": [
+ [
+ 7,
+ 59
+ ],
+ [
+ 47,
+ 80
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 97,
+ 43
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 46,
+ 100
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 4,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 88,
+ 34
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ [
+ 88,
+ 34
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 87,
+ 31,
+ 84,
+ 24,
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 26,
+ 38
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 36,
+ 90
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 3,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 311,
+ "cards": [
+ [
+ 36,
+ 90
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 3,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 87,
+ 31
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 3,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 311,
+ "cards": [
+ [
+ 36,
+ 90
+ ],
+ [
+ 87,
+ 31
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 35,
+ 89
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 33
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 308,
+ "cards": [
+ [
+ 33
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101,
+ "mustcard": [
+ 84
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 84
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 308,
+ "cards": [
+ [
+ 33
+ ],
+ [
+ 84
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 24,
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 91
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 5
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 74
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ null,
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 93
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ [
+ 93
+ ],
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 24
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 1,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 1,
+ "cardsinhand": [
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 17
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 1,
+ "maxcard": 205,
+ "cards": [
+ null,
+ [
+ 17
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101,
+ "cardsinhand": [
+ 78,
+ 77,
+ 22,
+ 21
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 13
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 214,
+ "cards": [
+ null,
+ [
+ 17
+ ],
+ [
+ 13
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 103
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 5
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 76
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 210,
+ "cards": [
+ null,
+ null,
+ [
+ 76
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 102
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 210,
+ "cards": [
+ [
+ 102
+ ],
+ null,
+ [
+ 76
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 78
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 1,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 1,
+ "grade": 20,
+ "cardsinhand": [
+ 77,
+ 22,
+ 21
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 21
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 1,
+ "maxcard": 209,
+ "cards": [
+ null,
+ [
+ 21
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101,
+ "cardsinhand": [
+ 77,
+ 22
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 25
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 213,
+ "cards": [
+ null,
+ [
+ 21
+ ],
+ [
+ 25
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 101
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 10
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 30
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 305,
+ "cards": [
+ null,
+ null,
+ [
+ 30
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 92
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 313,
+ "cards": [
+ [
+ 92
+ ],
+ null,
+ [
+ 30
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 77
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0,
+ "cardsinhand": [
+ 22
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 37
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 312,
+ "cards": [
+ [
+ 37
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101,
+ "mustcard": [
+ 22
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 22
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 312,
+ "cards": [
+ [
+ 37
+ ],
+ [
+ 22
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": []
+ }
+ },
+ {
+ "rpc": "jiesuan",
+ "data": {
+ "success": true,
+ "curmultiple": 1,
+ "chupai": {
+ "seat": 2,
+ "cards": [
+ 85
+ ],
+ "maxseat": 0
+ },
+ "bottom": {
+ "cards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ },
+ "step": 6,
+ "aset": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "flower": 1,
+ "grade": 50,
+ "upgrade": 1,
+ "bangwang": 0,
+ "climb": 0,
+ "seatlist": [
+ {
+ "cards": [
+ 52
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": 4,
+ "grade": 4,
+ "score": 4
+ },
+ {
+ "cards": [
+ 106
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ },
+ {
+ "cards": [
+ 53,
+ 107
+ ],
+ "chongguan": 0,
+ "wang": 2,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ }
+ ]
+ }
+ }
+ }
+ ],
+ [
+ {
+ "rpc": "fapai",
+ "data": {
+ "success": true,
+ "asetidx": 1,
+ "asetcount": 6,
+ "step": 1,
+ "seat": 0,
+ "countdown": 15,
+ "cards": [
+ 53,
+ 107,
+ 45,
+ 86,
+ 32,
+ 6,
+ 60,
+ 40,
+ 27,
+ 1,
+ 39,
+ 104,
+ 44,
+ 98,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74,
+ 61
+ ]
+ }
+ },
+ {
+ "rpc": "jiaofen",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "call": 65,
+ "currcall": 65,
+ "multiple": 2,
+ "step": 1,
+ "nextseat": 1,
+ "countdown": 15
+ }
+ },
+ {
+ "rpc": "jiaofen",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "call": 0,
+ "currcall": 65,
+ "multiple": 2,
+ "step": 1,
+ "nextseat": 2,
+ "countdown": 15
+ }
+ },
+ {
+ "rpc": "shangzhuang",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "call": 0,
+ "banker": 0,
+ "grade": 65,
+ "multiple": 2,
+ "step": 2,
+ "nextseat": 0,
+ "countdown": 20,
+ "touxiang": 0,
+ "curmultiple": 3
+ }
+ },
+ {
+ "rpc": "xuanzhu",
+ "data": {
+ "success": true,
+ "banker": 0,
+ "flower": 1,
+ "step": 3,
+ "nextseat": 0,
+ "countdown": 25,
+ "cards": [
+ 53,
+ 107,
+ 60,
+ 6,
+ 45,
+ 86,
+ 32,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 44,
+ 98,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "maipai",
+ "data": {
+ "success": true,
+ "step": 5,
+ "seat": 0,
+ "countdown": 30,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ }
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 14,
+ 68
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 2202,
+ "cards": [
+ [
+ 14,
+ 68
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 65,
+ 11
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 2202,
+ "cards": [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 107,
+ 53
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2,
+ "cardsinhand": [
+ 6,
+ 60,
+ 45,
+ 32,
+ 86,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 98,
+ 44,
+ 43,
+ 97,
+ 26,
+ 38,
+ 91,
+ 35,
+ 89,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 60,
+ 6
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ null,
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201,
+ "cardsinhand": [
+ 45,
+ 32,
+ 86,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 98,
+ 44,
+ 43,
+ 97,
+ 26,
+ 38,
+ 91,
+ 35,
+ 89,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 54,
+ 0
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ [
+ 54,
+ 0
+ ],
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 58,
+ 4
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2,
+ "grade": 10
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 86,
+ 32
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 7307,
+ "cards": [
+ null,
+ null,
+ [
+ 86,
+ 32
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 201,
+ "cardsinhand": [
+ 45,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 98,
+ 44,
+ 43,
+ 97,
+ 26,
+ 38,
+ 91,
+ 35,
+ 89,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 64,
+ 10
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 3,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 7307,
+ "cards": [
+ [
+ 64,
+ 10
+ ],
+ null,
+ [
+ 86,
+ 32
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 106,
+ 99
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 2
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 44,
+ 98
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 406,
+ "cards": [
+ null,
+ null,
+ [
+ 44,
+ 98
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 4,
+ "cardtype": 201,
+ "cardsinhand": [
+ 45,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 105,
+ 51
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 413,
+ "cards": [
+ [
+ 105,
+ 51
+ ],
+ null,
+ [
+ 44,
+ 98
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 50,
+ 49
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 52,
+ 19
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 7207,
+ "cards": [
+ [
+ 52,
+ 19
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 73,
+ 81
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 5,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 7207,
+ "cards": [
+ [
+ 52,
+ 19
+ ],
+ [
+ 73,
+ 81
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 45,
+ 1
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0,
+ "cardsinhand": [
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 55,
+ 94
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 12,
+ 9
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ [
+ 12,
+ 9
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 40,
+ 27
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "maxseat": 0,
+ "cardsinhand": [
+ 61,
+ 39,
+ 104,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 66,
+ 63,
+ 62
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 3,
+ "startflower": 1,
+ "starttype": 103,
+ "maxseat": 0,
+ "maxcard": 1109,
+ "cards": [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ },
+ "baozhu": 0,
+ "count": 3,
+ "flower": 1,
+ "cardtype": 103,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 8,
+ 5,
+ 48
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 7,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 3,
+ "startflower": 1,
+ "starttype": 103,
+ "maxseat": 0,
+ "maxcard": 1109,
+ "cards": [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ [
+ 8,
+ 5,
+ 48
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 3
+ }
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 61,
+ 39,
+ 104
+ ],
+ "seatlist": [
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 2,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0,
+ "cardsinhand": [
+ 43,
+ 97,
+ 26,
+ 38,
+ 91,
+ 35,
+ 89,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 7,
+ 59
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 1106,
+ "cards": [
+ [
+ 7,
+ 59
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 1,
+ "cardtype": 102,
+ "shuai": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 47,
+ 80
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 8,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 1106,
+ "cards": [
+ [
+ 7,
+ 59
+ ],
+ [
+ 47,
+ 80
+ ],
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 97,
+ 43
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0,
+ "cardsinhand": [
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 46,
+ 100
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 4,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 88,
+ 34
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ [
+ 88,
+ 34
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 26,
+ 38
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0,
+ "cardsinhand": [
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 36,
+ 90
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 3,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 311,
+ "cards": [
+ [
+ 36,
+ 90
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 2,
+ "flower": 3,
+ "cardtype": 201
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 87,
+ 31
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 10,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 2,
+ "startflower": 3,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 311,
+ "cards": [
+ [
+ 36,
+ 90
+ ],
+ [
+ 87,
+ 31
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "mustcard": [
+ 89,
+ 35
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 35,
+ 89
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0,
+ "cardsinhand": [
+ 91,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 33
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 308,
+ "cards": [
+ [
+ 33
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 84
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 11,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 308,
+ "cards": [
+ [
+ 33
+ ],
+ [
+ 84
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 91
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 5,
+ "cardsinhand": [
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ]
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 74
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ null,
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101,
+ "cardsinhand": [
+ 85,
+ 30,
+ 13,
+ 25,
+ 76
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 93
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ [
+ 93
+ ],
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 24
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 1,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 1
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 17
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 1,
+ "maxcard": 205,
+ "cards": [
+ null,
+ [
+ 17
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 13
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 13,
+ "start": 1,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 214,
+ "cards": [
+ null,
+ [
+ 17
+ ],
+ [
+ 13
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 85,
+ 30,
+ 25,
+ 76
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 103
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 5
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 76
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 210,
+ "cards": [
+ null,
+ null,
+ [
+ 76
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101,
+ "cardsinhand": [
+ 85,
+ 30,
+ 25
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 102
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 14,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 210,
+ "cards": [
+ [
+ 102
+ ],
+ null,
+ [
+ 76
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 78
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 1,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 1,
+ "grade": 20
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 21
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 1,
+ "maxcard": 209,
+ "cards": [
+ null,
+ [
+ 21
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 2,
+ "cardtype": 101,
+ "mustcard": [
+ 25
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 25
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 15,
+ "start": 1,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 213,
+ "cards": [
+ null,
+ [
+ 21
+ ],
+ [
+ 25
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "cardsinhand": [
+ 85,
+ 30
+ ]
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 101
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 2,
+ "grade": 10
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 2,
+ "cards": [
+ 30
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 305,
+ "cards": [
+ null,
+ null,
+ [
+ 30
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101,
+ "cardsinhand": [
+ 85
+ ]
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 92
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 16,
+ "start": 2,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 313,
+ "cards": [
+ [
+ 92
+ ],
+ null,
+ [
+ 30
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1
+ }
+ },
+ {
+ "rpc": "chupai3",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 77
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 0,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "maxseat": 0
+ }
+ },
+ {
+ "rpc": "chupai1",
+ "data": {
+ "success": true,
+ "seat": 0,
+ "cards": [
+ 37
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 1,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 312,
+ "cards": [
+ [
+ 37
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "count": 1,
+ "flower": 3,
+ "cardtype": 101
+ }
+ },
+ {
+ "rpc": "chupai2",
+ "data": {
+ "success": true,
+ "seat": 1,
+ "cards": [
+ 22
+ ],
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "nextseat": 2,
+ "countdown": 30,
+ "curmultiple": 1,
+ "playproc": {
+ "round": 17,
+ "start": 0,
+ "currseat": 2,
+ "startcount": 1,
+ "startflower": 3,
+ "starttype": 101,
+ "maxseat": 0,
+ "maxcard": 312,
+ "cards": [
+ [
+ 37
+ ],
+ [
+ 22
+ ],
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "mustcard": [
+ 85
+ ]
+ }
+ },
+ {
+ "rpc": "jiesuan",
+ "data": {
+ "success": true,
+ "curmultiple": 1,
+ "chupai": {
+ "seat": 2,
+ "cards": [
+ 85
+ ],
+ "maxseat": 0
+ },
+ "bottom": {
+ "cards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ },
+ "step": 6,
+ "aset": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "flower": 1,
+ "grade": 50,
+ "upgrade": 1,
+ "bangwang": 0,
+ "climb": 0,
+ "seatlist": [
+ {
+ "cards": [
+ 52
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": 4,
+ "grade": 4,
+ "score": 4
+ },
+ {
+ "cards": [
+ 106
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ },
+ {
+ "cards": [
+ 53,
+ 107
+ ],
+ "chongguan": 0,
+ "wang": 2,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ }
+ ]
+ }
+ }
+ }
+ ]
+ ],
+ "deskinfo": [
+ {
+ "step": 1,
+ "seat": 0,
+ "packetIndex": 1,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 1,
+ "MyCards": [
+ 52,
+ 19,
+ 94,
+ 68,
+ 14,
+ 55,
+ 93,
+ 105,
+ 103,
+ 102,
+ 46,
+ 92,
+ 37,
+ 36,
+ 90,
+ 67,
+ 75,
+ 18,
+ 72,
+ 71,
+ 54,
+ 0,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59
+ ],
+ "CallRun": {
+ "seat": 0,
+ "countdown": 15,
+ "nowcall": 0,
+ "multiple": 0,
+ "call": [
+ null,
+ null,
+ null
+ ]
+ }
+ }
+ },
+ {
+ "step": 1,
+ "seat": 1,
+ "packetIndex": 1,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 1,
+ "MyCards": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17,
+ 12,
+ 11,
+ 65,
+ 9,
+ 8,
+ 5,
+ 58,
+ 4
+ ],
+ "CallRun": {
+ "seat": 0,
+ "countdown": 15,
+ "nowcall": 0,
+ "multiple": 0,
+ "call": [
+ null,
+ null,
+ null
+ ]
+ }
+ }
+ },
+ {
+ "step": 1,
+ "seat": 2,
+ "packetIndex": 1,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 1,
+ "MyCards": [
+ 53,
+ 107,
+ 45,
+ 86,
+ 32,
+ 6,
+ 60,
+ 40,
+ 27,
+ 1,
+ 39,
+ 104,
+ 44,
+ 98,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74,
+ 61
+ ],
+ "CallRun": {
+ "seat": 0,
+ "countdown": 15,
+ "nowcall": 0,
+ "multiple": 0,
+ "call": [
+ null,
+ null,
+ null
+ ]
+ }
+ }
+ },
+ {
+ "step": 2,
+ "seat": 0,
+ "packetIndex": 4,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 2,
+ "MyCards": [
+ 52,
+ 19,
+ 94,
+ 68,
+ 14,
+ 55,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33,
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 72,
+ 18,
+ 71,
+ 0,
+ 54,
+ 66,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59
+ ],
+ "ChooseMain": {
+ "seat": 0,
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 20,
+ "bottomcards": [
+ 51,
+ 101,
+ 100,
+ 33,
+ 79,
+ 23,
+ 20,
+ 66
+ ],
+ "touxiang": 0,
+ "curmultiple": 3
+ }
+ }
+ },
+ {
+ "step": 2,
+ "seat": 1,
+ "packetIndex": 4,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 2,
+ "MyCards": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17,
+ 12,
+ 11,
+ 65,
+ 9,
+ 8,
+ 5,
+ 58,
+ 4
+ ],
+ "ChooseMain": {
+ "seat": 0,
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 20,
+ "touxiang": 0,
+ "curmultiple": 3
+ }
+ }
+ },
+ {
+ "step": 2,
+ "seat": 2,
+ "packetIndex": 4,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 2,
+ "MyCards": [
+ 53,
+ 107,
+ 45,
+ 86,
+ 32,
+ 6,
+ 60,
+ 40,
+ 27,
+ 1,
+ 39,
+ 104,
+ 44,
+ 98,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74,
+ 61
+ ],
+ "ChooseMain": {
+ "seat": 0,
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 20,
+ "touxiang": 0,
+ "curmultiple": 3
+ }
+ }
+ },
+ {
+ "step": 3,
+ "seat": 0,
+ "packetIndex": 5,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 3,
+ "MyCards": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 68,
+ 14,
+ 54,
+ 0,
+ 66,
+ 64,
+ 10,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33,
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 72,
+ 18,
+ 71
+ ],
+ "BuryCards": {
+ "seat": 0,
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 25,
+ "bottomcards": [
+ 51,
+ 101,
+ 100,
+ 33,
+ 79,
+ 23,
+ 20,
+ 66
+ ],
+ "flower": 1,
+ "curmultiple": 3
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 0,
+ "packetIndex": 6,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 68,
+ 14,
+ 54,
+ 0,
+ 66,
+ 64,
+ 10,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "burycards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ],
+ "grade": 0,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "pushlist": []
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 1,
+ "packetIndex": 6,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 106,
+ 99,
+ 73,
+ 81,
+ 12,
+ 11,
+ 65,
+ 9,
+ 8,
+ 5,
+ 58,
+ 4,
+ 50,
+ 49,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "gradecards": [],
+ "grade": 0,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "pushlist": []
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 2,
+ "packetIndex": 6,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 53,
+ 107,
+ 60,
+ 6,
+ 45,
+ 86,
+ 32,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 44,
+ 98,
+ 97,
+ 43,
+ 26,
+ 38,
+ 91,
+ 89,
+ 35,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "gradecards": [],
+ "grade": 0,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 1,
+ "start": 0,
+ "currseat": 0,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "pushlist": []
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 0,
+ "packetIndex": 10,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 52,
+ 19,
+ 55,
+ 94,
+ 0,
+ 54,
+ 66,
+ 10,
+ 64,
+ 63,
+ 62,
+ 7,
+ 59,
+ 93,
+ 51,
+ 105,
+ 103,
+ 102,
+ 101,
+ 46,
+ 100,
+ 92,
+ 37,
+ 36,
+ 90,
+ 33
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "burycards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ],
+ "grade": 0,
+ "curmultiple": 3,
+ "playproc": {
+ "round": 2,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 201,
+ "maxseat": 2,
+ "maxcard": 8107,
+ "cards": [
+ null,
+ null,
+ [
+ 60,
+ 6
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "pushlist": [
+ [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ [
+ 107,
+ 53
+ ]
+ ],
+ [
+ [],
+ [],
+ [
+ 60,
+ 6
+ ]
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 2,
+ "packetIndex": 15,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 45,
+ 1,
+ 40,
+ 27,
+ 61,
+ 39,
+ 104,
+ 98,
+ 44,
+ 43,
+ 97,
+ 26,
+ 38,
+ 91,
+ 35,
+ 89,
+ 85,
+ 30,
+ 13,
+ 25,
+ 76,
+ 74
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "gradecards": [
+ 4,
+ 58
+ ],
+ "grade": 10,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 4,
+ "start": 2,
+ "currseat": 2,
+ "startcount": -1,
+ "startflower": -1,
+ "starttype": -1,
+ "maxseat": -1,
+ "maxcard": -1,
+ "cards": [
+ null,
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 0,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "pushlist": [
+ [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ [
+ 107,
+ 53
+ ]
+ ],
+ [
+ [
+ 54,
+ 0
+ ],
+ [
+ 58,
+ 4
+ ],
+ [
+ 60,
+ 6
+ ]
+ ],
+ [
+ [
+ 64,
+ 10
+ ],
+ [
+ 106,
+ 99
+ ],
+ [
+ 86,
+ 32
+ ]
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 1,
+ "packetIndex": 22,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 12,
+ 9,
+ 8,
+ 5,
+ 48,
+ 47,
+ 80,
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 24,
+ 78,
+ 77,
+ 22,
+ 21,
+ 17
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "gradecards": [
+ 4,
+ 58
+ ],
+ "grade": 10,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 6,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 1,
+ "starttype": 102,
+ "maxseat": 0,
+ "maxcard": 2402,
+ "cards": [
+ [
+ 55,
+ 94
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": {
+ "tractors": [],
+ "pairs": 0,
+ "singles": 2
+ }
+ },
+ "baozhu": 0,
+ "seatlist": [
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ -1,
+ -1
+ ]
+ ],
+ [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ -1,
+ -1
+ ]
+ ]
+ ],
+ "pushlist": [
+ [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ [
+ 107,
+ 53
+ ]
+ ],
+ [
+ [
+ 54,
+ 0
+ ],
+ [
+ 58,
+ 4
+ ],
+ [
+ 60,
+ 6
+ ]
+ ],
+ [
+ [
+ 64,
+ 10
+ ],
+ [
+ 106,
+ 99
+ ],
+ [
+ 86,
+ 32
+ ]
+ ],
+ [
+ [
+ 105,
+ 51
+ ],
+ [
+ 50,
+ 49
+ ],
+ [
+ 44,
+ 98
+ ]
+ ],
+ [
+ [
+ 52,
+ 19
+ ],
+ [
+ 73,
+ 81
+ ],
+ [
+ 45,
+ 1
+ ]
+ ],
+ [
+ [
+ 55,
+ 94
+ ],
+ [],
+ []
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 1,
+ "packetIndex": 31,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 34,
+ 88,
+ 87,
+ 31,
+ 84,
+ 78,
+ 24,
+ 77,
+ 22,
+ 21,
+ 17
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "gradecards": [
+ 4,
+ 58
+ ],
+ "grade": 10,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 9,
+ "start": 0,
+ "currseat": 1,
+ "startcount": 2,
+ "startflower": 4,
+ "starttype": 201,
+ "maxseat": 0,
+ "maxcard": 408,
+ "cards": [
+ [
+ 46,
+ 100
+ ],
+ null,
+ null
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "pushlist": [
+ [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ [
+ 107,
+ 53
+ ]
+ ],
+ [
+ [
+ 54,
+ 0
+ ],
+ [
+ 58,
+ 4
+ ],
+ [
+ 60,
+ 6
+ ]
+ ],
+ [
+ [
+ 64,
+ 10
+ ],
+ [
+ 106,
+ 99
+ ],
+ [
+ 86,
+ 32
+ ]
+ ],
+ [
+ [
+ 105,
+ 51
+ ],
+ [
+ 50,
+ 49
+ ],
+ [
+ 44,
+ 98
+ ]
+ ],
+ [
+ [
+ 52,
+ 19
+ ],
+ [
+ 73,
+ 81
+ ],
+ [
+ 45,
+ 1
+ ]
+ ],
+ [
+ [
+ 55,
+ 94
+ ],
+ [
+ 12,
+ 9
+ ],
+ [
+ 40,
+ 27
+ ]
+ ],
+ [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ [
+ 8,
+ 5,
+ 48
+ ],
+ [
+ 61,
+ 39,
+ 104
+ ]
+ ],
+ [
+ [
+ 7,
+ 59
+ ],
+ [
+ 47,
+ 80
+ ],
+ [
+ 97,
+ 43
+ ]
+ ],
+ [
+ [
+ 46,
+ 100
+ ],
+ [],
+ []
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "step": 5,
+ "seat": 0,
+ "packetIndex": 40,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 0,
+ 0,
+ 0
+ ],
+ "step": 5,
+ "MyCards": [
+ 93,
+ 103,
+ 102,
+ 101,
+ 92,
+ 37
+ ],
+ "PushCards": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "countdown": 30,
+ "flower": 1,
+ "burycards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ],
+ "grade": 15,
+ "curmultiple": 2,
+ "playproc": {
+ "round": 12,
+ "start": 2,
+ "currseat": 0,
+ "startcount": 1,
+ "startflower": 2,
+ "starttype": 101,
+ "maxseat": 2,
+ "maxcard": 208,
+ "cards": [
+ null,
+ null,
+ [
+ 74
+ ]
+ ],
+ "shuai_demand": null
+ },
+ "baozhu": 1,
+ "seatlist": [
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 1
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ],
+ [
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 1
+ ],
+ [
+ 0,
+ 0
+ ]
+ ]
+ ],
+ "pushlist": [
+ [
+ [
+ 14,
+ 68
+ ],
+ [
+ 65,
+ 11
+ ],
+ [
+ 107,
+ 53
+ ]
+ ],
+ [
+ [
+ 54,
+ 0
+ ],
+ [
+ 58,
+ 4
+ ],
+ [
+ 60,
+ 6
+ ]
+ ],
+ [
+ [
+ 64,
+ 10
+ ],
+ [
+ 106,
+ 99
+ ],
+ [
+ 86,
+ 32
+ ]
+ ],
+ [
+ [
+ 105,
+ 51
+ ],
+ [
+ 50,
+ 49
+ ],
+ [
+ 44,
+ 98
+ ]
+ ],
+ [
+ [
+ 52,
+ 19
+ ],
+ [
+ 73,
+ 81
+ ],
+ [
+ 45,
+ 1
+ ]
+ ],
+ [
+ [
+ 55,
+ 94
+ ],
+ [
+ 12,
+ 9
+ ],
+ [
+ 40,
+ 27
+ ]
+ ],
+ [
+ [
+ 66,
+ 63,
+ 62
+ ],
+ [
+ 8,
+ 5,
+ 48
+ ],
+ [
+ 61,
+ 39,
+ 104
+ ]
+ ],
+ [
+ [
+ 7,
+ 59
+ ],
+ [
+ 47,
+ 80
+ ],
+ [
+ 97,
+ 43
+ ]
+ ],
+ [
+ [
+ 46,
+ 100
+ ],
+ [
+ 88,
+ 34
+ ],
+ [
+ 26,
+ 38
+ ]
+ ],
+ [
+ [
+ 36,
+ 90
+ ],
+ [
+ 87,
+ 31
+ ],
+ [
+ 35,
+ 89
+ ]
+ ],
+ [
+ [
+ 33
+ ],
+ [
+ 84
+ ],
+ [
+ 91
+ ]
+ ],
+ [
+ [],
+ [],
+ [
+ 74
+ ]
+ ]
+ ]
+ }
+ }
+ },
+ {
+ "step": 6,
+ "seat": 0,
+ "packetIndex": 57,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 4,
+ -2,
+ -2
+ ],
+ "step": 6,
+ "MyCards": [],
+ "Balance": {
+ "readystate": [
+ 0,
+ 0,
+ 0
+ ],
+ "aset": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "flower": 1,
+ "grade": 50,
+ "upgrade": 1,
+ "bangwang": 0,
+ "climb": 0,
+ "seatlist": [
+ {
+ "cards": [
+ 52
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": 4,
+ "grade": 4,
+ "score": 4
+ },
+ {
+ "cards": [
+ 106
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ },
+ {
+ "cards": [
+ 53,
+ 107
+ ],
+ "chongguan": 0,
+ "wang": 2,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ }
+ ]
+ },
+ "bottom": {
+ "cards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ }
+ }
+ }
+ },
+ {
+ "step": 6,
+ "seat": 1,
+ "packetIndex": 57,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 4,
+ -2,
+ -2
+ ],
+ "step": 6,
+ "MyCards": [],
+ "Balance": {
+ "readystate": [
+ 0,
+ 0,
+ 0
+ ],
+ "aset": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "flower": 1,
+ "grade": 50,
+ "upgrade": 1,
+ "bangwang": 0,
+ "climb": 0,
+ "seatlist": [
+ {
+ "cards": [
+ 52
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": 4,
+ "grade": 4,
+ "score": 4
+ },
+ {
+ "cards": [
+ 106
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ },
+ {
+ "cards": [
+ 53,
+ 107
+ ],
+ "chongguan": 0,
+ "wang": 2,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ }
+ ]
+ },
+ "bottom": {
+ "cards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ }
+ }
+ }
+ },
+ {
+ "step": 6,
+ "seat": 2,
+ "packetIndex": 57,
+ "info": {
+ "count": 6,
+ "idx": 1,
+ "PlayerInfo": [
+ 4,
+ -2,
+ -2
+ ],
+ "step": 6,
+ "MyCards": [],
+ "Balance": {
+ "readystate": [
+ 0,
+ 0,
+ 0
+ ],
+ "aset": {
+ "banker": 0,
+ "call": 65,
+ "multiple": 2,
+ "flower": 1,
+ "grade": 50,
+ "upgrade": 1,
+ "bangwang": 0,
+ "climb": 0,
+ "seatlist": [
+ {
+ "cards": [
+ 52
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": 4,
+ "grade": 4,
+ "score": 4
+ },
+ {
+ "cards": [
+ 106
+ ],
+ "chongguan": 0,
+ "wang": 1,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ },
+ {
+ "cards": [
+ 53,
+ 107
+ ],
+ "chongguan": 0,
+ "wang": 2,
+ "naward": 0,
+ "grade_aw": 0,
+ "grade_cg": 0,
+ "grade_bw": 0,
+ "grade_jf": -2,
+ "grade": -2,
+ "score": -2
+ }
+ ]
+ },
+ "bottom": {
+ "cards": [
+ 67,
+ 79,
+ 23,
+ 75,
+ 20,
+ 18,
+ 72,
+ 71
+ ]
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/client/tests/test_appstart_timing.js b/client/tests/test_appstart_timing.js
new file mode 100644
index 0000000..cd9af61
--- /dev/null
+++ b/client/tests/test_appstart_timing.js
@@ -0,0 +1,150 @@
+// 【C-1 回归】room.mySeat 的同步必须跑在【真实的平台初始化时序】上。
+//
+// 平台 12_Logic.js 的 Logic.AppStart 跨 332–531 行,里面:
+// line 389: Game_Modify.appStart() ← 此时 C_Player 只是 `var C_Player;`,未赋值
+// line 480: C_Player = new Player(-1) ← 对象到这里才存在,seat 仍是 -1
+// 之后玩家登录,07_Desk.js 的 C_Player.SetSeat(真座位),
+// 但 Game_Modify.appStart() 【再也不会被调用】。
+//
+// 所以「只在 appStart 里读一次 C_Player.seat」必然拿不到座位,mySeat 永远停在 -1:
+// 发包带 seat:-1(服务端 check_player 必拒,叫分/选主/埋牌/出牌全发不出去)、
+// StartWar 差异化下发认领不到本座位而整包丢弃、SeatMap.toDisplay(-1) 抛错。
+// ——全程无一处报错。本测试就是拿这条时序当尺子量。
+//
+// 本文件【不允许】用 `S.room.mySeat = N` 直接赋值来制造前提(其余 8 个测试文件那样做,
+// 正是它们全部漏掉 C-1 的原因)。座位只能经由平台入口同步进来。
+const { load, throws } = require('./_load');
+const t = require('./_assert')();
+
+global.window = global;
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/gameabc-framework/system/SpriteEventController.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/state/RoomOptions.js');
+['DealHandler','CallHandler','MainHandler','BuryHandler','PlayHandler',
+ 'QueryHandler','ReadyHandler','ResultHandler','ResyncHandler','FailHandler']
+ .forEach(h => load('client/js/01_SubGame/codes/net/handlers/' + h + '.js'));
+load('client/js/01_SubGame/codes/net/Dispatcher.js');
+load('client/js/01_SubGame/codes/core/SeatMap.js');
+
+const sent = [];
+global.RpcHelper = { sendRpc: (app, route, rpc, data) => sent.push({ rpc, data }) };
+load('client/js/01_SubGame/codes/net/Rpc.js');
+
+load('client/js/01_SubGame/codes/SubGameHooks.js');
+
+const S = EQW_GameState;
+
+// 平台 06_Player.js 的最小复刻:只有 seat 与 SetSeat 与本用例相关
+function Player(seat) { this.seat = seat; }
+Player.prototype.SetSeat = function (seat) { this.seat = seat; };
+
+// ============================================================================
+// 阶段 1:Logic.AppStart 第 389 行——C_Player 尚未 new
+// ============================================================================
+t.eq('前提:C_Player 此刻确实不存在', typeof global.C_Player, 'undefined');
+t.eq('前提:mySeat 初始值是 -1', S.room.mySeat, -1);
+
+let boom = false;
+try { SubGameHooks.appStart(); } catch (e) { boom = true; }
+t.eq('appStart 在 C_Player 缺席时不崩', boom, false);
+t.eq('appStart 拿不到座位,mySeat 仍是 -1(不是缺陷,是时序事实)', S.room.mySeat, -1);
+
+// 这个 -1 一旦被发包无声带出去就是 C-1 的第一重后果
+t.eq('座位未同步时发包 fail-fast', throws(() => EQW_Rpc.zhunbei()), true);
+t.eq('座位未同步时一个包都没发出去', sent.length, 0);
+
+// ============================================================================
+// 阶段 2:Logic.AppStart 第 480 行——C_Player = new Player(-1)
+// ============================================================================
+global.C_Player = new Player(-1);
+SubGameHooks.setRoomDes(1234, 6, '00010');
+t.eq('C_Player 存在但座位还是 -1 时,不写坏值', S.room.mySeat, -1);
+t.eq('setRoomDes 该干的正事照干(解析 roomtype)', S.room.options.climb, 1);
+
+// ============================================================================
+// 阶段 3:玩家登录,07_Desk.js:320 C_Player.SetSeat(真座位)
+// 紧接着 07_Desk.js:358 Game_Modify.setRoomDes(...)
+// ============================================================================
+C_Player.SetSeat(2);
+t.eq('SetSeat 本身不写 GameState(平台不知道子游戏)', S.room.mySeat, -1);
+
+SubGameHooks.setRoomDes(1234, 6, '00010');
+t.eq('【核心】走完平台时序后 mySeat = 真座位', S.room.mySeat, 2);
+
+// ---- 第一重后果解除:发包带真座位 ----
+sent.length = 0;
+EQW_Rpc.zhunbei();
+t.eq('发包带上真座位', sent[0].data.seat, 2);
+
+// ---- 第二重后果解除:StartWar 差异化下发能认领到自己那份 ----
+const share = (seat, cards) => ({ seat, data: { count: 6, idx: 1, PlayerInfo: [0,0,0], step: 1, MyCards: cards } });
+SubGameHooks.StartWar({ data: { deskwar: { sendtype: 1, seatlist: [
+ share(0, [10, 11]), share(1, [20, 21]), share(2, [30, 31])
+] } } });
+t.eq('StartWar 认领到本座位那份手牌', S.my.cards, [30, 31]);
+
+// ---- 第三重后果解除:SeatMap 不再对 -1 抛错 ----
+t.eq('SeatMap.toDisplay 用 mySeat 不抛错', throws(() => EQW_SeatMap.toDisplay(0, S.room.mySeat)), false);
+t.eq('自己映射到 SELF', EQW_SeatMap.toDisplay(S.room.mySeat, S.room.mySeat), 'SELF');
+
+// ============================================================================
+// 阶段 4:换座(07_Desk.js:186-191 先改 C_Player.seat,再回调 changeSeat)
+// 这是唯一「座位中途变化且不重走 setRoomDes」的路径
+// ============================================================================
+C_Player.SetSeat(0);
+SubGameHooks.changeSeat(2, 0);
+t.eq('换座后 mySeat 跟着变', S.room.mySeat, 0);
+sent.length = 0;
+EQW_Rpc.zhunbei();
+t.eq('换座后发包带新座位', sent[0].data.seat, 0);
+
+// ============================================================================
+// 阶段 5:断线重连(07_Desk.js:320 SetSeat → 358 setRoomDes → 423 Reconnect)
+// 这里单独校验 Reconnect 自身也同步,不依赖前面的入口是否跑过
+// ============================================================================
+S.room.mySeat = -1; // 人为退回未同步态,验证 Reconnect 能独立自愈
+C_Player.SetSeat(1);
+SubGameHooks.Reconnect({ count: 6, idx: 3, PlayerInfo: [0,0,0], step: 1, MyCards: [5,6] });
+t.eq('Reconnect 自身也同步座位', S.room.mySeat, 1);
+t.eq('Reconnect 的 reset() 不会把刚同步的座位清掉', S.room.mySeat, 1);
+t.eq('Reconnect 正常填状态', S.my.cards, [5, 6]);
+
+S.room.mySeat = -1;
+C_Player.SetSeat(2);
+SubGameHooks.ReconnectNoMakewar();
+t.eq('ReconnectNoMakewar 也同步座位', S.room.mySeat, 2);
+
+S.room.mySeat = -1;
+C_Player.SetSeat(0);
+SubGameHooks.StartWar({ data: { deskwar: { sendtype: 1, seatlist: [
+ share(0, [7, 8]), share(1, [9]), share(2, [1]) ] } } });
+t.eq('StartWar 自身也同步座位', S.room.mySeat, 0);
+t.eq('StartWar 同步后认领正确的那份', S.my.cards, [7, 8]);
+
+// ============================================================================
+// 幂等 / 防倒退:_syncMySeat 可重复调用,且绝不用坏值覆盖好值
+// ============================================================================
+SubGameHooks._syncMySeat();
+SubGameHooks._syncMySeat();
+t.eq('重复同步结果不变(幂等)', S.room.mySeat, 0);
+
+C_Player.SetSeat(-1); // 平台 new Player(-1) 的初值又冒出来
+SubGameHooks._syncMySeat();
+t.eq('座位为 -1 时保持原值,不倒退', S.room.mySeat, 0);
+C_Player.SetSeat(3);
+SubGameHooks._syncMySeat();
+t.eq('座位越界时保持原值,不倒退', S.room.mySeat, 0);
+C_Player.SetSeat('1');
+SubGameHooks._syncMySeat();
+t.eq('座位是字符串时保持原值,不倒退', S.room.mySeat, 0);
+
+delete global.C_Player;
+boom = false;
+try { SubGameHooks._syncMySeat(); } catch (e) { boom = true; }
+t.eq('C_Player 消失时同步不崩', boom, false);
+t.eq('C_Player 消失时保持原值', S.room.mySeat, 0);
+
+process.exit(t.done('appstart_timing') ? 0 : 1);
diff --git a/client/tests/test_consistency.js b/client/tests/test_consistency.js
new file mode 100644
index 0000000..2f10529
--- /dev/null
+++ b/client/tests/test_consistency.js
@@ -0,0 +1,114 @@
+// 【核心验收】增量累积的 GameState 必须与 deskinfo 全量重建的完全相等
+const fs = require('fs');
+const path = require('path');
+const { ROOT, load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/state/RoomOptions.js');
+load('client/js/01_SubGame/codes/net/handlers/DealHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/CallHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/MainHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/BuryHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/PlayHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/QueryHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ReadyHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ResultHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ResyncHandler.js');
+load('client/js/01_SubGame/codes/net/Dispatcher.js');
+
+const fx = JSON.parse(fs.readFileSync(path.join(ROOT, 'client/tests/fixtures/packets.json'), 'utf8'));
+
+// 这些字段是「一次性事件」或「重连不重放」的,不参与一致性比对。
+// 每加一条都必须写清理由——这个白名单是本测试唯一的松口处,滥用它等于废掉这条守卫。
+//
+// 【清单历史】result.bottom / result.account 已随服务端 Balance 补发而删除;
+// result.chupai 已随「本墩结果改走 EQW_TRICK_END 事件载荷、不进 GameState」而删除。
+// 现在只剩下面这一条。
+const EXCLUDE = [
+ // 70 分坐庄的 3 秒开底是上庄时的一次性事件,协议明确 deskinfo 不重放。
+ // ⚠️ 当前夹具无法触发(65 分坐庄、单局),此条未经验证——保留是因为删掉之后
+ // 将来真出现不一致会静默通过;但它此刻并没有在守住任何东西,不要当成已验证的豁免。
+ 'table.ancard3s'
+];
+
+function pick(snap, exclude) {
+ const out = JSON.parse(JSON.stringify(snap));
+ exclude.forEach(p => {
+ const parts = p.split('.');
+ let o = out;
+ for (let i = 0; i < parts.length - 1; i++) { o = o && o[parts[i]]; }
+ if (o) { delete o[parts[parts.length - 1]]; }
+ });
+ return out;
+}
+
+// 每个对齐点独立起步,不共用进程内任何残留(下面两个 build 函数各自从
+// EQW_GameState.reset() + 显式赋值房间级字段开始,互不依赖调用顺序/次数):
+//
+// 【曾经的假绿】EQW_GameState.reset() 按设计不清 room.playerScores(它是"跨局不清"的
+// 房间级累积量,见 GameState.js 注释)。旧版路径 A 从不写 playerScores(fapai 包不带),
+// 于是它的值全靠"上一次由谁最后写过"——如果上一次恰好是路径 B(deskinfo 带 PlayerInfo)
+// 跑过,路径 A 就会【继承】那份残留,凑巧与路径 B 的期望值相等,测试假绿;换一个座位
+// 顺序、或把它放到第一个跑,残留没了就立刻转红。
+// 真实客户端不存在这个残留——它在 fapai 之前先收到 StartWar(deskwar,见夹具补捕),
+// 由它给出 room.playerScores 的开局初值。所以路径 A 的真实起点也改成先喂 StartWar,
+// 每次都显式重新赋值,不再依赖任何"恰好还没被冲掉的旧值"。
+function buildIncremental(seat, packetIndex) {
+ EQW_GameState.reset();
+ EQW_GameState.room.mySeat = seat;
+ EQW_GameState.room.options = EQW_RoomOptions_Parse.parse(fx.roomtype);
+ EQW_ResyncHandler.handleStartWar({ data: fx.startwar[seat] });
+ fx.seats[seat].slice(0, packetIndex).forEach(p => {
+ EQW_Dispatcher.dispatch({ rpc: p.rpc, data: p.data });
+ });
+ return EQW_GameState.snapshot();
+}
+
+function buildFull(seat, info) {
+ EQW_GameState.reset();
+ EQW_GameState.room.mySeat = seat;
+ EQW_GameState.room.options = EQW_RoomOptions_Parse.parse(fx.roomtype);
+ EQW_ResyncHandler.handleDeskinfo(info);
+ return EQW_GameState.snapshot();
+}
+
+// 对每个座位、每个 deskinfo 快照点做比对
+let compared = 0;
+fx.deskinfo.forEach(snapPoint => {
+ const seat = snapPoint.seat;
+
+ const incremental = buildIncremental(seat, snapPoint.packetIndex);
+ const full = buildFull(seat, snapPoint.info);
+
+ t.eq('座位' + seat + ' step' + snapPoint.step + ' 增量 == 全量',
+ pick(incremental, EXCLUDE), pick(full, EXCLUDE));
+ compared++;
+});
+
+t.eq('比对点数量 > 0', compared > 0, true);
+
+// ================= Ruling 3:step3(埋牌)deskinfo 直读断言 =================
+// 一致性比对无法单独覆盖 ResyncHandler._applyBuryCards 的字段映射正确性——
+// 若增量路径压根不写某字段,两边可能「同为初始值」而比对照样绿。
+// 这里独立取 step3 快照,逐字段直读 GameState 并核对具体值。
+const step3 = fx.deskinfo.filter(d => d.step === 3)[0];
+if (!step3) {
+ console.warn('[test_consistency] 夹具里没有 step3 快照,Ruling 3 的直读断言无法执行');
+} else {
+ EQW_GameState.reset();
+ EQW_ResyncHandler.handleDeskinfo(step3.info);
+ const bc = step3.info.BuryCards;
+ t.eq('step3 直读 aset.banker', EQW_GameState.aset.banker, bc.banker);
+ t.eq('step3 直读 aset.call', EQW_GameState.aset.call, bc.call);
+ t.eq('step3 直读 aset.multiple', EQW_GameState.aset.multiple, bc.multiple);
+ t.eq('step3 直读 aset.flower', EQW_GameState.aset.flower, bc.flower);
+ t.eq('step3 直读 my.bottomCards', EQW_GameState.my.bottomCards, bc.bottomcards);
+ t.eq('step3 直读 turn.countdown', EQW_GameState.turn.countdown, bc.countdown);
+ t.eq('step3 直读 turn.seat(埋牌由庄家做)', EQW_GameState.turn.seat, bc.banker);
+ t.eq('step3 直读 aset.step', EQW_GameState.aset.step, step3.info.step);
+}
+
+process.exit(t.done('consistency') ? 0 : 1);
diff --git a/client/tests/test_dispatcher.js b/client/tests/test_dispatcher.js
new file mode 100644
index 0000000..42a42c2
--- /dev/null
+++ b/client/tests/test_dispatcher.js
@@ -0,0 +1,59 @@
+// 分发器:success 优先分流、未知 rpc 不崩、路由表完整性
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/state/RoomOptions.js');
+load('client/js/01_SubGame/codes/net/handlers/FailHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/DealHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/CallHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/MainHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/BuryHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/PlayHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/QueryHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ReadyHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ResultHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ResyncHandler.js');
+load('client/js/01_SubGame/codes/net/Dispatcher.js');
+
+// ---- 注册与分发 ----
+const got = [];
+EQW_Dispatcher.register('testRpc', function (d) { got.push(d); });
+EQW_Dispatcher.dispatch({ rpc: 'testRpc', data: { success: true, v: 1 } });
+t.eq('成功包进业务 handler', got, [{ success: true, v: 1 }]);
+
+// ---- success:false 优先走 FailHandler,不进业务 handler ----
+const failed = [];
+EventBus.on(EQW_Events.EQW_RPC_FAILED, function (e) { failed.push(e); });
+got.length = 0;
+EQW_Dispatcher.dispatch({ rpc: 'testRpc', data: { success: false, errcode: 5 } });
+t.eq('失败包不进业务 handler', got, []);
+t.eq('失败包 emit RPC_FAILED', failed, [{ rpc: 'testRpc', errcode: 5 }]);
+
+// ---- 失败包即使 rpc 没注册过也要被处理(如 chupai 只会以失败包出现)----
+failed.length = 0;
+EQW_Dispatcher.dispatch({ rpc: 'chupai', data: { success: false, errcode: 6 } });
+t.eq('未注册 rpc 的失败包也处理', failed, [{ rpc: 'chupai', errcode: 6 }]);
+
+// ---- 未知 rpc 的成功包:只警告、不抛错 ----
+let threw = false;
+try { EQW_Dispatcher.dispatch({ rpc: 'brandNewRpc', data: { success: true } }); }
+catch (e) { threw = true; }
+t.eq('未知 rpc 不抛错', threw, false);
+
+// ---- 畸形入参不崩 ----
+[null, undefined, {}, { rpc: 'testRpc' }, { data: {} }].forEach((bad, i) => {
+ let boom = false;
+ try { EQW_Dispatcher.dispatch(bad); } catch (e) { boom = true; }
+ t.eq('畸形入参 #' + i + ' 不崩', boom, false);
+});
+
+// ---- 路由表完整性:协议里每个服务端推送 rpc 都要有 handler ----
+['fapai','jiaofen','shangzhuang','xuanzhu','maipai',
+ 'chupai1','chupai2','chupai3','mingpai','tishi','jiesuan','zhunbei'].forEach(rpc => {
+ t.eq('已注册 handler: ' + rpc, EQW_Dispatcher.hasHandler(rpc), true);
+});
+
+process.exit(t.done('dispatcher') ? 0 : 1);
diff --git a/client/tests/test_fixture.js b/client/tests/test_fixture.js
new file mode 100644
index 0000000..27bddcf
--- /dev/null
+++ b/client/tests/test_fixture.js
@@ -0,0 +1,111 @@
+// 夹具自检:真包必须覆盖全部阶段,且保留座位差异
+const fs = require('fs');
+const path = require('path');
+const { ROOT } = require('./_load');
+const t = require('./_assert')();
+
+const fx = JSON.parse(fs.readFileSync(path.join(ROOT, 'client/tests/fixtures/packets.json'), 'utf8'));
+
+t.eq('三个座位', fx.seats.length, 3);
+
+const rpcsOf = seat => fx.seats[seat].map(p => p.rpc);
+const allRpcs = [].concat(rpcsOf(0), rpcsOf(1), rpcsOf(2));
+
+['fapai','jiaofen','shangzhuang','xuanzhu','maipai','chupai1','chupai2','chupai3','jiesuan']
+ .forEach(rpc => t.eq('夹具覆盖 ' + rpc, allRpcs.indexOf(rpc) >= 0, true));
+
+// 差异化下发确实存在:bottomcards 恰好只发给庄家一人(不是"少于三家"这种可被空集合蒙混过去的弱断言)
+const withBottom = [0,1,2].filter(s =>
+ fx.seats[s].some(p => p.rpc === 'shangzhuang' && p.data.hasOwnProperty('bottomcards')));
+t.eq('底牌恰好发给1个座位(庄家)', withBottom.length, 1);
+
+// deskinfo 快照必须覆盖全部 5 个阶段(下游任务要用 step3 埋牌快照做回归断言)
+const steps = fx.deskinfo.map(d => d.step).filter((v, i, a) => a.indexOf(v) === i).sort((a,b) => a - b);
+t.eq('deskinfo 覆盖全部阶段 1/2/3/5/6', steps, [1, 2, 3, 5, 6]);
+
+// roomtype 必须存在且非空,Task 12 要用它还原房间选项
+t.eq('roomtype 存在且非空', typeof fx.roomtype === 'string' && fx.roomtype.length > 0, true);
+
+// startwar 是增量路径的真实起点(开局握手,结构=deskwar=deskinfo),必须每座位各一份,
+// 且不得混进 seats[] 数组头部——那会让全部 packetIndex 集体错位一位(见夹具补捕说明)
+t.eq('startwar 存在且为 3 份', Array.isArray(fx.startwar) && fx.startwar.length, 3);
+[0, 1, 2].forEach(s => {
+ const dw = fx.startwar[s] && fx.startwar[s].deskwar;
+ t.eq('startwar[' + s + '].deskwar 存在', !!dw, true);
+ t.eq('startwar[' + s + '] 结构=deskinfo(带 step/PlayerInfo/MyCards)',
+ dw && dw.hasOwnProperty('step') && dw.hasOwnProperty('PlayerInfo') && dw.hasOwnProperty('MyCards'), true);
+ t.eq('startwar[' + s + '].PlayerInfo 为开局初值(三家均 0)', dw && dw.PlayerInfo, [0, 0, 0]);
+});
+// seats[] 的第一包必须仍是 fapai——确认 startwar 没有被误插进 seats[] 头部
+[0, 1, 2].forEach(s => {
+ t.eq('seats[' + s + '][0] 仍是 fapai(startwar 未混入 seats)', fx.seats[s][0].rpc, 'fapai');
+});
+
+// packetIndex 必须落在 (0, 该座位包总数] 区间内,且与 info.step 自洽
+fx.deskinfo.forEach(d => {
+ const inRange = d.packetIndex > 0 && d.packetIndex <= fx.seats[d.seat].length;
+ t.eq('packetIndex 落在有效区间 (step' + d.step + ' seat' + d.seat + ')', inRange, true);
+ t.eq('deskinfo.step 与 info.step 一致 (seat' + d.seat + ' pi' + d.packetIndex + ')', d.step, d.info.step);
+});
+
+// C-1 回归锁:同一座位的两条 step5 快照,playproc(当前轮桌面牌/currseat/round)不得相同——
+// 否则说明 deskinfo 快照存的是活引用,被后续出牌篡改成了终局状态(曾经的真实缺陷)
+const step5BySeat = {};
+fx.deskinfo.filter(d => d.step === 5).forEach(d => {
+ (step5BySeat[d.seat] = step5BySeat[d.seat] || []).push(d);
+});
+Object.keys(step5BySeat).forEach(seat => {
+ const list = step5BySeat[seat];
+ if (list.length < 2) { return; }
+ for (let i = 1; i < list.length; i++) {
+ const same = JSON.stringify(list[0].info.PushCards.playproc) === JSON.stringify(list[i].info.PushCards.playproc);
+ t.eq('seat' + seat + ' 两条 step5 快照 playproc 不相同(未被活引用污染)', same, false);
+ }
+});
+
+// I-3:推进阶段的下发包必须自带 step,且与该时刻的 deskinfo.step 属于同一套取值。
+// 前端不得按 rpc 名硬编码阶段(前端红线「数据驱动、前端无对局状态机」)。
+const STEP_OF = { fapai: 1, jiaofen: 1, shangzhuang: 2, xuanzhu: 3, maipai: 5, jiesuan: 6 };
+const noStep = [], badStep = [];
+[0, 1, 2].forEach(s => fx.seats[s].forEach(p => {
+ if (!STEP_OF.hasOwnProperty(p.rpc)) { return; }
+ if (!p.data.hasOwnProperty('step')) { noStep.push(s + ':' + p.rpc); return; }
+ if (p.data.step !== STEP_OF[p.rpc]) { badStep.push(s + ':' + p.rpc + '=' + p.data.step); }
+}));
+t.eq('推进阶段的下发包都带 step', noStep, []);
+t.eq('各包的 step 就是该时点的真实阶段', badStep, []);
+
+// I-3:选主/埋牌阶段的「轮到谁」必须由服务端显式给出(增量 nextseat / 重连分组的 seat)
+const noNext = [];
+[0, 1, 2].forEach(s => fx.seats[s].forEach(p => {
+ if ((p.rpc === 'shangzhuang' || p.rpc === 'xuanzhu') && !p.data.hasOwnProperty('nextseat')) {
+ noNext.push(s + ':' + p.rpc);
+ }
+}));
+t.eq('shangzhuang/xuanzhu 都带 nextseat(控制权)', noNext, []);
+fx.deskinfo.filter(d => d.step === 2).forEach(d => {
+ t.eq('重连 ChooseMain 带控制权 seat (seat' + d.seat + ')', typeof d.info.ChooseMain.seat, 'number');
+});
+fx.deskinfo.filter(d => d.step === 3).forEach(d => {
+ t.eq('重连 BuryCards 带控制权 seat (seat' + d.seat + ')', typeof d.info.BuryCards.seat, 'number');
+});
+
+// I-2:结算阶段的重连包必须与 jiesuan 推送给同一份结算数据(抠底明细 bottom 恒有;
+// account 只在末局,本夹具是第 1/6 局,故两条路径都没有)
+const jiesuanPk = fx.seats[0].filter(p => p.rpc === 'jiesuan')[0];
+t.eq('夹具的 jiesuan 带抠底明细 bottom', !!(jiesuanPk && jiesuanPk.data.bottom), true);
+fx.deskinfo.filter(d => d.step === 6).forEach(d => {
+ t.eq('重连 Balance.bottom == jiesuan.bottom (seat' + d.seat + ')',
+ JSON.stringify(d.info.Balance.bottom), JSON.stringify(jiesuanPk.data.bottom));
+ t.eq('非末局:两条路径都不带 account (seat' + d.seat + ')',
+ [d.info.Balance.hasOwnProperty('account'), jiesuanPk.data.hasOwnProperty('account')], [false, false]);
+});
+
+// 每个包都有 success(协议 §0.1:每个下发包的 data 必带 success)
+const noSuccess = [];
+[0,1,2].forEach(s => fx.seats[s].forEach(p => {
+ if (!p.data || !p.data.hasOwnProperty('success')) { noSuccess.push(s + ':' + p.rpc); }
+}));
+t.eq('每个下发包都带 success', noSuccess, []);
+
+process.exit(t.done('fixture') ? 0 : 1);
diff --git a/client/tests/test_gamestate.js b/client/tests/test_gamestate.js
new file mode 100644
index 0000000..d8e3a35
--- /dev/null
+++ b/client/tests/test_gamestate.js
@@ -0,0 +1,56 @@
+// GameState 骨架:初始值、reset 的清空范围、snapshot 深拷贝
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+
+// ---- 事件常量已追加进 EventBus.Events ----
+t.eq('事件常量非空', Object.keys(EQW_Events).length > 0, true);
+t.eq('RESET 已入 EventBus.Events', EventBus.Events.EQW_RESET, EQW_Events.EQW_RESET);
+t.eq('事件名互不重复',
+ Object.keys(EQW_Events).length,
+ Object.keys(EQW_Events).map(k => EQW_Events[k]).filter((v, i, a) => a.indexOf(v) === i).length);
+
+// ---- 初始值 ----
+t.eq('初始 mySeat', EQW_GameState.room.mySeat, -1);
+t.eq('初始 banker', EQW_GameState.aset.banker, -1);
+t.eq('初始 flower', EQW_GameState.aset.flower, 0);
+t.eq('初始手牌', EQW_GameState.my.cards, []);
+t.eq('初始三家叫分', EQW_GameState.call.calls, [null, null, null]);
+
+// ---- snapshot 是深拷贝、且不含方法 ----
+EQW_GameState.my.cards = [1, 2, 3];
+const snap = EQW_GameState.snapshot();
+t.eq('snapshot 取到值', snap.my.cards, [1, 2, 3]);
+EQW_GameState.my.cards.push(4);
+t.eq('snapshot 是深拷贝(不随后续修改)', snap.my.cards, [1, 2, 3]);
+t.eq('snapshot 不含方法', typeof snap.reset, 'undefined');
+t.eq('snapshot 含全部分组',
+ Object.keys(snap).sort(),
+ ['aset', 'call', 'my', 'result', 'room', 'table', 'turn']);
+
+// ---- reset:清对局态,但 mySeat / options 不清(跨局不变)----
+EQW_GameState.room.mySeat = 2;
+EQW_GameState.room.options = { climb: 1 };
+EQW_GameState.aset.banker = 1;
+EQW_GameState.aset.flower = 3;
+EQW_GameState.table.pushlist = [[1], [2]];
+EQW_GameState.result.aset = { grade: 5 };
+EQW_GameState.reset();
+
+t.eq('reset 后 mySeat 保留', EQW_GameState.room.mySeat, 2);
+t.eq('reset 后 options 保留', EQW_GameState.room.options, { climb: 1 });
+t.eq('reset 后 banker 清空', EQW_GameState.aset.banker, -1);
+t.eq('reset 后 flower 清空', EQW_GameState.aset.flower, 0);
+t.eq('reset 后 pushlist 清空', EQW_GameState.table.pushlist, []);
+t.eq('reset 后 result 清空', EQW_GameState.result.aset, null);
+t.eq('reset 后手牌清空', EQW_GameState.my.cards, []);
+
+// ---- reset 两次结果一致(幂等)----
+const afterFirst = EQW_GameState.snapshot();
+EQW_GameState.reset();
+t.eq('reset 幂等', EQW_GameState.snapshot(), afterFirst);
+
+process.exit(t.done('gamestate') ? 0 : 1);
diff --git a/client/tests/test_handlers_bury.js b/client/tests/test_handlers_bury.js
new file mode 100644
index 0000000..55e2b50
--- /dev/null
+++ b/client/tests/test_handlers_bury.js
@@ -0,0 +1,85 @@
+// 选主 / 埋牌 handler
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/net/handlers/MainHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/BuryHandler.js');
+
+const S = EQW_GameState;
+const evts = [];
+Object.keys(EQW_Events).forEach(k => EventBus.on(EQW_Events[k], () => evts.push(k)));
+
+// ================= xuanzhu =================
+S.reset(); evts.length = 0;
+EQW_MainHandler.handleXuanzhu({ success: true, banker: 1, flower: 3, step: 3, nextseat: 1,
+ countdown: 30, cards: [5, 6, 7] });
+t.eq('主牌花色', S.aset.flower, 3);
+t.eq('庄家', S.aset.banker, 1);
+t.eq('选主后手牌', S.my.cards, [5, 6, 7]);
+t.eq('step 读包字段(埋牌=3)', S.aset.step, 3);
+t.eq('控制权读包里的 nextseat', S.turn.seat, 1);
+t.eq('倒计时', S.turn.countdown, 30);
+t.eq('选主发事件',
+ ['EQW_MAIN_SET','EQW_HAND_CHANGED'].every(e => evts.indexOf(e) >= 0), true);
+
+// 闲家视角:没有 cards,不抹已有手牌
+S.reset(); S.my.cards = [1, 2];
+EQW_MainHandler.handleXuanzhu({ success: true, banker: 0, flower: 2, step: 3, nextseat: 0, countdown: 30 });
+t.eq('闲家手牌不被抹', S.my.cards, [1, 2]);
+t.eq('闲家也拿到花色', S.aset.flower, 2);
+
+// 【守卫】阶段与控制权都必须来自包,不得按 rpc 名硬编码、也不得用本地 banker 反推。
+// 真实链路里 nextseat 恒 == banker,相等时"读哪个"分辨不出来;这里刻意让它们不同,
+// 退回 `aset.step = 3` / `turn.seat = aset.banker` 的写法必转红。
+S.reset();
+EQW_MainHandler.handleXuanzhu({ success: true, banker: 0, flower: 2, step: 4, nextseat: 2, countdown: 30 });
+t.eq('xuanzhu step 照读包字段', S.aset.step, 4);
+t.eq('xuanzhu 控制权取 nextseat,不取 banker', S.turn.seat, 2);
+
+// ================= maipai =================
+S.reset(); evts.length = 0;
+EQW_BuryHandler.handle({ success: true, cards: [1,2,3], burycards: [9,10,11,12,13,14,15,16],
+ step: 5, seat: 1, countdown: 20 });
+t.eq('埋牌后手牌', S.my.cards, [1,2,3]);
+t.eq('埋牌底牌', S.my.buryCards, [9,10,11,12,13,14,15,16]);
+t.eq('step 读包字段(出牌=5)', S.aset.step, 5);
+t.eq('首出者(控制权读包里的 seat)', S.turn.seat, 1);
+t.eq('埋牌发事件',
+ ['EQW_BURY_DONE','EQW_HAND_CHANGED','EQW_TURN_CHANGED'].every(e => evts.indexOf(e) >= 0), true);
+
+// 闲家视角:无 cards/burycards,但可能有 liangpai
+S.reset(); S.my.cards = [4, 5]; evts.length = 0;
+EQW_BuryHandler.handle({ success: true, step: 5, seat: 0, countdown: 20,
+ liangpai: { cards: [52, 53, 2, 15] } });
+t.eq('闲家手牌不被抹', S.my.cards, [4, 5]);
+t.eq('闲家无埋牌底牌', S.my.buryCards, []);
+t.eq('亮牌数据', S.table.liangpai, { cards: [52, 53, 2, 15] });
+
+// 不达标/不查牌:无 liangpai 字段,保持 null
+S.reset();
+EQW_BuryHandler.handle({ success: true, step: 5, seat: 0, countdown: 20 });
+t.eq('无亮牌时保持 null', S.table.liangpai, null);
+
+// 【守卫】maipai 的 step 同样只读包字段
+S.reset();
+EQW_BuryHandler.handle({ success: true, step: 4, seat: 0, countdown: 20 });
+t.eq('maipai step 照读包字段(不按 rpc 名硬编码)', S.aset.step, 4);
+
+// playproc:埋牌完成时服务端已就地开好 round-1 的进行态,恒有、原样拷贝进 table.playproc,
+// 与 chupai1/2/3.playproc 同源同结构(协议 §9),漏接会让「埋牌完成→庄家首出」这段窗口
+// 增量路径 table.playproc 停在上一阶段的 null、与重连路径不一致
+S.reset();
+EQW_BuryHandler.handle({ success: true, step: 5, seat: 0, countdown: 20, cards: [1, 2, 3],
+ burycards: [9, 10, 11, 12, 13, 14, 15, 16],
+ playproc: { round: 1, start: 0, currseat: 0, startcount: -1,
+ startflower: -1, starttype: -1, maxseat: -1, maxcard: -1,
+ cards: [null, null, null], shuai_demand: null } });
+t.eq('埋牌 playproc 原样拷贝服务端本轮进行态', S.table.playproc, {
+ round: 1, start: 0, currseat: 0, startcount: -1, startflower: -1, starttype: -1,
+ maxseat: -1, maxcard: -1, cards: [null, null, null], shuai_demand: null
+});
+
+process.exit(t.done('handlers_bury') ? 0 : 1);
diff --git a/client/tests/test_handlers_call.js b/client/tests/test_handlers_call.js
new file mode 100644
index 0000000..75f1c45
--- /dev/null
+++ b/client/tests/test_handlers_call.js
@@ -0,0 +1,96 @@
+// 发牌 / 叫分 / 上庄 handler
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/net/handlers/DealHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/CallHandler.js');
+
+const S = EQW_GameState;
+const evts = [];
+Object.keys(EQW_Events).forEach(k => EventBus.on(EQW_Events[k], () => evts.push(k)));
+
+// ================= fapai =================
+S.reset(); S.room.mySeat = 1; evts.length = 0;
+EQW_DealHandler.handle({ success: true, asetidx: 2, asetcount: 6,
+ cards: [1, 2, 3], step: 1, seat: 0, countdown: 15 });
+
+t.eq('局数', S.room.asetIdx, 2);
+t.eq('总局数', S.room.asetCount, 6);
+t.eq('手牌', S.my.cards, [1, 2, 3]);
+t.eq('当前叫分者', S.turn.seat, 0);
+t.eq('倒计时', S.turn.countdown, 15);
+t.eq('step 读包字段(叫分=1)', S.aset.step, 1);
+t.eq('mySeat 未被 reset 冲掉', S.room.mySeat, 1);
+t.eq('fapai 发了 RESET/HAND/TURN 事件',
+ ['EQW_RESET','EQW_HAND_CHANGED','EQW_TURN_CHANGED'].every(e => evts.indexOf(e) >= 0), true);
+
+// fapai 必须先 reset:上一局的残留要清掉
+S.aset.banker = 2; S.table.pushlist = [[9]];
+EQW_DealHandler.handle({ success: true, asetidx: 3, asetcount: 6, cards: [4], step: 1, seat: 1, countdown: 15 });
+t.eq('fapai 清掉上局 banker', S.aset.banker, -1);
+t.eq('fapai 清掉上局 pushlist', S.table.pushlist, []);
+
+// 【守卫】step 必须来自包,不得按 rpc 名硬编码。这里刻意给一个"服务端将来新插的阶段"值:
+// 前端应照单收下(服务端权威),而不是把它改回 1。退回 `aset.step = 1` 的写法必转红。
+EQW_DealHandler.handle({ success: true, asetidx: 3, asetcount: 6, cards: [4], step: 4, seat: 1, countdown: 15 });
+t.eq('fapai step 照读包字段(不按 rpc 名硬编码)', S.aset.step, 4);
+
+// ================= jiaofen =================
+S.reset(); evts.length = 0;
+EQW_CallHandler.handleJiaofen({ success: true, seat: 0, call: 60, currcall: 60,
+ multiple: 3, step: 1, nextseat: 1, countdown: 15 });
+t.eq('记录该家叫分', S.call.calls, [60, null, null]);
+t.eq('当前叫到的分', S.call.currcall, 60);
+t.eq('基础子数', S.aset.multiple, 3);
+t.eq('jiaofen step 读包字段(仍在叫分=1)', S.aset.step, 1);
+t.eq('下一个叫分者', S.turn.seat, 1);
+t.eq('jiaofen 发事件', evts.indexOf('EQW_CALL_CHANGED') >= 0, true);
+
+// 不叫(call = 0)也要记下来,与「还没叫」(null) 区分
+EQW_CallHandler.handleJiaofen({ success: true, seat: 1, call: 0, currcall: 60,
+ multiple: 3, step: 1, nextseat: 2, countdown: 15 });
+t.eq('不叫记为 0,与未叫 null 区分', S.call.calls, [60, 0, null]);
+
+// ================= shangzhuang =================
+S.reset(); evts.length = 0;
+EQW_CallHandler.handleShangzhuang({ success: true, seat: 0, call: 70, banker: 0, grade: 70,
+ multiple: 2, step: 2, nextseat: 0,
+ bottomcards: [1,2,3,4,5,6,7,8], ancard3s: 1,
+ cards: [1,2,3], countdown: 20, touxiang: 1, curmultiple: 3 });
+t.eq('庄家', S.aset.banker, 0);
+t.eq('庄家叫分', S.aset.call, 70);
+t.eq('基础子数', S.aset.multiple, 2);
+t.eq('允许投降', S.aset.touxiang, 1);
+t.eq('抓分倍数', S.aset.curmultiple, 3);
+t.eq('开底标志', S.table.ancard3s, 1);
+t.eq('底牌', S.my.bottomCards, [1,2,3,4,5,6,7,8]);
+t.eq('摸底后手牌', S.my.cards, [1,2,3]);
+t.eq('step 读包字段(选主=2)', S.aset.step, 2);
+t.eq('控制权读包里的 nextseat', S.turn.seat, 0);
+t.eq('shangzhuang 发事件',
+ ['EQW_BANKER_SET','EQW_HAND_CHANGED'].every(e => evts.indexOf(e) >= 0), true);
+
+// 【守卫】控制权必须来自包里的 nextseat,不得用本地 banker 反推。
+// 真实链路里 nextseat 恒 == banker,两者相等时"读哪个"分辨不出来;这里刻意让它们不同,
+// 退回 `turn.seat = EQW_GameState.aset.banker` 的写法必转红。
+// 同理 data.seat(=最后一个叫分者)也不是控制权,这里也给成第三个值。
+S.reset();
+EQW_CallHandler.handleShangzhuang({ success: true, seat: 1, call: 55, banker: 0, grade: 55,
+ multiple: 4, step: 2, nextseat: 2,
+ countdown: 20, touxiang: 0, curmultiple: 3 });
+t.eq('控制权取 nextseat,不取 banker', S.turn.seat, 2);
+t.eq('控制权不取 data.seat(最后一个叫分者)', S.turn.seat !== 1, true);
+
+// 闲家视角:没有 cards / bottomcards(非 70 分),缺字段不能把已有值抹掉
+S.reset(); S.my.cards = [7, 8, 9];
+EQW_CallHandler.handleShangzhuang({ success: true, seat: 0, call: 55, banker: 0, grade: 55,
+ multiple: 4, step: 2, nextseat: 0,
+ countdown: 20, touxiang: 0, curmultiple: 3 });
+t.eq('闲家手牌不被抹', S.my.cards, [7, 8, 9]);
+t.eq('闲家无底牌', S.my.bottomCards, []);
+t.eq('非 70 分无开底标志', S.table.ancard3s, 0);
+
+process.exit(t.done('handlers_call') ? 0 : 1);
diff --git a/client/tests/test_handlers_play.js b/client/tests/test_handlers_play.js
new file mode 100644
index 0000000..508240f
--- /dev/null
+++ b/client/tests/test_handlers_play.js
@@ -0,0 +1,107 @@
+// 出牌 handler(chupai1/2/3)
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/net/handlers/PlayHandler.js');
+
+const S = EQW_GameState;
+const evts = [];
+Object.keys(EQW_Events).forEach(k => EventBus.on(EQW_Events[k], () => evts.push(k)));
+
+// ================= chupai1(首家出牌,自己出的)=================
+S.reset(); S.room.mySeat = 0; S.my.cards = [1,2,3,4]; evts.length = 0;
+EQW_PlayHandler.handle({ success: true, seat: 0, cards: [1,2], count: 2, flower: 3,
+ cardtype: 201, nextseat: 1, countdown: 15, baozhu: 0,
+ cardsinhand: [3,4], curmultiple: 2,
+ seatlist: [[0,0,0,0,[5,2]],[0,0,0,0,[6,1]],[0,0,0,0,[7,3]]] }, 1);
+
+t.eq('出牌者手牌被更新', S.my.cards, [3,4]);
+t.eq('下一个出牌者', S.turn.seat, 1);
+t.eq('倒计时', S.turn.countdown, 15);
+t.eq('抓分倍数', S.aset.curmultiple, 2);
+t.eq('牌况表', S.table.seatlist.length, 3);
+t.eq('出牌发事件', evts.indexOf('EQW_CARD_PLAYED') >= 0, true);
+
+// ================= chupai2(别人出牌)=================
+// 别人出牌时没有 cardsinhand,不能抹掉自己的手牌
+S.reset(); S.room.mySeat = 0; S.my.cards = [3,4]; evts.length = 0;
+EQW_PlayHandler.handle({ success: true, seat: 1, cards: [5,6], nextseat: 2,
+ countdown: 15, baozhu: 0 }, 2);
+t.eq('别人出牌不抹自己手牌', S.my.cards, [3,4]);
+t.eq('控制权推进', S.turn.seat, 2);
+
+// mustcard 只发给下一个出牌者
+S.reset(); S.room.mySeat = 2;
+EQW_PlayHandler.handle({ success: true, seat: 1, cards: [5,6], nextseat: 2,
+ countdown: 15, mustcard: [7,8] }, 2);
+t.eq('必出牌', S.my.mustCard, [7,8]);
+
+// 没有 mustcard 时要清空——否则上一轮的建议会残留
+S.reset(); S.my.mustCard = [7,8];
+EQW_PlayHandler.handle({ success: true, seat: 1, cards: [5,6], nextseat: 0, countdown: 15 }, 2);
+t.eq('无 mustcard 时清空', S.my.mustCard, []);
+
+// ================= chupai3(末家出牌,本轮结束)=================
+// 真实的 chupai3 也带 nextseat(下一轮首家):服务端在 switch 前统一设置,
+// 只有整轮结束进入结算时才会整体换成 jiesuan rpc(不会以 chupai3 到达 handler)
+// 本墩结果(谁最大 / 这墩得几分)是一次性表现,走 EQW_TRICK_END 事件载荷,【不进 GameState】——
+// 任何 deskinfo 都不下发它,存进来就是一处服务端不知道的私有对局态(出牌中途重连即归零)
+S.reset(); evts.length = 0;
+let lastTrick = null;
+EventBus.on(EQW_Events.EQW_TRICK_END, function (p) { lastTrick = p; });
+EQW_PlayHandler.handle({ success: true, seat: 2, cards: [9,10], maxseat: 0, grade: 25,
+ nextseat: 0, countdown: 15, baozhu: 1, curmultiple: -2 }, 3);
+t.eq('本墩结果走事件载荷', lastTrick, { seat: 2, cards: [9,10], maxseat: 0, grade: 25 });
+t.eq('本墩结果不进 GameState', S.result.hasOwnProperty('chupai'), false);
+t.eq('累计捡分', S.aset.grade, 25);
+t.eq('报无主标志', S.aset.baozhu, 1);
+t.eq('抓分倍数(带符号)', S.aset.curmultiple, -2);
+t.eq('下一轮首家', S.turn.seat, 0);
+t.eq('本轮结束发事件', evts.indexOf('EQW_TRICK_END') >= 0, true);
+
+// 本轮闲家没得分时不带 grade,累计分不该被抹;事件载荷也不得凭空补 grade(缺字段不兜底)
+S.reset(); S.aset.grade = 40; lastTrick = null;
+EQW_PlayHandler.handle({ success: true, seat: 2, cards: [9,10], maxseat: 1,
+ nextseat: 1, countdown: 15 }, 3);
+t.eq('无 grade 时累计分保持', S.aset.grade, 40);
+t.eq('无 grade 时事件载荷不带该键', lastTrick.hasOwnProperty('grade'), false);
+t.eq('无 grade 时仍推进控制权', S.turn.seat, 1);
+
+// grade 是「本轮」得分,要跨包累加进小局捡分,不是赋值——
+// 若实现改成 aset.grade = data.grade,这条会把 25 覆盖成 15 而非累加到 25
+S.reset();
+EQW_PlayHandler.handle({ success: true, seat: 2, cards: [1,2], maxseat: 0, grade: 10,
+ nextseat: 0, countdown: 15 }, 3);
+t.eq('第一轮捡分', S.aset.grade, 10);
+t.eq('第一轮下一轮首家', S.turn.seat, 0);
+EQW_PlayHandler.handle({ success: true, seat: 2, cards: [3,4], maxseat: 1, grade: 15,
+ nextseat: 1, countdown: 15 }, 3);
+t.eq('第二轮跨包累加捡分', S.aset.grade, 25);
+t.eq('第二轮下一轮首家(证明第二个包确被处理)', S.turn.seat, 1);
+
+// ================= 甩错 =================
+// shuaicuo 是「这一手」的一次性信息,走 EQW_CARD_PLAYED 事件载荷,不进 GameState;
+// playproc 是服务端下发的本轮进行态,原样拷贝,两者结构不同、不得混写(见 PlayHandler 顶部注释)。
+S.reset(); evts.length = 0;
+var lastPlayed = null;
+EventBus.on(EQW_Events.EQW_CARD_PLAYED, function (p) { lastPlayed = p; });
+EQW_PlayHandler.handle({ success: true, seat: 0, cards: [11], shuaicuo: 1, count: 1,
+ flower: 3, cardtype: 101, nextseat: 1, countdown: 15,
+ playproc: { round: 2, start: 0, currseat: 1, cards: [[11], null, null] } }, 1);
+t.eq('甩错标志走事件载荷', lastPlayed.shuaicuo, 1);
+t.eq('本手信息走事件载荷', [lastPlayed.order, lastPlayed.seat, lastPlayed.cards,
+ lastPlayed.count, lastPlayed.flower, lastPlayed.cardtype],
+ [1, 0, [11], 1, 3, 101]);
+t.eq('本手信息不进 GameState', S.table.playproc.hasOwnProperty('shuaicuo'), false);
+t.eq('playproc 原样拷贝服务端本轮进行态', S.table.playproc,
+ { round: 2, start: 0, currseat: 1, cards: [[11], null, null] });
+
+// ================= 缺必需字段 =================
+S.reset();
+EQW_PlayHandler.handle({ success: true, cards: [1] }, 1); //缺 seat
+t.eq('缺 seat 时跳过该包', S.turn.seat, -1);
+
+process.exit(t.done('handlers_play') ? 0 : 1);
diff --git a/client/tests/test_handlers_query.js b/client/tests/test_handlers_query.js
new file mode 100644
index 0000000..a558076
--- /dev/null
+++ b/client/tests/test_handlers_query.js
@@ -0,0 +1,47 @@
+// 明牌 / 提示 / 准备 handler
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/net/handlers/QueryHandler.js');
+load('client/js/01_SubGame/codes/net/handlers/ReadyHandler.js');
+
+const S = EQW_GameState;
+const payloads = [];
+EventBus.on(EQW_Events.EQW_TIP, d => payloads.push(d));
+const evts = [];
+Object.keys(EQW_Events).forEach(k => EventBus.on(EQW_Events[k], () => evts.push(k)));
+
+// ================= mingpai =================
+S.reset(); evts.length = 0;
+EQW_QueryHandler.handleMingpai({ success: true, seat: 1,
+ others: [{ seat: 0, zhucards: [52, 2] }, { seat: 2, zhucards: [53, 15] }] });
+t.eq('明牌数据入状态', S.table.mingpai,
+ [{ seat: 0, zhucards: [52, 2] }, { seat: 2, zhucards: [53, 15] }]);
+t.eq('明牌发事件', evts.indexOf('EQW_MINGPAI') >= 0, true);
+
+// ================= tishi =================
+// 提示是一次性的,不进 GameState,只随事件带出去
+S.reset(); payloads.length = 0; evts.length = 0;
+const before = S.snapshot(); // 处理前的状态快照
+EQW_QueryHandler.handleTishi({ success: true, seat: 2, tip: 1 });
+t.eq('提示发事件', evts.indexOf('EQW_TIP') >= 0, true);
+t.eq('提示内容随事件带出', payloads, [{ seat: 2, tip: 1 }]);
+t.eq('提示未改动 GameState 任何字段', S.snapshot(), before);
+
+// ================= zhunbei =================
+S.reset(); evts.length = 0;
+EQW_ReadyHandler.handle({ success: true, seat: 1 });
+t.eq('准备发事件', evts.indexOf('EQW_READY_CHANGED') >= 0, true);
+
+// ================= 缺必需字段 =================
+S.reset();
+EQW_QueryHandler.handleMingpai({ success: true }); //缺 others
+t.eq('明牌缺 others 时不写', S.table.mingpai, null);
+payloads.length = 0;
+EQW_QueryHandler.handleTishi({ success: true, seat: 0 }); //缺 tip
+t.eq('提示缺 tip 时不发事件', payloads, []);
+
+process.exit(t.done('handlers_query') ? 0 : 1);
diff --git a/client/tests/test_handlers_result.js b/client/tests/test_handlers_result.js
new file mode 100644
index 0000000..f9d232d
--- /dev/null
+++ b/client/tests/test_handlers_result.js
@@ -0,0 +1,85 @@
+// 结算 / 解散 handler
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/net/handlers/ResultHandler.js');
+
+const S = EQW_GameState;
+const evts = [];
+Object.keys(EQW_Events).forEach(k => EventBus.on(EQW_Events[k], () => evts.push(k)));
+let lastTrick = null;
+EventBus.on(EQW_Events.EQW_TRICK_END, p => { lastTrick = p; });
+
+const ASET = { banker: 0, call: 60, multiple: 3, flower: 3, grade: 45, upgrade: 1,
+ bangwang: 0, climb: 0,
+ seatlist: [{ grade: 6, score: 6 }, { grade: -3, score: -3 }, { grade: -3, score: -3 }] };
+
+// ================= 正常出牌结算 =================
+S.reset(); evts.length = 0; lastTrick = null;
+EQW_ResultHandler.handleJiesuan({ success: true, step: 6,
+ chupai: { seat: 2, cards: [9], maxseat: 0, grade: 10 },
+ bottom: { cards: [1,2,3,4,5,6,7,8], multiple: 2, grade1: 20, grade2: 40 },
+ aset: ASET });
+
+t.eq('小局结算入状态', S.result.aset, ASET);
+t.eq('抠底入状态', S.result.bottom.grade2, 40);
+// 收尾墩(服务端不发 chupai3,整包换成 jiesuan)走事件载荷,与 chupai3 同一对事件、同一份字段;
+// 【不进 GameState】——deskinfo step6 的 Balance 只有 aset/bottom/account,多存就是私有对局态
+t.eq('收尾墩走事件载荷', lastTrick, { seat: 2, cards: [9], maxseat: 0, grade: 10 });
+t.eq('收尾墩也发本墩结束事件', evts.indexOf('EQW_TRICK_END') >= 0, true);
+t.eq('收尾墩不进 GameState', S.result.hasOwnProperty('chupai'), false);
+t.eq('step 读包字段(结算=6)', S.aset.step, 6);
+t.eq('结算发事件', evts.indexOf('EQW_ASET_RESULT') >= 0, true);
+t.eq('非末局不发大局结算事件', evts.indexOf('EQW_ACCOUNT_RESULT') < 0, true);
+
+// ================= 投降结算(无 chupai / 无 bottom)=================
+S.reset(); evts.length = 0; lastTrick = null;
+EQW_ResultHandler.handleJiesuan({ success: true, step: 6, aset: { banker: 0, call: 70, multiple: 1,
+ upgrade: -99, seatlist: [] } });
+t.eq('投降结算入状态', S.result.aset.upgrade, -99);
+t.eq('投降无 chupai → 不发本墩结束事件', lastTrick, null);
+t.eq('投降无 bottom', S.result.bottom, null);
+t.eq('投降也发结算事件', evts.indexOf('EQW_ASET_RESULT') >= 0, true);
+
+// ================= 末局:带 account =================
+const ACCOUNT = [{ score: 12, grades: [6,6], grade_jf_total: 8, grade_cg_total: 4, grade_bw_total: 0 },
+ { score: -6, grades: [-3,-3], grade_jf_total: -4, grade_cg_total: -2, grade_bw_total: 0 },
+ { score: -6, grades: [-3,-3], grade_jf_total: -4, grade_cg_total: -2, grade_bw_total: 0 }];
+S.reset(); evts.length = 0;
+EQW_ResultHandler.handleJiesuan({ success: true, step: 6, aset: ASET, account: ACCOUNT });
+t.eq('大局结算入状态', S.result.account, ACCOUNT);
+t.eq('末局发大局结算事件', evts.indexOf('EQW_ACCOUNT_RESULT') >= 0, true);
+
+// ================= 解散:走 Free 入口,取值少一层 =================
+S.reset(); evts.length = 0;
+EQW_ResultHandler.handleFree({ rpc: 'jiesuan',
+ data: { success: true, step: 6, aset: { banker: -1, call: -1, multiple: 0, upgrade: 0, seatlist: [] },
+ account: ACCOUNT } });
+t.eq('解散取到 aset', S.result.aset.multiple, 0);
+t.eq('解散取到 account', S.result.account, ACCOUNT);
+t.eq('解散发大局结算事件', evts.indexOf('EQW_ACCOUNT_RESULT') >= 0, true);
+t.eq('解散包的 step 也读包字段', S.aset.step, 6);
+
+// 【守卫】结算 step 只读包字段,不按 rpc 名硬编码为 6。
+// 退回 `aset.step = 6` 的写法必转红。
+S.reset();
+EQW_ResultHandler.handleJiesuan({ success: true, step: 4, aset: ASET });
+t.eq('jiesuan step 照读包字段', S.aset.step, 4);
+
+// ================= 解散:deskfree 为 null(首局发牌前解散)=================
+S.reset(); evts.length = 0;
+let boom = false;
+try { EQW_ResultHandler.handleFree(null); } catch (e) { boom = true; }
+t.eq('deskfree 为 null 不崩', boom, false);
+t.eq('deskfree 为 null 不写状态', S.result.aset, null);
+t.eq('deskfree 为 null 不发结算事件', evts.indexOf('EQW_ACCOUNT_RESULT') < 0, true);
+
+// deskfree 存在但 data 缺失也要容忍
+boom = false;
+try { EQW_ResultHandler.handleFree({ rpc: 'jiesuan' }); } catch (e) { boom = true; }
+t.eq('deskfree.data 缺失不崩', boom, false);
+
+process.exit(t.done('handlers_result') ? 0 : 1);
diff --git a/client/tests/test_hooks.js b/client/tests/test_hooks.js
new file mode 100644
index 0000000..c53df31
--- /dev/null
+++ b/client/tests/test_hooks.js
@@ -0,0 +1,72 @@
+// SubGameHooks 接线:平台入口 → 新架构
+const { load } = require('./_load');
+const t = require('./_assert')();
+
+global.window = global; // 转发壳用 window.SubGameHooks 判定
+
+load('client/js/gameabc-framework/system/EventBus.js');
+load('client/js/gameabc-framework/system/SpriteEventController.js');
+load('client/js/01_SubGame/codes/state/Events.js');
+load('client/js/01_SubGame/codes/state/GameState.js');
+load('client/js/01_SubGame/codes/state/RoomOptions.js');
+['DealHandler','CallHandler','MainHandler','BuryHandler','PlayHandler',
+ 'QueryHandler','ReadyHandler','ResultHandler','ResyncHandler','FailHandler']
+ .forEach(h => load('client/js/01_SubGame/codes/net/handlers/' + h + '.js'));
+load('client/js/01_SubGame/codes/net/Dispatcher.js');
+load('client/js/01_SubGame/codes/SubGameHooks.js');
+
+const S = EQW_GameState;
+
+// ---- _ReceiveData:完整包 → 分发 ----
+S.reset(); S.room.mySeat = 0;
+SubGameHooks._ReceiveData({ app: 'youle', route: 'erqiwang', rpc: 'fapai',
+ data: { success: true, asetidx: 1, asetcount: 6,
+ cards: [1,2], seat: 0, countdown: 15 } });
+t.eq('_ReceiveData 分发到 handler', S.my.cards, [1,2]);
+
+// ---- setRoomDes ----
+SubGameHooks.setRoomDes(123, 12, '00010');
+t.eq('setRoomDes 解析选项', S.room.options.climb, 1);
+
+// ---- Free:null 不崩 ----
+let boom = false;
+try { SubGameHooks.Free(null); } catch (e) { boom = true; }
+t.eq('Free(null) 不崩', boom, false);
+
+// ---- Reconnect ----
+S.reset(); S.room.mySeat = 1;
+SubGameHooks.Reconnect({ count: 6, idx: 3, PlayerInfo: [0,0,0], step: 1, MyCards: [5,6] });
+t.eq('Reconnect 填状态', S.my.cards, [5,6]);
+t.eq('Reconnect 填局数', S.room.asetIdx, 3);
+
+// ---- DeskInfo(中途进桌)----
+// 平台 07_Desk.js 在「牌局已开始 + 走加入房间而非重连」时调 Game_Modify.DeskInfo(_msg.data.deskinfo)。
+// 入参结构与 Reconnect 完全一致(服务端两条路径都是 get_deskinfo(o_room, 本人座位)),
+// 故必须走同一条重画路径;不接线的话整份 deskinfo 被转发壳静默丢弃、界面空白且不报错。
+const MIDJOIN = { count: 6, idx: 2, PlayerInfo: [3,-1,-2], step: 5, MyCards: [10,11],
+ PushCards: { banker: 1, call: 60, multiple: 3, flower: 2, grade: 25,
+ curmultiple: -1, baozhu: 0, countdown: 30,
+ playproc: { round: 4, start: 1, currseat: 2, cards: [null,[12],null] } } };
+S.reset(); S.room.mySeat = 2;
+SubGameHooks.DeskInfo(MIDJOIN);
+t.eq('DeskInfo 已接线(不再静默丢包)', S.my.cards, [10,11]);
+t.eq('DeskInfo 复用重连的重画路径(阶段)', S.aset.step, 5);
+t.eq('DeskInfo 复用重连的重画路径(控制权读 playproc.currseat)', S.turn.seat, 2);
+t.eq('DeskInfo 与 Reconnect 落地结果逐字段相同',
+ (function () { const a = JSON.stringify(EQW_GameState.snapshot());
+ S.reset(); S.room.mySeat = 2; SubGameHooks.Reconnect(MIDJOIN);
+ return a === JSON.stringify(EQW_GameState.snapshot()); })(), true);
+
+// ---- 畸形入参一律不崩 ----
+boom = false;
+try {
+ SubGameHooks._ReceiveData(null);
+ SubGameHooks._ReceiveData({});
+ SubGameHooks.StartWar(null);
+ SubGameHooks.Reconnect(null);
+ SubGameHooks.DeskInfo(null);
+ SubGameHooks.setRoomDes();
+} catch (e) { boom = true; }
+t.eq('畸形入参不崩', boom, false);
+
+process.exit(t.done('hooks') ? 0 : 1);
diff --git a/client/tests/test_index_load_order.js b/client/tests/test_index_load_order.js
new file mode 100644
index 0000000..c347d9f
--- /dev/null
+++ b/client/tests/test_index_load_order.js
@@ -0,0 +1,109 @@
+// index.html 加载顺序自检(task-13 步骤6守卫):
+// 按 index.html 里