diff --git a/server/games/erqiwang/class.arith.js b/server/games/erqiwang/class.arith.js index ac5a4ca..f5c27eb 100644 --- a/server/games/erqiwang/class.arith.js +++ b/server/games/erqiwang/class.arith.js @@ -1,6 +1,11 @@ /////////////////////////////////////////////////// ////////// cls_youle_erqiwang_arith: 算法 ///////// /////////////////////////////////////////////////// +//跨模块依赖:Node 走 require 守卫;友乐/浏览器无 require,由 min_loadJsFile 加载为同名全局(dev-guide 01 §1) +if (typeof require !== "undefined"){ + var youle_erqiwang_shared_cards = require("./shared/cards.js"); +} + var cls_youle_erqiwang_arith = cls_youle_erqiwang_arith || { //根据叫分计算基础子数(design §7.1 常规算子 / §7.3.1 爬坡) @@ -59,234 +64,16 @@ var cls_youle_erqiwang_arith = cls_youle_erqiwang_arith || { return 40; }, - //牌id转花色 - id_to_flower: function(cardid){ - if (cardid == 52 || cardid == 106){ - //小王 - return 5; - } - if (cardid == 53 || cardid == 107){ - //大王 - return 5; - } - return parseInt((cardid % 54) / 13) + 1; - }, - - //牌id转牌面数值 - id_to_number: function(cardid){ - if (cardid == 52 || cardid == 106){ - //小王 - return 53; - } - if (cardid == 53 || cardid == 107){ - //大王 - return 54; - } - return (cardid % 54) % 13 + 1; - }, - - //牌id转牌编码 - id_to_code: function(mainflower, cardid){ - if (cardid == 52 || cardid == 106){ - //小王 - return 9553; - } - if (cardid == 53 || cardid == 107){ - //大王 - return 9554; - } - var flower = cls_youle_erqiwang_arith.id_to_flower(cardid); - var number = cls_youle_erqiwang_arith.id_to_number(cardid); - var code = flower * 100 + number; - //特殊处理 - switch (number) - { - case 1: - code = code + 13; - break; - case 2: - code = code + 2000; - break; - case 7: - code = code + 7000; - break; - } - //主牌花色处理 - if (flower == mainflower){ - code = code + 1000; - } - return code; - }, - - //从大到小排序(冒泡排序) - order_cards: function(mainflower, aryCardIDs){ - if (aryCardIDs){ - for (var i = 0; i < aryCardIDs.length; i++) { - for (var j = i + 1; j < aryCardIDs.length; j++) { - var i_code = cls_youle_erqiwang_arith.id_to_code(mainflower, aryCardIDs[i]); - var j_code = cls_youle_erqiwang_arith.id_to_code(mainflower, aryCardIDs[j]); - if (i_code < j_code) { - var tmp = aryCardIDs[j]; - aryCardIDs[j] = aryCardIDs[i]; - aryCardIDs[i] = tmp; - } - } - } - } - return aryCardIDs; - }, - - //判断两个牌是否是连续的 - is_continuous: function(bigcode, smallcode){ - //是小王 - var is_xiaowang = function(_code){ - if (_code == 9553){ - return true; - } - return false; - } - //是正7 - var is_zheng7 = function(_code){ - if (_code > 8000 && _code < 9000){ - return true; - } - return false; - } - //是负7 - var is_fu7 = function(_code){ - if (_code > 7000 && _code < 8000){ - return true; - } - return false; - } - //是正2 - var is_zheng2 = function(_code){ - if (_code > 3000 && _code < 4000){ - return true; - } - return false; - } - //是负2 - var is_fu2 = function(_code){ - if (_code > 2000 && _code < 3000){ - return true; - } - return false; - } - //是主A - var is_zhuA = function(_code){ - if (_code > 1000 && _code < 2000 && _code % 100 == 14){ - return true; - } - return false; - } - //是8 - var is_8 = function(_code){ - if (_code % 100 == 8){ - return true; - } - return false; - } - //是6 - var is_6 = function(_code){ - if (_code % 100 == 6){ - return true; - } - return false; - } - - //普通的连牌 - if (bigcode - smallcode == 1){ - return true; - } - //小王、正7 - if (is_xiaowang(bigcode) && is_zheng7(smallcode)){ - return true; - } - //正7、负7 - if (is_zheng7(bigcode) && is_fu7(smallcode)){ - return true; - } - //负7、正2 - if (is_fu7(bigcode) && is_zheng2(smallcode)){ - return true; - } - //正2、负2 - if (is_zheng2(bigcode) && is_fu2(smallcode)){ - return true; - } - //负2、主A - if (is_fu2(bigcode) && is_zhuA(smallcode)){ - return true; - } - //8、6(design §5.3:7 不与 6/8 相连,6 与 8 相连) - //必须是同一花色的 8 与 6:牌编码同花色相差 2(主花色 1408-1406=2、副牌 308-306=2), - //跨花色则相差 100 的倍数。只判 %100 会把「♥8 + ♣6」误判为连对, - //进而在 get_chongguan 扫描整手牌时让副牌的 6 对错误接续主 8、多算奖(design §8.1 连对链) - if (is_8(bigcode) && is_6(smallcode) && (bigcode - smallcode == 2)){ - return true; - } - return false; - }, - - //获取对子列表,cards为同一花色的排过序的牌列表 - get_pairlist: function(mainflower, flowercards){ - var pairlist = []; - var i = 0; - while (i < flowercards.length - 1){ - //牌编码,两张牌两张牌一取 - var code_i = cls_youle_erqiwang_arith.id_to_code(mainflower, flowercards[i]); - var code_j = cls_youle_erqiwang_arith.id_to_code(mainflower, flowercards[i + 1]); - if (code_i == code_j){ - pairlist.push([flowercards[i], flowercards[i + 1]]); - i = i + 2; - } else { - i++; - } - } - return pairlist; - }, - - //获取拖拉机列表,pairlist为同一花色的排过序的对子列表,cardtype为拖拉机牌型 - get_tuolaji_list: function(mainflower, pairlist, cardtype){ - var tuolaji = []; - var count = cardtype - 300; //拖拉机的对子数量 - var i = 0; - for (var i = 0; i < pairlist.length - count + 1; i++){ - //每次按拖拉机的对子数量取N对 - var tmplist = pairlist.slice(i, i + count); - //判断是否是cardtype类型的拖拉机 - var is_tlj = true; - for (var j = 0; j < tmplist.length - 1; j++) { - var code_j1 = cls_youle_erqiwang_arith.id_to_code(mainflower, tmplist[j][0]); - var code_j2 = cls_youle_erqiwang_arith.id_to_code(mainflower, tmplist[j+1][0]); - if (!cls_youle_erqiwang_arith.is_continuous(code_j1, code_j2)){ - is_tlj = false; - break; - } - } - if (is_tlj){ - var tlj = []; - for (var k = 0; k < tmplist.length; k++){ - tlj = tlj.concat(tmplist[k]); - } - tuolaji.push(tlj); - } - } - return tuolaji; - }, - - //主牌归一化大小(副7 同级取 7000、副2 同级取 2000,其余用牌编码),仅用于甩牌分量比大小 - trump_rank: function(mainflower, cardid){ - var code = cls_youle_erqiwang_arith.id_to_code(mainflower, cardid); - if (code > 7000 && code < 8000){ //副7 - return 7000; - } - if (code > 2000 && code < 3000){ //副2 - return 2000; - } - return code; - }, + //以下 8 项为前后端共享算法,权威实现在 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, //把一组主牌分解为分量列表(design §5.4.3/§5.4.4) //返回 [{type:'single'|'pair'|'tractor', len, value, cards:[]}...],value 为该分量最大牌的归一化 rank diff --git a/server/games/erqiwang/mod.js b/server/games/erqiwang/mod.js index 1f7cd60..487d885 100644 --- a/server/games/erqiwang/mod.js +++ b/server/games/erqiwang/mod.js @@ -12,6 +12,8 @@ min_loadJsFile("games/erqiwang/class.pai.js", function(){ min_loadJsFile("games/erqiwang/class.paiju.js", function(){ //牌桌类 min_loadJsFile("games/erqiwang/class.desk.js", function(){ +//前后端共享牌值算法 +min_loadJsFile("games/erqiwang/shared/cards.js", function(){ //算法类 min_loadJsFile("games/erqiwang/class.arith.js", function(){ //对内的输入接口类 @@ -26,6 +28,7 @@ min_loadJsFile("games/erqiwang/class.export.js", function(){ }); }); }); +}); //对内的输入接口类(需要的外部接口) youle_erqiwang.import = null; diff --git a/server/games/erqiwang/shared/cards.js b/server/games/erqiwang/shared/cards.js new file mode 100644 index 0000000..a9d2c65 --- /dev/null +++ b/server/games/erqiwang/shared/cards.js @@ -0,0 +1,250 @@ +/////////////////////////////////////////////////////////////// +////// youle_erqiwang_shared_cards: 前后端共享牌值算法 ////////// +/////////////////////////////////////////////////////////////// +// 【权威源】本文件是前后端共享算法的唯一可修改处。 +// 前端副本 client/js/01_SubGame/codes/shared/cards.js 由仓库根目录的 +// sync_shared.cmd 单向同步生成,为只读;任何改动都必须落在本文件, +// 改完跑一次 sync_shared.cmd,否则 client/tests/test_shared_sync.js 会红。 +// +// 内容:牌值编码与牌型分组的纯函数,不依赖对局状态、不依赖平台 API。 +// 牌编码区间约定(id_to_code 的产物,各处判定据此): +// 9554 大王 / 9553 小王 / 8000–9000 正7 / 7000–8000 副7 +// 3000–4000 正2 / 2000–3000 副2 / 1000–2000 主花色普通牌 / 100–500 副牌普通牌 +// ⇒ 主牌 ⟺ code > 1000 +var youle_erqiwang_shared_cards = youle_erqiwang_shared_cards || { + + //牌id转花色 + id_to_flower: function(cardid){ + if (cardid == 52 || cardid == 106){ + //小王 + return 5; + } + if (cardid == 53 || cardid == 107){ + //大王 + return 5; + } + return parseInt((cardid % 54) / 13) + 1; + }, + + //牌id转牌面数值 + id_to_number: function(cardid){ + if (cardid == 52 || cardid == 106){ + //小王 + return 53; + } + if (cardid == 53 || cardid == 107){ + //大王 + return 54; + } + return (cardid % 54) % 13 + 1; + }, + + //牌id转牌编码 + id_to_code: function(mainflower, cardid){ + if (cardid == 52 || cardid == 106){ + //小王 + return 9553; + } + if (cardid == 53 || cardid == 107){ + //大王 + return 9554; + } + var flower = youle_erqiwang_shared_cards.id_to_flower(cardid); + var number = youle_erqiwang_shared_cards.id_to_number(cardid); + var code = flower * 100 + number; + //特殊处理 + switch (number) + { + case 1: + code = code + 13; + break; + case 2: + code = code + 2000; + break; + case 7: + code = code + 7000; + break; + } + //主牌花色处理 + if (flower == mainflower){ + code = code + 1000; + } + return code; + }, + + //从大到小排序(冒泡排序) + order_cards: function(mainflower, aryCardIDs){ + if (aryCardIDs){ + for (var i = 0; i < aryCardIDs.length; i++) { + for (var j = i + 1; j < aryCardIDs.length; j++) { + var i_code = youle_erqiwang_shared_cards.id_to_code(mainflower, aryCardIDs[i]); + var j_code = youle_erqiwang_shared_cards.id_to_code(mainflower, aryCardIDs[j]); + if (i_code < j_code) { + var tmp = aryCardIDs[j]; + aryCardIDs[j] = aryCardIDs[i]; + aryCardIDs[i] = tmp; + } + } + } + } + return aryCardIDs; + }, + + //判断两个牌是否是连续的 + is_continuous: function(bigcode, smallcode){ + //是小王 + var is_xiaowang = function(_code){ + if (_code == 9553){ + return true; + } + return false; + } + //是正7 + var is_zheng7 = function(_code){ + if (_code > 8000 && _code < 9000){ + return true; + } + return false; + } + //是负7 + var is_fu7 = function(_code){ + if (_code > 7000 && _code < 8000){ + return true; + } + return false; + } + //是正2 + var is_zheng2 = function(_code){ + if (_code > 3000 && _code < 4000){ + return true; + } + return false; + } + //是负2 + var is_fu2 = function(_code){ + if (_code > 2000 && _code < 3000){ + return true; + } + return false; + } + //是主A + var is_zhuA = function(_code){ + if (_code > 1000 && _code < 2000 && _code % 100 == 14){ + return true; + } + return false; + } + //是8 + var is_8 = function(_code){ + if (_code % 100 == 8){ + return true; + } + return false; + } + //是6 + var is_6 = function(_code){ + if (_code % 100 == 6){ + return true; + } + return false; + } + + //普通的连牌 + if (bigcode - smallcode == 1){ + return true; + } + //小王、正7 + if (is_xiaowang(bigcode) && is_zheng7(smallcode)){ + return true; + } + //正7、负7 + if (is_zheng7(bigcode) && is_fu7(smallcode)){ + return true; + } + //负7、正2 + if (is_fu7(bigcode) && is_zheng2(smallcode)){ + return true; + } + //正2、负2 + if (is_zheng2(bigcode) && is_fu2(smallcode)){ + return true; + } + //负2、主A + if (is_fu2(bigcode) && is_zhuA(smallcode)){ + return true; + } + //8、6(design §5.3:7 不与 6/8 相连,6 与 8 相连) + //必须是同一花色的 8 与 6:牌编码同花色相差 2(主花色 1408-1406=2、副牌 308-306=2), + //跨花色则相差 100 的倍数。只判 %100 会把「♥8 + ♣6」误判为连对, + //进而在 get_chongguan 扫描整手牌时让副牌的 6 对错误接续主 8、多算奖(design §8.1 连对链) + if (is_8(bigcode) && is_6(smallcode) && (bigcode - smallcode == 2)){ + return true; + } + return false; + }, + + //获取对子列表,cards为同一花色的排过序的牌列表 + get_pairlist: function(mainflower, flowercards){ + var pairlist = []; + var i = 0; + while (i < flowercards.length - 1){ + //牌编码,两张牌两张牌一取 + var code_i = youle_erqiwang_shared_cards.id_to_code(mainflower, flowercards[i]); + var code_j = youle_erqiwang_shared_cards.id_to_code(mainflower, flowercards[i + 1]); + if (code_i == code_j){ + pairlist.push([flowercards[i], flowercards[i + 1]]); + i = i + 2; + } else { + i++; + } + } + return pairlist; + }, + + //获取拖拉机列表,pairlist为同一花色的排过序的对子列表,cardtype为拖拉机牌型 + get_tuolaji_list: function(mainflower, pairlist, cardtype){ + var tuolaji = []; + var count = cardtype - 300; //拖拉机的对子数量 + var i = 0; + for (var i = 0; i < pairlist.length - count + 1; i++){ + //每次按拖拉机的对子数量取N对 + var tmplist = pairlist.slice(i, i + count); + //判断是否是cardtype类型的拖拉机 + var is_tlj = true; + for (var j = 0; j < tmplist.length - 1; j++) { + var code_j1 = youle_erqiwang_shared_cards.id_to_code(mainflower, tmplist[j][0]); + var code_j2 = youle_erqiwang_shared_cards.id_to_code(mainflower, tmplist[j+1][0]); + if (!youle_erqiwang_shared_cards.is_continuous(code_j1, code_j2)){ + is_tlj = false; + break; + } + } + if (is_tlj){ + var tlj = []; + for (var k = 0; k < tmplist.length; k++){ + tlj = tlj.concat(tmplist[k]); + } + tuolaji.push(tlj); + } + } + return tuolaji; + }, + + //主牌归一化大小(副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 > 2000 && code < 3000){ //副2 + return 2000; + } + return code; + }, + +} + +//Node 导出;友乐/浏览器无 module 时跳过(dev-guide 01 §1) +if (typeof module !== "undefined"){ + module.exports = youle_erqiwang_shared_cards; +}