二七王:拖拉机极大连续段扫描与牌编码区间常量收敛进 shared

前后端此前各写了一遍「分对子 → is_continuous 扫极大连续段 → ≥2 对算拖拉机」:
服务端 class.arith.js decompose_trump 与前端 core/CardMark.js。规则内容(什么算连续、
几对起算拖拉机)散在两处,正是 shared/ 要消除的重复。

- shared/cards.js 新增 group_tractor_runs(mainflower, pairlist):返回极大连续段数组,
  孤立对子作为长度 1 的段返回,拼接后恰好还原入参;阈值导出为 TRACTOR_MIN_PAIRS。
- shared/cards.js 导出牌编码区间边界具名常量(CODE_ZHU_MIN / CODE_ZHENG2_* /
  CODE_FU2_* / CODE_ZHENG7_* / CODE_FU7_* / CODE_XIAOWANG / CODE_DAWANG /
  CODE_A_IN_ZHU),并在 id_to_code / is_continuous / trump_rank 内部改用它们。
- decompose_trump 改调共享扫描,对外行为完全不变(分量类型、顺序、cards 归属不变)。
- CardMark 删掉本地扫描改调共享版本,区间判定与标记值改引用常量,去掉 1000/3000/
  4000/8000/9000 裸字面量。
- 前端副本经 sync_shared.cmd 同步;服务端单测 624→645 checks 全绿,前端 8 个测试全绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 22:17:09 +08:00
co-authored by Claude Opus 5
parent b22558d0b3
commit b81be36f6e
6 changed files with 254 additions and 87 deletions
+25 -29
View File
@@ -64,31 +64,32 @@ var cls_youle_erqiwang_arith = cls_youle_erqiwang_arith || {
return 40;
},
//以下 8 项为前后端共享算法,权威实现在 shared/cards.js(经 sync_shared.cmd 同步到前端)。
//以下 9 项为前后端共享算法,权威实现在 shared/cards.js(经 sync_shared.cmd 同步到前端)。
//此处只挂引用:对外方法名与签名不变,全部既有调用点无需改动。
id_to_flower: youle_erqiwang_shared_cards.id_to_flower,
id_to_number: youle_erqiwang_shared_cards.id_to_number,
id_to_code: youle_erqiwang_shared_cards.id_to_code,
order_cards: youle_erqiwang_shared_cards.order_cards,
is_continuous: youle_erqiwang_shared_cards.is_continuous,
get_pairlist: youle_erqiwang_shared_cards.get_pairlist,
get_tuolaji_list: youle_erqiwang_shared_cards.get_tuolaji_list,
trump_rank: youle_erqiwang_shared_cards.trump_rank,
id_to_flower: youle_erqiwang_shared_cards.id_to_flower,
id_to_number: youle_erqiwang_shared_cards.id_to_number,
id_to_code: youle_erqiwang_shared_cards.id_to_code,
order_cards: youle_erqiwang_shared_cards.order_cards,
is_continuous: youle_erqiwang_shared_cards.is_continuous,
get_pairlist: youle_erqiwang_shared_cards.get_pairlist,
group_tractor_runs: youle_erqiwang_shared_cards.group_tractor_runs,
get_tuolaji_list: youle_erqiwang_shared_cards.get_tuolaji_list,
trump_rank: youle_erqiwang_shared_cards.trump_rank,
//把一组主牌分解为分量列表(design §5.4.3/§5.4.4)
//返回 [{type:'single'|'pair'|'tractor', len, value, cards:[]}...],value 为该分量最大牌的归一化 rank
decompose_trump: function(mainflower, cards){
var ordered = cls_youle_erqiwang_arith.order_cards(mainflower, cards.concat());
//先分出对子与单张
var pairs = []; //{code, cards:[id,id]}
var singles = []; //{card}
//先分出对子与单张(对子形状与 shared get_pairlist 一致:[id, id])
var pairs = []; //[[id,id], ...]
var singles = []; //[id, ...]
var i = 0;
while (i < ordered.length){
var code_i = cls_youle_erqiwang_arith.id_to_code(mainflower, ordered[i]);
if (i + 1 < ordered.length){
var code_n = cls_youle_erqiwang_arith.id_to_code(mainflower, ordered[i + 1]);
if (code_i == code_n){
pairs.push({code: code_i, cards: [ordered[i], ordered[i + 1]]});
pairs.push([ordered[i], ordered[i + 1]]);
i = i + 2;
continue;
}
@@ -96,26 +97,21 @@ var cls_youle_erqiwang_arith = cls_youle_erqiwang_arith || {
singles.push(ordered[i]);
i++;
}
//连续对子组成拖拉机,其余为单独对子
//连续对子组成拖拉机,其余为单独对子。极大连续段的扫描是前后端共享算法(shared/cards.js),
//此处只负责把段翻译成分量,不再自己实现「什么算连续、几对起算拖拉机」
var runs = youle_erqiwang_shared_cards.group_tractor_runs(mainflower, pairs);
var comps = [];
var j = 0;
while (j < pairs.length){
var run = [pairs[j]];
var k = j;
while (k + 1 < pairs.length && cls_youle_erqiwang_arith.is_continuous(pairs[k].code, pairs[k + 1].code)){
run.push(pairs[k + 1]);
k++;
for (var j = 0; j < runs.length; j++){
var run = runs[j];
var tcards = [];
for (var r = 0; r < run.length; r++){
tcards = tcards.concat(run[r]);
}
if (run.length >= 2){
var tcards = [];
for (var r = 0; r < run.length; r++){
tcards = tcards.concat(run[r].cards);
}
comps.push({type: "tractor", len: run.length, value: cls_youle_erqiwang_arith.trump_rank(mainflower, run[0].cards[0]), cards: tcards});
if (run.length >= youle_erqiwang_shared_cards.TRACTOR_MIN_PAIRS){
comps.push({type: "tractor", len: run.length, value: cls_youle_erqiwang_arith.trump_rank(mainflower, run[0][0]), cards: tcards});
} else {
comps.push({type: "pair", len: 1, value: cls_youle_erqiwang_arith.trump_rank(mainflower, run[0].cards[0]), cards: run[0].cards});
comps.push({type: "pair", len: 1, value: cls_youle_erqiwang_arith.trump_rank(mainflower, run[0][0]), cards: tcards});
}
j = k + 1;
}
for (var s = 0; s < singles.length; s++){
comps.push({type: "single", len: 1, value: cls_youle_erqiwang_arith.trump_rank(mainflower, singles[s]), cards: [singles[s]]});