二七王文档:同步 design 流程/协议,新增合规核对
design.md - 修正选主/埋牌顺序笔误(确认「先选主后埋牌」) - 投降改为选主阶段与选主互斥的选择(不选主/不埋牌直接结算),算奖用庄家36张、无连对链 - 70分暗牌改为「庄家摸牌之前、向所有玩家亮3秒」 - 选主阶段前端显示每花色对数、70分并列投降按钮 - 新增 §12「完整牌局游玩流程」(含大局与阶段速览) packet_protocol.md(随代码如实同步) - 结算 aset/seatlist 新结构(multiple=基础子数、upgrade 判定倍率、bangwang/climb、 chongguan/wang/naward/grade_aw);shangzhuang bottomcards 按70分门控 + ancard3s - maipai/PushCards 亮牌 liangpai;chupai info/baozhu 按查牌门控;新增 mingpai 收发包 - step 枚举去掉废弃的投降4;删除 Surrender 重连视图 docs/compliance/(新增) - 00 初步不一致清单、01 design 合规逐节核对(含整改进度与 roomtype 位串约定) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# 二七王 服务端实现 vs 设计文档:初步不一致清单
|
||||
|
||||
> 本文是首轮通读 `server/games/erqiwang/*.js` 与 `docs/design/design.md` 后记录的**初步**疑点清单,作为逐条深入核对的线索。完整、逐节的核对结论见同目录 [`01-design合规逐节核对.md`](./01-design合规逐节核对.md)。
|
||||
>
|
||||
> 方向约定(与根 `CLAUDE.md` 一致):`design.md` 是玩法规则的唯一权威,代码与其冲突时**应改代码去符合规则**(除非规则标「待确认」);本清单只做记录,不代表已修复。
|
||||
|
||||
---
|
||||
|
||||
## 1. 算子(基础子数)与设计表格完全对不上
|
||||
|
||||
- 设计 §7.1 常规算子基础子数:65→2、60→3、55→4、50 及以下→6;70 投降→1、70 打牌→2。
|
||||
- 代码 `arith.get_multiple_bycall` 只返回 1/2/4/0(`call>60→1`、`call>40→2`、`call>0→4`),与设计四档数值、结构均不符。
|
||||
|
||||
## 2. 爬坡 / 傍王 房间选项未做成开关
|
||||
|
||||
- `arith.get_upgrade` 用 `halfcall = ceil(call/2)` 作小光/过庄分界与升级级距;设计常规算子该分界**固定 40**、级距**固定 40**。
|
||||
- 代码里**没有任何读取房间「爬坡 / 傍王」勾选的分支**:`grade_w`(傍王)无条件计算、`halfcall` 恒用。设计 §7.3 / §8.3 / §10.3 中这两项都是可选规则,需要开关。
|
||||
|
||||
## 3. 扣底倍数不符
|
||||
|
||||
- 设计 §6.3:单张主 ×1、主对子 ×2、两连对 ×4、三连对 ×6、N 连对 ×2N(线性)。
|
||||
- 代码 `arith.get_bottom_multiple`:单张 →2、对子 →4、两连对 →8、三连对 →16、四连对 →32、五连对 →64(近似指数),且单张即翻倍;六连对落空返回 `undefined`。
|
||||
|
||||
## 4. 「固定主牌 ≥10 张」给了算奖
|
||||
|
||||
- 代码 `arith.get_chongguan` 对 `王+2+7≥10` 追加 `total−9` 奖(「10 个老主判断」)。
|
||||
- 设计 §8.2 明确举例:固定主牌 ≥10 张只触发**亮牌**、**不触发任何算奖**。
|
||||
|
||||
## 5. 算奖归属模型与结算方式不同
|
||||
|
||||
- 设计 §8.1:常规算奖**只计庄家**;§8.3 傍王才庄闲都算(且需勾选);§8.4:算奖额外支付额 = `X × N`(`X` 为第 7 节算出的基础输赢子数)。
|
||||
- 代码:对**三家**都算「冲关」并两两结算,且叠加「冲关必叫 / 四王必踢」的**作废**规则(设计中无此机制);`grade_cg`、`grade_w` 是**扁平奖数差**(每奖记 1 子),**未乘 `X`**。
|
||||
|
||||
## 6. 叫分缺上限校验
|
||||
|
||||
- 设计 §4:叫分范围 5 ~ 70。
|
||||
- 代码 `mod.jiaofen` 只校验 `call%5==0` 与「后叫必须更低」,**缺 `call<=70` 上限**,首家可叫出 75、100 等非法分值。
|
||||
|
||||
## 7. 投降资格范围过宽
|
||||
|
||||
- 设计 §4.4 / §7.1:投降是 **70 分坐庄专属**分支。
|
||||
- 代码 `mod.touxiang` 用 `call < 65 → 拒绝`,即 **65 分也能投降**。
|
||||
|
||||
## 8. 70 分暗牌亮 3 秒等交互细节未实现
|
||||
|
||||
- 设计 §4.4 / §7.1:70 分坐庄,8 张暗牌需向两个闲家亮 3 秒(投降 / 打牌都要)。
|
||||
- 代码未见相关下发逻辑。
|
||||
|
||||
---
|
||||
|
||||
以上为初步清单。经逐节精查后,另发现若干**运行期代码缺陷**(如 `can_followcard` 引用未定义变量 `tuolaji_list` 会抛异常、`can_playcard` 的 14 张上限 `do_returnfalse` 漏写括号导致失效等)、以及**甩牌合法性 / 强制跟牌 / 局数扣卡 / 查牌模式**等更多不一致,详见 [`01-design合规逐节核对.md`](./01-design合规逐节核对.md)。
|
||||
@@ -0,0 +1,295 @@
|
||||
# 二七王 服务端实现 vs `design.md` 逐节合规核对
|
||||
|
||||
> 权威方向(同根 `CLAUDE.md`):`design.md` 是玩法规则的**唯一权威**。凡代码与规则冲突,除非规则项标注「待确认」,一律视为**代码缺陷**,应改代码去符合规则,**不得改规则迁就代码**。
|
||||
>
|
||||
> 核对对象:`server/games/erqiwang/` 下 `mod.js`、`class.pai.js`、`class.paiju.js`、`class.arith.js`、`class.desk.js`、`class.export.js`、`class.import.js`。
|
||||
>
|
||||
> 严重度标记:
|
||||
> - 🟥 **严重**:影响发牌 / 计分 / 输赢 / 核心玩法正确性。
|
||||
> - 🟧 **中等**:可选规则缺失、边界校验缺失、局部规则偏差。
|
||||
> - 🟨 **轻微**:交互提示 / 展示细节 / 死代码。
|
||||
> - 🐞 **代码缺陷**:运行期异常或明显写错(无论是否直接违背规则)。
|
||||
> - ✅ **符合**。
|
||||
|
||||
---
|
||||
|
||||
## 整改进度
|
||||
|
||||
> 本轮已按 design.md 修正的项(均已用 Node 脚本对 design 判定表逐格验证,见"验证"列)。下方"结论摘要"与各节详评仍保留**原始不一致**描述以留痕;本节记录当前状态。
|
||||
|
||||
| 项 | 位置 | 整改内容 | 验证 |
|
||||
| --- | --- | --- | --- |
|
||||
| D1 崩溃 | `class.arith.js` `can_followcard` | 用 `get_pairlist`+`get_tuolaji_list(_followcards)` 校验拖拉机牌型,消除未定义 `tuolaji_list` | 语法通过 |
|
||||
| D3 | `can_playcard` | `do_returnfalse;` → `do_returnfalse()`,恢复 14 张上限 | 语法通过 |
|
||||
| D4 | `do_choiceflower` | 删除空循环死代码 | 语法通过 |
|
||||
| §6.3 | `get_bottom_multiple` | 改为线性:单张 1、主对 2、N 连对 2N | ✅ 10 用例 |
|
||||
| §7 算子 | `get_base_bycall`/`get_qvalue`/`get_upgrade`(新)+ `get_paiju_account` | 实现常规算子(大光×3、Q=40) 与爬坡(§7.3 分段) 两套,按爬坡位(roomtype 位3)切换;`X=multiple×|upgrade|` | ✅ 74 格逐档 |
|
||||
| §8.1 | `get_chongguan` + `get_paiju_account` | 常规算奖只计庄家;连对链修复(传实际主花色);移除 ≥10 老主奖;去掉设计外的"冲关必叫/四王必踢"作废 | ✅ get_chongguan 用例 |
|
||||
| §8 快照 | `get_seat_cards_award`(新) | 庄家用埋牌后 28 张(排除已埋)、闲家用发牌后 28 张 | 语法通过 |
|
||||
| §8.3 傍王 | `get_paiju_account` | 改为按傍王位(roomtype 位2)开关,庄闲每王 1 奖,去作废 | ✅ 公式用例 |
|
||||
| §8.4 | `get_paiju_account` | 算奖并入结算改为 `X×(2Ni−Nj−Nk)`(乘 X) | ✅ §8.4 举例 |
|
||||
| §4.2 | `mod.jiaofen` | 增加叫分上限 `call<=70` | — |
|
||||
| §4.4 投降 | `mod.touxiang` + `do_burycard` + `get_deskinfo` | 投降仅 70 分、在**选主阶段 step2** 与选主互斥、不选主不埋牌;结算基础 1、庄输 1 子/闲;算奖用庄家 **36 张**(发牌+暗牌,无主花色→无连对链);埋牌后 step 直接进 5、删除已废弃的投降 step4 与重连 Surrender 视图 | — |
|
||||
| §10.1 | `get_asetcount`/`get_needroomcard` | 局数 6/12;房主扣卡 2/4 | — |
|
||||
| §5.4 甩牌 | `arith`(新增 `trump_rank`/`decompose_trump`/`opp_can_beat_flush`)+ `can_playcard` + `do_playcard` + `mod.chupai` | 副牌绝对禁甩;最大性改为**按对手全部主牌**逐分量判定;甩错惩罚(收回、只打最小一张、下发 `shuaicuo`) | ✅ 分解/最大性/各路径用例 |
|
||||
| roomtype | `class.config.js`(新)+ `export`/`paiju` | 数组下标改为**位串**(`"01011"`),`class.config.js` 唯一解析(SSOT) | ✅ parse/扣卡/局数用例 |
|
||||
| §4 暗牌亮牌 | `shangzhuang` + `get_deskinfo` | 70 分坐庄把 8 张暗牌下发给所有玩家 + `ancard3s=1`(供摸牌前亮 3 秒);修正**非 70 分暗牌泄露给闲家**(现暗牌只发庄家,符合 §4"只有庄家可见");重连的暗牌一律仅庄家可见 | — |
|
||||
| §8.2 亮牌 | `get_liangpai`(新)+ `mod.maipai` + `get_deskinfo` | 庄家埋牌后 28 张按阈值(固定主≥10/王≥3/7≥6/2≥6)统计,出牌开始向闲家亮(只亮数量/结构);可查牌模式才下发 | ✅ 阈值/统计用例 |
|
||||
| §9 查牌 | `class.config.js`(nocheck) + `mod.chupai` + `get_deskinfo` + `mod.mingpai`(新) | 报无主的座位统计 `info`/`baozhu`/`PushCards.seatlist` 与亮牌均按 `cfg.nocheck` 门控;新增 `mingpai` RPC(可查牌+已报无主时查看他家全部主牌) | — |
|
||||
|
||||
**尚未整改(多需与客户端/协议协同,或为较大的独立子系统)**:
|
||||
|
||||
- 🟩 **§5.4 甩牌(已改大部)**:副牌绝对禁甩、最大性按对手全部主牌逐分量判定、甩错惩罚均已实现并验证。**剩余**:§5.4.4 跟甩牌时"拖拉机→对子→单张"的**分量优先级**未强约束——现状是跟牌方必须用主牌按张数跟、且合法甩牌恒不可反超(结果正确),但不强制它优先用主对/主拖拉机去对位。因该细节不改变本轮胜负归属,列为后续精化项。
|
||||
- 🟩 **§9 查牌 / §8.2 亮牌(服务端已完成)**:亮牌 `get_liangpai`(庄家埋牌后 28 张按阈值统计)在 `maipai`/重连下发给闲家;报无主的座位统计(`info`/`baozhu`/`PushCards.seatlist`)与亮牌均按查牌位 `cfg.nocheck` 门控(不查牌一律不下发);新增 `mingpai` RPC(可查牌 + 已报无主时查看他家全部主牌)。剩客户端展示(面板/明牌按钮)。
|
||||
- 🟩 **§4.4 70 分暗牌亮 3 秒(服务端已就绪)**:`shangzhuang` 对 70 分坐庄把 8 张暗牌下发给所有玩家并带 `ancard3s=1`,供客户端在庄家摸暗牌前亮 3 秒;顺带修正了**非 70 分把暗牌泄露给闲家**的问题(现非 70 分暗牌只发庄家,符合 §4"只有庄家可见")。剩下的 3 秒动画由客户端完成。
|
||||
- 🟧 **§11 闲家 3 提示(踩/没分/有分)**:交互展示,需客户端配合。
|
||||
- 🟧 **客户端建房配置**:`roomtype` 已改为位串(见下表:位0局数/位1扣卡/位2傍王/位3爬坡/位4查牌),需在 erqiwang 客户端建房界面按位勾选拼串;服务端已按该格式读取(`class.config.js`)。
|
||||
|
||||
### roomtype 位串约定(服务端已按此读取;解析入口 `class.config.js`)
|
||||
|
||||
`roomtype` 为**定长数字位串(字符串)**,每一位(`charAt`)一个开关 `'0'/'1'`,便于前端逐项勾选。缺省 `"00000"` = 6局/房主扣卡/无傍王/常规算子/可查牌。解析对缺失/过短字符串按 `'0'` 兜底。
|
||||
|
||||
| 位 | 含义 | `'0'` | `'1'` |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 | 局数 | 6局(缺省)| 12局 |
|
||||
| 1 | 扣卡方式 | 房主扣卡(缺省)| AA每人扣卡 |
|
||||
| 2 | 傍王 | 关(缺省)| 开 |
|
||||
| 3 | 爬坡 | 常规算子(缺省)| 爬坡 |
|
||||
| 4 | 查牌模式 | 可查牌(缺省)| 不查牌 |
|
||||
|
||||
示例 `"10100"` = 12局 / 房主扣卡 / 傍王开 / 常规算子 / 可查牌。
|
||||
|
||||
---
|
||||
|
||||
## 结论摘要
|
||||
|
||||
| 设计章节 | 主题 | 结论 |
|
||||
| --- | --- | --- |
|
||||
| §2 | 牌局构成(92 张、去 3/4) | ✅ 符合 |
|
||||
| §3 | 主牌顺序(牌编码) | ✅ 符合(副 7/副 2 跨花色成对存疑,见下) |
|
||||
| §4.1-3 | 发牌 / 叫分 / 埋牌流程 | 🟧 缺叫分上限、投降资格过宽 |
|
||||
| §4.4 | 70 分投降 / 打牌分支 | 🟥 投降资格 65 分即可、暗牌亮 3 秒未实现、投降结算数值错 |
|
||||
| §4.6 | 坐庄轮换 | ✅ 符合 |
|
||||
| §5.1-5.3 | 跟牌 / 毙牌 / 垫牌 / 拖拉机 | ✅ 基本符合(1 处运行期崩溃缺陷) |
|
||||
| §5.4 | 甩牌 | 🟥 合法性判定模型、副牌禁甩、强制跟牌拆解、甩错惩罚均不符 |
|
||||
| §6.1-6.2 | 分牌 / 捡分 | ✅ 符合 |
|
||||
| §6.3 | 扣底倍数 | 🟥 倍数表完全不同 |
|
||||
| §7 | 算子(基础子数 / 大光小光过庄 / 升级 / 爬坡) | 🟥 系统性不符,且无爬坡开关 |
|
||||
| §8.1 | 常规算奖(只庄家 + 连对链) | 🟥 三家都算、含额外作废规则、连对链因传参失效、≥10 老主误给奖、庄家快照含底/埋牌牌 |
|
||||
| §8.2 | 亮牌 | 🟧 未实现 |
|
||||
| §8.3 | 傍王 | 🟥 无开关(恒开)、含设计外作废、且未乘 X |
|
||||
| §8.4 | 算奖并入结算 | 🟥 扁平奖数(每奖 1 子),未按 `X × N` |
|
||||
| §9 | 查牌 / 不查牌模式 | 🟧 无模式开关、明牌功能无服务端支持 |
|
||||
| §10.1 | 扣卡 / 局数 | 🟥 局数 2/4(应 6/12)、房主扣卡数不符 |
|
||||
| §10.2-10.3 | 查牌 / 傍王 / 爬坡选项 | 🟥 均无开关 |
|
||||
| §11 | 交互提示 | 🟨 亮底牌 ✅;甩牌见 §5.4;闲家 3 提示无服务端支持 |
|
||||
|
||||
---
|
||||
|
||||
## §2 牌局构成 — ✅ 符合
|
||||
|
||||
`class.paiju.js` `init_cards`:两副牌,点数 3、4 置 `dealowner=-1` 且不发(`if (k != 3 && k != 4) do_dealpai`);`tmpdeal` 共 `28×3 + 8 = 92` 个槽位。发出 92 张(11 种点数 ×4 ×2 + 4 王),去掉 16 张 3/4,与设计一致。分值 `5→5、10→10、K(13)→10` 亦一致(§6.1)。
|
||||
|
||||
---
|
||||
|
||||
## §3 主牌顺序 — ✅ 符合(1 处存疑)
|
||||
|
||||
`arith.id_to_code` 以 `flower*100+number` 为底,`A(+13)`、`2(+2000)`、`7(+7000)`、主花色 `(+1000)`、王固定 `9553/9554`,得到 code 降序即设计主牌顺序:
|
||||
|
||||
大王 9554 > 小王 9553 > 正 7(8xxx)> 副 7(7xxx)> 正 2(3xxx)> 副 2(2xxx)> 主花色普通牌 A…5(1xxx)> 副牌 A…5(<1000)。
|
||||
|
||||
`is_continuous` 正确实现全部相邻段(含 `小王↔正7`、`副2↔主A`、`8↔6`,以及「7 不与 6/8 连」——7 恒为主、不在普通链内)。
|
||||
|
||||
**存疑(待规则确认)**:不同花色的副 7(code 7107/7207/7307)、副 2(2107/2207/2307)编码互不相等。因此:
|
||||
|
||||
- 排序上三家副 7 被强行分出大小;比大小时 `can_followcard` 又把落在 7xxx/2xxx 段的牌**归一为 7000/2000**(视作同级),二者存在不对称,但因跟牌只能跟同花色、毙牌只能用更大主牌,实测不产生错误赢家。
|
||||
- 成对判定 `get_pairlist` 要求 `code 相等`,故「副 7 对 / 副 2 对」只认**同花色两张**,跨花色的两张副 7 不算一对。设计 §3 把副 7 列为同一等级,但未明确「两张不同花色副 7 是否成对」——**需与规则设计者确认**。若规则要求跨花色副牌可成对,则此处需改。
|
||||
|
||||
---
|
||||
|
||||
## §4 开局与坐庄
|
||||
|
||||
### §4.1 发牌(每人 28 + 8 暗牌)— ✅ 符合
|
||||
|
||||
### §4.2 叫分 — 🟧 缺上限校验
|
||||
|
||||
- ✅ 步进 5:`mod.jiaofen` 校验 `call % 5 != 0` 拒绝。
|
||||
- ✅ 暂定庄家必须叫:首家 `currcall==0 && call==0` 被拒。
|
||||
- ✅ 后叫更低:`curr_call != 0 && call >= curr_call` 被拒。
|
||||
- ✅ 叫 5 立即上庄、两家不叫则上庄:`paiju.do_callgrade` 实现。
|
||||
- 🟧 **缺 `call <= 70` 上限**:`mod.jiaofen` 未限制上界,首家可叫 75、100 等(设计 §4 范围 5~70)。
|
||||
|
||||
### §4.3 埋牌 — ✅ 符合(选主/埋牌顺序已确认解决)
|
||||
|
||||
庄家得暗牌(`get_seat_cards` 对庄家含 `dealowner==0`),`mod.maipai` 校验埋 8 张且都在庄家手上,`do_burycard` 置 `playround=0` 后由庄家开首轮。
|
||||
|
||||
✅ **顺序问题已解决**:复查时曾发现 design §4 列举顺序是「埋牌→选主」、而代码是「选主(step2)→埋牌(step3)」,二者相反。经与规则设计者确认,正确顺序为**先选主、后埋牌**(庄家知道主副后才好决定埋哪 8 张)——即**代码本就正确**,是 design §4 的列举笔误。已修正 design §4 的步骤顺序,并新增 design §12「完整牌局游玩流程」明确整局先后。代码无需改动。
|
||||
|
||||
### §4.4 70 分投降 / 打牌 — 🟥 多处不符
|
||||
|
||||
- 🟥 **投降资格过宽**:设计仅 **70 分**可投降;代码 `mod.touxiang` 判 `o_paiju.call < 65` 才拒绝,即 **65 分也能投降**。应改为「仅 `call == 70` 允许」。同样地 `shangzhuang.touxiang`、`get_deskinfo` 各阶段的 `touxiang` 字段都用 `call < 65` 判定,需一并改。
|
||||
- 🟥 **投降结算数值错**:设计投降为「基础子数 1、庄家每闲家输 1 子」。代码 `get_paiju_account(type=1)`:`upgrade=-99→-2`、`multiple=get_multiple_bycall(70)=1`,庄家 `grade_jf = -2 × 1 × 2 = -4`(每闲家 2 子)。金额翻倍且模型不对。
|
||||
- 🟨 **暗牌亮 3 秒未实现**:设计要求 70 分坐庄把 8 张暗牌向两闲家亮 3 秒(投降 / 打牌都要),代码无此下发。
|
||||
|
||||
### §4.6 坐庄轮换 — ✅ 符合
|
||||
|
||||
`class.export.js` `makewar` → `do_new_paiju(0)`:首局暂定庄家为 0 号座位(设计 §4.6)。`class.desk.js` `do_prepare`:`result==1(闲赢)||result==2(投降)` 时下一局 `firstseat=(banker+1)%3`(下家),否则连庄。两点均与设计一致。
|
||||
|
||||
---
|
||||
|
||||
## §5 出牌规则
|
||||
|
||||
### §5.1 / §5.2 跟牌 / 毙牌 / 垫牌 — ✅ 基本符合(含 1 处崩溃缺陷)
|
||||
|
||||
- 每轮由上轮最大者先出、每局首轮庄家先出:`new_playround(maxseat)` / `do_burycard→new_playround(1, banker)`。✅
|
||||
- 跟同花色、缺门可毙(主牌压过)或垫(任意副牌不争):`arith.get_followcard`/`can_followcard` 按「同花色是否够 / 有无对子 / 有无拖拉机」给必出牌与可出牌,毙牌能否赢由 `cardvalue`(副 7/副 2 归一、跨花色垫牌记 0、主牌对/拖拉机才 >0)裁定。✅ 与 §5.2「毙牌必须用对应牌型」一致。
|
||||
- 🐞 **`can_followcard` 引用未定义变量 `tuolaji_list` 会抛异常**(`class.arith.js` 约 801 行,`get.cantype` 为拖拉机型 3xx 分支内 `if (tuolaji_list.length == 0)`,该变量在此作用域未赋值)。当跟牌方手中存在**多个**符合长度的候选拖拉机(`get_followcard` 返回 `cantype=startcardtype`)时命中此分支,`DoPack` 抛错、该次出牌链中断。需修复。
|
||||
|
||||
### §5.3 拖拉机定义 — ✅ 符合(见 §3 的 `is_continuous`)
|
||||
|
||||
### §5.4 甩牌 — 🟥 原合法性 / 禁令 / 跟牌 / 惩罚多处不符(🟩 第 1/2/4 点已整改,见「整改进度」)
|
||||
|
||||
> **整改状态**:下述第 1(副牌禁甩)、第 2(最大性按全手牌判定)、第 4(甩错惩罚)已实现并验证;第 3(强制跟牌分量优先级)为剩余精化项。以下保留原始不一致描述留痕。
|
||||
|
||||
1. 🟥 **副牌禁甩未落实**:设计 §5.4.1「所有副牌完全禁止甩牌,无论外面是否剩余该花色副牌」。代码 `can_playcard` 对副牌甩牌只要求「其他两家没有主牌 **且** 没有该副花色(`other_noflower`)」即**放行**(约 415-419、427-438 行),等于「对手缺门时允许甩副牌」,与绝对禁令冲突。
|
||||
2. 🟥 **甩牌合法性判定模型不符**:设计 §5.4.2 要求服务端**按全部手牌**判断「对手是否持有能压过甩牌任一分量的更大主牌 / 主对 / 更长主拖拉机」。代码改用出牌过程累积的 `seatlist[i][flower-1][0/1]`、`[4]` 等**报副标志**(`other_noflower`/`other_noflowerpair`),语义是「对手是否已被记录为该花色 / 主牌 / 对子出空」,既非「按全部手牌」也非「更大」而是「有没有」,与设计判定完全不同。
|
||||
3. 🟥 **强制跟牌拆解未按分量**:设计 §5.4.4 要求闲家把甩牌拆成各分量(拖拉机优先跟拖拉机、对子优先跟对子…)逐个匹配。代码把甩牌压成单一 `cardtype`(如 `102 两张单张甩`、`203 三对甩`),`get_followcard` 对甩牌型只要求「同花色、张数相等」,不校验对子 / 拖拉机结构(如多对甩分支 `cantype=100+startcount` 只按单张计数)。
|
||||
4. 🟥 **甩错惩罚未实现**:设计 §5.4.5 规定甩错要「收回甩牌、本轮只强制打出最小一张、失去甩牌资格」。代码把非法甩牌当作无效输入(`can_playcard` 返回 `result=false`→`mod.chupai` 直接 `return`),无任何惩罚流程。
|
||||
5. ✅ **报无主**:`do_playcard` 出牌后自动检测主牌出空并置 `seatlist[..][4][0]=0`,属自动判定(设计允许由服务端精确计算)。
|
||||
|
||||
---
|
||||
|
||||
## §6 捡分与扣底
|
||||
|
||||
### §6.1 分牌 / §6.2 捡分 — ✅ 符合
|
||||
|
||||
`do_playcard` 仅在 `maxseat != banker` 时累计本轮分;`get_jian_grade` 汇总 `playowner != banker` 的分牌。庄家赢的轮分牌作废、不计入。与设计一致。
|
||||
|
||||
### §6.3 扣底 — 🟥 倍数表完全不同
|
||||
|
||||
- ✅ 触发条件「闲家用主牌赢下最后一轮」:`get_bottom_account` 仅在 `maxseat != banker` 时调 `get_bottom_multiple`,后者首末张非主(code<1000)即返回 0(不扣底),与「非主牌赢末轮不算扣底」一致。
|
||||
- 🟥 **倍数不符**:
|
||||
|
||||
| 闲家扣底牌型 | 设计倍数 | 代码 `get_bottom_multiple` |
|
||||
| --- | --- | --- |
|
||||
| 单张主 | 1 | 2 |
|
||||
| 主对子 | 2 | 4 |
|
||||
| 两连对 | 4 | 8 |
|
||||
| 三连对 | 6 | 16 |
|
||||
| 四连对 | 8 | 32 |
|
||||
| 五连对 | 10 | 64 |
|
||||
| 六连对 | 12 | `undefined`(🐞 落空返回未定义) |
|
||||
|
||||
代码为近似 `2^k`、且单张即翻倍;设计为线性 `2N`(单张不翻倍)。
|
||||
|
||||
---
|
||||
|
||||
## §7 结算:子数与升级 — 🟥 系统性不符
|
||||
|
||||
设计的四层模型(§7.0):`基础子数(由叫分定) × 判定倍率(大光×3/小光×2/过庄×1/升N级×N) = X`(每「庄–闲」对子的基础金额)。常规算子分界 `Q` 固定 40、升级级距固定 40;勾选爬坡才改用 §7.3 的梯度子数与分段 `Q`。
|
||||
|
||||
代码实际实现(`get_paiju_account` + `get_multiple_bycall` + `get_upgrade`):
|
||||
|
||||
- `multiple = get_multiple_bycall(call)`:`call>60→1`、`>40→2`、`>0→4`、`≤0→0`。
|
||||
- `upgrade = get_upgrade(call, grade)`:大光→4、小光→2、过庄→1、升级→`-(floor((grade-call)/halfcall)+1)`;分界与级距用 `halfcall = ceil(call/2)`。
|
||||
- 每对子金额 = `multiple × upgrade`,庄家收两家(×2)、闲家各付(×-1)。
|
||||
|
||||
**问题**:
|
||||
|
||||
1. 🟥 **基础子数不符**:代码 `multiple∈{1,2,4}` 与设计常规算子 `{65:2, 60:3, 55:4, 50↓:6}` 数值、档位都不同。
|
||||
2. 🟥 **大光倍率错**:代码大光 `=4`,设计大光 `×3`。(小光 ×2、过庄 ×1 与设计一致。)
|
||||
3. 🟥 **小光 / 过庄分界与升级级距错**:代码用 `halfcall=ceil(call/2)`;设计常规算子固定 40。例如叫 65:代码分界 35(35~64 记过庄),设计分界 40(40~64 才过庄,35~39 仍是小光)。
|
||||
4. 🟥 **无爬坡开关**:设计 §7.3 是「勾选爬坡才生效」的可选梯度;代码无任何房间选项分支,只有一套公式。巧合的是 `halfcall` 对低档(40/35→20、30/25→15、20/15→10、10/5→5)恰等于爬坡的 `Q`,但对高档(65→35≠40、60→30≠40、50→25≠40)不等,且基础子数 `{1,2,4}` 与常规 `{2,3,4,6}`、爬坡 `{2,3,4,6,7,8,…}` 都不符——即代码既不是常规算子、也不是爬坡,而是第三套数值。
|
||||
|
||||
**常规算子每对子最终子数对照(代码 vs 设计)**:
|
||||
|
||||
| 叫分 | 判定 | 设计 | 代码 |
|
||||
| --- | --- | --- | --- |
|
||||
| 65 | 大光 / 小光 / 过庄 / 升1 / 升2 | 6 / 4 / 2 / 2 / 4 | 4 / 2 / 1 / 1 / 2 |
|
||||
| 60 | 同上 | 9 / 6 / 3 / 3 / 6 | 8 / 4 / 2 / 2 / 4 |
|
||||
| 55 | 同上 | 12 / 8 / 4 / 4 / 8 | 8 / 4 / 2 / 2 / 4 |
|
||||
| 50 | 同上 | 18 / 12 / 6 / 6 / 12 | 8 / 4 / 2 / 2 / 4 |
|
||||
| 40 | 大光 / 小光 /(无过庄)/ 升1 / 升2 | 18 / 12 / — / 6 / 12 | 16 / 8 /(误记过庄 4)/ 4 / 8 |
|
||||
|
||||
(叫 40 一行还暴露分界问题:设计 `call≤40` 无过庄档,代码却在 grade 20~39 记出「过庄」。代码升级子数 = `multiple(=4) × 级数`,故升 1 级 4 子、升 2 级 8 子。)
|
||||
|
||||
> **70 分打牌**同样落在本套公式里且同样不符:代码 `multiple = get_multiple_bycall(70) = 1`(设计打牌基础子数应为 2),`halfcall = 35`(设计 `Q = 40`);大光 4×1=4、小光 2、过庄 1,而设计应为大光 6、小光 4、过庄 2。
|
||||
|
||||
---
|
||||
|
||||
## §8 算奖规则 — 🟥 归属、开关、数值均不符
|
||||
|
||||
### §8.1 常规算奖(只庄家) — 🟥
|
||||
|
||||
- ✅ 基础组合计数正确:`get_chongguan` 中 `3 王→1、4 王→3`、`6/7/8 个 7→1/2/3`、`6/7/8 个 2→1/2/3`。
|
||||
- 🟥 **只应计庄家,代码却算三家**:`get_paiju_account` 对 0/1/2 号位都调 `get_chongguan` 并两两结算(`grade_cg = cg×2 − 另两家`)。设计 §8.1「常规算奖只认庄家身份,不看闲家手牌」。
|
||||
- 🟥 **设计外的作废规则**:`do_obsolete_with_call` 引入「冲关必叫」「四王必踢(`call>60`)」,设计中不存在。
|
||||
- 🟥 **连对链算奖失效(传参 bug)**:设计 §8.1「三 / 四王在有正 7 及以后连续对子时每对 +1 奖」。但 `get_chongguan` 在正常 / 投降结算被以 `mainflower = -1` 调用(`get_paiju_account` 中 `if (type != 2) _flower = -1;`)。此时 `id_to_code` 不加 `+1000`,正 7 与副 7 都落到 7xxx、主花色普通对子不被识别为主,`is_continuous(小王, 正7)` 因 `is_zheng7` 只认 8xxx 而返回假——连对链一对也加不上。即该奖项**实质未生效**。
|
||||
- 🟥 **≥10 老主误给奖**:`if (_total >= 10) re.count += _total - 9`(「10 个老主判断」)。设计 §8.2 明示固定主牌 ≥10 只亮牌、**不算奖**。
|
||||
- 🟥 **庄家算奖手牌快照错(含底牌与已埋牌)**:设计 §8「算奖依据静态初始手牌——庄家是**埋牌完成后的那 28 张**,闲家是发牌后的 28 张」。代码 `get_paiju_account` 用 `get_seat_cards_owner(seat)` 取牌喂给 `get_chongguan`,而该函数按 `dealowner ∈ {seat+1, 0}` 取牌、**不排除 `playround==0`(已埋)**:庄家因此拿到「28 张发牌 + 8 张底牌 = 36 张」,把**已埋进底牌的 8 张也计入算奖**,闲家则是正确的 28 张。庄家的常规算奖与傍王都因此可能虚高(例如把埋掉的王/7/2 仍算进去)。应改为庄家取「埋牌后保留的 28 张」(排除 `playround==0`)。
|
||||
|
||||
### §8.2 亮牌 — 🟧 未实现
|
||||
|
||||
设计 §8.2 的四档亮牌门槛(固定主 ≥10 / 王 ≥3 / 7 ≥6 / 2 ≥6,向闲家亮统计信息,受「不查牌」模式抑制)在服务端无对应计算与下发。
|
||||
|
||||
### §8.3 傍王(可选) — 🟥
|
||||
|
||||
- 🟥 **无开关(恒开)**:`grade_w`(傍王得分)在 `get_paiju_account` 中无条件计算。设计 §8.3 / §10.3 傍王需勾选才生效。
|
||||
- 🟥 **含设计外作废**:`grade_w` 复用 `obsolete` 置 0 逻辑;设计傍王「每张王算一奖、庄闲都算」,无作废概念。
|
||||
|
||||
### §8.4 算奖并入结算 — 🟥 未乘 X
|
||||
|
||||
- 设计 §8.4:某玩家 `N` 奖,则另外两人各额外付 `X × N`(`X` 为 §7 第三层的每对子子数)。
|
||||
- 代码:`grade_cg`、`grade_w` 是**扁平奖数两两差**(`cg×2 − 另两家`),每奖等价 1 子,**未乘 `X`**。举例设计(X=6、庄家 1 奖)应从每闲家收 6 子;代码只收 1 子/家。
|
||||
- ✅ 结构上「所有两两之间(含闲–闲)都结算」这一点与设计一致,仅倍率错。
|
||||
|
||||
---
|
||||
|
||||
## §9 牌局查看模式 — 🟧
|
||||
|
||||
- 报无主后,服务端确有记录他家剩余主牌数 / 对子数(`seatlist[..][4]` 与花色标志),并通过 `chupai*`/`PushCards` 的 `info`、`seatlist`、`baozhu` 下发,供客户端展示——方向正确。
|
||||
- 🟧 **无「可查牌 / 不查牌」模式开关**:`baozhu` 恒发、`seatlist` 恒带,未按房间设置抑制(设计 §9 / §10.2 的不查牌模式应屏蔽这些信息)。
|
||||
- 🟧 **「明牌」(查看他家具体主牌)无服务端支持**:服务端不会向闲家下发他家的具体主牌牌面。
|
||||
|
||||
---
|
||||
|
||||
## §10 房间设置选项 — 🟥
|
||||
|
||||
### §10.1 扣卡方式 / 局数 — 🟥
|
||||
|
||||
- 🟥 **局数不符**:`export.get_asetcount` 返回 `roomtype[0]==1→2`、`==2→4`。设计 §10.1 为 **6 局 / 12 局**。(疑为占位 / 测试值,与 `do_playcard` 中被注释的 `round==2` 测试残留同源。)
|
||||
- 🟥 **房主扣卡数不符**:`get_needroomcard` 房主档(`roomtype[2]==1`)返回 1/2;设计房主扣卡为 6 局 2 张 / 12 局 4 张。AA 档(`roomtype[2]==2`)返回 1/2,与设计 AA「每人 1 张 / 2 张」一致。
|
||||
|
||||
### §10.2 查牌模式 — 🟥 无开关(见 §9)
|
||||
|
||||
### §10.3 附加规则(傍王 / 爬坡,可同时勾选)— 🟥 均无开关(见 §7.4、§8.3)
|
||||
|
||||
---
|
||||
|
||||
## §11 牌局交互提示 — 🟨
|
||||
|
||||
- ✅ 牌局结束亮底牌:`get_bottom_account` 结算恒带 `bottom.cards`。
|
||||
- 甩牌相关提示:见 §5.4(甩牌本身实现不符)。
|
||||
- 🟨 **闲家 3 提示(踩 / 没分 / 有分)无服务端支持**:设计 §11 要求闲家出牌时提供这三种对家提示;服务端未计算 / 下发对应信号(当前仅有 `grade` 等原始信息,需客户端或后续补充)。
|
||||
|
||||
---
|
||||
|
||||
## 附录:运行期代码缺陷汇总(与规则合规相对独立,但都需修)
|
||||
|
||||
| # | 位置 | 现象 | 影响 |
|
||||
| --- | --- | --- | --- |
|
||||
| D1 | `class.arith.js` `can_followcard`(约 801 行) | 引用未赋值的 `tuolaji_list.length` | 跟牌方有多个候选拖拉机时抛异常、出牌中断 🐞 |
|
||||
| D2 | `class.arith.js` `get_bottom_multiple`(六连对分支) | 六连对时无返回值 | 返回 `undefined`,扣底倍数计算异常 🐞 |
|
||||
| D3 | `class.arith.js` `can_playcard`(约 276 行) | `do_returnfalse;` 漏写 `()`,语句空转 | 「一次最多 14 张」上限失效,超量出牌被当作合法 🐞 |
|
||||
| D4 | `class.paiju.js` `do_choiceflower`(约 417-421 行) | `for` 循环体为空、取出 `o_card` 后无操作 | 死代码(主牌重编码实际在 `id_to_code` 动态完成),无害但应清理 🟨 |
|
||||
|
||||
> D1~D3 均为真实缺陷;虽多数属「代码写错」而非「规则理解错」,但会导致对应规则(跟拖拉机校验、扣底、出牌张数上限)无法正确执行,需按缺陷修复处理。
|
||||
|
||||
---
|
||||
|
||||
## 总体判断
|
||||
|
||||
- **流程骨架**(发牌、叫分推进、选主埋牌、逐轮出牌收束、断线重连快照、战绩存储)实现完整且大体正确。
|
||||
- **牌型与大小体系**(编码、排序、相邻、跟牌 / 毙牌 / 垫牌的必出可出计算)设计良好、基本符合规则,主要缺陷是 D1 崩溃与甩牌子系统。
|
||||
- **不符合集中在「算分 / 算奖 / 房间选项」三块**:§6.3 扣底倍数、§7 算子全套、§8 算奖归属与并入方式、§10 局数 / 扣卡 / 各类开关——这几处需按 `design.md` 重做,是后续整改的重点。
|
||||
- 结合数值特征(`multiple` 只有 1/2/4、局数 2/4、扣底 2^k、含「冲关必叫 / 四王必踢」等设计外机制),**服务端很可能是在早期或另一套规则版本上实现的,与当前 `design.md` 已系统性脱节**,而非个别笔误。
|
||||
Reference in New Issue
Block a user