二七王:前端 CardCodec,cardId 转牌面帧号

清单 §0.4 的唯一权威转换:服务端花色段序与美术帧顺序相反,
转换把花色段反过来数。用清单给的 10 条样例逐条锁死,
另加两副牌同帧、值域、单射三组断言。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 19:56:32 +08:00
co-authored by Claude Opus 5
parent 3b9828c8ca
commit 65a73c5c2a
2 changed files with 79 additions and 0 deletions
@@ -0,0 +1,22 @@
///////////////////////////////////////////////////////////////
////////// EQW_CardCodec: 牌 id → 牌面资源帧号(纯逻辑)//////////
///////////////////////////////////////////////////////////////
// 清单 §0.4:牌面资源统一 10 列 × 6 行 = 60 帧,行优先编号
// 帧 1–13 黑桃 A–K / 14–26 红桃 / 27–39 梅花 / 40–52 方块
// 帧 53 小王 / 54 大王 / 55–60 牌背 1–6(二七王用帧 55)
// 服务端牌 id 的花色段序是 方块→梅花→红心→黑桃(class.pai.js:16),
// 与美术帧的花色顺序【正好相反】,转换必须把花色段反过来数。
// 【全前端只此一处实现】其余模块一律调用它,不得另写一份。
var EQW_CardCodec = EQW_CardCodec || {
//牌背帧号(二七王固定用牌背 1)
CARD_BACK_FRAME: 55,
//牌 id → 牌面资源帧号。deck1 与 deck2 的同一张牌共用同一帧(牌面完全相同)
cardIdToFrame: function (cardId) {
var n = cardId % 54; // 去掉副数
if (n === 52) { return 53; } // 小王
if (n === 53) { return 54; } // 大王
return (3 - Math.floor(n / 13)) * 13 + (n % 13) + 1;
}
};
+57
View File
@@ -0,0 +1,57 @@
// cardId → 牌面帧号(清单 §0.4 唯一权威转换)
const { load } = require('./_load');
const t = require('./_assert')();
load('client/js/01_SubGame/codes/core/CardCodec.js');
// ---- 清单 §0.4 的 10 条校验样例,逐条锁死 ----
const cases = [
[0, 40, '方块A'],
[4, 44, '方块5'],
[12, 52, '方块K'],
[13, 27, '梅花A'],
[26, 14, '红桃A'],
[39, 1, '黑桃A'],
[51, 13, '黑桃K'],
[52, 53, '小王'],
[53, 54, '大王'],
[106, 53, '小王(deck2)']
];
cases.forEach(c => t.eq('帧号 ' + c[2] + ' id=' + c[0], EQW_CardCodec.cardIdToFrame(c[0]), c[1]));
// ---- 两副牌同一张牌共用同一帧 ----
for (let id = 0; id < 54; id++) {
if (EQW_CardCodec.cardIdToFrame(id) !== EQW_CardCodec.cardIdToFrame(id + 54)) {
t.eq('deck1/deck2 同帧 id=' + id, false, true);
}
}
t.eq('deck1/deck2 全部同帧', true, true);
// ---- 帧号值域:54 张牌只落在 1–54 ----
let inRange = true;
for (let id = 0; id < 108; id++) {
const f = EQW_CardCodec.cardIdToFrame(id);
if (!(f >= 1 && f <= 54)) { inRange = false; }
}
t.eq('帧号值域 1-54', inRange, true);
// ---- 单射:deck1 的 54 张牌映射出 54 个互不相同的帧 ----
const seen = {};
let dup = false;
for (let id = 0; id < 54; id++) {
const f = EQW_CardCodec.cardIdToFrame(id);
if (seen[f]) { dup = true; }
seen[f] = true;
}
t.eq('54 张牌帧号互不重复', dup, false);
t.eq('恰好覆盖 54 个帧', Object.keys(seen).length, 54);
// ---- 二七王不使用 3、4 的帧(design §2 去掉 3 和 4)----
// 方块3 id=2 → 帧42,方块4 id=3 → 帧43;资源仍按 60 帧通用排版出图,只是不引用
t.eq('方块3 帧号', EQW_CardCodec.cardIdToFrame(2), 42);
t.eq('方块4 帧号', EQW_CardCodec.cardIdToFrame(3), 43);
// ---- 牌背常量 ----
t.eq('牌背帧', EQW_CardCodec.CARD_BACK_FRAME, 55);
process.exit(t.done('cardcodec') ? 0 : 1);