二七王:不可查牌模式屏蔽出牌历史(design §9 消歧义 + 代码门控)
design §9 原文把可查牌模式的四项功能列成无序列表,不可查牌只写 「不提供以上任何查看 / 明牌功能」,随后展开的例子却只提了主牌数量、 对子结构、明牌按钮、亮牌统计,没提第一条「已经打出的牌可以查看」, 导致"以上"的范围有歧义。代码据此照发出牌历史,前一轮核对把它记为 待确认项(01 表 D6)。 规则设计者已确认:**不可查牌模式必须屏蔽出牌历史,只有可查牌模式 才能查看**。据此: - design §9 改写为无歧义表述:先给出「查牌 = 回看不该随时可见的信 息」的定义,把可查牌的四项功能编号列出(① 出牌历史 ② 报无主后的 他家主牌数/对子数 ③ 明牌 ④ 庄家亮牌),不可查牌逐条对应说明一项 都不提供;并补一段边界说明——**当前这一轮桌面上的牌两种模式下都 必须可见**,否则后出的人无从跟牌、无从判断本轮谁最大,断线重连同 理(当前轮照常恢复,往轮历史按模式开关给不给)。 - class.export.js:get_deskinfo 的 PushCards.pushlist 构建与排序整 体移入 `if (!cfg.nocheck)`,不查牌时不带该属性;当前轮桌面牌由 PushCards.playproc.cards 恢复,两种模式都有。 - 协议文档:pushlist 补「仅可查牌模式下有此属性」,playproc 补「两 种模式恒有」及其原因。 - 合规文档:01 表 D6 由「待确认/未修」改为已确认并修复,从「尚未处 置」移出;02 测试计划断言总数 357→361 并新增该维度覆盖行。 test_rpc 新增 4 项:不查牌房无 pushlist、可查牌房有 pushlist,且两 种模式下当前轮桌面牌 playproc.cards 都能恢复。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -166,38 +166,42 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
|
||||
}
|
||||
}
|
||||
}
|
||||
//出牌历史
|
||||
deskinfo.PushCards.pushlist = [];
|
||||
for (var i = 0; i < paiju.cards.length; i++) {
|
||||
var pai = paiju.cards[i];
|
||||
if (pai.playround > 0){
|
||||
if (deskinfo.PushCards.pushlist.length < pai.playround){
|
||||
deskinfo.PushCards.pushlist.length = pai.playround;
|
||||
}
|
||||
if (!deskinfo.PushCards.pushlist[pai.playround - 1]){
|
||||
deskinfo.PushCards.pushlist[pai.playround - 1] = [[], [], []];
|
||||
}
|
||||
//出牌历史:往轮打出、已被收走的牌属于「查牌」范畴,仅可查牌模式下发(design §9)。
|
||||
//不查牌模式下不带此属性——当前这一轮桌面上的牌由上面的 playproc 恢复,两种模式都有,
|
||||
//否则后出的人无从跟牌(design §9 末尾的边界说明)
|
||||
if (!cfg.nocheck){
|
||||
deskinfo.PushCards.pushlist = [];
|
||||
for (var i = 0; i < paiju.cards.length; i++) {
|
||||
var pai = paiju.cards[i];
|
||||
if (pai.playround > 0){
|
||||
if (deskinfo.PushCards.pushlist.length < pai.playround){
|
||||
deskinfo.PushCards.pushlist.length = pai.playround;
|
||||
}
|
||||
if (!deskinfo.PushCards.pushlist[pai.playround - 1]){
|
||||
deskinfo.PushCards.pushlist[pai.playround - 1] = [[], [], []];
|
||||
}
|
||||
|
||||
var owner = pai.dealowner;
|
||||
if (owner == 0){
|
||||
owner = paiju.banker + 1;
|
||||
var owner = pai.dealowner;
|
||||
if (owner == 0){
|
||||
owner = paiju.banker + 1;
|
||||
}
|
||||
deskinfo.PushCards.pushlist[pai.playround - 1][owner - 1].push(i);
|
||||
}
|
||||
}
|
||||
//按大小排序:外层下标=轮次、内层恒为 3 个座位;排序须按本局主牌花色
|
||||
//(内层曾误用轮次数作上界,会把内层数组撑成轮次长度并填入 undefined;
|
||||
// 且曾误用上一个循环泄漏出来的 pai.flower——那是最后一张牌大王的花色 5,
|
||||
// 等于按「无主牌」排序)
|
||||
for (var i = 0; i < deskinfo.PushCards.pushlist.length; i++) {
|
||||
if (!deskinfo.PushCards.pushlist[i]){
|
||||
continue;
|
||||
}
|
||||
for (var j = 0; j < 3; j++) {
|
||||
deskinfo.PushCards.pushlist[i][j] = cls_youle_erqiwang_arith.order_cards(paiju.flower, deskinfo.PushCards.pushlist[i][j]);
|
||||
}
|
||||
deskinfo.PushCards.pushlist[pai.playround - 1][owner - 1].push(i);
|
||||
}
|
||||
}
|
||||
//按大小排序:外层下标=轮次、内层恒为 3 个座位;排序须按本局主牌花色
|
||||
//(内层曾误用轮次数作上界,会把内层数组撑成轮次长度并填入 undefined;
|
||||
// 且曾误用上一个循环泄漏出来的 pai.flower——那是最后一张牌大王的花色 5,
|
||||
// 等于按「无主牌」排序)
|
||||
for (var i = 0; i < deskinfo.PushCards.pushlist.length; i++) {
|
||||
if (!deskinfo.PushCards.pushlist[i]){
|
||||
continue;
|
||||
}
|
||||
for (var j = 0; j < 3; j++) {
|
||||
deskinfo.PushCards.pushlist[i][j] = cls_youle_erqiwang_arith.order_cards(paiju.flower, deskinfo.PushCards.pushlist[i][j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case 6: //结算
|
||||
deskinfo.Balance = {};
|
||||
//玩家的准备状态
|
||||
|
||||
@@ -82,13 +82,14 @@
|
||||
| D3 | 🟧 中等 | `class.desk.js` | 下发了 `countdown`,但服务端**无任何定时器**做超时兜底/托管,玩家掉线不操作则牌局永久卡死(server dev-guide「服务端自动操作复用真人链路」) | **未修**,见下方「待办」 |
|
||||
| D4 | 🟨 轻微 | `paiju.do_playcard` | 报无主后只更新出牌者自己那一格,B/C 要等各自轮到出牌才补上,界面最多滞后两次出牌(design §9 要求「为全体三人显示另外两家」) | 改为一旦 `have_baofu()` 成立即按实际手牌刷新三个座位 |
|
||||
| D5 | 🟨 轻微 | `can_playcard` / `chupai1` | `cardtype` 只能表达单一牌型,甩牌被压平成 1xx/2xx(「主K对+主5」→103),客户端连"这是一次甩牌"都看不出来 | `chupai1` 增发 `shuai` 分量构成(与 `shuai_demand` 同源) |
|
||||
| D6 | 🟨 待确认 | `export.get_deskinfo` | 不查牌模式下 `pushlist`(已打出牌历史)仍照发。design §9「不可查牌模式:不提供以上任何查看功能」的"以上"是否含第一条(查看已打出的牌),**规则本身有歧义** | **未修**,需规则设计者确认 |
|
||||
| D6 | 🟧 中等 | `export.get_deskinfo` | 不查牌模式下 `pushlist`(已打出牌历史)仍照发。规则设计者已确认:**不可查牌模式必须屏蔽出牌历史,只有可查牌模式才能查看** | 已按确认修复:`pushlist` 按 `cfg.nocheck` 门控;design §9 同步改写为无歧义表述(四项功能整体开关 + 「当前轮桌面牌不属于查牌、两种模式都可见」的边界) |
|
||||
| D7 | 🟨 轻微 | `check_cards_inhand` | 牌已出/已埋时 `return;`(undefined)而非 `false` | 已改为 `return false` |
|
||||
|
||||
### 尚未处置(需外部输入)
|
||||
|
||||
- **D3 超时托管**:服务端对四个阶段只下发 `countdown` 数值,没有对应的定时器与超时后的默认动作(自动叫分/选主/埋牌/出牌)。design.md 未规定托管行为,且默认动作与前端表现强相关,需与前端和规则设计者一起定义后再实现。当前状态下**任一玩家掉线不操作,该局会永久停住**。
|
||||
- **D6 不查牌模式的范围**:见上表,等规则设计者确认后再决定是否屏蔽 `pushlist`。
|
||||
|
||||
> D6 原为「规则歧义、待确认」,现已由规则设计者拍板(不可查牌屏蔽出牌历史),已改 design §9 + 代码 + 单测,不再是待办。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
## 5. 当前状态小结 —— ✅ DoD 达成,P0/P1/P2 全部完成
|
||||
|
||||
**共 357 项断言全绿**(`test_arith` 106 / `test_callgrade` 6 / `test_config` 19 / `test_deal` 12 / `test_desk` 6 / `test_follow` 50 / `test_input` 34 / `test_paiju` 31 / `test_rpc` 64 / `test_success` 29)。
|
||||
**共 361 项断言全绿**(`test_arith` 106 / `test_callgrade` 6 / `test_config` 19 / `test_deal` 12 / `test_desk` 6 / `test_follow` 50 / `test_input` 34 / `test_paiju` 31 / `test_rpc` 68 / `test_success` 29)。
|
||||
|
||||
design 全部服务端可验证章节均有正/反/边界单测:
|
||||
- **§2 构成**:`test_deal` 92张/去3-4/三家28+底8/分值。
|
||||
@@ -179,9 +179,11 @@ design 全部服务端可验证章节均有正/反/边界单测:
|
||||
| §7.3 下发面 multiple 与结算同源 | `mod.jiaofen` / `get_deskinfo` / `get_paiju_account` | 正:叫 45 分在常规房/爬坡房分别得 6/7;正:上庄包与重连 ChooseMain 一致;边界:与结算包 `aset.multiple` 逐一对齐 | `test_rpc` | ✅ 7 |
|
||||
| §5.4 甩牌下发面 | `chupai1` | 正:合法甩牌带 `shuai` 分量;反:非甩牌不带;反:甩错只打最小一张且不带 `shuai` | `test_rpc` | ✅ 8 |
|
||||
| 重连 pushlist 结构 | `get_deskinfo` | 正:轮数正确、内层恒 3 个座位、无 undefined;正:按本局主牌花色排序(主5 排在副A 前) | `test_rpc` | ✅ 4 |
|
||||
| §9 出牌历史按查牌模式门控 | `get_deskinfo` | 反:不查牌房无 `pushlist`;正:可查牌房有 `pushlist`;边界:两种模式下当前轮桌面牌 `playproc.cards` 都必须恢复 | `test_rpc` | ✅ 4 |
|
||||
| §3 相邻链花色 | `is_continuous` | 正:同花色 8-6 连;反:三组跨花色 8-6 不连;反:差 2 但非 8/6 不连 | `test_arith` | ✅ 5 |
|
||||
|
||||
**仍无自动化覆盖(需外部输入后才能定义期望)**:
|
||||
|
||||
- **超时托管**(`01` 表 D3):服务端只下发 `countdown`、无定时器与默认动作,玩家不操作则牌局永久停住。默认动作需与前端/规则设计者共同定义,定义前无法写期望。
|
||||
- **不查牌模式是否屏蔽出牌历史**(`01` 表 D6):design §9 表述有歧义,待规则设计者确认。
|
||||
|
||||
> 原列在此处的「不查牌模式是否屏蔽出牌历史」已由规则设计者确认(屏蔽),design §9 改写为无歧义表述,代码与用例均已落地,见上表「§9 出牌历史按查牌模式门控」。
|
||||
|
||||
@@ -619,12 +619,20 @@
|
||||
|
||||
## 9. 牌局查看模式
|
||||
|
||||
- **可查牌模式**:
|
||||
- 当前牌局中,所有玩家已经打出的牌,任何时候都可以查看。
|
||||
- 一旦有玩家的主牌全部打空(即 5.4.2 节所说的"报无主"状态;"主牌"指大王、小王、所有花色的 2、所有花色的 7,以及主花色的其余普通牌,见第 3 节),系统就为**全体三人**(含刚刚报无主的这位玩家自己)显示另外两家各自的主牌数量、以及这些主牌里有多少对子。
|
||||
- 同时界面上会出现一个"明牌"按钮:点击后可以查看另外两家手中全部主牌的**具体牌面**(不只是数量 / 对子结构),再点一次取消查看、恢复隐藏。
|
||||
- 庄家若触发了 8.2 节的"亮牌要求",两个闲家可以看到对应的统计信息(只亮数量 / 结构,不亮具体牌面,见 8.2 节)。
|
||||
- **不可查牌模式**:不提供以上任何查看 / 明牌功能——即使有玩家报无主,也不会显示任何人的主牌数量、对子结构,也没有"明牌"按钮;庄家即使触发了 8.2 节的亮牌条件,闲家也看不到相应的统计信息(见 8.2 节)。
|
||||
"查牌"指的是**回看那些不该随时可见的信息**:已经打过去的牌、他家手里还剩多少主牌、他家主牌的具体牌面。房间的"查牌模式"开关决定下列四项功能**整体**是否提供。
|
||||
|
||||
- **可查牌模式**(四项功能全部提供):
|
||||
1. **出牌历史**:当前牌局中,所有玩家已经打出的牌(含之前每一轮打出、已被收走的牌),任何时候都可以回看。
|
||||
2. 一旦有玩家的主牌全部打空(即 5.4.2 节所说的"报无主"状态;"主牌"指大王、小王、所有花色的 2、所有花色的 7,以及主花色的其余普通牌,见第 3 节),系统就为**全体三人**(含刚刚报无主的这位玩家自己)显示另外两家各自的主牌数量、以及这些主牌里有多少对子。
|
||||
3. 同时界面上会出现一个"明牌"按钮:点击后可以查看另外两家手中全部主牌的**具体牌面**(不只是数量 / 对子结构),再点一次取消查看、恢复隐藏。
|
||||
4. 庄家若触发了 8.2 节的"亮牌要求",两个闲家可以看到对应的统计信息(只亮数量 / 结构,不亮具体牌面,见 8.2 节)。
|
||||
|
||||
- **不可查牌模式**:以上四项**一项都不提供**——
|
||||
- **不提供出牌历史**:之前各轮打出过什么牌一律不能回看,只能靠玩家自己记牌;
|
||||
- 即使有玩家报无主,也不会显示任何人的主牌数量、对子结构,也没有"明牌"按钮;
|
||||
- 庄家即使触发了 8.2 节的亮牌条件,闲家也看不到相应的统计信息(见 8.2 节)。
|
||||
|
||||
> **"查牌"与"当前这一轮桌面上的牌"是两回事**:本轮已经出过牌的玩家、打在桌面上的这几张牌,**两种模式下都必须对全体可见**——否则后出的人无从跟牌、也无从判断本轮谁最大。查牌模式管的是**已经收走的往轮牌**与**他家手牌信息**能不能回看,不影响当前这一轮的正常出牌展示(断线重连回到牌桌时同理:当前轮桌面上的牌照常恢复,往轮历史则按本节的模式开关决定给不给)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -466,10 +466,10 @@
|
||||
| bottomcards | 数组 | 埋的牌,只有庄家有此属性 |
|
||||
| grade | 整数 | 当前的捡分分数 |
|
||||
| gradecards | 数组 | 当前的捡分分牌,只有闲家有此属性 |
|
||||
| playproc | json | 当前出牌情况 `o_paiju.playproc`:`round` 第几轮、`start` 本轮首出位置、`currseat` 当前出牌位置、`startcount` 首出张数、`startflower` 首出花色、`starttype` 首出牌型、`maxseat` 本轮最大者位置、`maxcard` 最大牌编码、`cards` 本轮三家各自出的牌(数组下标=位置序号)、`shuai_demand` 首家甩牌的分量需求 `{tractors:[连对数...],pairs,singles}`(非甩牌为 null,供跟牌逐分量强制匹配,design §5.4.4)|
|
||||
| playproc | json | 当前出牌情况 `o_paiju.playproc`:`round` 第几轮、`start` 本轮首出位置、`currseat` 当前出牌位置、`startcount` 首出张数、`startflower` 首出花色、`starttype` 首出牌型、`maxseat` 本轮最大者位置、`maxcard` 最大牌编码、`cards` 本轮三家各自出的牌(数组下标=位置序号)、`shuai_demand` 首家甩牌的分量需求 `{tractors:[连对数...],pairs,singles}`(非甩牌为 null,供跟牌逐分量强制匹配,design §5.4.4)。**两种查牌模式下恒有此属性**——`cards` 是当前这一轮桌面上的牌,不属于「查牌」,屏蔽了后出的人就无从跟牌(design §9 末尾)|
|
||||
| seatlist | 数组 | 三家座位牌况,元素结构同上文 chupai 包的 `info`(每个玩家一个 5 元素数组:4 个花色的 `[无该花色,无对]` + 报副 `[剩余主牌数,剩余主对数]`)。**仅可查牌模式下有此属性**(design §9)|
|
||||
| liangpai | json | 庄家亮牌信息,结构同 maipai 包的 `liangpai`;**仅可查牌模式、且请求者为闲家、且庄家达标时有**(供闲家重连后仍能看到亮牌,design §8.2)|
|
||||
| pushlist | 数组 | 出牌历史 `[[[], [], []], [[], [], []], ...]`,外层下标=轮次(从第 1 轮起,含进行中的当前轮),内层**恒为 3 个数组**、按位置序号存该轮各家出的牌id,各自已按**本局主牌花色**从大到小排序 |
|
||||
| pushlist | 数组 | 出牌历史 `[[[], [], []], [[], [], []], ...]`,外层下标=轮次(从第 1 轮起,含进行中的当前轮),内层**恒为 3 个数组**、按位置序号存该轮各家出的牌id,各自已按**本局主牌花色**从大到小排序。**仅可查牌模式下有此属性**——往轮打出、已被收走的牌属于「查牌」范畴,不查牌模式一律不下发(design §9)。注意当前这一轮桌面上的牌由 `playproc.cards` 恢复,**两种模式下都有**,否则后出的人无从跟牌 |
|
||||
|
||||
**Balance(不在结算阶段无此属性)**
|
||||
|
||||
|
||||
@@ -198,13 +198,17 @@ function mkPushlist() {
|
||||
// seat0 第1轮补出:主5(flower1) 与 副♥A(flower3),主牌必须排在副牌前
|
||||
const zhu5 = id(1, 1, 5), fuA = id(1, 3, 1);
|
||||
for (const c of [zhu5, fuA]) { pj.cards[c].dealowner = 1; pj.cards[c].playround = 1; }
|
||||
// 当前轮(第5轮)桌面上的牌:两种查牌模式下都必须能恢复(design §9 边界说明)
|
||||
const cur = id(2, 1, 10);
|
||||
pj.cards[cur].dealowner = 1; pj.cards[cur].playround = 5;
|
||||
pj.playproc.cards[0] = [cur];
|
||||
pj.method = {
|
||||
get_seat_cards: s => P.get_seat_cards(pj, s),
|
||||
get_jian_grade: () => P.get_jian_grade(pj),
|
||||
get_burycard: () => P.get_burycard(pj),
|
||||
get_liangpai: () => null
|
||||
};
|
||||
return { pj, zhu5, fuA };
|
||||
return { pj, zhu5, fuA, cur };
|
||||
}
|
||||
const pl = mkPushlist();
|
||||
const plRoom = setup("00000", pl.pj).o_room;
|
||||
@@ -215,6 +219,14 @@ t.eq('重连 pushlist 不含 undefined', di.some(r => r.some(x => x === undefine
|
||||
// 第1轮 seat0 的牌:主5 必须排在副♥A 之前(按 paiju.flower=1 排序,而非泄漏的大王花色5)
|
||||
t.eq('重连 pushlist 按本局主牌花色排序(主5在副A前)',
|
||||
di[0][0].indexOf(pl.zhu5) < di[0][0].indexOf(pl.fuA), true);
|
||||
// §9 不查牌模式:出牌历史属于「查牌」,一律不下发;但当前轮桌面上的牌必须照常恢复
|
||||
const pl2 = mkPushlist();
|
||||
const pc2 = E.get_deskinfo(setup("00001", pl2.pj).o_room, 1).PushCards;
|
||||
t.eq('重连 不查牌 无出牌历史 pushlist', pc2.pushlist, undefined);
|
||||
t.eq('重连 不查牌 当前轮桌面牌仍恢复(playproc.cards)', pc2.playproc.cards[0], [pl2.cur]);
|
||||
const pc1 = E.get_deskinfo(plRoom, 1).PushCards;
|
||||
t.eq('重连 可查牌 有出牌历史 pushlist', Array.isArray(pc1.pushlist), true);
|
||||
t.eq('重连 可查牌 当前轮桌面牌也在', pc1.playproc.cards[0], [pl.cur]);
|
||||
|
||||
// ===================== §7.3 下发面 multiple 必须随「爬坡」开关取值(与结算 aset.multiple 同源)=====================
|
||||
// 叫 45 分:常规算子基础子数 6(design §7.1「50 及以下统一 6」),爬坡 7(design §7.3.1)
|
||||
|
||||
Reference in New Issue
Block a user