二七王:拖拉机极大连续段扫描与牌编码区间常量收敛进 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:
@@ -7,14 +7,14 @@
|
||||
// 'zhu' 蓝色五角星 = 其余主牌(双王、副2、副7、主花色普通牌)
|
||||
// 三者【互斥】、每张牌至多一个,优先级 tractor > zheng > zhu。
|
||||
//
|
||||
// 牌编码区间(shared/cards.js 的 id_to_code 产物):
|
||||
// 9553/9554 王 / 8000-9000 正7 / 7000-8000 副7 / 3000-4000 正2
|
||||
// 2000-3000 副2 / 1000-2000 主花色普通牌 / 100-500 副牌普通牌
|
||||
// ⇒ 主牌 ⟺ code > 1000
|
||||
// 牌编码区间边界一律取自 shared/cards.js 导出的具名常量(CODE_ZHU_MIN / CODE_ZHENG2_* /
|
||||
// CODE_ZHENG7_* …)——那里是 id_to_code 的权威说明处,本文件不重抄 1000/3000/8000 之类裸值。
|
||||
// ⇒ 主牌 ⟺ code > CODE_ZHU_MIN
|
||||
//
|
||||
// 拖拉机只在【同一牌组】内成立:全部主牌算一组(副2/副7/王与主花色牌可相连),
|
||||
// 副牌按各自花色分组。识别方式与服务端 decompose_trump 同思路:
|
||||
// 先分出对子,再用 is_continuous 扫极大连续段,长度 ≥2 的段内所有牌标 tractor。
|
||||
// 副牌按各自花色分组。极大连续段的扫描与「几对起算拖拉机」是前后端共享算法
|
||||
// (shared/cards.js 的 group_tractor_runs / TRACTOR_MIN_PAIRS,服务端 decompose_trump 同源),
|
||||
// 本文件只负责按段落标记,不再自己实现一遍扫描。
|
||||
var EQW_CardMark = EQW_CardMark || {
|
||||
|
||||
MARK_TRACTOR: 'tractor',
|
||||
@@ -34,7 +34,7 @@ var EQW_CardMark = EQW_CardMark || {
|
||||
var groups = {};
|
||||
for (i = 0; i < cards.length; i++) {
|
||||
code = S.id_to_code(mf, cards[i]);
|
||||
var key = (code > 1000) ? 'Z' : String(S.id_to_flower(cards[i]));
|
||||
var key = (code > S.CODE_ZHU_MIN) ? 'Z' : String(S.id_to_flower(cards[i]));
|
||||
if (!groups[key]) { groups[key] = []; }
|
||||
groups[key].push(cards[i]);
|
||||
}
|
||||
@@ -44,22 +44,14 @@ var EQW_CardMark = EQW_CardMark || {
|
||||
if (!groups.hasOwnProperty(g)) { continue; }
|
||||
var ordered = S.order_cards(mf, groups[g].concat());
|
||||
var pairs = S.get_pairlist(mf, ordered);
|
||||
var j = 0;
|
||||
while (j < pairs.length) {
|
||||
var run = [pairs[j]];
|
||||
var k = j;
|
||||
while (k + 1 < pairs.length &&
|
||||
S.is_continuous(S.id_to_code(mf, pairs[k][0]), S.id_to_code(mf, pairs[k + 1][0]))) {
|
||||
run.push(pairs[k + 1]);
|
||||
k++;
|
||||
var runs = S.group_tractor_runs(mf, pairs);
|
||||
for (var j = 0; j < runs.length; j++) {
|
||||
var run = runs[j];
|
||||
if (run.length < S.TRACTOR_MIN_PAIRS) { continue; } //孤立对子(长度1的段)不是拖拉机
|
||||
for (var r = 0; r < run.length; r++) {
|
||||
marks[run[r][0]] = this.MARK_TRACTOR;
|
||||
marks[run[r][1]] = this.MARK_TRACTOR;
|
||||
}
|
||||
if (run.length >= 2) { //≥2 个连续对子才是拖拉机
|
||||
for (var r = 0; r < run.length; r++) {
|
||||
marks[run[r][0]] = 'tractor';
|
||||
marks[run[r][1]] = 'tractor';
|
||||
}
|
||||
}
|
||||
j = k + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +59,11 @@ var EQW_CardMark = EQW_CardMark || {
|
||||
for (i = 0; i < cards.length; i++) {
|
||||
if (marks[cards[i]]) { continue; }
|
||||
code = S.id_to_code(mf, cards[i]);
|
||||
if ((code > 3000 && code < 4000) || (code > 8000 && code < 9000)) {
|
||||
marks[cards[i]] = 'zheng'; //正2 / 正7
|
||||
} else if (code > 1000) {
|
||||
marks[cards[i]] = 'zhu'; //其余主牌
|
||||
if ((code > S.CODE_ZHENG2_MIN && code < S.CODE_ZHENG2_MAX) ||
|
||||
(code > S.CODE_ZHENG7_MIN && code < S.CODE_ZHENG7_MAX)) {
|
||||
marks[cards[i]] = this.MARK_ZHENG; //正2 / 正7
|
||||
} else if (code > S.CODE_ZHU_MIN) {
|
||||
marks[cards[i]] = this.MARK_ZHU; //其余主牌
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,28 @@
|
||||
// ⇒ 主牌 ⟺ code > 1000
|
||||
var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || {
|
||||
|
||||
//——— 牌编码区间边界:上面那张区间表的机器可读形式,是【全部区间判定的唯一权威来源】———
|
||||
//一律是【开区间】:CODE_ZHENG7_MIN < code < CODE_ZHENG7_MAX ⟺ 正7,其余同理。
|
||||
//本文件内部(is_continuous / trump_rank / id_to_code)与前端 core/CardMark.js 都引用这些常量,
|
||||
//不再各处重抄 3000/4000/8000/9000 之类裸字面量(工程总则 §1 SSOT / §6 配置优先于硬编码)。
|
||||
CODE_XIAOWANG: 9553, //小王(单值,非区间)
|
||||
CODE_DAWANG: 9554, //大王(单值,非区间)
|
||||
CODE_ZHENG7_MIN: 8000, //正7:8000 < code < 9000
|
||||
CODE_ZHENG7_MAX: 9000,
|
||||
CODE_FU7_MIN: 7000, //副7:7000 < code < 8000
|
||||
CODE_FU7_MAX: 8000,
|
||||
CODE_ZHENG2_MIN: 3000, //正2:3000 < code < 4000
|
||||
CODE_ZHENG2_MAX: 4000,
|
||||
CODE_FU2_MIN: 2000, //副2:2000 < code < 3000
|
||||
CODE_FU2_MAX: 3000,
|
||||
CODE_ZHU_MIN: 1000, //主牌 ⟺ code > CODE_ZHU_MIN;主花色普通牌:1000 < code < 2000
|
||||
CODE_ZHU_MAX: 2000,
|
||||
CODE_A_IN_ZHU: 14, //主A 的 code % 100(A 在 id_to_code 里 +13 提到 14)
|
||||
|
||||
//几对连续对子起算拖拉机(design §5.3)。group_tractor_runs 返回极大连续段,
|
||||
//段长 >= 本值即拖拉机——服务端分解甩牌与前端手牌标记共用这一个阈值
|
||||
TRACTOR_MIN_PAIRS: 2,
|
||||
|
||||
//牌id转花色
|
||||
id_to_flower: function(cardid){
|
||||
if (cardid == 52 || cardid == 106){
|
||||
@@ -43,11 +65,11 @@ var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || {
|
||||
id_to_code: function(mainflower, cardid){
|
||||
if (cardid == 52 || cardid == 106){
|
||||
//小王
|
||||
return 9553;
|
||||
return youle_erqiwang_shared_cards.CODE_XIAOWANG;
|
||||
}
|
||||
if (cardid == 53 || cardid == 107){
|
||||
//大王
|
||||
return 9554;
|
||||
return youle_erqiwang_shared_cards.CODE_DAWANG;
|
||||
}
|
||||
var flower = youle_erqiwang_shared_cards.id_to_flower(cardid);
|
||||
var number = youle_erqiwang_shared_cards.id_to_number(cardid);
|
||||
@@ -56,18 +78,21 @@ var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || {
|
||||
switch (number)
|
||||
{
|
||||
case 1:
|
||||
//A 提到 K 之上(14)
|
||||
code = code + 13;
|
||||
break;
|
||||
case 2:
|
||||
code = code + 2000;
|
||||
//2 提到副2 段
|
||||
code = code + youle_erqiwang_shared_cards.CODE_FU2_MIN;
|
||||
break;
|
||||
case 7:
|
||||
code = code + 7000;
|
||||
//7 提到副7 段
|
||||
code = code + youle_erqiwang_shared_cards.CODE_FU7_MIN;
|
||||
break;
|
||||
}
|
||||
//主牌花色处理
|
||||
//主牌花色处理:整体再上移一个主牌段(副2→正2、副7→正7、普通牌→主花色普通牌)
|
||||
if (flower == mainflower){
|
||||
code = code + 1000;
|
||||
code = code + youle_erqiwang_shared_cards.CODE_ZHU_MIN;
|
||||
}
|
||||
return code;
|
||||
},
|
||||
@@ -92,44 +117,45 @@ var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || {
|
||||
|
||||
//判断两个牌是否是连续的
|
||||
is_continuous: function(bigcode, smallcode){
|
||||
var S = youle_erqiwang_shared_cards;
|
||||
//是小王
|
||||
var is_xiaowang = function(_code){
|
||||
if (_code == 9553){
|
||||
if (_code == S.CODE_XIAOWANG){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//是正7
|
||||
var is_zheng7 = function(_code){
|
||||
if (_code > 8000 && _code < 9000){
|
||||
if (_code > S.CODE_ZHENG7_MIN && _code < S.CODE_ZHENG7_MAX){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//是负7
|
||||
var is_fu7 = function(_code){
|
||||
if (_code > 7000 && _code < 8000){
|
||||
if (_code > S.CODE_FU7_MIN && _code < S.CODE_FU7_MAX){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//是正2
|
||||
var is_zheng2 = function(_code){
|
||||
if (_code > 3000 && _code < 4000){
|
||||
if (_code > S.CODE_ZHENG2_MIN && _code < S.CODE_ZHENG2_MAX){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//是负2
|
||||
var is_fu2 = function(_code){
|
||||
if (_code > 2000 && _code < 3000){
|
||||
if (_code > S.CODE_FU2_MIN && _code < S.CODE_FU2_MAX){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//是主A
|
||||
var is_zhuA = function(_code){
|
||||
if (_code > 1000 && _code < 2000 && _code % 100 == 14){
|
||||
if (_code > S.CODE_ZHU_MIN && _code < S.CODE_ZHU_MAX && _code % 100 == S.CODE_A_IN_ZHU){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -201,6 +227,34 @@ var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || {
|
||||
return pairlist;
|
||||
},
|
||||
|
||||
//把对子列表扫成【极大连续段】。pairlist 为同一牌组内、已按牌编码从大到小排好的对子列表
|
||||
//(形如 get_pairlist 的返回值:[[cardid, cardid], ...])。
|
||||
//返回 [[pair, ...], ...]:每一段是若干【相邻且 is_continuous】的对子,段与段之间必不连续;
|
||||
//孤立对子也作为长度 1 的段返回,故 concat 所有段 == 原 pairlist(顺序不变)。
|
||||
//段长 >= TRACTOR_MIN_PAIRS 即拖拉机。
|
||||
//【唯一实现】服务端 decompose_trump(甩牌分解)与前端 core/CardMark.js(手牌「拖」标记)
|
||||
//此前各写过一遍同样的扫描,规则内容(什么算连续、几对起算拖拉机)散在两处——现收敛到这里。
|
||||
group_tractor_runs: function(mainflower, pairlist){
|
||||
var runs = [];
|
||||
var i = 0;
|
||||
while (i < pairlist.length){
|
||||
var run = [pairlist[i]];
|
||||
var k = i;
|
||||
while (k + 1 < pairlist.length){
|
||||
var code_k = youle_erqiwang_shared_cards.id_to_code(mainflower, pairlist[k][0]);
|
||||
var code_k1 = youle_erqiwang_shared_cards.id_to_code(mainflower, pairlist[k + 1][0]);
|
||||
if (!youle_erqiwang_shared_cards.is_continuous(code_k, code_k1)){
|
||||
break;
|
||||
}
|
||||
run.push(pairlist[k + 1]);
|
||||
k++;
|
||||
}
|
||||
runs.push(run);
|
||||
i = k + 1;
|
||||
}
|
||||
return runs;
|
||||
},
|
||||
|
||||
//获取拖拉机列表,pairlist为同一花色的排过序的对子列表,cardtype为拖拉机牌型
|
||||
get_tuolaji_list: function(mainflower, pairlist, cardtype){
|
||||
var tuolaji = [];
|
||||
@@ -233,11 +287,11 @@ var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || {
|
||||
//主牌归一化大小(副7 同级取 7000、副2 同级取 2000,其余用牌编码),仅用于甩牌分量比大小
|
||||
trump_rank: function(mainflower, cardid){
|
||||
var code = youle_erqiwang_shared_cards.id_to_code(mainflower, cardid);
|
||||
if (code > 7000 && code < 8000){ //副7
|
||||
return 7000;
|
||||
if (code > youle_erqiwang_shared_cards.CODE_FU7_MIN && code < youle_erqiwang_shared_cards.CODE_FU7_MAX){ //副7
|
||||
return youle_erqiwang_shared_cards.CODE_FU7_MIN;
|
||||
}
|
||||
if (code > 2000 && code < 3000){ //副2
|
||||
return 2000;
|
||||
if (code > youle_erqiwang_shared_cards.CODE_FU2_MIN && code < youle_erqiwang_shared_cards.CODE_FU2_MAX){ //副2
|
||||
return youle_erqiwang_shared_cards.CODE_FU2_MIN;
|
||||
}
|
||||
return code;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user