├── .gitattributes ├── Logic.lua └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /Logic.lua: -------------------------------------------------------------------------------- 1 | -- 更新历史 2 | -- 2014.9.19 小弘 测试了四川麻将各种胡法 3 | -- 2014.9.20 jim 更新了ValidHu()中ValidAA()的限制条件,修复了112233的情况 4 | -- 2014.9.26 小弘 更新了三元牌的检测,用于加番计算。 5 | -- 2014.10.28 小弘 添加广东麻将AI出牌边界限定 6 | -- 2014.10.30 小弘 添加四川麻将听牌检测Beta,未合并重复项(未充分测试) 7 | -- 2014.10.31 小弘 进一步完善四川麻将听牌,已合并重复项 8 | -- 2015.1.23 小弘 把weight评估值从取最小值修改为负数值之和,使评估更完善,从而修复此前AI不能杠的情况。 9 | -- 并且添加AI出牌阶段的分级模式,使其一定概率随意出牌,AI从强到弱依次是0,1,2,3级 10 | -- 2015.1.27 小弘 修复一个因为吃碰杠中因为没有对手牌排序而导致的无法转换出序号的bug,在转换前加了一个排序。 11 | -- 还有把AI出牌里剔除非手牌的操作提到最前。 12 | -- 2015.3.10 小弘 合并大量重复的代码,新增一堆注释,未完全测试 13 | -- 2015.3.11 小弘 修改了暗杠 14 | 15 | ----------------------- 广东麻将胡法 ------------------- 16 | --CheckJH() --鸡胡 什么牌都可以胡,可吃碰杠 17 | --CheckPH() --平胡 全部都是顺子没有刻子 18 | --CheckPPH() --碰碰胡 全部是刻子没有顺子 19 | --CheckHYS() --混一色 整副牌由字牌及另外单一花色(筒、条或万)组成 20 | --CheckQYS() --清一色 整副牌由同一花色组成 21 | --CheckHP() --碰混 混一色 + 碰碰胡 22 | --CheckQP() --清碰 清一色 + 碰碰胡 23 | --CheckHYJ() --混幺九 由幺九牌和字牌组成的牌型 24 | --CheckXSY() --小三元 拿齐中、发、白三种三元牌,但其中一种是将 25 | --CheckXSX() --小四喜 胡牌者完成东、南、西、北其中三组刻子,一组对子 26 | --CheckZYS() --字一色 由字牌组合成的刻子牌型 27 | --CheckQYJ() --清幺九 只由幺九两种牌组成的刻子牌型 28 | --CheckDSX() --大三元 胡牌时,有中、发、白三组刻子 29 | --CheckDSX() --大四喜 胡牌者完成东、南、西、北四组刻子 30 | --CheckJLBD() --九莲宝灯 同种牌形成 1112345678999 ,在摸到该种牌任何一张即可胡牌,不计清一色 31 | --CheckSSY() --十三幺 1 、 9 万筒索,东、南、西、北、中、发、白;以上牌型任意一张牌作将 32 | 33 | ----------------------- 四川麻将胡法 ------------------- 34 | -- CheckPh_SC() --平胡 普通的四个搭子一对将 35 | -- CheckDdz_SC() --大对子 四个搭子均为三张一样的牌 36 | -- CheckQys_SC() --清一色 胡牌时只有一色牌 37 | -- CheckDy_SC() --带幺 所有牌都带1或9 38 | -- CheckAqd_SC() --暗七对 特殊胡牌类型,不遵循四个搭子一对将,胡牌时为7个对子 39 | -- CheckQdd_SC() --清大对 清一色+大对子 40 | -- CheckLqd_SC() --龙七对 暗七对的改进,七对中有两对(或更多)相同,可视作带根的暗七对。 41 | -- CheckQqd_SC() --清七对 清一色+暗七对 42 | -- CheckQdy_SC() --清带幺 清一色+带幺 43 | -- CheckQlqd_SC() --清龙七对 清一色+龙七对 44 | 45 | local MJ_WAN = 1 --万 46 | local MJ_TIAO = 2 --条 47 | local MJ_BING = 3 --饼 48 | local MJ_FENG = 4 --东南西北(1357) 49 | local MJ_ZFB = 5 --中发白(135) 50 | 51 | local Pai_MING = 0 --明 52 | local Pai_AN = 1 --暗 53 | 54 | local Pai_My = 0 --手牌组 55 | local Pai_Chi = 1 --吃牌组 56 | local Pai_Peng = 2 --碰牌组 57 | local Pai_Gang = 3 --杠牌组 58 | local Pai_Ting = 4 --听牌组 59 | 60 | math.randomseed(tostring(os.time()):reverse():sub(1,6)) 61 | 62 | --检查牌的明暗或者空 63 | local function CheckSinglePaiMingAn(pai) 64 | return math.floor(pai%10000/1000) 65 | end 66 | 67 | --检查单张牌所属牌组 68 | local function CheckSinglePaiGroup(pai) 69 | return math.floor(pai%1000/100) 70 | end 71 | 72 | --检查单张牌的类型,万饼筒条 73 | local function CheckSinglePaiType(pai) 74 | return math.floor(pai%100/10) 75 | end 76 | 77 | --检查单张牌的数值 78 | local function CheckSinglePaiNum(pai) 79 | return math.floor(pai%10) 80 | end 81 | 82 | --返回标准牌型数值(包括牌型与数字) 83 | local function GetPaiTypeNum(pai) 84 | return math.floor(pai%100) 85 | end 86 | 87 | --检测一对 88 | local function CheckAAPai(iValue1,iValue2) 89 | if iValue1 == iValue2 then return true 90 | else return false 91 | end 92 | end 93 | 94 | --检测三连张 95 | local function CheckABCPai(iValue1,iValue2,iValue3) 96 | if (iValue1 == iValue2-1)and(iValue2 == iValue3-1) then return true 97 | else return false 98 | end 99 | end 100 | 101 | --检测三重张 102 | local function CheckAAAPai(iValue1,iValue2,iValue3) 103 | local p12 = CheckAAPai(iValue1,iValue2) 104 | local p23 = CheckAAPai(iValue2,iValue3) 105 | if p12 and p23 then return true 106 | else return false 107 | end 108 | end 109 | 110 | -- 将用户的牌分成 万,条,饼,风,中发白五组并排序返回 111 | local function SortByType(userPai) 112 | local sort_pai = { 113 | ["My"] = { 114 | [MJ_WAN] = {}, 115 | [MJ_TIAO] = {}, 116 | [MJ_BING] = {}, 117 | [MJ_FENG] = {}, 118 | [MJ_ZFB] = {} 119 | },--手牌组 120 | ["Chi"] = { 121 | [MJ_WAN] = {}, 122 | [MJ_TIAO] = {}, 123 | [MJ_BING] = {}, 124 | [MJ_FENG] = {}, 125 | [MJ_ZFB] = {} 126 | },--吃牌组 127 | ["Peng"] = { 128 | [MJ_WAN] = {}, 129 | [MJ_TIAO] = {}, 130 | [MJ_BING] = {}, 131 | [MJ_FENG] = {}, 132 | [MJ_ZFB] = {} 133 | },--碰牌组 134 | ["Gang"] = { 135 | [MJ_WAN] = {}, 136 | [MJ_TIAO] = {}, 137 | [MJ_BING] = {}, 138 | [MJ_FENG] = {}, 139 | [MJ_ZFB] = {} 140 | },--杠牌组 141 | ["Ting"] = { 142 | [MJ_WAN] = {}, 143 | [MJ_TIAO] = {}, 144 | [MJ_BING] = {}, 145 | [MJ_FENG] = {}, 146 | [MJ_ZFB] = {} 147 | }--听牌组 148 | } 149 | for i = 1,#userPai,1 do 150 | if CheckSinglePaiGroup(userPai[i]) == Pai_My then 151 | local paiType = CheckSinglePaiType(userPai[i]) 152 | table.insert(sort_pai["My"][paiType],userPai[i]) 153 | end 154 | if CheckSinglePaiGroup(userPai[i]) == Pai_Chi then 155 | local paiType = CheckSinglePaiType(userPai[i]) 156 | table.insert(sort_pai["Chi"][paiType],userPai[i]) 157 | end 158 | if CheckSinglePaiGroup(userPai[i]) == Pai_Peng then 159 | local paiType = CheckSinglePaiType(userPai[i]) 160 | table.insert(sort_pai["Peng"][paiType],userPai[i]) 161 | end 162 | if CheckSinglePaiGroup(userPai[i]) == Pai_Gang then 163 | local paiType = CheckSinglePaiType(userPai[i]) 164 | table.insert(sort_pai["Gang"][paiType],userPai[i]) 165 | end 166 | if CheckSinglePaiGroup(userPai[i]) == Pai_Ting then 167 | local paiType = CheckSinglePaiType(userPai[i]) 168 | table.insert(sort_pai["Ting"][paiType],userPai[i]) 169 | end 170 | end 171 | 172 | for i = 1,5,1 do 173 | table.sort(sort_pai["My"][i]) 174 | table.sort(sort_pai["Chi"][i]) 175 | table.sort(sort_pai["Peng"][i]) 176 | table.sort(sort_pai["Gang"][i]) 177 | table.sort(sort_pai["Ting"][i]) 178 | end 179 | 180 | return sort_pai 181 | end 182 | 183 | --复制一副牌并返回 184 | --提示:由于lua传递table时是传递引用,所以在函数内部修改table会影响到外部的table,因此用此函数拷贝一份table 185 | local function CopyPai(userPai) 186 | local t_pai = {} 187 | for i = 1,#userPai do 188 | table.insert(t_pai,userPai[i]) 189 | end 190 | return t_pai 191 | end 192 | 193 | --测试胡AA牌(将牌) 194 | local function ValidAA(pai,i,n) 195 | if i + 1 <= n and pai[i] == pai[i+1] then 196 | return true 197 | else 198 | return false 199 | end 200 | end 201 | 202 | --测试胡AAA牌(刻子) 203 | local function ValidAAA(pai,i,n) 204 | if i + 2 <= n and pai[i] == pai[i+1] and pai[i] == pai[i+2] then 205 | return true 206 | else 207 | return false 208 | end 209 | end 210 | 211 | --测试胡ABC牌(顺子) 212 | local function ValidABC(pai,i,n) 213 | -- 顺子要避开 1 222 3 这种可能组成 123 22 的情况 214 | -- 所以拆成两个列表 (1 2 3)|(22) 215 | -- 然后判断 ValidHu(22) 216 | 217 | local t_pai = CopyPai(pai) 218 | -- 只有两张牌,必定没顺 219 | if n - i < 2 then 220 | return false 221 | end 222 | 223 | local found_B = false 224 | local found_C = false 225 | for j = i+1,#t_pai,1 do 226 | if found_B == false and t_pai[j] == ( t_pai[i] + 1 ) then 227 | found_B = true 228 | -- 交换两张牌的位置 229 | local t = t_pai[i + 1] 230 | t_pai[i + 1] = t_pai[j] 231 | t_pai[j] = t 232 | end 233 | end 234 | 235 | for k = i + 2,#pai,1 do 236 | if found_C== false and t_pai[k] == ( t_pai[i] + 2 ) then 237 | found_C = true 238 | -- 交换两张牌的位置 239 | local t = t_pai[i + 2] 240 | t_pai[i + 2] = t_pai[k] 241 | t_pai[k] = t 242 | end 243 | end 244 | 245 | -- 如果能找到顺,判断剩下的牌是否能胡 246 | if found_B == true and found_C == true then 247 | -- 创建待判断列表 248 | local new_list = {} 249 | local k = 1 250 | for j = i + 3,#pai,1 do 251 | new_list[k] = t_pai[j] 252 | k = k + 1 253 | end 254 | -- 对新建数组进行排序 255 | table.sort(new_list) 256 | 257 | return true,new_list 258 | else 259 | return false,{nil} 260 | end 261 | end 262 | 263 | --测试胡n张牌 264 | --IN:用户牌,检测起点,检测总数 265 | --OUT:是否胡牌,将牌数 266 | local function ValidHu(pai,i,n) 267 | -- 空牌组直接胡 268 | if n == 0 then 269 | return true,0 270 | end 271 | 272 | -- 存在两个将牌或少于两个牌,不可能胡 273 | if n%3==1 then 274 | return false,0 275 | end 276 | 277 | -- 检测到末尾,胡 278 | if i > n then 279 | return true,0 280 | end 281 | 282 | -- 测试 AAA 283 | if ValidAAA(pai,i,n) then 284 | local t = false 285 | local k = 0 286 | t,k = ValidHu(pai,i+3,n,0) 287 | if t == true then return true,k end 288 | end 289 | 290 | -- 测试AA 291 | if ValidAA(pai,i,n) then 292 | local t = false 293 | local k = 0 294 | t,k = ValidHu(pai,i+2,n) 295 | if t == true and k == 0 then return true,1 end 296 | end 297 | 298 | -- 对万,条,饼测试ABC 299 | if CheckSinglePaiType(pai[1]) ~= MJ_FENG and CheckSinglePaiType(pai[1]) ~= MJ_ZFB then 300 | local new_pai = {} 301 | local t = false 302 | t,new_pai = ValidABC(pai,i,n) 303 | if t == true then 304 | local t2 = false 305 | local k = 0 306 | t2,k = ValidHu(new_pai,1,#new_pai) 307 | if t2 == true then return t2,k end 308 | end 309 | end 310 | return false,0 311 | end 312 | 313 | --检测吃牌 314 | --IN:用户牌,上家的牌(只有上家能吃) 315 | --OUT:所有可吃的牌的(!序号!)的table集合 316 | --举例:手牌:11,12,12,14,上家牌:13 317 | -- 输出:{{1,2},{2,4}} 318 | --客户端会把序号重新转换成牌值,至于为什么这里转成序号,那是历史遗留问题。。。 319 | function CheckChiPai(userPai,prePai) 320 | --吃牌,用上家牌与自身牌遍历对比 321 | local paiGroup = SortByType(userPai) 322 | 323 | local attribute = {["Chi"]={}} 324 | local paiType = CheckSinglePaiType(prePai) 325 | if(#paiGroup["My"][paiType]) 326 | then 327 | for i=1,#(paiGroup["My"][paiType])-1 328 | do 329 | --上家牌在顺子最左 330 | if (paiGroup["My"][paiType][i] == prePai+1) and (paiGroup["My"][paiType][i+1] == prePai+2) 331 | then 332 | local shunzi = {paiGroup["My"][paiType][i],paiGroup["My"][paiType][i+1]} 333 | table.insert(attribute["Chi"],shunzi) 334 | end 335 | --上家牌在顺子中间 336 | if (paiGroup["My"][paiType][i] == prePai-1) and (paiGroup["My"][paiType][i+1] == prePai+1) 337 | then 338 | local shunzi = {paiGroup["My"][paiType][i],paiGroup["My"][paiType][i+1]} 339 | table.insert(attribute["Chi"],shunzi) 340 | --下面这块是用在出现AB**BC时吃B的情况 341 | elseif (paiGroup["My"][paiType][i] ==prePai-1) and (paiGroup["My"][paiType][i+1] == prePai) 342 | then 343 | for k=i+1,#(paiGroup["My"][paiType]) 344 | do 345 | if (paiGroup["My"][paiType][k] == prePai+1) 346 | then 347 | local shunzi = {paiGroup["My"][paiType][i],paiGroup["My"][paiType][k]} 348 | table.insert(attribute["Chi"],shunzi) 349 | end 350 | end 351 | end 352 | --上家牌在顺子最右 353 | if (paiGroup["My"][paiType][i] == prePai-2) and (paiGroup["My"][paiType][i+1] == prePai-1) 354 | then 355 | local shunzi = {paiGroup["My"][paiType][i],paiGroup["My"][paiType][i+1]} 356 | table.insert(attribute["Chi"],shunzi) 357 | end 358 | end 359 | end 360 | 361 | --转换至userPai的绝对位置 362 | table.sort( userPai, function (a,b) return a= 3 and found_ting == false then 537 | -- 统计万条饼数目 538 | local num_count = {[MJ_WAN] = 0,[MJ_TIAO] = 0,[MJ_BING] = 0} 539 | for i = 1,#userPai do 540 | local paitype = CheckSinglePaiType(userPai[i]) 541 | if paitype <= 3 then 542 | num_count[paitype] = num_count[paitype] + 1 543 | end 544 | end 545 | 546 | -- 检查是哪种牌 >= 13 547 | local pai_type = 0 548 | for i = 1,#num_count do 549 | if num_count[i] >= 13 then 550 | pai_type = i 551 | end 552 | end 553 | 554 | -- 相同牌数的牌 >= 13 555 | if pai_type ~= 0 then 556 | 557 | local count = {[1] = 0,[2] = 0,[3] = 0,[4] = 0,[5] = 0,[6] = 0,[7] = 0,[8] = 0,[9] = 0} 558 | for i = 1,#userPai do 559 | local m_type = CheckSinglePaiType(userPai[i]) 560 | local m_num = CheckSinglePaiNum(userPai[i]) 561 | if m_type == pai_type then 562 | count[m_num] = count[m_num] + 1 563 | end 564 | end 565 | 566 | -- 统计缺少牌的情况 567 | local zero_count = 0 568 | local zero_num = 0 569 | local size2_count= 0 -- 除111 和 999外,数目 >= 2 的组的数目 570 | local size2_num1 = 0 -- 该牌的索引 571 | local size2_num2 = 0 572 | for i = 1,#count do 573 | if count[i] == 0 then 574 | zero_count = zero_count + 1 575 | zero_num = i 576 | end 577 | if i ~= 1 and i ~= 9 then 578 | if count[i] >= 2 then 579 | size2_count = size2_count + 1 580 | if size2_num1 == 0 then 581 | size2_num1 = i 582 | else size2_num2 = i 583 | end 584 | end 585 | end 586 | end 587 | 588 | local target = 0 589 | local need = 0 590 | if num_count[pai_type] == 13 then 591 | for i = 1,#userPai do 592 | if CheckSinglePaiType(userPai[i]) ~= pai_type then target = userPai[i] end 593 | end 594 | end 595 | 596 | ----------讨论13张同花色情况 ----- 597 | if zero_count <= 1 and size2_count <= 1 and num_count[pai_type] == 13 then 598 | if zero_count == 1 and count[1] >= 3 and count[9] >= 3 then 599 | found_ting = true 600 | need = zero_num + 10 * pai_type 601 | end 602 | if zero_num == 0 and count[1] == 2 and count[9] >= 3 then 603 | found_ting = true 604 | need = 1 + 10 * pai_type 605 | end 606 | if zero_num == 0 and count[1] >= 3 and count[9] == 2 then 607 | found_ting = true 608 | need = 9 * 10 * pai_type 609 | end 610 | 611 | if found_ting == true then 612 | local t_list = {target,need} 613 | table.insert(ting_list,t_list) 614 | end 615 | 616 | if zero_num == 0 and count[1] >= 3 and count[9] >= 3 then 617 | found_ting = true 618 | if count[1] ~= 4 then 619 | need = 1 + 10 * pai_type 620 | local t_list = {target,need} 621 | table.insert(ting_list,t_list) 622 | end 623 | if count[9] ~= 4 then 624 | need = 9 + 10 * pai_type 625 | local t_list = {target,need} 626 | table.insert(ting_list,t_list) 627 | end 628 | end 629 | end 630 | 631 | 632 | -------- 下面讨论14张同花牌的情况 633 | -- 统计 2 ~ 8 的牌数和 634 | if num_count[pai_type] == 14 then 635 | local mid_count = 0 636 | for i = 2,8,1 do 637 | mid_count = mid_count + count[i] 638 | end 639 | -- 0 (2 8 4) or (4 2 8) 640 | if zero_count == 0 and mid_count == 8 and ( count[1] + count[9] == 6 ) and ( count[1] == 2 or count[1] == 4 ) then 641 | found_ting = true 642 | if count[1] == 2 then 643 | target = 9 + 10 * pai_type 644 | need = 1 + 10 * pai_type 645 | else 646 | target = 1 + 10 * pai_type 647 | need = 9 + 10 * pai_type 648 | end 649 | local t_list = {target,need} 650 | table.insert(ting_list,t_list) 651 | end 652 | -- 0 (2 9 3 ) or (3 9 2) 653 | if zero_count == 0 and mid_count == 9 and ( count[1] + count[9] == 5 ) and ( count[1] == 2 or count[1] == 3 ) then 654 | found_ting = true 655 | if count[1] == 2 then 656 | need = 1 + 10 * pai_type 657 | else 658 | need = 9 + 10 * pai_type 659 | end 660 | if size2_count == 1 then 661 | target = size2_num1 + 10 * pai_type 662 | local t_list = {target,need} 663 | table.insert(ting_list,t_list) 664 | elseif size2_count == 2 then 665 | local target1 = size2_num1 + 10 * pai_type 666 | local target2 = size2_num2 + 10 * pai_type 667 | local t_list1 = {target1,need} 668 | local t_list2 = {target2,need} 669 | table.insert(ting_list,t_list1) 670 | table.insert(ting_list,t_list2) 671 | end 672 | end 673 | -- 1 (3 7 4) or (4 7 3) 674 | if zero_count == 1 and mid_count == 7 and ( count[1] + count[9] == 7 ) and ( count[1] == 3 or count[1] == 4 ) then 675 | found_ting = true 676 | if count[1] == 4 then 677 | target1 = 1 + 10 * pai_type 678 | else target1 = 9 + 10 * pai_type end 679 | need = zero_num + 10 * pai_type 680 | local target2 = size2_num1 + 10 * pai_type 681 | local t_list1 = {target1,need} 682 | local t_list2 = {target2,need} 683 | table.insert(ting_list,t_list1) 684 | table.insert(ting_list,t_list2) 685 | 686 | end 687 | -- 1 (3 8 3) 688 | if zero_count == 1 and mid_count == 8 and ( count[1] == 3 and count[9] == 3 ) then 689 | found_ting = true 690 | need = zero_num + 10 * pai_type 691 | if size2_count == 1 then 692 | target = size2_num1 + 10 * pai_type 693 | local t_list = {target,need} 694 | table.insert(ting_list,t_list) 695 | elseif size2_count == 2 then 696 | local target1 = size2_num1 + 10 * pai_type 697 | local target2 = size2_num2 + 10 * pai_type 698 | local t_list1 = {target1,need} 699 | local t_list2 = {target2,need} 700 | table.insert(ting_list,t_list1) 701 | table.insert(ting_list,t_list2) 702 | end 703 | end 704 | end 705 | end 706 | -- if sum_hu == 5 then return false,0,0 end 707 | 708 | -- ********** 四组“胡”且将 == 0 或 1 ************************************************ 709 | 710 | if sum_hu == 4 and sum_jiang < 2 and found_ting == false then 711 | -- 剩下的一组所需要的将 为 1 或 0 712 | local jiang_need = 1 - sum_jiang 713 | -- 找出是哪一组没法“胡” 714 | local target = 0 715 | for i = 1,#pai_info do 716 | if pai_info[i][1] == false then target = i end 717 | end 718 | 719 | -- 从该组第一张牌开始替换 720 | for i = 1,#sort_pai["My"][target] do 721 | local t_pai = sort_pai["My"][target][i] 722 | for j = 1,#collection[target] do 723 | -- local save_pai = sort_pai["My"][target][i] 724 | sort_pai["My"][target][i] = collection[target][j] 725 | local t = false 726 | local k = 0 727 | t,k = ValidHu(sort_pai["My"][target],1,#sort_pai["My"][target]) 728 | -- 胡 且 达到所需将牌数目 729 | -- if t == true and k == jiang_need then return true,t_pai,collection[target][j] end 730 | if t == true and k == jiang_need then 731 | found_ting = true 732 | -- 注意本轮被替换的数实际是 733 | local t_list = {t_pai,collection[target][j]} 734 | table.insert(ting_list,t_list) 735 | end 736 | end 737 | sort_pai["My"][target][i] = t_pai 738 | end 739 | end 740 | 741 | -- ********** 三组“胡” 且将 == 0 或 1 ******************************************************** 742 | if sum_hu == 3 and sum_jiang < 2 and found_ting == false then 743 | -- 找出是哪两组没法“胡” 744 | local target1 = 0 745 | local target2 = 0 746 | for i = 1,#pai_info do 747 | if pai_info[i][1] == false then 748 | if target1 == 0 then target1 = i 749 | else target2 = i 750 | end 751 | end 752 | end 753 | -- 剩下的两组所需要的将 为 1 或 0 754 | local jiang_need = 1 - sum_jiang 755 | -- 删掉第一组中的牌,往第二组加一张牌 756 | for i = 1,#sort_pai["My"][target1] do 757 | -- 记录下第一组牌中被删除的牌 758 | local t_pai = sort_pai["My"][target1][i] 759 | table.remove(sort_pai["My"][target1],i) 760 | local t1 = false 761 | local k1 = 0 762 | t1,k1 = ValidHu(sort_pai["My"][target1],1,#sort_pai["My"][target1]) 763 | -- 删掉第一组牌能胡,给第二组牌添加一张牌,测试 胡 和将 764 | if t1 == true then 765 | for k = 1,#collection[target2] do 766 | local t_pai2 = CopyPai(sort_pai["My"][target2]) 767 | table.insert(t_pai2,collection[target2][k]) 768 | table.sort(t_pai2) 769 | local t2 = false 770 | local k2 = 0 771 | t2,k2 = ValidHu(t_pai2,1,#t_pai2) 772 | -- if t2 == true and k2 + k1 == jiang_need then return true,t_pai,collection[target2][k] end 773 | if t2 == true and k2 + k1 == jiang_need then 774 | found_ting = true 775 | local t_list = {t_pai,collection[target2][k]} 776 | table.insert(ting_list,t_list) 777 | end 778 | end 779 | end 780 | -- 还原第一组牌 781 | table.insert(sort_pai["My"][target1],i,t_pai) 782 | end 783 | -- 交换下位置 784 | -- 删掉第二组中的牌,往第一组加一张牌 785 | for i = 1,#sort_pai["My"][target2] do 786 | -- 记录下第二组牌中被删除的牌 787 | local t_pai = sort_pai["My"][target2][i] 788 | table.remove(sort_pai["My"][target2],i) 789 | local t1 = false 790 | local k1 = 0 791 | t1,k1 = ValidHu(sort_pai["My"][target2],1,#sort_pai["My"][target2]) 792 | -- 第二组牌能胡,往第一组牌中加入牌,测试 胡、将 793 | if t1 == true then 794 | -- 往第一组牌加入一张牌 795 | for k = 1,#collection[target1] do 796 | local t_pai2 = CopyPai(sort_pai["My"][target1]) 797 | table.insert(t_pai2,collection[target1][k]) 798 | table.sort(t_pai2) 799 | local t2 = false 800 | local k2 = 0 801 | t2,k2 = ValidHu(t_pai2,1,#t_pai2) 802 | -- if t2 == true and k2 + k1 == jiang_need then return true,t_pai,collection[target1][k] end 803 | if t2 == true and k2 + k1 == jiang_need then 804 | found_ting = true 805 | local t_list = {t_pai,collection[target1][k]} 806 | table.insert(ting_list,t_list) 807 | end 808 | end 809 | end 810 | -- 还原第二组牌 811 | table.insert(sort_pai["My"][target2],i,t_pai) 812 | end 813 | end 814 | 815 | -- ************************************ 检查是否听 十三幺 ********************************************************** 816 | if sum_hu < 3 and found_ting == false then 817 | local ssy_list = {11,19,21,29,31,39,41,43,45,47,51,53,55} 818 | local ssy_count = { 819 | [11] = 0,[19] = 0,[21] = 0,[29] = 0,[31] = 0,[39] = 0,[41] = 0,[43] = 0,[45] = 0,[47] = 0,[51] = 0, 820 | [53] = 0,[55] = 0} 821 | 822 | -- 扫描用户牌,统计出现十三幺各牌的个数 823 | for i = 1,#userPai do 824 | for j = 1,#ssy_list do 825 | if userPai[i] == ssy_list[j] then 826 | ssy_count[ssy_list[j]] = ssy_count[ssy_list[j]] + 1 827 | end 828 | end 829 | end 830 | 831 | local sum = 0 832 | local zero = 0 833 | for i = 1,#ssy_list do 834 | if ssy_count[ssy_list[i]] == 0 then zero = zero + 1 end 835 | sum = sum + ssy_count[ssy_list[i]] 836 | end 837 | 838 | -- 能听的情况只有 839 | -- 1. zero = 0 && sum = 13 : 找出不在ssy_list 中的牌,返回 -- 11,19,21,29,31,39,41,43,45,47,51,53,55,() 840 | -- 2. zero = 1 && sum = 13 : 同 1 11,(18),21,29,31,39,41,43,45,47,51,53,55,55 841 | -- 3. zero = 1 && sum = 14 : 找出牌数大于1的组,可能为两组或一组(3张相同)11,(11),21,29,31,39,41,43,45,47,51,53,55,55 842 | 843 | ---- 讨论 1,2 合为一种情况 ------------- 844 | if ( zero == 0 or zero == 1 ) and sum == 13 then 845 | local target = 0 846 | for i = 1,#userPai do 847 | local found = false 848 | for j = 1,#ssy_list do 849 | if userPai[i] == ssy_list[j] then found = true end 850 | end 851 | if found == false then target = i end 852 | end 853 | -- 这里就只插入一张 854 | found_ting = true 855 | local t_list = {userPai[target],ssy_list[1]} 856 | table.insert(ting_list,t_list) 857 | end 858 | ---- 讨论3 ---------------------- 859 | if zero == 1 and sum == 14 then 860 | -- 找出欠缺的牌 861 | local pai_need = 0 862 | for i = 1,#ssy_list do 863 | if ssy_count[ssy_list[i]] == 0 then pai_need = ssy_list[i] end 864 | end 865 | -- 找出牌数大于1的组,添加 866 | for i = 1,#ssy_list do 867 | if ssy_count[ssy_list[i]] > 1 then 868 | found_ting = true 869 | local t_list = {ssy_list[i],pai_need} 870 | table.insert(ting_list,t_list) 871 | end 872 | end 873 | end 874 | end 875 | end 876 | -- 过滤掉相同的组 877 | local unique_list = {} 878 | for i = 1,#ting_list do 879 | local found_same = false 880 | for j = 1,#unique_list do 881 | if ting_list[i][1] == unique_list[j][1] and ting_list[i][2] == unique_list[j][2] then found_same = true end 882 | end 883 | if found_same ~= true then table.insert(unique_list,ting_list[i]) end 884 | end 885 | 886 | 887 | return unique_list 888 | end 889 | 890 | --递归检测刻子 891 | --IN:用户手牌,检测起点,待检测牌总数 892 | --OUT:是否全是刻子 893 | local function CheckKe(userPai,i,n) 894 | -- 小于三张不可能刻 895 | if i > n then return true end 896 | if n - i < 2 then return false end 897 | if CheckAAAPai(userPai[i],userPai[i+1],userPai[i+2]) and CheckKe(userPai,i+3,n) then 898 | return true 899 | else 900 | return false end 901 | end 902 | 903 | --递归检测顺子 904 | --IN:用户手牌,检测起点,待检测牌总数 905 | --OUT:是否全是顺子 906 | local function CheckShun(userPai,i,n) 907 | -- 小于三张不可能是顺子 908 | if i > n then return true end 909 | if n - i < 2 then return false end 910 | 911 | if CheckABCPai(userPai[i],userPai[i+1],userPai[i+2]) and CheckShun(userPai,i+3,n) then 912 | return true 913 | else 914 | return false end 915 | end 916 | 917 | -- 删除传进来的牌中的将牌 918 | -- 仅用于删除 平胡或碰碰胡中的将牌 如 11 11 11 12 12 12 13 13 13 14 14 14 |(43 43) 919 | -- 可能会删除 (11 11) 12 12 13 13 慎用 920 | local function Deletejiang(userPai) 921 | local count = 0 922 | local last_pai = 0 923 | for i = 1,#userPai do 924 | local cur_pai = userPai[i] 925 | if cur_pai ~= last_pai then 926 | last_pai = cur_pai 927 | if count == 1 then 928 | table.remove(userPai,i-1) 929 | table.remove(userPai,i-2) 930 | end 931 | count = 0 932 | else 933 | count = count + 1 934 | last_pai = cur_pai 935 | end 936 | end 937 | 938 | -- 检测末尾部分 939 | if count == 1 then 940 | table.remove(userPai) 941 | table.remove(userPai) 942 | end 943 | end 944 | 945 | -- 检测平胡 946 | local function CheckPH(userPai) 947 | -- 拷贝数组 948 | local t_pai = CopyPai(userPai) 949 | 950 | -- 删除将牌 951 | Deletejiang(t_pai) 952 | 953 | -- 保证只有一组将牌被删除,多组则返回 954 | if #t_pai ~= ( #userPai - 2 ) then return false end 955 | 956 | -- --检查剩下的牌是否只由顺子组成 957 | local sort_pai = SortByType(t_pai) 958 | for i = 1,#sort_pai["My"] do 959 | if #sort_pai["My"][i] ~= 0 then 960 | if CheckShun(sort_pai["My"][i],1,#sort_pai["My"][i]) == false then return false end 961 | end 962 | end 963 | 964 | return true 965 | end 966 | 967 | -- 检测碰碰胡 968 | local function CheckPPH(userPai) 969 | -- 无顺子即可 970 | -- 拷贝数组 971 | local t_pai = CopyPai(userPai) 972 | 973 | -- 删除将牌 974 | Deletejiang(t_pai) 975 | 976 | -- 保证只有一组将牌被删除,多组则返回 977 | if #t_pai ~= ( #userPai - 2 ) then return false end 978 | 979 | local sort_pai = SortByType(t_pai) 980 | 981 | --检查剩下的牌是否只由刻组成 982 | for i = 1,#sort_pai["My"] do 983 | if #sort_pai["My"][i] ~= 0 then 984 | if CheckKe(sort_pai["My"][i],1,#sort_pai["My"][i]) == false then return false end 985 | end 986 | end 987 | 988 | return true 989 | 990 | end 991 | 992 | -- 检测混一色 993 | local function CheckHYS(userPai) 994 | local sort_pai = SortByType(userPai) 995 | local count_wan = #sort_pai["My"][MJ_WAN] 996 | local count_bing = #sort_pai["My"][MJ_BING] 997 | local count_tiao = #sort_pai["My"][MJ_TIAO] 998 | local count_feng = #sort_pai["My"][MJ_FENG] 999 | local count_zfb = #sort_pai["My"][MJ_ZFB] 1000 | -- 保证有字牌 1001 | if count_feng == 0 and count_zfb == 0 then return false end 1002 | -- 保证单花色 1003 | if count_wan ~= 0 and count_tiao == 0 and count_bing == 0 then return true end 1004 | if count_wan == 0 and count_tiao ~= 0 and count_bing == 0 then return true end 1005 | if count_wan == 0 and count_tiao == 0 and count_bing ~= 0 then return true end 1006 | 1007 | return false 1008 | end 1009 | 1010 | -- 检测混碰 1011 | local function CheckHP(userPai) 1012 | if CheckHYS(userPai) and CheckPPH(userPai) then return true end 1013 | return false 1014 | end 1015 | 1016 | --检测混幺九 1017 | local function CheckHYJ(userPai) 1018 | -- 对 (万,饼,条 )满足 幺九 1019 | for i = 1,#userPai do 1020 | local paitype = CheckSinglePaiType(userPai[i]) 1021 | local num = CheckSinglePaiNum(userPai[i]) 1022 | 1023 | if paitype == MJ_WAN or paitype == MJ_BING or paitype == MJ_TIAO then 1024 | if num ~= 1 and num ~= 9 then 1025 | return false 1026 | end 1027 | end 1028 | end 1029 | 1030 | -- 检查是否有字牌,如果先检查 清幺九,则可以省去这步 1031 | local found = false 1032 | for i = 1,#userPai do 1033 | if CheckSinglePaiType(userPai[i]) == MJ_FENG or CheckSinglePaiType(userPai[i]) == MJ_ZFB then 1034 | found = true 1035 | end 1036 | end 1037 | 1038 | return found 1039 | end 1040 | 1041 | -- 检测小三元 1042 | local function CheckXSY(userPai) 1043 | local zhong = 0 1044 | local fa = 0 1045 | local bai = 0 1046 | for i = 1,#userPai do 1047 | local paitype = CheckSinglePaiType(userPai[i]) 1048 | if paitype == MJ_ZFB then 1049 | if CheckSinglePaiNum(userPai[i]) == 1 then 1050 | zhong = zhong + 1 1051 | elseif CheckSinglePaiNum(userPai[i]) == 3 then 1052 | fa = fa + 1 1053 | elseif CheckSinglePaiNum(userPai[i]) == 5 then 1054 | bai = bai + 1 1055 | end 1056 | end 1057 | end 1058 | if zhong == 2 and fa >= 3 and bai >= 3 then return true end 1059 | if zhong >= 3 and fa == 2 and bai >= 3 then return true end 1060 | if zhong >= 3 and fa >= 3 and bai == 2 then return true end 1061 | return false 1062 | end 1063 | 1064 | -- 检测清一色 1065 | local function CheckQYS(userPai) 1066 | local paitype = CheckSinglePaiType(userPai[1]) 1067 | if paitype == MJ_WAN or paitype == MJ_TIAO or paitype == MJ_BING then 1068 | for i = 2,#userPai do 1069 | local t = CheckSinglePaiType(userPai[i]) 1070 | if t ~= paitype then 1071 | return false 1072 | end 1073 | end 1074 | elseif paitype == MJ_FENG or paitype == MJ_ZFB then 1075 | for i = 2,#userPai do 1076 | if CheckSinglePaiType(userPai[i]) ~= MJ_FENG and CheckSinglePaiType(userPai[i]) ~= MJ_ZFB then 1077 | return false 1078 | end 1079 | end 1080 | end 1081 | 1082 | return true 1083 | end 1084 | 1085 | -- 检测清碰 1086 | local function CheckQP(userPai) 1087 | if CheckQYS(userPai) and CheckPPH(userPai) then return true end 1088 | return false 1089 | end 1090 | 1091 | -- 检测小四喜 1092 | local function CheckXSX(userPai) 1093 | local dong = 0 1094 | local nan = 0 1095 | local xi = 0 1096 | local bei = 0 1097 | for i = 1,#userPai do 1098 | if CheckSinglePaiType(userPai[i]) == MJ_FENG then 1099 | if CheckSinglePaiNum(userPai[i]) == 1 then 1100 | dong = dong + 1 1101 | elseif CheckSinglePaiNum(userPai[i]) == 3 then 1102 | nan = nan + 1 1103 | elseif CheckSinglePaiNum(userPai[i]) == 5 then 1104 | xi = xi + 1 1105 | elseif CheckSinglePaiNum(userPai[i]) == 7 then 1106 | bei = bei + 1 1107 | end 1108 | end 1109 | end 1110 | if dong == 2 and nan == 3 and xi == 3 and bei == 3 then return true end 1111 | if dong == 3 and nan == 2 and xi == 3 and bei == 3 then return true end 1112 | if dong == 3 and nan == 3 and xi == 2 and bei == 3 then return true end 1113 | if dong == 3 and nan == 3 and xi == 3 and bei == 2 then return true end 1114 | return false 1115 | end 1116 | 1117 | -- 检测字一色 1118 | local function CheckZYS(userPai) 1119 | for i = 1,#userPai do 1120 | local paitype = CheckSinglePaiType(userPai[i]) 1121 | if paitype ~= MJ_ZFB and paitype ~= MJ_FENG then 1122 | return false 1123 | end 1124 | end 1125 | return true 1126 | end 1127 | 1128 | -- 检测清幺九 1129 | local function CheckQYJ(userPai) 1130 | for i = 1,#userPai do 1131 | local paitype = CheckSinglePaiType(userPai[i]) 1132 | local num = CheckSinglePaiNum(userPai[i]) 1133 | -- 遇到字牌返回 1134 | if paitype == MJ_FENG or paitype == MJ_ZFB then return false end 1135 | -- 非字牌遇到非 1 9 ,返回 1136 | if num ~= 1 and num ~= 9 then return false end 1137 | end 1138 | return true 1139 | end 1140 | 1141 | -- 检测九莲宝灯 1142 | local function CheckJLBD(userPai) 1143 | -- 穷举法 1144 | -- 构造新列表 ( 使牌只有牌型和牌号) 1145 | local t_pai = {} 1146 | for i = 1,#userPai do 1147 | table.insert(t_pai,GetPaiTypeNum(userPai[i])) 1148 | end 1149 | 1150 | local JLBD_list = { 1151 | {11,11,11,12,13,14,15,16,17,18,19,19,19}, 1152 | {11,11,11,11,12,13,14,15,16,17,18,19,19,19},{11,11,11,12,12,13,14,15,16,17,18,19,19,19}, 1153 | {11,11,11,12,13,13,14,15,16,17,18,19,19,19},{11,11,11,12,13,14,14,15,16,17,18,19,19,19}, 1154 | {11,11,11,12,13,14,15,15,16,17,18,19,19,19},{11,11,11,12,13,14,15,16,16,17,18,19,19,19}, 1155 | {11,11,11,12,13,14,15,16,17,17,18,19,19,19},{11,11,11,12,13,14,15,16,17,18,18,19,19,19}, 1156 | {11,11,11,12,13,14,15,16,17,18,19,19,19,19},{21,21,21,21,22,23,24,25,26,27,28,29,29,29}, 1157 | {21,21,21,22,22,23,24,25,26,27,28,29,29,29},{21,21,21,22,23,23,24,25,26,27,28,29,29,29}, 1158 | {21,21,21,22,23,24,24,25,26,27,28,29,29,29},{21,21,21,22,23,24,25,25,26,27,28,29,29,29}, 1159 | {21,21,21,22,23,24,25,26,26,27,28,29,29,29},{21,21,21,22,23,24,25,26,27,27,28,29,29,29}, 1160 | {21,21,21,22,23,24,25,26,27,28,28,29,29,29},{21,21,21,22,23,24,25,26,27,28,29,29,29,29}, 1161 | {31,31,31,31,32,33,34,35,36,37,38,39,39,39},{31,31,31,32,32,33,34,35,36,37,38,39,39,39}, 1162 | {31,31,31,32,33,33,34,35,36,37,38,39,39,39},{31,31,31,32,33,34,34,35,36,37,38,39,39,39}, 1163 | {31,31,31,32,33,34,35,35,36,37,38,39,39,39},{31,31,31,32,33,34,35,36,36,37,38,39,39,39}, 1164 | {31,31,31,32,33,34,35,36,37,37,38,39,39,39},{31,31,31,32,33,34,35,36,37,38,38,39,39,39}, 1165 | {31,31,31,32,33,34,35,36,37,38,39,39,39,39}} 1166 | 1167 | local found = false 1168 | local n = #t_pai 1169 | local count = 0 1170 | for i = 1,#JLBD_list do 1171 | for j = 1,#t_pai do 1172 | if t_pai[j] == JLBD_list[i][j] then 1173 | count = count + 1 1174 | else 1175 | count = 0 end 1176 | if count == n then 1177 | return true end 1178 | end 1179 | end 1180 | return false 1181 | end 1182 | 1183 | -- 检测大三元 1184 | local function CheckDSY(userPai) 1185 | --从CheckHuScore传入的userPai包含自摸牌 1186 | local zhong = 0 1187 | local fa = 0 1188 | local bai = 0 1189 | for i=1,#userPai 1190 | do 1191 | if CheckSinglePaiType(userPai[i]) == MJ_ZFB then 1192 | if CheckSinglePaiNum(userPai[i]) == 1 then 1193 | zhong = zhong+1 1194 | end 1195 | if CheckSinglePaiNum(userPai[i]) == 3 then 1196 | fa = fa+1 1197 | end 1198 | if CheckSinglePaiNum(userPai[i]) == 5 then 1199 | bai = bai+1 1200 | end 1201 | end 1202 | end 1203 | if zhong<3 or fa<3 or bai<3 then 1204 | return false 1205 | else 1206 | return true end 1207 | end 1208 | 1209 | --大四喜 1210 | local function CheckDSX(userPai) 1211 | --从CheckHuScore传入的userPai包含自摸牌 1212 | local dong = 0 1213 | local nan = 0 1214 | local xi = 0 1215 | local bei = 0 1216 | 1217 | for i=1,#userPai do 1218 | if CheckSinglePaiType(userPai[i]) == MJ_FENG then 1219 | --东 1220 | if CheckSinglePaiNum(userPai[i]) == 1 then 1221 | dong = dong + 1 end 1222 | --南 1223 | if CheckSinglePaiNum(userPai[i]) == 3 then 1224 | nan = nan + 1 end 1225 | --西 1226 | if CheckSinglePaiNum(userPai[i]) == 5 then 1227 | xi = xi + 1 end 1228 | --北 1229 | if CheckSinglePaiNum(userPai[i]) == 7 then 1230 | bei = bei + 1 end 1231 | end 1232 | end 1233 | 1234 | if dong < 3 or xi < 3 or nan < 3 or bei < 3 then 1235 | return false 1236 | else 1237 | return true end 1238 | end 1239 | 1240 | --十三幺 1241 | local function CheckSSY(userPai) 1242 | -- (万筒索)1,9各一张, (东、南、西、北、中、发、白) 其中一个为两张,其余为一张 1243 | -- 典型 11 19 21 29 31 39 41 43 45 47 51 53 55 55 1244 | 1245 | local sort_pai = SortByType(userPai) 1246 | -- 判断每组牌是否起码有2张 1247 | -- if #sort_pai["My"][MJ_WAN] < 2 or #sort_pai["My"][MJ_TIAO] < 2 or #sort_pai["My"][MJ_BING] < 2 then return false end 1248 | 1249 | -- if CheckSinglePaiNum(sort_pai["My"][MJ_WAN][1]) ~= 1 or CheckSinglePaiNum(sort_pai["My"][MJ_WAN][2]) ~= 9 then return false end 1250 | -- if CheckSinglePaiNum(sort_pai["My"][MJ_TIAO][1]) ~= 1 or CheckSinglePaiNum(sort_pai["My"][MJ_TIAO][2]) ~= 9 then return false end 1251 | -- if CheckSinglePaiNum(sort_pai["My"][MJ_BING][1]) ~= 1 or CheckSinglePaiNum(sort_pai["My"][MJ_BING][2]) ~= 9 then return false end 1252 | local wan1 = 0 1253 | local wan9 = 0 1254 | local tiao1 = 0 1255 | local tiao9 = 0 1256 | local bing1 = 0 1257 | local bing9 = 0 1258 | local dong = 0 1259 | local nan = 0 1260 | local xi = 0 1261 | local bei = 0 1262 | local zhong = 0 1263 | local fa = 0 1264 | local bai = 0 1265 | 1266 | -- 统计 1万、9万 个数 1267 | for i = 1,#sort_pai["My"][MJ_WAN] do 1268 | local pai = sort_pai["My"][MJ_WAN][i] 1269 | local num = CheckSinglePaiNum(pai) 1270 | if num == 1 then wan1 = wan1 + 1 end 1271 | if num == 9 then wan9 = wan9 + 1 end 1272 | end 1273 | -- 统计 1条、9条 个数 1274 | for i = 1,#sort_pai["My"][MJ_TIAO] do 1275 | local pai = sort_pai["My"][MJ_TIAO][i] 1276 | local num = CheckSinglePaiNum(pai) 1277 | if num == 1 then tiao1 = tiao1 + 1 end 1278 | if num == 9 then tiao9 = tiao9 + 1 end 1279 | end 1280 | 1281 | -- 统计 1饼、9饼个数 1282 | for i = 1,#sort_pai["My"][MJ_BING] do 1283 | local pai = sort_pai["My"][MJ_BING][i] 1284 | local num = CheckSinglePaiNum(pai) 1285 | if num == 1 then bing1 = bing1 + 1 end 1286 | if num == 9 then bing9 = bing9 + 1 end 1287 | end 1288 | 1289 | -- 统计 ( 东南西北 )数目 1290 | for i = 1,#sort_pai["My"][MJ_FENG] do 1291 | local pai = sort_pai["My"][MJ_FENG][i] 1292 | local num = CheckSinglePaiNum(pai) 1293 | if num == 1 then dong = dong + 1 end 1294 | if num == 3 then nan = nan + 1 end 1295 | if num == 5 then xi = xi + 1 end 1296 | if num == 7 then bei = bei + 1 end 1297 | end 1298 | --统计 ( 中发白 )数目 1299 | for i = 1,#sort_pai["My"][MJ_ZFB] do 1300 | local pai = sort_pai["My"][MJ_ZFB][i] 1301 | local num = CheckSinglePaiNum(pai) 1302 | if num == 1 then zhong = zhong + 1 end 1303 | if num == 3 then fa = fa + 1 end 1304 | if num == 5 then bai = bai + 1 end 1305 | end 1306 | 1307 | -- 所有牌起码一个并且总和为 14 1308 | local sum = wan1 + wan9 + tiao1 + tiao9 + bing1 + bing9 + dong + nan + xi + bei + zhong + fa + bai 1309 | if wan1 > 0 and wan9 > 0 and tiao1 > 0 and tiao9 > 0 and bing1 > 0 and bing9 > 0 and dong > 0 and nan > 0 and xi > 0 and bei > 0 and 1310 | zhong > 0 and fa > 0 and bai > 0 and sum == 14 then return true end 1311 | return false 1312 | 1313 | end 1314 | 1315 | --测试胡牌 1316 | --IN:用户牌 1317 | --OUT:true-胡,false-不能胡 1318 | function CheckHu(userPai) 1319 | 1320 | -- 先检测特殊牌型 (十三幺 和 九莲宝灯) 1321 | if CheckSSY(userPai) or CheckJLBD(userPai) then return true end 1322 | 1323 | -- 检测普通胡牌牌型 1324 | local sort_pai = SortByType(userPai) 1325 | 1326 | local count_hu = 0 1327 | local count_jiang = 0 1328 | local t = false -- 临时变量 1329 | local k = 0 -- 临时变量 1330 | -- 对各分组求"胡 1331 | for i = 1,#sort_pai["My"] do 1332 | t,k = ValidHu(sort_pai["My"][i],1,#sort_pai["My"][i]) 1333 | if t == true then count_hu = count_hu + 1 1334 | else break end 1335 | count_jiang = count_jiang + k 1336 | end 1337 | 1338 | if count_hu == 5 and count_jiang == 1 then 1339 | return true 1340 | else 1341 | return false end 1342 | end 1343 | 1344 | --打印牌组(debug用) 1345 | local function PrintPai(userPai) 1346 | local sort_pai = SortByType(userPai) 1347 | for i = 1,#sort_pai["My"] do 1348 | for j = 1,#sort_pai["My"][i] do 1349 | io.write(sort_pai["My"][i][j],",") 1350 | end 1351 | io.write(" ") 1352 | end 1353 | 1354 | end 1355 | 1356 | --计算广东麻将胡牌番数 1357 | --此方法要求在已经胡牌的前提下调用 1358 | function CheckPaiXing(userPai) 1359 | 1360 | if CheckSSY(userPai) == true then print("十三幺") return 16 end 1361 | if CheckJLBD(userPai) == true then print("九莲宝灯") return 15 end 1362 | if CheckDSX(userPai) == true then print("大四喜") return 14 end 1363 | if CheckDSY(userPai) == true then print("大三元") return 13 end 1364 | if CheckQYJ(userPai) == true then print("清幺九") return 12 end 1365 | if CheckZYS(userPai) == true then print("字一色") return 11 end 1366 | if CheckXSX(userPai) == true then print("小四喜") return 10 end 1367 | if CheckXSY(userPai) == true then print("小三元") return 9 end 1368 | if CheckHYJ(userPai) == true then print("混幺九") return 8 end 1369 | if CheckQP(userPai) == true then print("清碰") return 7 end 1370 | if CheckHP(userPai) == true then print("混碰") return 6 end 1371 | if CheckQYS(userPai) == true then print("清一色") return 5 end 1372 | if CheckHYS(userPai) == true then print("混一色") return 4 end 1373 | if CheckPPH(userPai) == true then print("碰碰胡") return 3 end 1374 | if CheckPH(userPai) == true then print("平胡") return 2 end 1375 | print("鸡胡") 1376 | return 1 1377 | end 1378 | 1379 | --检查所有吃、碰、杠、听、胡(该方法为傻瓜式,服务端似乎已弃用,但客户端可能仍然在用) 1380 | --IN:用户牌,待检测牌,待检测牌来源标志 1381 | --flag参数指定第二个参数:0-自摸牌,1-上家牌,2-其他用户牌 1382 | --自摸牌不包含在userPai 1383 | function CheckAll(userPai,aPai,flag) 1384 | 1385 | --临时属性表(用于返回可操作牌型) 1386 | local attribute = { 1387 | ["Peng"] = {},--填每组能碰的牌,用表表示,以1开始,如第三第四张能碰,则填“{3,4}” 1388 | ["Chi"] = {},--填每组能吃的牌,同上 1389 | ["Gang"] = {},--同上,填个三个数,如{7,8,9} 1390 | ["Ting"] = {},--填可以扔掉的牌,一位数,如{9} 1391 | ["Hu"] = 0 --0表示不能胡,1表示能胡 1392 | } 1393 | 1394 | --只有是上家牌才能吃 1395 | if flag == 1 1396 | then 1397 | --吃 1398 | attribute["Chi"] = CheckChiPai(userPai,aPai) 1399 | end 1400 | --不是自摸才能碰 1401 | if flag ~= 0 1402 | then 1403 | --碰 1404 | attribute["Peng"] = CheckPengPai(userPai,aPai) 1405 | end 1406 | 1407 | --胡(自摸) 1408 | local tempPai = CopyPai(userPai) 1409 | table.insert(tempPai,aPai) 1410 | if CheckHu(tempPai) == true then 1411 | attribute["Hu"] = 1 1412 | end 1413 | 1414 | --杠(判断胡牌后再判断自摸加杠) 1415 | attribute["Gang"] = CheckGangPai(userPai,aPai,flag) 1416 | return attribute 1417 | end 1418 | 1419 | -- 检测三元牌 1420 | -- 中发白任意组成刻子,多少组加多少番 1421 | -- In:用户牌 1422 | -- Out:三元牌加番数 1423 | function CheckSYP( userPai ) 1424 | local paiarray = {} 1425 | local count = 0 1426 | for i=1,#userPai do 1427 | table.insert(paiarray,userPai[i]) 1428 | end 1429 | table.sort(paiarray) 1430 | for i=1,#paiarray-2 do 1431 | if GetPaiTypeNum(paiarray[i]) == GetPaiTypeNum(paiarray[i+1]) and 1432 | GetPaiTypeNum(paiarray[i]) == GetPaiTypeNum(paiarray[i+2]) and 1433 | CheckSinglePaiType(paiarray[i]) == MJ_ZFB then 1434 | count = count + 1 1435 | end 1436 | end 1437 | return count 1438 | end 1439 | 1440 | 1441 | --------------------------四川麻将专有胡法----------------------------------- 1442 | 1443 | -------------带幺解法子操作开始------------- 1444 | --以下子操作仅限于检测四川麻将中的带幺九牌型 1445 | local function ValidABC_Split(pai,i,n,split_pai) 1446 | -- 顺子要避开 1 222 3 这种可能组成 123 22 的情况 1447 | -- 所以拆成两个列表 (1 2 3)|(22) 1448 | -- 然后判断 ValidHu(22) 1449 | 1450 | local t_pai = CopyPai(pai) 1451 | -- 只有两张牌,必定没顺 1452 | if n - i < 2 then 1453 | return false 1454 | end 1455 | 1456 | local found_B = false 1457 | local found_C = false 1458 | for j = i+1,#t_pai,1 do 1459 | if found_B == false and t_pai[j] == ( t_pai[i] + 1 ) then 1460 | found_B = true 1461 | -- 交换两张牌的位置 1462 | local t = t_pai[i + 1] 1463 | t_pai[i + 1] = t_pai[j] 1464 | t_pai[j] = t 1465 | end 1466 | end 1467 | 1468 | for k = i + 2,#pai,1 do 1469 | if found_C== false and t_pai[k] == ( t_pai[i] + 2 ) then 1470 | found_C = true 1471 | -- 交换两张牌的位置 1472 | local t = t_pai[i + 2] 1473 | t_pai[i + 2] = t_pai[k] 1474 | t_pai[k] = t 1475 | end 1476 | end 1477 | 1478 | -- for k = 1,#pai,1 do 1479 | -- print (pai[k]) 1480 | -- end 1481 | 1482 | -- 如果能找到顺,判断剩下的牌是否能胡 1483 | if found_B == true and found_C == true then 1484 | --split_pai = {t_pai[i],t_pai[i+1],t_pai[i+2]} --- 能组成顺子的列表 1485 | table.insert(split_pai,t_pai[i]) 1486 | table.insert(split_pai,t_pai[i+1]) 1487 | table.insert(split_pai,t_pai[i+2]) 1488 | -- 创建待判断列表 1489 | local new_list = {} 1490 | local k = 1 1491 | for j = i + 3,#pai,1 do 1492 | new_list[k] = t_pai[j] 1493 | k = k + 1 1494 | end 1495 | -- 对新建数组进行排序 1496 | table.sort(new_list) 1497 | -- for k = 1,#new_list,1 do 1498 | -- print (new_list[k]) 1499 | -- end 1500 | return true,new_list 1501 | else 1502 | return false,{nil} 1503 | end 1504 | end 1505 | 1506 | local function ValidHu_Split(pai,i,n,split_list,is_out) -- 最后一个参数表示是最外层 1507 | -- 空牌组直接胡 1508 | if n == 0 then 1509 | return true,0 1510 | end 1511 | 1512 | -- 存在两个将牌或少于两个牌,不可能胡 1513 | if n%3==1 then 1514 | return false,0 1515 | end 1516 | 1517 | -- 检测到末尾,胡 1518 | if i > n then 1519 | return true,0 1520 | end 1521 | 1522 | -- 测试 AAA 1523 | if ValidAAA(pai,i,n) then 1524 | local merge_list = {} 1525 | local t = false 1526 | local k = 0 1527 | t,k = ValidHu(pai,i+3,n,0) 1528 | if t == true then 1529 | local list = {} 1530 | --local list = {pai[i],pai[i],pai[i]} 1531 | table.insert(list,pai[i]) 1532 | table.insert(list,pai[i]) 1533 | table.insert(list,pai[i]) 1534 | 1535 | -- 如果是最外层,则将列表插入到新建的二级列表 1536 | if is_out == true then 1537 | table.insert(merge_list,list) 1538 | ValidHu_Split(pai,i+3,n,merge_list,false) 1539 | else 1540 | -- 如果不是最外层,则将列表插入到传入的二级列表 1541 | table.insert(split_list,list) 1542 | return ValidHu_Split(pai,i+3,n,split_list,false) 1543 | end 1544 | 1545 | -- 如果是最外层,则将列表的列表插入到三级列表 1546 | if is_out == true then 1547 | table.insert(split_list,merge_list) 1548 | end 1549 | end 1550 | end 1551 | 1552 | -- 测试AA 1553 | if ValidAA(pai,i,n) then 1554 | local merge_list = {} 1555 | local t = false 1556 | local k = 0 1557 | t,k = ValidHu(pai,i+2,n) 1558 | if t == true and k == 0 then 1559 | -- 限制 k == 0 可以解决 如 {11,11,11,12,12,12,13,13,13} 被拆分成 1560 | -- {11,12,13} {11,11} {12,12} {13,13} 的情况 1561 | local list = {} 1562 | table.insert(list,pai[i]) 1563 | table.insert(list,pai[i]) 1564 | -- 如果是最外层,则将列表插入到新建的二级列表 1565 | if is_out == true then 1566 | table.insert(merge_list,list) 1567 | ValidHu_Split(pai,i+2,n,merge_list,false) 1568 | else 1569 | -- 如果不是最外层,则将列表插入到传入的二级列表 1570 | table.insert(split_list,list) 1571 | return ValidHu_Split(pai,i+2,n,split_list,false) 1572 | end 1573 | 1574 | 1575 | -- 如果是最外层,则将列表的列表插入三级列表 1576 | if is_out == true then 1577 | table.insert(split_list,merge_list) 1578 | end 1579 | end 1580 | end 1581 | 1582 | -- 对万,条,饼测试ABC 1583 | if CheckSinglePaiType(pai[1]) ~= MJ_FENG and CheckSinglePaiType(pai[1]) ~= MJ_ZFB then 1584 | local merge_list = {} 1585 | local new_pai = {} 1586 | local t = false 1587 | local list = {} 1588 | t,new_pai = ValidABC_Split(pai,i,n,list) 1589 | if t == true then 1590 | local t2 = false 1591 | local k = 0 1592 | t2,k = ValidHu(new_pai,1,#new_pai) 1593 | if t2 == true then 1594 | -- 如果是最外层,则将列表插入到新建的二级列表 1595 | if is_out == true then 1596 | table.insert(merge_list,list) 1597 | ValidHu_Split(new_pai,1,#new_pai,merge_list,false) 1598 | else 1599 | -- 如果不是最外层,则将列表插入到传入的二级列表 1600 | table.insert(split_list,list) 1601 | return ValidHu_Split(new_pai,1,#new_pai,split_list,false) 1602 | end 1603 | 1604 | -- 如果是最外层,则将列表的列表插入三级列表 1605 | if is_out == true then 1606 | table.insert(split_list,merge_list) 1607 | end 1608 | end 1609 | end 1610 | end 1611 | return false,0 1612 | end 1613 | 1614 | local function SplitHuPai(userPai) 1615 | -- 这个函数最初是为了解决四川麻将中的【带幺牌】设计的 1616 | -- 接收 : 14张胡牌 1617 | -- 返回 : 胡牌的组合 1618 | ---- 如接收 {11,11,11,12,12,12,13,13,13,51,51} 1619 | ---- 返回 { 1620 | -- {{11,11,11},{12,12,12},{13,13,13},{51,51}}, 1621 | -- {{11,12,13},{11,12,13},{11,12,13},{51,51}} 1622 | -- } 1623 | -- 分成四组牌 1624 | local sort_pai = SortByType(userPai) 1625 | 1626 | local split_pai = { 1627 | [MJ_WAN] = {}, 1628 | [MJ_TIAO] = {}, 1629 | [MJ_BING] = {}, 1630 | [MJ_FENG] = {}, 1631 | [MJ_ZFB] = {} 1632 | } 1633 | 1634 | for i = 1,#sort_pai["My"] do 1635 | ValidHu_Split(sort_pai["My"][i],1,#sort_pai["My"][i],split_pai[i],true) -- 最后一个参数表示是最外层 1636 | end 1637 | -- return split_pai 1638 | local split_list = {} 1639 | -- 下面的解法详见《编程之美》 ---- 3.2 1640 | -- 各种子拆法的全组合 1641 | -- 看起来比较恶心,实际上用递归更直观 1642 | for i = 1,#split_pai do 1643 | if #split_pai[i] ~= 0 then 1644 | table.insert(split_list,split_pai[i]) 1645 | end 1646 | end 1647 | local n = #split_list -- 一共有n个非空组 1648 | local capacity = {} -- 每个非空组的容量 1649 | local pos = {} -- 每个非空组当前的位置 1650 | for i = 1,n do 1651 | table.insert(capacity,#split_list[i]) 1652 | table.insert(pos,1) 1653 | end 1654 | 1655 | local merge_list = {} 1656 | while true do 1657 | local t_list = {} 1658 | for i = 1,#split_list do 1659 | --table.insert(t_list,split_list[i][pos[i]]) 1660 | for j = 1,#split_list[i][pos[i]] do 1661 | table.insert(t_list,split_list[i][pos[i]][j]) 1662 | end 1663 | end 1664 | table.insert(merge_list,t_list) 1665 | local k = n 1666 | while k >= 1 do 1667 | if pos[k] < capacity[k] then 1668 | pos[k] =pos[k] + 1 1669 | break 1670 | else 1671 | pos[k] = 1 1672 | k = k - 1 1673 | end 1674 | end 1675 | if k < 1 then 1676 | break 1677 | end 1678 | end 1679 | return merge_list 1680 | end 1681 | -------------带幺解法子操作结束------------- 1682 | 1683 | -- 检测缺门 1684 | local function CheckQueMen( userPai ,dingzhang ) 1685 | for i,v in ipairs(userPai) do 1686 | if CheckSinglePaiType(v) == dingzhang then 1687 | return false 1688 | end 1689 | end 1690 | 1691 | return true 1692 | end 1693 | 1694 | -- 平胡 1695 | -- 同广东麻将平胡,需缺门 1696 | local function CheckPh_SC( userPai ) 1697 | if CheckPH(userPai) == true then 1698 | return true 1699 | else 1700 | return false 1701 | end 1702 | end 1703 | 1704 | -- 大对子 1705 | -- 同广东麻将碰碰胡,需缺门 1706 | local function CheckDdz_SC( userPai ) 1707 | if CheckPPH(userPai) == true then 1708 | return true 1709 | else 1710 | return false 1711 | end 1712 | end 1713 | 1714 | -- 清一色 1715 | -- 同广东麻将,自身满足缺门条件 1716 | local function CheckQys_SC( userPai ) 1717 | if CheckQYS(userPai) == true then 1718 | return true 1719 | else 1720 | return false 1721 | end 1722 | end 1723 | 1724 | -- 带幺 1725 | -- 每组搭子及将牌都带1或9,需缺门 1726 | local function CheckDy_SC( userPai ) 1727 | 1728 | ----------------------优化--------------------------- 1729 | --如果牌中包含4~6的牌,不可能是带幺九,过滤之 1730 | for i = 1,#userPai do 1731 | local num = CheckSinglePaiNum(userPai[i]) 1732 | if num >= 4 and num <= 6 then return false end 1733 | end 1734 | ----------------------优化---------------------------- 1735 | 1736 | local found = false -- 能不能找到1或9 1737 | local split_pai = SplitHuPai(userPai) 1738 | 1739 | for i = 1,#split_pai do 1740 | -- 扫描每一个胡牌组合 1741 | for j = 1,#split_pai[i] do 1742 | found = false 1743 | -- 扫描每一种组合里面的牌对 1744 | for k = 1,#split_pai[i][j] do 1745 | local num = CheckSinglePaiNum(split_pai[i][j][k]) 1746 | if num == 1 or num == 9 then found = true end 1747 | end 1748 | -- 如果出现不含1,9的牌对,跳到上一层 1749 | if found == false then break end 1750 | end 1751 | -- 如果扫完完整的一组时found == true 直接返回 1752 | if found == true then return true end 1753 | end 1754 | 1755 | return found 1756 | end 1757 | 1758 | 1759 | -- 暗七对 1760 | -- 七个对子,不遵循一般胡牌规律,需缺门 1761 | local function CheckAqd_SC( userPai ) 1762 | local sort_pai = {} 1763 | for i=1,#userPai do 1764 | if CheckSinglePaiGroup(userPai[i]) == Pai_My then 1765 | table.insert(sort_pai,userPai[i]) 1766 | end 1767 | end 1768 | table.sort( sort_pai ) 1769 | 1770 | if #sort_pai ~= 14 then 1771 | return false 1772 | end 1773 | 1774 | local isAQD = true 1775 | for i=1,14 do 1776 | if i % 2 == 1 then 1777 | if sort_pai[i] ~= sort_pai[i+1] then 1778 | isAQD = false 1779 | break 1780 | end 1781 | end 1782 | end 1783 | 1784 | if isAQD == true then 1785 | return true 1786 | else 1787 | return false 1788 | end 1789 | end 1790 | 1791 | -- 清大对 1792 | -- 即清一色+大对子 1793 | local function CheckQdd_SC( userPai ) 1794 | if CheckQys_SC(userPai) and CheckDdz_SC(userPai) then 1795 | return true 1796 | end 1797 | return false 1798 | end 1799 | 1800 | -- 龙七对 1801 | -- 暗七对的改进,七对中有两对或更多相同的牌,不能有杠 1802 | -- Out:bool 是否龙七对,number 相同的对数 1803 | local function CheckLqd_SC( userPai ) 1804 | if CheckAqd_SC(userPai) == false then 1805 | return false,0 1806 | end 1807 | 1808 | local num_gen = 0 1809 | local sort_pai = {} 1810 | --取手牌 1811 | for i=1,#userPai do 1812 | if CheckSinglePaiGroup(userPai[i]) == Pai_My then 1813 | table.insert(sort_pai,userPai[i]) 1814 | end 1815 | end 1816 | table.sort( sort_pai ) 1817 | 1818 | --双数位与单数位对比(相同则说明两对是相同的) 1819 | for i=1,13 do 1820 | if i%2 == 0 then 1821 | if sort_pai[i] == sort_pai[i+1] then 1822 | num_gen = num_gen + 1 1823 | end 1824 | end 1825 | end 1826 | 1827 | if num_gen ~= 0 then 1828 | return true,num_gen 1829 | else 1830 | return false,0 1831 | end 1832 | end 1833 | 1834 | -- 清七对 1835 | -- 清一色+暗七对 1836 | local function CheckQqd_SC( userPai ) 1837 | if CheckQys_SC(userPai) and CheckAqd_SC(userPai) then 1838 | return true 1839 | end 1840 | return false 1841 | end 1842 | 1843 | -- 清龙七对 1844 | -- 清一色+龙七对 1845 | -- 返回值同龙七对 1846 | local function CheckQlqd_SC( userPai ) 1847 | local qys = CheckQys_SC(userPai); 1848 | local lqd,num_gen = CheckLqd_SC(userPai) 1849 | 1850 | if qys and lqd then 1851 | return true,num_gen 1852 | else 1853 | return false,0 1854 | end 1855 | end 1856 | 1857 | -- 清带幺 1858 | -- 清一色+带幺 1859 | local function CheckQdy_SC( userPai ) 1860 | if CheckQys_SC(userPai) and CheckDy_SC(userPai) then 1861 | return true 1862 | else 1863 | return false 1864 | end 1865 | end 1866 | 1867 | -- 四川胡牌检测 1868 | -- 要检测定张,定张:1.万,2.条,3.饼 1869 | -- IN:用户牌,定张 1870 | -- OUT:是否胡牌 1871 | function CheckHu_SC( userPai,dingzhang ) 1872 | 1873 | if CheckQueMen(userPai,dingzhang) == false then 1874 | return false 1875 | end 1876 | 1877 | --检测特殊牌型(七对系列) 1878 | if CheckAqd_SC(userPai) == true then return true end 1879 | 1880 | -- 检测普通胡牌牌型 1881 | local sort_pai = SortByType(userPai) 1882 | 1883 | local count_hu = 0 1884 | local count_jiang = 0 1885 | local t = false -- 临时变量 1886 | local k = 0 -- 临时变量 1887 | -- 对各分组求"胡 1888 | for i = 1,#sort_pai["My"] do 1889 | t,k = ValidHu(sort_pai["My"][i],1,#sort_pai["My"][i]) 1890 | if t == true then count_hu = count_hu + 1 1891 | else break end 1892 | count_jiang = count_jiang + k 1893 | end 1894 | 1895 | if count_hu == 5 and count_jiang == 1 then 1896 | return true 1897 | else 1898 | return false end 1899 | 1900 | end 1901 | 1902 | -- 四川胡牌型的检测 1903 | -- 前提是能胡,此处不作判定 1904 | -- IN:用户牌 1905 | -- OUT:胡牌型编号 1906 | function CheckPaiXing_SC( userPai ) 1907 | 1908 | if CheckQlqd_SC(userPai) == true then print("清龙七对") return 10 end 1909 | if CheckQdy_SC(userPai) == true then print("清带幺") return 9 end 1910 | if CheckQqd_SC(userPai) == true then print("清七对") return 8 end 1911 | if CheckLqd_SC(userPai) == true then print("龙七对") return 7 end 1912 | if CheckQdd_SC(userPai) == true then print("清大对") return 6 end 1913 | if CheckAqd_SC(userPai) == true then print("暗七对") return 5 end 1914 | if CheckDy_SC(userPai) == true then print("带幺") return 4 end 1915 | if CheckQys_SC(userPai) == true then print("清一色") return 3 end 1916 | if CheckDdz_SC(userPai) == true then print("大对子") return 2 end 1917 | print("平胡") 1918 | return 1 1919 | end 1920 | 1921 | -- 四川麻将听牌(叫) 1922 | -- IN:用户牌,定张 1923 | -- OUT:听牌组(同广东麻将听牌结构) 1924 | function checkTingPai_SC( userPai , dingzhang ) 1925 | 1926 | --暂时认为存在定张也能听(这里规则有待细化,需留意) 1927 | local ting_list = {} 1928 | local sort_pai = SortByType(userPai) 1929 | 1930 | local count_hu = 0 --表示万条饼中每一类可胡的总数 1931 | local signal = 0 --表示未胡牌的牌种类,用作听牌检测 1932 | local t = false 1933 | local kj = 0 1934 | for i=1,3 do 1935 | --存在两张或以上的定张直接不能听 1936 | if i == dingzhang and #sort_pai["My"][i] > 1 then 1937 | return ting_list 1938 | end 1939 | -- if i ~= dingzhang then 1940 | --只计算分组是否胡牌(包括定张) 1941 | t,kj = ValidHu(sort_pai["My"][i],1,#sort_pai["My"][i]) 1942 | if t == true then 1943 | count_hu = count_hu + 1 1944 | else 1945 | --记录下缺胡的牌种(不包括定张) 1946 | if i~= dingzhang then 1947 | signal = i 1948 | end 1949 | end 1950 | -- end 1951 | end 1952 | 1953 | --剩一门未胡,对其穷举 1954 | if count_hu >= 1 then 1955 | --先看听定张 1956 | if #sort_pai["My"][dingzhang] == 1 then 1957 | for i=signal*10+1,signal*10+9 do 1958 | --造一个新的table 1959 | local list = {} 1960 | list = CopyPai(sort_pai["My"][signal]) 1961 | table.insert(list,i) 1962 | table.sort(list) 1963 | 1964 | t,kj = ValidHu(list,1,#list) 1965 | if t == true then 1966 | local oneTing = {sort_pai["My"][dingzhang][1],i} 1967 | table.insert(ting_list,oneTing) 1968 | end 1969 | end 1970 | end 1971 | --从没有定张的手牌中穷举判断 1972 | local shouPai = {} 1973 | for i=1,#userPai do 1974 | table.insert(shouPai,userPai[i]) 1975 | end 1976 | for i=1,3 do 1977 | for j=1,9 do 1978 | local insertPai = i*10+j 1979 | for k=1,#shouPai do 1980 | local tempPai = {} 1981 | for m=1,#shouPai do 1982 | if m~=k then 1983 | table.insert(tempPai,shouPai[m]) 1984 | end 1985 | end 1986 | table.insert(tempPai,insertPai) 1987 | table.sort( tempPai ) 1988 | if CheckHu_SC(tempPai , dingzhang) then 1989 | local oneTing = {shouPai[k],insertPai} 1990 | table.insert(ting_list,oneTing) 1991 | end 1992 | end 1993 | end 1994 | end 1995 | end 1996 | 1997 | --合并重复的听牌组 1998 | --先排序 1999 | local function sortTingList( a,b ) 2000 | if a[1] == b[1] then 2001 | return a[2] < b[2] 2002 | else 2003 | return a[1] < b[1] 2004 | end 2005 | end 2006 | table.sort( ting_list, sortTingList ) 2007 | --逆序删除 2008 | for i = #ting_list,2,-1 do 2009 | if ting_list[i][1] == ting_list[i-1][1] and 2010 | ting_list[i][2] == ting_list[i-1][2] then 2011 | table.remove(ting_list,i) 2012 | end 2013 | end 2014 | 2015 | return ting_list 2016 | end 2017 | 2018 | 2019 | -------------------------AI部分-------------------- 2020 | 2021 | -- 建立牌的权值 2022 | -- IN:用户牌 2023 | -- OUT:一个和用户牌一一对应的权值数组 2024 | local function createWeight(userPai) 2025 | 2026 | -- 根据手牌的权值决定出牌 2027 | --这里不用剔除非手牌,因为非手牌的权值会很低 2028 | local AIPai = CopyPai(userPai) 2029 | table.sort( AIPai, function(a,b) return a100 then 2127 | table.remove(AIPai,i) 2128 | end 2129 | end 2130 | 2131 | local rand = math.random(10) 2132 | print("rand:"..rand) 2133 | --随便丢,0级AI概率为0,1级为20%,2级为50%,3级为80% 2134 | if rand < level*3 then 2135 | return AIPai[math.random(#AIPai)] 2136 | end 2137 | 2138 | local ting_list = CheckTingPai(AIPai) 2139 | if #ting_list > 0 then 2140 | return ting_list[1][1] 2141 | end 2142 | 2143 | --返回出牌的序号 2144 | local weight = createWeight(AIPai) 2145 | local k = 1 2146 | local typeNumGroup = {[MJ_WAN] = {}, 2147 | [MJ_TIAO] = {}, 2148 | [MJ_BING] = {}, 2149 | [MJ_FENG] = {}, 2150 | [MJ_ZFB] = {} 2151 | } 2152 | for i=1,#weight do 2153 | if weight[i] >0 then 2154 | table.insert(typeNumGroup[CheckSinglePaiType(weight[i])],i) 2155 | end 2156 | if weight[k] < weight[i] then 2157 | k=i 2158 | end 2159 | end 2160 | --若存在正数(weight[k]>0),则选正数里最少的牌型出牌 2161 | if weight[k]>0 and CheckSinglePaiType(weight[k]) < 4 then 2162 | local min = CheckSinglePaiType(weight[k]) 2163 | for j=1,3 do 2164 | if #typeNumGroup[j]>0 and (#typeNumGroup[j])<(#typeNumGroup[min]) then 2165 | min = j 2166 | end 2167 | end 2168 | --最少牌的牌组中选择最后一张丢出 2169 | k = typeNumGroup[min][#typeNumGroup[min]] 2170 | end 2171 | return AIPai[k] 2172 | end 2173 | 2174 | -- 检测AI是否吃碰杠 2175 | -- IN:用户牌,待检测的牌(注意:这里不作testPai来源判定,在外部逻辑已经有判断) 2176 | -- OUT:AItype(指示进行何种操作,0:忽略操作,1:吃,2:碰,3:杠),AIlist(可操作对应牌的值) 2177 | function checkAIAction(userPai,testPai) 2178 | 2179 | local AIPai = CopyPai(userPai) 2180 | table.sort(AIPai) 2181 | local AItype = 0 2182 | local AIlist = {} 2183 | 2184 | local paitype = CheckSinglePaiType(testPai) 2185 | local t_list = SortByType(AIPai) 2186 | local t 2187 | local k 2188 | t,k = ValidHu(t_list["My"][paitype],1,#t_list["My"][paitype]) 2189 | if t == true then 2190 | return AItype,AIlist 2191 | end 2192 | 2193 | local weight = createWeight(AIPai) 2194 | local chi = CheckChiPai(AIPai,testPai) 2195 | local peng = CheckPengPai(AIPai,testPai) 2196 | local gang = CheckGangPai(AIPai,testPai,1) 2197 | 2198 | --为了综合评判整个手牌的状况,防止无脑的吃碰杠,设定吃碰杠的最小下界,必须大于此下界才会吃碰杠 2199 | local gangline = -12 --杠下界 2200 | local pengline = -7 --碰下界 2201 | local chiline = -4 --吃下界 2202 | 2203 | --先杠 2204 | if #gang>0 then 2205 | local maxgroup = {gang[1][1],gang[1][2],gang[1][3]} 2206 | if weight[maxgroup[1]] > gangline and weight[maxgroup[2]] > gangline and weight[maxgroup[3]] > gangline then 2207 | table.insert(AIlist,AIPai[maxgroup[1]]) 2208 | table.insert(AIlist,AIPai[maxgroup[2]]) 2209 | table.insert(AIlist,AIPai[maxgroup[3]]) 2210 | AItype = 3 2211 | return AItype,AIlist 2212 | end 2213 | end 2214 | --再碰 2215 | if #peng>0 then 2216 | local maxgroup = {peng[1][1],peng[1][2]} 2217 | if weight[maxgroup[1]] > pengline and weight[maxgroup[2]] > pengline then 2218 | table.insert(AIlist,AIPai[maxgroup[1]]) 2219 | table.insert(AIlist,AIPai[maxgroup[2]]) 2220 | AItype = 2 2221 | return AItype,AIlist 2222 | end 2223 | end 2224 | --最后吃 2225 | if #chi>0 then 2226 | local maxgroup = {chi[1][1],chi[1][2]} 2227 | for i=1,#chi do 2228 | if weight[chi[i][1]]+weight[chi[i][2]] > weight[maxgroup[1]]+weight[maxgroup[2]] then 2229 | maxgroup[1] = chi[i][1] 2230 | maxgroup[2] = chi[i][2] 2231 | end 2232 | end 2233 | if weight[maxgroup[1]] > pengline and weight[maxgroup[2]] > pengline then 2234 | table.insert(AIlist,AIPai[maxgroup[1]]) 2235 | table.insert(AIlist,AIPai[maxgroup[2]]) 2236 | AItype = 1 2237 | return AItype,AIlist 2238 | end 2239 | end 2240 | return AItype,AIlist 2241 | end 2242 | 2243 | 2244 | ---------------------------------- 测试 -------------------------------------------------------------------- 2245 | 2246 | -- local list = { 2247 | -- -- 删除将牌测试 2248 | -- {11,11,22,22,33,33,33,34,35,51,51,51,55,55}, 2249 | -- {11,11,12,12,13,13,33,34,35,51,51,51,55,55}, 2250 | -- --(十三幺) 所有可能 2251 | -- {11,19,21,29,31,39,39,41,43,45,47,51,53,55}, 2252 | -- {11,19,21,29,31,39,41,41,43,45,47,51,53,55}, 2253 | -- {11,19,21,29,31,39,41,43,43,45,47,51,53,55}, 2254 | -- {11,19,21,29,31,39,41,43,45,45,47,51,53,55}, 2255 | -- {11,19,21,29,31,39,41,43,45,47,47,51,53,55}, 2256 | -- {11,19,21,29,31,39,41,43,45,47,51,51,53,55}, 2257 | -- {11,19,21,29,31,39,41,43,45,47,51,53,53,55}, 2258 | -- {11,19,21,29,31,39,41,43,45,47,51,53,55,55}, 2259 | -- -- 十三幺 失败 2260 | -- {11,11,19,21,29,31,39,41,43,45,47,51,53,55}, 2261 | -- {11,19,19,21,29,31,39,41,43,45,47,51,53,55}, 2262 | -- {11,19,21,21,29,31,39,41,43,45,47,51,53,55}, 2263 | -- {11,19,21,29,29,31,39,41,43,45,47,51,53,55}, 2264 | -- {11,19,21,29,31,31,39,41,43,45,47,51,53,55}, 2265 | -- {11,19,21,29,31,39,39,41,43,45,47,51,53,55}, 2266 | -- -- (九莲宝灯) 所有可能 2267 | -- --{11,11,11,12,13,14,15,16,17,18,19,19,19} 2268 | -- {11,11,11,11,12,13,14,15,16,17,18,19,19,19}, 2269 | -- {11,11,11,12,12,13,14,15,16,17,18,19,19,19}, 2270 | -- {11,11,11,12,13,13,14,15,16,17,18,19,19,19}, 2271 | -- {11,11,11,12,13,14,14,15,16,17,18,19,19,19}, 2272 | -- {11,11,11,12,13,14,15,15,16,17,18,19,19,19}, 2273 | -- {11,11,11,12,13,14,15,16,16,17,18,19,19,19}, 2274 | -- {11,11,11,12,13,14,15,16,17,17,18,19,19,19}, 2275 | -- {11,11,11,12,13,14,15,16,17,18,18,19,19,19}, 2276 | -- {11,11,11,12,13,14,15,16,17,18,19,19,19,19}, 2277 | -- -- 大四喜 2278 | -- {41,41,41,43,43,43,45,45,45,47,47,47,51,51}, 2279 | -- {41,41,41,43,43,43,45,45,45,47,47,47,11,11}, 2280 | -- -- 大三元 2281 | -- {51,51,51,53,53,53,55,55,55,11,11,11,12,12}, 2282 | -- -- 清幺九 2283 | -- {11,11,11,19,19,19,21,21,21,29,29,29,31,31}, 2284 | -- -- 字一色 2285 | -- {41,41,41,43,43,43,45,45,45,51,51,51,53,53}, 2286 | -- {41,41,41,43,43,43,45,45,45,51,51,51,55,55}, 2287 | -- -- 小四喜 2288 | -- {41,41,41,43,43,43,45,45,45,47,47,11,11,11}, 2289 | -- {41,41,41,43,43,45,45,45,47,47,47,11,12,13}, 2290 | -- {41,41,43,43,43,45,45,45,47,47,47,11,12,13}, 2291 | -- -- 小三元 2292 | -- {51,51,51,53,53,53,55,55,11,11,11,12,12,12}, 2293 | -- {51,51,53,53,53,55,55,55,41,41,41,43,43,43}, 2294 | -- {51,51,51,53,53,55,55,55,22,23,24,25,26,27}, 2295 | -- -- 混幺九 2296 | -- {11,11,11,19,19,19,21,21,21,29,29,29,51,51}, 2297 | -- {11,11,11,19,19,19,21,21,21,29,29,29,53,53}, 2298 | -- --清碰 2299 | -- {11,11,11,12,12,12,13,13,13,14,14,14,15,15}, 2300 | -- {12,12,12,15,15,15,17,17,17,18,18,18,19,19}, 2301 | -- -- 清一色 2302 | -- {11,11,11,12,13,14,15,15,15,16,17,18,19,19}, 2303 | -- {11,11,11,11,12,13,14,15,16,17,17,17,18,18}, 2304 | -- -- 混碰 2305 | -- {11,11,11,12,12,12,13,13,13,14,14,14,51,51}, 2306 | -- {11,11,11,12,12,12,13,13,13,14,14,14,53,53}, 2307 | -- -- 混一色 2308 | -- {11,11,11,12,12,12,13,13,13,51,51,53,53,53}, 2309 | -- -- 碰碰胡 2310 | -- {11,11,11,21,21,21,34,34,34,41,41,41,53,53}, 2311 | -- -- 平胡 2312 | -- {11,12,13,21,22,23,31,32,33,17,18,19,51,51}, 2313 | -- -- 鸡胡 2314 | -- {11,12,13,21,22,23,33,33,33,41,41,41,51,51}, 2315 | -- {11,12,12,12,12,13,13,14,31,31,31,32,32,32}, 2316 | -- -- 四川麻将测试 2317 | -- {31,31,33,33,37,37,38,38,39,53,53,55,55,55}, 2318 | -- {11,11,11,12,12,12,15,15,15,14,14,25,25,25}, 2319 | -- {11,11,12,12,12,22,22,22,23,23,23,24,24,24}, 2320 | -- {11,12,13,12,12,12,13,13,13,14,14,14,16,16}, 2321 | -- {11,11,12,12,13,13,14,14,16,16,18,18,19,19}, 2322 | -- {11,11,11,11,12,13,17,18,19,17,18,19,19,19}, 2323 | -- {11,11,12,12,13,13,13,13} 2324 | -- } 2325 | 2326 | --广东麻将吃牌测试 2327 | local ss = {11,214,214,214,13,15,23,23,25,26,26,26,31} 2328 | local sss = CheckGangPai(ss,14,0) 2329 | for i=1,#sss do 2330 | for j=1,#sss[i] do 2331 | print(sss[i][j]) 2332 | end 2333 | print(" ") 2334 | end 2335 | 2336 | -- 广东麻将胡牌测试 2337 | -- for i = 1,#list do 2338 | -- if CheckHu() == true then 2339 | -- CheckPaiXing(list[i]) 2340 | -- else 2341 | -- print("no") 2342 | -- end 2343 | -- end 2344 | 2345 | -- --四川麻将测试 2346 | -- for i = 1,#list do 2347 | -- if CheckHu_SC(list[i],MJ_BING) == true then 2348 | -- CheckPaiXing_SC(list[i]) 2349 | -- else 2350 | -- print("no") 2351 | -- end 2352 | -- end 2353 | 2354 | -- 测试三元牌 2355 | -- local syplist = { 2356 | -- {11,11,11,12,12,12,13,13,13,51,51,51,26,26}, 2357 | -- {11,11,11,12,12,12,51,51,53,53,53,26,26,26}, 2358 | -- {11,11,11,51,51,51,53,53,53,55,55,55,26,26}, 2359 | -- {12,13,14,26,26,32,32,32,251,251,251,253,253,253} 2360 | -- } 2361 | -- for i=1,#syplist do 2362 | -- print(CheckSYP(syplist[i])) 2363 | -- end 2364 | 2365 | -- Debug用 2366 | -- temp = {11,11,13,15,16,16,22,23,24,24,25,26,34} 2367 | -- local atype,alist = checkAIAction(temp,12) 2368 | -- print(atype) 2369 | -- for i,v in ipairs(alist) do 2370 | -- print(i,v) 2371 | -- end 2372 | 2373 | --10-28测试AI出牌 2374 | -- local aitest = {111,112,113,212,212,212,15,15,17,21,35} 2375 | 2376 | -- print(checkAIChuPai(aitest)); 2377 | 2378 | -- local sc_list = { 2379 | -- {11,12,13,13,13,14,15,17,19,32,33,34,36,36}, 2380 | -- {11,12,13,13,14,15,17,19,27,32,33,34,36,36}, 2381 | -- {11,11,11,12,13,14,15,16,17,18,19,19,19,21}, 2382 | -- {11,12,13,13,14,15,17,19,32,33,34,36,36,39}, 2383 | -- {11,12,12,13,13,13,33,33} 2384 | -- } 2385 | 2386 | -- for i=1,#sc_list do 2387 | -- local l = checkTingPai_SC(sc_list[i],MJ_TIAO) 2388 | -- --local l = CheckTingPai(sc_list[i]) 2389 | -- for j=1,#l do 2390 | -- print(i) 2391 | -- io.write("丢:") 2392 | -- print(l[j][1]) 2393 | -- io.write("听:") 2394 | -- print(l[j][2]) 2395 | -- end 2396 | -- end 2397 | 2398 | --1-23测试AI出牌 2399 | -- local testlist = {11,11,13,13,13,16,19} 2400 | -- aatype,resultlist = checkAIAction(testlist,13) 2401 | -- print(aatype) 2402 | -- for i=1,#resultlist do 2403 | -- print(resultlist[i]) 2404 | -- end 2405 | -- print(checkAIChuPai(testlist,3)) 2406 | -- print(checkAIChuPai(testlist,3)) 2407 | -- print(checkAIChuPai(testlist,3)) 2408 | -- print(checkAIChuPai(testlist,3)) 2409 | -- print(checkAIChuPai(testlist,3)) 2410 | -- print(checkAIChuPai(testlist,3)) 2411 | -- print(checkAIChuPai(testlist,3)) 2412 | -- print(checkAIChuPai(testlist,3)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MahjongLogic 2 | 广东四川麻将的吃碰杠听胡逻辑以及广东麻将简易的AI实现 3 | 4 | 5 | 6 | 由于项目已搁置,重新放回github,供学习交流。 7 | --------------------------------------------------------------------------------