logic.js 158 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625
  1. 'use strict';
  2. //// 为什么选择 Lodash ?
  3. //// Lodash 通过降低 array、number、objects、string 等等的使用难度从而让 JavaScript 变得更简单。
  4. //// Lodash 的模块化方法 非常适用于:
  5. //// 遍历 array、object 和 string
  6. //// 对值进行操作和检测
  7. //// 创建符合功能的函数
  8. var _ = require('lodash');
  9. var bigInt = require('big-integer');
  10. var quick = require('quick-pomelo');
  11. var logger = quick.logger.getLogger('fhmj', __filename);
  12. // 所有牌
  13. const CARDS = [
  14. 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, //一到十
  15. 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
  16. 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
  17. 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
  18. 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, //壹到拾
  19. 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
  20. 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
  21. 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
  22. ];
  23. // 块类型
  24. const STYLE = {
  25. NULL: 0, // 无效
  26. CHI: 1, // 吃牌
  27. SUN: 2, // 顺序
  28. PENG: 3, // 碰子
  29. KE: 4, // 刻子(起手3张一样的,不能把牌拖上去)
  30. GANG: 5, // 跑(杠子)能跑必须跑
  31. ANGANG: 6, // 提(暗杠)能提必须提
  32. ZMGANG: 7, // 跑(自摸明杠)能跑必须跑
  33. JIAO:8, // 绞牌
  34. BIPAI:9, // 比牌,吃牌能比时若要吃必须比
  35. WEI: 10, // 偎牌(手上2张摸牌过程中又摸到一张)能偎必须偎
  36. };
  37. // 有胡息类型
  38. const HUXITYPE = {
  39. NULL: 0, // 无效
  40. XIAOSHUN: 1, // 小顺
  41. DASHUN: 2, // 大顺
  42. XIAOKE: 3, // 小刻
  43. DAKE: 4, // 大刻
  44. XIAOPENG: 5, // 小碰
  45. DAPENG: 6, // 大碰
  46. XIAOWEI: 7, // 小偎
  47. DAWEI: 8, // 大偎
  48. XIAOPAO: 9, // 小跑
  49. DAPAO: 10, // 大跑
  50. XIAOTI: 11, // 小提
  51. DATI: 12, // 大提
  52. };
  53. // 哥有胡息类型对应的息数
  54. const HUXIVALUE = [
  55. 0, // 无效
  56. 3, // 小顺
  57. 6, // 大顺
  58. 3, // 小刻
  59. 6, // 大刻
  60. 1, // 小碰
  61. 3, // 大碰
  62. 3, // 小偎
  63. 6, // 大偎
  64. 6, // 小跑
  65. 9, // 大跑
  66. 9, // 小提
  67. 12 // 大提
  68. ];
  69. // 椅子数
  70. const CHAIR_COUNT = 3;
  71. const CARDS_COUNT = 20;
  72. // 天胡可以过胡
  73. // 庄家必须出牌,就算是起手连提两个(8块)也必须打牌
  74. // 3提五坎只发生在庄家打出第一张牌之前
  75. // A玩家摸的牌A要偎提B玩家要胡,应该偎提(如果是起手庄家的那张则应该胡的优先级高于庄家的偎提)
  76. // 提牌情况1:起手就有4张
  77. // 提牌情况2:当自己从墩上摸得牌,是“三”形成了4个“三”
  78. // 提牌情况3:当自己从墩上摸得牌,是自己已经“偎牌”的牌
  79. // 有跑必须跑,但是没有胡的优先级高(比如自己可胡可跑显示胡过点胡就胡点过就自动跑)
  80. // 跑牌情况1:当别人从墩上摸得牌,自己手上有一坎
  81. // 跑牌情况2:当自己从墩上摸得牌,是自己已经碰的牌
  82. // 跑牌情况3:当别人从墩上摸得牌,是自己已经碰的牌
  83. // 跑牌情况4:当别人从墩上摸得牌,是自己已经偎的牌
  84. // 跑牌情况5:当别人滑动手牌打出的牌,是自己已经偎的牌
  85. // 跑牌情况6:当别人滑动手牌打出的牌,自己手上有一坎
  86. // 偎牌情况1:当自己从墩上摸得牌,是自己手上已经有一对的牌(只发生在摸牌阶段)
  87. // 碰牌情况1:当别人从墩上摸得牌,是自己手上已经有一对的牌
  88. // 碰牌情况2:当别人滑动手牌打出的牌,自己手上有一对
  89. // 吃牌情况1:当自己或别人从墩上摸得牌,可以和自己手牌组成顺子或绞牌且可以成功比牌
  90. // 吃牌情况2:当别人滑动手牌打出的牌,可以和自己手牌组成顺子或绞牌且可以成功比牌
  91. // 构造方法
  92. var Logic = function (type,gameKindTL,playerAllCount,other) {
  93. // 类型
  94. this.type = type;
  95. this.other = other;//////TL++2人3人的游戏规则
  96. this.gameKindTL = gameKindTL || 1;//游戏类型 = 1代表全名堂 = 2代表大小子 = 3表示土跑胡
  97. let otherstr = ""+other;
  98. this.fengDingValue = parseInt(otherstr[6])*100;////封顶值:0:不封顶,1:封顶100,2:封顶200,3:封顶300
  99. this.tunshuValue = parseInt(otherstr[5]);////充囤值:0:不充囤,1:充1囤,2:充2囤,3:充3囤,4:充4囤,5:充5囤
  100. // 点灯规则:流局后黄+1,不切换庄家,闲家胡牌后黄-1,若庄家胡牌则黄不减1,有黄则满足“黄番”牌型
  101. // 不勾选点灯规则的话则黄的个数不会累加(要么是0要么是1)
  102. this.dengshuValue = -1;//-1:未勾选点灯规则,>-1:已勾选点灯规则下剩余的灯数,
  103. if(gameKindTL == 1 || gameKindTL == 2) this.dengshuValue = 0;
  104. let kexuanstr = " 可选规则: ";//
  105. if(this.gameKindTL == 1){
  106. if(parseInt(otherstr[1]) == 1) kexuanstr += " 点灯 ";
  107. if(parseInt(otherstr[3]) == 1) kexuanstr += " 有丁 ";
  108. if(parseInt(otherstr[4]) == 1) kexuanstr += " 有捉小三 ";
  109. }
  110. this.chouPaiShu = 0;
  111. if(playerAllCount == 2) {
  112. if(parseInt(otherstr[2]) == 1) this.chouPaiShu = 20;////抽牌张数:0:不抽牌,1:抽20张牌
  113. }
  114. console.warn("logic 游戏人数 抽牌张数 ",playerAllCount,this.chouPaiShu)
  115. console.warn("logic 游戏类型 = 1代表全名堂 = 2代表大小子 = 3表示土跑胡 "+this.gameKindTL+kexuanstr)
  116. console.warn("logic 封顶值 "+this.fengDingValue)
  117. console.warn("logic 充囤值 "+this.tunshuValue)
  118. this.xiaoType = 1;//小牌花色值(一到十)
  119. this.daType = 2;//大牌花色值(壹到拾)
  120. this.khMinHX = 15;//可胡需满足的最低胡息
  121. // 全牌
  122. var cards = _.clone(CARDS);
  123. this.castCount = 0;
  124. this.disCount = 0;
  125. this.lastCount = cards.length;//////设置剩余牌数为了测试海底捞月
  126. this.cardsPool = cards;
  127. this.cardsyl = _.clone(cards);
  128. //////以下变量全是TL++
  129. this.playerAllCount = playerAllCount || 3;////游戏人数 = 2表示2人局 = 3表示3人局
  130. this.setCardFilerIndex = 0
  131. this.chuPaiCountTL = 0//////TL++,记录出牌的个数,为了判断天胡和地胡
  132. this.moPaiCountTL = 0//////TL++,记录出牌的个数,为了判断天胡和地胡
  133. this.lastMoPaiChairID = 0//////TL++,最后一个摸牌玩家的id
  134. this.everyCPGS = [0,0,0]//////TL++,记录每个玩家的出牌的个数,为了判断听胡
  135. this.chBanker = -1;
  136. this.addThisIsHu = 0//////TL++,胡牌判断时加上这张能不能胡
  137. this.isGuoHuList = [[false,0],[false,0],[false,0],[false,0],]/////TL++,各个玩家在本轮是否点击了过胡和点击过牌时的台数
  138. this.setCardFileName = '';/////设置手牌的文件名
  139. this.cardGroupIndex = 0;////要设置的牌型种类
  140. // let bigNumber = BigInt(12345678901234567890);
  141. this.isZHYZ = false;/////是不是最后1张
  142. ////////下面是判胡算法新加的变量
  143. this.paiArr = [];//2位数牌数组,索引是牌id,值是牌数量
  144. this.lastNum = 0;//拆分的剩余牌的数量,只有全部拆完才算胡
  145. this.fromPaiId = 0;//进牌id
  146. this.SHUNZI = 1;//普通顺子
  147. this.HUNDUN = 2;//绞牌
  148. this.KAN = 3; //坎(刻子)
  149. this.TI = 4; //提
  150. this.DUIZI = 5; //对子
  151. this.S2710 = 6; //二七十和贰柒拾顺子
  152. this.bitType = 3;//占的二进制位数
  153. this.bitPai = 5;//占的二进制位数
  154. this.byteType = 7;//0x7;//占的二进制字节16进制代表7
  155. this.bytePai = 31;//0x1F;//占的二进制字节16进制代表31
  156. this.duiList = [];//保存所有可以胡的牌型,数组元素是long类型,有几个元素代表有几种不同的可组合成胡的方式
  157. this.laiZiList = [];//所有可以胡牌型用到的癞子保存在这里,元素代表本次可胡方式中百搭当的牌
  158. this.MAX_PAI_CNT = 22;
  159. this.WANG = 21;//表示百搭牌
  160. this.DA_SHI = 20;//表示拾(11~20分别代表壹~拾)
  161. this.XIAO_YI = 1;//表示一(1~10分别代表一~十)
  162. ////////判胡算法新加的变量结束
  163. this.huTypeTL = [/////分别表示牌型和台数
  164. ['天胡',6],//庄家刚刚起手21张牌的时候胡牌(庄家天胡也肯定自摸),或者庄家的三提五坎
  165. ['地胡',6],//庄家起手亮张的子(牌)闲家胡了。(庄家没有地胡闲家没有天胡)
  166. ['海胡',6],//胡的是墩上(剩余牌牌堆)最后一张牌。
  167. ['听胡',6],//没有进子(手牌没有改变)的情况下胡牌,满足天胡或者地胡的话一定是满足“听胡”的。
  168. ['红胡',3],//(3+N)胡牌时,手上的牌和操作(吃碰偎提跑)过的牌有超过10张红牌(二七十,贰柒拾)即满足“红胡”牌型,红牌张数在10张基础之上每多1张的话番数在基础3番多加1番。如11张红牌(3+1=4)番,12张红牌(3+2=5)番
  169. ['黑胡',8],//胡牌时没有一张红牌
  170. ['点胡',6],//胡牌时只有一张红牌
  171. ['大胡',8],//(8+N)胡牌时大子(大写的牌,如叁)张数>=18张,18张基础之上每多1张在起始8番上加1番
  172. ['小胡',10],//(10+N)胡牌时小子(小写的牌,如八)张数>=16张,16张基础之上每多1张在起始10番上加 1番,如17张小子=11番,20张小子=10+(20-16)=14番。
  173. ['耍猴',6],//胡牌时只剩一张牌(子)单吊。
  174. ['黄番',2],//结算本局算分完得到最后总得分之后,如果还有黄(灯数,参照点灯规则,则这局的最终总得分=本局总得分*2
  175. ['对子胡',8],//7个成句中每个成句都是同一个牌面数值(大小写不可交替),即7个成句 都是由偎碰刻或(提跑偎碰刻+1对,因为有提手上有个1对)构成的。(手上只能有一个对子)
  176. ['大团圆',10],//(10*N)跑或提了2个相同的牌,如跑或提“九”且跑或提“玖”,每个大团圆都要+10番
  177. ['真行息',10],//7个成句都是由有息的成句组成的(成句有息无息见胡息计算规则)(有 提不可能是真行,因为有个对子,这个对子没有息)
  178. ['假行息',6],//7个成句中有6个成句都是由有息的成句组成的,且没息的那列为对子(是对子意味着肯定有跑或提)
  179. ['四七红',2],//胡牌的时候手上有4张或7张红色牌(二七 十,贰柒拾)。
  180. ['真背靠背',10],//手中听牌是一对大小字,胡了其中一张。如手上有一对“一”和一对“壹”, 听牌“一”和“壹”,胡了“一”或“壹”,形成了“一”和“壹”一个刻子一个对子 则形成背靠背。只剩4张的时候是真背靠背
  181. ['假背靠背',6],//假背靠背:6番。手中听牌是一对大小字,胡了其中一张。如手上有一对“一”和一对“壹”, 听牌“一”和“壹”,胡了“一”或“壹”,形成了“一”和“壹”一个刻子一个对子 则形成背靠背。手牌大于4张的时候是假背靠背
  182. ['三提五坎',6],//有3个提或着5个坎,直接胡,仅限在发牌期间,在全名堂和大小子中不算牌型,土跑胡中算1番
  183. ['金屋藏娇',6],//提了小三
  184. ['丁',2],//可选,胡的那张牌是用来做对子的(肯定有提或者跑,满足耍猴肯定是满足“丁”的) 如“三三四五”别人明示的“三”,自己摸到的“三”不算,因为必须先偎掉
  185. ['捉小三',6]//可选,胡的那张牌是“三”,并且是自己摸到的。别人抓到的不算。
  186. ]
  187. this.xypddpxlb = [];//需要判断的牌型列表
  188. if(this.gameKindTL == 1){
  189. for (var i = 0; i < 19; i++) {//前20个牌型
  190. this.xypddpxlb.push(i);
  191. }
  192. if(parseInt(otherstr[3]) == 1) this.xypddpxlb.push(20);//丁
  193. if(parseInt(otherstr[4]) == 1) {
  194. this.xypddpxlb.push(19);//金屋藏娇
  195. this.xypddpxlb.push(21);//捉小三
  196. }
  197. }
  198. else if(this.gameKindTL == 2){
  199. for (var i = 0; i < 12; i++) {//前12个牌型
  200. this.xypddpxlb.push(i);
  201. }
  202. this.xypddpxlb.push(18);//三提五坎
  203. }
  204. else if(this.gameKindTL == 3){
  205. this.xypddpxlb.push(4);
  206. this.xypddpxlb.push(5);
  207. this.xypddpxlb.push(6);
  208. this.xypddpxlb.push(11);
  209. this.xypddpxlb.push(18);//三提五坎
  210. this.huTypeTL[18][1] = 1;
  211. }
  212. // console.warn("ppppppppppp 长度 "+cards.length+" cards "+cards);
  213. };
  214. Logic.setHCfileNameList =[/////全部设置牌型的文件名
  215. "setHandCard_tianhu",
  216. ]
  217. // 导出常量
  218. Logic.CARDS = CARDS;
  219. Logic.STYLE = STYLE;
  220. Logic.HUXITYPE = HUXITYPE;
  221. Logic.HUXIVALUE = HUXIVALUE;
  222. Logic.CHAIR_COUNT = CHAIR_COUNT;
  223. Logic.CARDS_COUNT = CARDS_COUNT;
  224. // 导出类
  225. module.exports = Logic;
  226. // 原型对象
  227. var proto = Logic.prototype;
  228. ///////TL++在table类中出牌函数中调用,为了判断天胡和地胡
  229. proto.chuPaiTL = function () {
  230. // console.warn("111c天胡判断不正确",this.chuPaiCountTL);
  231. this.chuPaiCountTL += 1//////这个其实并不是真实的出牌个数,因为这个方法的调用在判断吃碰杠胡之前调用的
  232. }
  233. ///////TL++在table类中出牌函数中调用,为了判断天胡和地胡
  234. proto.moPaiTL = function () {
  235. // console.warn("111c天胡判断不正确",this.chuPaiCountTL);
  236. this.moPaiCountTL += 1//////TL++,记录出牌的个数,为了判断天胡和地胡 += 1
  237. }
  238. ///////TL++重置默认数据,在每局游戏开始的洗牌函数中调用
  239. proto.resetDataOnGameStart = function () {
  240. this.everyCPGS = [0,0,0]//////TL++,记录每个玩家的出牌的个数,为了判断听胡
  241. this.chBanker = -1;
  242. this.chuPaiCountTL = 0//////TL++出牌个数
  243. this.moPaiCountTL = 0//////TL++,记录摸牌的个数,为了判断天胡和地胡
  244. this.lastMoPaiChairID = 0//////TL++,最后一个摸牌玩家的id
  245. this.addThisIsHu = 0//////TL++,胡牌判断时加上这张能不能胡
  246. this.isGuoHuList = [[false,0],[false,0],[false,0],[false,0],]/////TL++,各个玩家在本轮是否点击了过胡和点击过牌时的台数
  247. this.isZHYZ = false;/////是不是最后1张
  248. }
  249. // 重新洗牌
  250. proto.shuffle = function () {
  251. this.castCount = 0;
  252. this.disCount = 0;
  253. this.cardsPool = _.clone(this.cardsyl);
  254. this.lastCount = this.cardsPool.length;
  255. this.cardsPool = _.shuffle(this.cardsPool);////随机打乱一个数组
  256. this.cardsPool = _.shuffle(this.cardsPool);////洗第二次牌
  257. // this.cardsPool = _.shuffle(this.cardsPool);////洗第三次牌
  258. };
  259. // 填充数组
  260. proto.fillDeep = function (array, o, isMore) {
  261. for (let i = 0; i < array.length; ++i) {
  262. if (!isMore) {
  263. array[i] = _.clone(o);
  264. } else {
  265. array[i] = _.cloneDeep(o);/////深度拷贝,就是重新分配内存空间,浅拷贝则是两个变量共用一个内存对象
  266. }
  267. }
  268. return array;
  269. };
  270. //////TL++ 深复制一个object类型的数据
  271. proto.deepCloneTL = function (obj){
  272. let objClone = Array.isArray(obj)?[]:{};
  273. if(obj && typeof obj==="object"){
  274. for(let key in obj){
  275. if(obj.hasOwnProperty(key)){
  276. //判断ojb子元素是否为对象,如果是,递归复制
  277. if(obj[key]&& typeof obj[key] ==="object"){
  278. objClone[key] = this.deepCloneTL(obj[key]);
  279. }else{
  280. //如果不是,简单复制
  281. objClone[key] = obj[key];
  282. }
  283. }
  284. }
  285. }
  286. return objClone;
  287. };
  288. // 获取类型
  289. proto.getType = function (card) {
  290. return Math.floor(card / 100);
  291. };
  292. // 获取牌值
  293. proto.getValue = function (card) {
  294. return card % 100;
  295. };
  296. // 手牌排序
  297. proto.sort = function (handCards) {
  298. return _.sortBy(handCards);///降序排序
  299. };
  300. // 删除手牌
  301. proto.remove = function (handCards, cards) {
  302. if (typeof (cards) == 'number') {
  303. if (cards == handCards[handCards.length - 1]) {
  304. return (handCards.pop(), true);
  305. } else {
  306. let pos = handCards.indexOf(cards);
  307. if (pos == -1) return false;
  308. return (handCards.splice(pos, 1), true);
  309. }
  310. }
  311. // console.warn("删除手牌");
  312. var length = cards.length;
  313. for (let i = 0; i < length; ++i) {
  314. let pos = handCards.indexOf(cards[i]);
  315. if (pos == -1) return false;
  316. }
  317. for (let i = 0; i < length; ++i) {
  318. let pos = handCards.indexOf(cards[i]);
  319. if (pos == -1) return false;
  320. handCards.splice(pos, 1);
  321. }
  322. return true;
  323. };
  324. // 删除手牌,适合提跑中使用
  325. proto.removes = function (_handCards, cards) {
  326. // console.warn("删除手牌",_handCards, cards);
  327. let handCards = this.arr2ToArr1(_handCards,false);
  328. if (typeof (cards) == 'number') {
  329. if (cards == handCards[handCards.length - 1]) {
  330. return (handCards.pop(), true);
  331. } else {
  332. let pos = handCards.indexOf(cards);
  333. if (pos == -1) return false;
  334. return (handCards.splice(pos, 1), true);
  335. }
  336. }
  337. var length = cards.length;
  338. for (let i = 0; i < length; ++i) {
  339. let pos = handCards.indexOf(cards[i]);
  340. if (pos == -1) return false;
  341. }
  342. for (let i = 0; i < length; ++i) {
  343. let pos = handCards.indexOf(cards[i]);
  344. if (pos == -1) return false;
  345. handCards.splice(pos, 1);
  346. }
  347. return this.changeRes2(handCards);
  348. };
  349. // 删除手牌,适合吃碰偎中使用,为了实现
  350. proto.removes2 = function (_handCards, cards) {
  351. // console.warn("删除吃碰偎手牌111 ",JSON.stringify(_handCards), cards);
  352. // let handCards = _.cloneDeep(_handCards);
  353. //不能按照上面那个写法,否则会在吃方法里调用的时候使用如下参数会出现两个207都被删除(handCards[x][y] = 0;这句里把2个207改为0)
  354. //如果使用上面那种写法就需要在调用的时候使用this.deepCloneTL()方法对参数进行深复制
  355. //[[202,207,210],[202,207,210],[102,107,110],[102,107,110],[206,205,204],[104],[105,105],[106,103]] [206,207,205,105,105]
  356. let handCards = this.deepCloneTL(_handCards);
  357. let clgs = 0;//处理个数
  358. for (var i = 0; i < cards.length; i++) {
  359. let c = cards[i];
  360. for (var x = handCards.length-1; x >= 0; x--) {
  361. let sfzd = false;
  362. for (var y = 0; y < handCards[x].length; y++) {
  363. if(handCards[x][y] == c){
  364. handCards[x][y] = 0;
  365. sfzd = true;
  366. clgs++;
  367. break;
  368. }
  369. }
  370. if(sfzd) break;
  371. }
  372. }
  373. if(clgs != cards.length) return false;
  374. for (var i = 0; i < handCards.length; i++) {
  375. handCards[i] = handCards[i].filter(item => item !== 0);
  376. }
  377. handCards = handCards.filter(subArray => subArray.length > 0);
  378. let res = {
  379. changeRes: 0,
  380. };
  381. res.handCards = _.cloneDeep(handCards);
  382. // console.warn("删除吃碰偎手牌222 ",JSON.stringify(res.handCards));
  383. let styles = this.getCardStyle(res.handCards);
  384. let hxTypes = this.getCardHXType(res.handCards,styles);
  385. let hxValues = this.getCardHXValue(hxTypes);
  386. res.handHXInfos = {
  387. styles: styles,//玩家i的手牌块列表,为了计算这列手牌的胡息类型
  388. hxTypes: hxTypes,//玩家i的手牌胡息类型列表,为了计算这列手牌的胡息息数
  389. hxValues: hxValues,//玩家i的手牌胡息类型的息数列表,为了前端显示
  390. }
  391. // console.warn("删除手牌之后的结果333 ",JSON.stringify(handCards),res);
  392. return res;
  393. };
  394. // 删除指定位置的一张手牌,适合出牌时使用
  395. proto.removeACardByPos = function (handCards, handHXInfos, card, oldIndex) {
  396. // console.warn("删除指定位置的一张手牌,适合出牌时使用",handCards, card, oldIndex);
  397. let res = {
  398. changeRes: 0,
  399. };
  400. if(!handCards[oldIndex[0]] || !handCards[oldIndex[0]][oldIndex[1]] || handCards[oldIndex[0]][oldIndex[1]] != card){
  401. res.changeRes = 2;//传上来的牌值和所在位置对不上
  402. return res;
  403. }
  404. let oldStyle = handHXInfos.styles[oldIndex[0]];//手牌中老坐标所在列的块类型
  405. if(oldStyle == STYLE.ANGANG || oldStyle == STYLE.KE){
  406. res.changeRes = 4;//手牌的提和刻子所在的列的牌不允许挪动
  407. return res;
  408. }
  409. // let pos = _.findIndex(handCards[oldIndex[0]],card);//这么写不行
  410. let pos = handCards[oldIndex[0]].indexOf(card);
  411. if(pos == -1){
  412. res.changeRes = 5;//系统错误了,能走到这里肯定是有问题的
  413. return res;
  414. }
  415. handCards[oldIndex[0]].splice(pos, 1);
  416. if(handCards[oldIndex[0]].length > 0){
  417. let styles = this.getCardStyle([handCards[oldIndex[0]]]);
  418. let hxTypes = this.getCardHXType([handCards[oldIndex[0]]],[styles[0]]);
  419. let hxValues = this.getCardHXValue([hxTypes[0]]);
  420. handHXInfos.styles[oldIndex[0]] = styles[0];
  421. handHXInfos.hxTypes[oldIndex[0]] = hxTypes[0];
  422. handHXInfos.hxValues[oldIndex[0]] = hxValues[0];
  423. }
  424. else {
  425. let filteredArray = handCards.filter(subArray => subArray.length > 0);
  426. handCards = filteredArray;
  427. handHXInfos.styles.splice(oldIndex[0],1);
  428. handHXInfos.hxTypes.splice(oldIndex[0],1);
  429. handHXInfos.hxValues.splice(oldIndex[0],1);
  430. }
  431. res.handCards = handCards;
  432. res.handHXInfos = handHXInfos;
  433. // console.warn("调删除指定位置的一张手牌,适合出牌时使用 之后的结果 ",handCards,handHXInfos,res);
  434. return res;
  435. };
  436. // 调整手牌
  437. proto.adjust = function (handCards) {
  438. var length = handCards.length;
  439. if (length < 2) return false;
  440. if (handCards[length - 1] < handCards[length - 2]) {
  441. let moCard = handCards.pop();
  442. let pos = _.findIndex(handCards, (i) => (i >= moCard));
  443. if (pos == -1) handCards.push(moCard);
  444. else handCards.splice(pos, 0, moCard);
  445. }
  446. return true;
  447. };
  448. // 调整手牌(手指滑动挪牌调整)
  449. proto.changeRes = function (_handCards,handHXInfos,card,oldIndex,newIndex) {
  450. let res = {
  451. changeRes: 0,
  452. };
  453. let handCards = this.deepCloneTL(_handCards);
  454. // console.warn("调整之前的手牌 ",handCards," card ",card," ",handCards[oldIndex[0]]);
  455. if(newIndex[0] >= handCards.length) {
  456. newIndex[0] = handCards.length;
  457. handCards[handCards.length] = [];
  458. }
  459. if(handCards[newIndex[0]].length > 3){
  460. res.changeRes = 1;//每列最多只能放4张牌
  461. return res;
  462. }
  463. if(!handCards[oldIndex[0]] || !handCards[oldIndex[0]][oldIndex[1]] || handCards[oldIndex[0]][oldIndex[1]] != card){
  464. // console.warn("调整之前的手牌 ",handCards," card ",card," ",handCards[oldIndex[0]]);
  465. res.changeRes = 2;//传上来的牌值和所在位置对不上
  466. return res;
  467. }
  468. let newStyle = handHXInfos.styles[newIndex[0]];//手牌中新坐标所在列的块类型
  469. if(newStyle == STYLE.ANGANG || newStyle == STYLE.KE){
  470. res.changeRes = 3;//手牌的提和刻子所在的列不能放入其他牌
  471. return res;
  472. }
  473. let oldStyle = handHXInfos.styles[oldIndex[0]];//手牌中老坐标所在列的块类型
  474. if(oldStyle == STYLE.ANGANG || oldStyle == STYLE.KE){
  475. res.changeRes = 4;//手牌的提和刻子所在的列的牌不允许挪动
  476. return res;
  477. }
  478. // let pos = _.findIndex(handCards[oldIndex[0]],card);//这么写不行
  479. let pos = handCards[oldIndex[0]].indexOf(card);
  480. if(pos == -1){
  481. res.changeRes = 5;//系统错误了,能走到这里肯定是有问题的
  482. return res;
  483. }
  484. let ylspcount = this.getSPZS(handCards);//调整前原来手牌数量
  485. // console.warn("添加前 ",handCards[oldIndex[0]],handCards[newIndex[0]]," handCards ",handCards);
  486. if(newIndex[0] == oldIndex[0]){
  487. let xwzyldp = handCards[newIndex[0]][newIndex[1]];//新位置原来的牌
  488. handCards[newIndex[0]][newIndex[1]] = handCards[oldIndex[0]][oldIndex[1]];
  489. handCards[oldIndex[0]][oldIndex[1]] = xwzyldp;
  490. if(!xwzyldp){//新位置没有牌
  491. handCards[oldIndex[0]] = _.compact(handCards[oldIndex[0]]);
  492. }
  493. }
  494. else{
  495. if(handCards[newIndex[0]].length >= newIndex[1]){
  496. handCards[newIndex[0]].splice(newIndex[1], 0, card); //在下标为newIndex[1]的位置添加元素card
  497. }
  498. else{
  499. handCards[newIndex[0]].push(card)
  500. }
  501. // handCards[oldIndex[0]].splice(pos, 1);因为是操作参数这么写不行
  502. // 上面这写法操作方法参数时会多删除(如果操作的是局局部变量就没有问题),所以改成了下面这样
  503. // 例如最左侧的两列都是1,2,3,把2挪到最右侧新建一列时会把前2列里的2都删除
  504. let oldIndexCards = _.cloneDeep(handCards[oldIndex[0]]);
  505. oldIndexCards.splice(pos, 1);
  506. handCards[oldIndex[0]] = _.cloneDeep(oldIndexCards);
  507. }
  508. // console.warn("添加后 ",handCards[oldIndex[0]],handCards[newIndex[0]]," handCards ",handCards);
  509. let xzspcount = this.getSPZS(handCards);//调整后现在手牌数量
  510. if(ylspcount != xzspcount){
  511. //调整过程中出问题了
  512. res.changeRes = 6;//调整前后牌的张数不一样了
  513. return res;
  514. }
  515. if(handCards[newIndex[0]].length > 0){
  516. let styles = this.getCardStyle([handCards[newIndex[0]]]);
  517. let hxTypes = this.getCardHXType([handCards[newIndex[0]]],[styles[0]]);
  518. let hxValues = this.getCardHXValue([hxTypes[0]]);
  519. handHXInfos.styles[newIndex[0]] = styles[0];
  520. handHXInfos.hxTypes[newIndex[0]] = hxTypes[0];
  521. handHXInfos.hxValues[newIndex[0]] = hxValues[0];
  522. }
  523. if(handCards[oldIndex[0]].length > 0){
  524. let styles = this.getCardStyle([handCards[oldIndex[0]]]);
  525. let hxTypes = this.getCardHXType([handCards[oldIndex[0]]],[styles[0]]);
  526. let hxValues = this.getCardHXValue([hxTypes[0]]);
  527. handHXInfos.styles[oldIndex[0]] = styles[0];
  528. handHXInfos.hxTypes[oldIndex[0]] = hxTypes[0];
  529. handHXInfos.hxValues[oldIndex[0]] = hxValues[0];
  530. }
  531. else {
  532. let filteredArray = handCards.filter(subArray => subArray.length > 0);
  533. // console.warn("去掉长度为0的数组元素",filteredArray,handCards);
  534. handCards = filteredArray;
  535. // console.warn("去掉长度为0的数组元素2222 ",handCards);
  536. handHXInfos.styles.splice(oldIndex[0],1);
  537. handHXInfos.hxTypes.splice(oldIndex[0],1);
  538. handHXInfos.hxValues.splice(oldIndex[0],1);
  539. }
  540. res.handCards = handCards;
  541. res.handHXInfos = handHXInfos;
  542. // console.warn("调整牌之后的结果 ",handCards,handHXInfos,res);
  543. return res;
  544. };
  545. // 调整手牌(理牌按钮理牌)
  546. proto.changeRes2 = function (_handCards) {
  547. let res = {
  548. changeRes: 0,
  549. };
  550. let handCards = this.arr2ToArr1(_handCards,true);
  551. res.handCards = this.groupCards(handCards);
  552. let styles = this.getCardStyle(res.handCards);
  553. let hxTypes = this.getCardHXType(res.handCards,styles);
  554. let hxValues = this.getCardHXValue(hxTypes);
  555. res.handHXInfos = {
  556. styles: styles,//玩家i的手牌块列表,为了计算这列手牌的胡息类型
  557. hxTypes: hxTypes,//玩家i的手牌胡息类型列表,为了计算这列手牌的胡息息数
  558. hxValues: hxValues,//玩家i的手牌胡息类型的息数列表,为了前端显示
  559. }
  560. // console.warn("调整牌之后的结果222 ",JSON.stringify(handCards),res);
  561. return res;
  562. };
  563. // 剩余牌数
  564. proto.leaveCount = function () {
  565. return this.lastCount;
  566. };
  567. //////TL++,人工设置测试牌型
  568. proto.setTestSendCardsTL = function () {
  569. this.setCardFileName = '';
  570. let fileName = "setHandCard_tianhu"
  571. if(!fileName) return;/////文件名字不存在
  572. // console.warn("设置手牌的文件名",fileName);
  573. this.setCardFileName = fileName
  574. var SetHandCardTL = require('./setcard/' + fileName);
  575. if(!SetHandCardTL) return;
  576. //////TL++,设置手牌的文件
  577. this.setHandCardTL = new SetHandCardTL();
  578. ////console.error("洗牌之后的牌",this.cardsPool);
  579. let ccc = this.setHandCardTL.getSCFP(this.cardGroupIndex)
  580. if(ccc){//////已经设置的手牌发完了就随机发牌
  581. this.cardsPool = ccc/////设置手牌的地方 设置牌
  582. this.cardGroupIndex += 1; ////此句是否存在代表是否只发某一种牌型
  583. // SetHandCardTL.cardGroupIndex += 1; ////此句是否存在代表是否只发某一种牌型
  584. // if(SetHandCardTL.cardGroupIndex == 1) SetHandCardTL.cardGroupIndex = 0
  585. }
  586. else{
  587. this.setCardFilerIndex += 1;
  588. this.cardGroupIndex = 0;
  589. // SetHandCardTL.cardGroupIndex = 0;
  590. if(Logic.setCardFilerIndex < Logic.setHCfileNameList.length) this.setTestSendCardsTL()
  591. }
  592. }
  593. // 初始手牌
  594. proto.handCards = function (chBanker,_PJHF,_over,recordid,_ctime) {
  595. this.resetDataOnGameStart()//////TL++
  596. this.shuffle();
  597. // this.cardsPool = [102,107,208,202,108,209,101,101,109,110,201,104,202,210,206,202,105,207,207,204,205,210,102,109,206,207,204,209,205,109,108,106,208,204,102,204,201,206,107,107,105,203,207,110,103,106,103,203,203,208,203,201,110,101,201,209,205,208,103,210,108,105,101,205,104,106,210,107,104,106,109,104,102,202,206,103,209,110,105,108]
  598. // this.cardsPool = [101,207,203,106,208,102,201,206,102,104,107,206,204,108,104,209,109,101,201,207,108,205,204,102,106,208,205,110,208,105,105,210,103,110,206,106,209,108,105,207,107,209,201,208,104,203,109,105,107,101,210,102,109,205,107,109,203,104,202,106,103,110,204,202,201,103,210,209,202,103,101,203,108,206,110,205,210,204,202,207]
  599. // this.cardsPool = [203,105,101,107,108,201,105,109,101,108,107,105,202,210,108,110,107,101,203,102,208,204,210,206,201,205,202,105,208,208,103,109,201,209,202,209,106,106,103,109,207,110,203,204,101,210,102,205,104,206,106,208,204,209,108,102,109,201,209,107,110,207,103,110,205,207,207,206,206,204,104,104,104,106,203,103,102,202,210,205]
  600. // this.cardsPool = [105,104,107,102,107,204,209,207,203,205,206,210,101,201,106,108,104,101,105,206,103,209,210,203,110,210,108,103,209,207,206,105,210,110,203,201,101,110,109,202,104,204,205,102,208,202,109,108,208,204,206,110,102,108,207,205,201,105,103,106,109,104,202,202,201,209,208,102,106,207,101,204,106,203,208,109,103,107,205,107]
  601. // this.cardsPool = [202,103,206,206,108,208,102,201,209,107,202,105,208,101,205,102,106,110,207,205,207,103,103,104,101,106,104,109,205,202,102,204,210,203,210,109,209,208,106,209,209,210,207,107,206,101,201,101,203,208,102,108,110,105,108,103,107,104,204,105,109,110,203,201,203,201,106,210,109,207,105,108,104,204,204,206,110,202,107,205]
  602. // this.cardsPool = [202,208,106,103,205,107,203,210,201,206,210,209,209,204,108,201,104,110,102,108,107,108,209,203,104,205,202,203,207,109,203,206,101,107,110,109,103,105,210,101,102,205,204,209,108,101,105,105,109,104,207,106,206,105,202,102,204,102,201,204,208,210,104,208,202,109,201,205,206,101,107,106,106,103,110,103,208,207,110,207]
  603. // this.cardsPool = [102,109,207,106,206,102,210,208,207,107,201,101,206,208,201,205,105,210,206,110,204,201,205,209,110,103,107,104,109,202,104,208,101,107,201,204,108,206,210,107,209,109,202,205,108,203,109,202,103,202,105,208,207,108,108,104,110,207,101,210,106,102,110,203,209,101,204,205,103,104,203,105,103,102,209,105,106,203,106,204]
  604. // this.setTestSendCardsTL()///////设置手牌的地方,设置发牌 不需要设置的时候注释掉这个方法就可以了
  605. // this.lastCount = this.cardsPool.length;//////设置剩余牌数设置剩余牌数,测试完这句要删掉
  606. this.lastCount -= this.chouPaiShu;//处理抽牌张数可选项
  607. let dqjs = _over + 1;
  608. _PJHF.writexpJson(this.deepCloneTL(this.cardsPool),dqjs,recordid,_ctime);
  609. this.chBanker = chBanker;
  610. var result = [];
  611. var castCount = 0;
  612. for (let i = 0; i < this.playerAllCount; ++i) {
  613. let cardCount = CARDS_COUNT;
  614. let cards = this.cardsPool.slice(castCount, castCount + cardCount);
  615. castCount += cardCount;
  616. result.push({ cards: this.sort(cards) });
  617. }
  618. this.castCount += castCount
  619. this.lastCount -= castCount;
  620. // console.warn("发的手牌111",JSON.stringify(result));
  621. for (let i = 0; i < this.playerAllCount; ++i) {
  622. result[i].cards = this.groupCards(result[i].cards);
  623. // console.warn("发牌函数里面获取手牌的块getCardStyle ");
  624. let styles = this.getCardStyle(result[i].cards);
  625. let hxTypes = this.getCardHXType(result[i].cards,styles);
  626. let hxValues = this.getCardHXValue(hxTypes);
  627. result[i].hxinfo = {
  628. styles: styles,//玩家i的手牌块列表,为了计算这列手牌的胡息类型
  629. hxTypes: hxTypes,//玩家i的手牌胡息类型列表,为了计算这列手牌的胡息息数
  630. hxValues: hxValues,//玩家i的手牌胡息类型的息数列表,为了前端显示
  631. }
  632. }
  633. // console.warn("发的手牌222",JSON.stringify(result));
  634. return result;
  635. };
  636. //整理手牌(新的,逻辑是逻辑是先去掉坎和刻,再去掉所有对子,然后将剩下单牌组成有息的顺子(一二三,二七十)并去掉,之后去掉能和对子组成绞牌的单牌,剩余单牌去掉普通顺子,最后剩余的单牌称为杂牌,杂牌每3张一列进行放置)
  637. proto.groupCards = function (_Cards) {
  638. // console.error("整理手牌 ",JSON.stringify(_Cards));
  639. let Cards = _.cloneDeep(_Cards);
  640. let CardsCopy = _.cloneDeep(_Cards);
  641. // console.error("判断桌子是否存在 ");
  642. // let Cards = [101,102,102,102,103,106,106,106,107,108,201,202,202,203,203,205,205,206,206,207,208]
  643. // let CardsCopy = [101,102,102,102,103,106,106,106,107,108,201,202,202,203,203,205,205,206,206,207,208]
  644. // // // let group1 = _.groupBy(Cards, function (n) { return n % 100 })
  645. // // //// group1 = {"1":[101,201],"2":[102,102,102,202,202],"3":[103,203,203],"5":[205,205],"6":[106,106,106,206,206],"7":[107,207],"8":[108,208]}
  646. // // // let map1 = _.map(Object.keys(group1), function (n) { return Number(n) });
  647. // // //// map1 = [1,2,3,5,6,7,8]
  648. // let group2 = _.groupBy(Cards, function (n) { return n})
  649. // console.error("group2 = ",group2);
  650. // // // // group2 = {"101":[101],"102":[102,102,102],"103":[103],"106":[106,106,106],"107":[107],"108":[108],
  651. // // // // "201":[201],"202":[202,202],"203":[203,203],"205":[205,205],"206":[206,206],"207":[207],"208":[208]}
  652. // // let map2 = _.map(Object.keys(group2), function (n) { return Number(n) });
  653. // // // map2 [101,102,103,106,107,108,201,202,203,205,206,207,208]
  654. // // console.error("group2 = ",JSON.stringify(group2)," map2 ",JSON.stringify(map2));
  655. // // let order2 = _.sortBy((group2, function (n) { return n.length})
  656. // // console.error("group2 = ",group2," order2 ",order2);
  657. // var values2 = _.values(group2);
  658. // // values2 = [[101],[102,102,102],[103],[106,106,106],[107],[108],[201],[202,202],[203,203],[205,205],[206,206],[207],[208]]
  659. // console.error(" values2 ",values2,JSON.stringify(values2));
  660. // var sortVal = _.sortBy(values2, function (n) { return n.length }).reverse();
  661. // //sortVal = [[106,106,106],[102,102,102],[206,206],[205,205],[203,203],[202,202],[208],[207],[201],[108],[107],[103],[101]]
  662. // console.error(" sortVal ",sortVal,JSON.stringify(sortVal));
  663. let dspzs = [[201,202,203],[202,207,210]];//大顺的牌值列表
  664. let xspzs = [[101,102,103],[102,107,110]];//小顺的牌值列表
  665. let res = [];
  666. let gc = _.groupBy(Cards, function (n) { return n})
  667. let vc = _.values(gc);
  668. var sortVal = _.sortBy(vc, function (n) { return n.length }).reverse();
  669. // console.error(" sortVal ",sortVal);
  670. // console.error(" Cards111 ",JSON.stringify(Cards));
  671. let tikeList = [];//手牌中的提刻列表
  672. let dashunList = [];//手牌中的大顺列表
  673. let xiaoshunList = [];//手牌中的小顺列表
  674. let chengshuangList = [];//手牌中的成双列表
  675. let jiaoList = [];//手牌中的绞牌列表
  676. let duiziList = [];//手牌中的对子列表
  677. let danpaiList = [];//手牌中的单牌列表
  678. for (var i = 0; i < sortVal.length; i++) {
  679. if(sortVal[i].length >= 3){
  680. tikeList.push(sortVal[i]);
  681. let issccg = this.remove(Cards,sortVal[i]);//从手牌中剔除可组成刻提的牌
  682. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  683. console.error("牌出错了111 ",JSON.stringify(Cards));
  684. return [];
  685. }
  686. }
  687. }
  688. let gc2 = _.groupBy(Cards, function (n) { return n})
  689. let vc2 = _.values(gc2);
  690. for (var i = 0; i < vc2.length; i++) {
  691. if(vc2[i].length == 1) danpaiList = danpaiList.concat(vc2[i]);
  692. else if(vc2[i].length == 2) chengshuangList.push(vc2[i]);
  693. else {
  694. console.error("牌出错了222 ",JSON.stringify(CardsCopy));
  695. return [];
  696. }
  697. }
  698. // console.warn("整理手牌 最开始的单牌 danpaiList ",JSON.stringify(danpaiList));
  699. for (var j = 0; j < dspzs.length; j++) {
  700. for (var i = 0; i < 2; i++) {//手牌中剔除刻子之后最多2个大顺
  701. let issccg = this.remove(danpaiList,dspzs[j]);//从手牌中剔除可组成大顺的牌
  702. // if(issccg) console.warn(" 成了大顺之后Cards222 ",JSON.stringify(danpaiList));
  703. if(issccg) dashunList.push(dspzs[j])
  704. else break;
  705. }
  706. }
  707. for (var j = 0; j < xspzs.length; j++) {
  708. for (var i = 0; i < 2; i++) {//手牌中剔除刻子之后最多2个小顺
  709. let issccg = this.remove(danpaiList,xspzs[j]);//从手牌中剔除可组成小顺的牌
  710. // if(issccg) console.warn(" 成了小顺之后Cards333 ",JSON.stringify(danpaiList));
  711. if(issccg) xiaoshunList.push(xspzs[j])
  712. else break;
  713. }
  714. }
  715. // console.warn("整理手牌 去掉大小顺之后的单牌 danpaiList ",JSON.stringify(danpaiList));
  716. //下面是在单牌中提取普通顺子和能和对子组成绞牌的牌
  717. let dpszList = [];//手牌中的单牌可组成的顺子列表
  718. let dphdzkzjpList = [];//手牌中的单牌和对子可组成的绞牌列表
  719. for (var j = 0; j < chengshuangList.length; j++) {
  720. let sfcj = false;//是否可以成绞
  721. for (var i = 0; i < danpaiList.length; i++) {
  722. if(this.getValue(danpaiList[i]) == this.getValue(chengshuangList[j][0])){
  723. dphdzkzjpList.push(danpaiList[i])
  724. jiaoList.push([danpaiList[i],chengshuangList[j][0],chengshuangList[j][1]]);
  725. sfcj = true;
  726. break;
  727. }
  728. }
  729. if(!sfcj) duiziList.push(chengshuangList[j])
  730. }
  731. if(dphdzkzjpList.length > 0){
  732. let issccg = this.remove(danpaiList, dphdzkzjpList);//删除手牌结果
  733. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  734. console.error("删除出错了555 ",JSON.stringify(danpaiList),dphdzkzjpList);
  735. return [];
  736. }
  737. }
  738. // console.warn("整理手牌 dphdzkzjpList ",JSON.stringify(dphdzkzjpList));
  739. for (var i = 0; i < danpaiList.length-2; i++) {
  740. if(danpaiList[i]+1 == danpaiList[i+1] && danpaiList[i]+2 == danpaiList[i+2]){
  741. dpszList.push([danpaiList[i],danpaiList[i+1],danpaiList[i+2]])
  742. i+=2;
  743. }
  744. }
  745. // console.warn("整理手牌 单牌可组顺子 dpszList ",JSON.stringify(dpszList));
  746. for (var i = 0; i < dpszList.length; i++) {
  747. let needRemoves = dpszList[i];
  748. let issccg = this.remove(danpaiList, needRemoves);//删除手牌结果
  749. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  750. console.error("删除出错了444 ",JSON.stringify(danpaiList),needRemoves);
  751. return [];
  752. }
  753. }
  754. // console.warn("整理手牌 剔除可组顺子的单牌 danpaiList ",JSON.stringify(danpaiList));
  755. // console.warn("整理手牌 最hou的单牌 danpaiList ",JSON.stringify(danpaiList));
  756. danpaiList = _.chunk(danpaiList, 3);
  757. // console.warn("手牌中的提刻列表 ",tikeList);//
  758. // console.warn("手牌中的大顺列表 ",dashunList);//
  759. // console.warn("手牌中的小顺列表 ",xiaoshunList);//
  760. // console.warn("手牌中的普通顺子列表 ",dpszList);//
  761. // console.warn("手牌中的绞牌列表 ",jiaoList);//
  762. // console.warn("手牌中的对子列表 ",duiziList);//
  763. // console.warn("手牌中的单牌列表 ",danpaiList);//
  764. res = res.concat(tikeList);//
  765. res = res.concat(dashunList);//
  766. res = res.concat(xiaoshunList);//
  767. res = res.concat(dpszList);//
  768. res = res.concat(jiaoList);//
  769. res = res.concat(duiziList);//
  770. res = res.concat(danpaiList);//
  771. // console.warn("整理之后的牌 res ",res);
  772. //下面是检验整理之后的牌和原来的牌有没有发生变化
  773. let zlzh = [];//整理之后的牌
  774. for (var i = 0; i < res.length; i++) {
  775. zlzh = zlzh.concat(res[i].reverse());
  776. }
  777. zlzh = _.sortBy(zlzh);///降序排序
  778. let lygdys = _.difference(zlzh,CardsCopy);////另一个队员的人数
  779. let lygdys2 = _.difference(CardsCopy,zlzh);////另一个队员的人数
  780. if(lygdys.length > 0 || lygdys2.length > 0 || zlzh.length != CardsCopy.length){
  781. console.error("111==============================这把派出错了 =======",JSON.stringify(zlzh));
  782. console.error("222==============================这把派出错了 =======",JSON.stringify(CardsCopy));
  783. return [];
  784. }
  785. return res;
  786. };
  787. //整理手牌(旧的,先去掉坎和刻,再去掉大顺(壹贰叁,贰柒拾)和小顺(一二三,二七十),然后将剩下的手牌分成单牌和成对的牌,然后在单牌里面去掉顺子,剩余的单牌在和成对牌组成绞牌并去掉,然后去掉不能成绞牌的对子,最终剩余的单牌称为杂牌并3张一组放在最后。)
  788. proto.groupCardsOld = function (_Cards) {
  789. // console.warn("整理手牌 ",JSON.stringify(_Cards));
  790. let Cards = _.cloneDeep(_Cards);
  791. let CardsCopy = _.cloneDeep(_Cards);
  792. // console.error("判断桌子是否存在 ");
  793. // let Cards = [101,102,102,102,103,106,106,106,107,108,201,202,202,203,203,205,205,206,206,207,208]
  794. // let CardsCopy = [101,102,102,102,103,106,106,106,107,108,201,202,202,203,203,205,205,206,206,207,208]
  795. // // // let group1 = _.groupBy(Cards, function (n) { return n % 100 })
  796. // // //// group1 = {"1":[101,201],"2":[102,102,102,202,202],"3":[103,203,203],"5":[205,205],"6":[106,106,106,206,206],"7":[107,207],"8":[108,208]}
  797. // // // let map1 = _.map(Object.keys(group1), function (n) { return Number(n) });
  798. // // //// map1 = [1,2,3,5,6,7,8]
  799. // let group2 = _.groupBy(Cards, function (n) { return n})
  800. // console.error("group2 = ",group2);
  801. // // // // group2 = {"101":[101],"102":[102,102,102],"103":[103],"106":[106,106,106],"107":[107],"108":[108],
  802. // // // // "201":[201],"202":[202,202],"203":[203,203],"205":[205,205],"206":[206,206],"207":[207],"208":[208]}
  803. // // let map2 = _.map(Object.keys(group2), function (n) { return Number(n) });
  804. // // // map2 [101,102,103,106,107,108,201,202,203,205,206,207,208]
  805. // // console.error("group2 = ",JSON.stringify(group2)," map2 ",JSON.stringify(map2));
  806. // // let order2 = _.sortBy((group2, function (n) { return n.length})
  807. // // console.error("group2 = ",group2," order2 ",order2);
  808. // var values2 = _.values(group2);
  809. // // values2 = [[101],[102,102,102],[103],[106,106,106],[107],[108],[201],[202,202],[203,203],[205,205],[206,206],[207],[208]]
  810. // console.error(" values2 ",values2,JSON.stringify(values2));
  811. // var sortVal = _.sortBy(values2, function (n) { return n.length }).reverse();
  812. // //sortVal = [[106,106,106],[102,102,102],[206,206],[205,205],[203,203],[202,202],[208],[207],[201],[108],[107],[103],[101]]
  813. // console.error(" sortVal ",sortVal,JSON.stringify(sortVal));
  814. let dspzs = [[201,202,203],[202,207,210]];//大顺的牌值列表
  815. let xspzs = [[101,102,103],[102,107,110]];//小顺的牌值列表
  816. let res = [];
  817. let gc = _.groupBy(Cards, function (n) { return n})
  818. let vc = _.values(gc);
  819. var sortVal = _.sortBy(vc, function (n) { return n.length }).reverse();
  820. // console.error(" sortVal ",sortVal);
  821. // console.error(" Cards111 ",JSON.stringify(Cards));
  822. let tikeList = [];//手牌中的提刻列表
  823. let dashunList = [];//手牌中的大顺列表
  824. let xiaoshunList = [];//手牌中的小顺列表
  825. let chengshuangList = [];//手牌中的成双列表
  826. let jiaoList = [];//手牌中的绞牌列表
  827. let duiziList = [];//手牌中的对子列表
  828. let danpaiList = [];//手牌中的单牌列表
  829. for (var i = 0; i < sortVal.length; i++) {
  830. if(sortVal[i].length >= 3){
  831. tikeList.push(sortVal[i]);
  832. let issccg = this.remove(Cards,sortVal[i]);//从手牌中剔除可组成刻提的牌
  833. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  834. console.error("牌出错了111 ",JSON.stringify(Cards));
  835. return [];
  836. }
  837. // console.error(" Cards222 ",JSON.stringify(Cards));
  838. }
  839. }
  840. for (var j = 0; j < dspzs.length; j++) {
  841. for (var i = 0; i < 2; i++) {//手牌中剔除刻子之后最多2个大顺
  842. let issccg = this.remove(Cards,dspzs[j]);//从手牌中剔除可组成大顺的牌
  843. // if(issccg) console.error(" Cards222 ",JSON.stringify(Cards));
  844. if(issccg) dashunList.push(dspzs[j])
  845. else break;
  846. }
  847. }
  848. for (var j = 0; j < xspzs.length; j++) {
  849. for (var i = 0; i < 2; i++) {//手牌中剔除刻子之后最多2个小顺
  850. let issccg = this.remove(Cards,xspzs[j]);//从手牌中剔除可组成小顺的牌
  851. // if(issccg) console.error(" Cards333 ",JSON.stringify(Cards));
  852. if(issccg) xiaoshunList.push(xspzs[j])
  853. else break;
  854. }
  855. }
  856. let gc2 = _.groupBy(Cards, function (n) { return n})
  857. let vc2 = _.values(gc2);
  858. for (var i = 0; i < vc2.length; i++) {
  859. if(vc2[i].length == 1) danpaiList = danpaiList.concat(vc2[i]);
  860. else if(vc2[i].length == 2) chengshuangList.push(vc2[i]);
  861. else {
  862. console.error("牌出错了222 ",JSON.stringify(CardsCopy));
  863. return [];
  864. }
  865. }
  866. //下面是在单牌中提取普通顺子和能和对子组成绞牌的牌
  867. let dpszList = [];//手牌中的单牌可组成的顺子列表
  868. let dphdzkzjpList = [];//手牌中的单牌和对子可组成的绞牌列表
  869. // console.warn("整理手牌 最开始的单牌 danpaiList ",JSON.stringify(danpaiList));
  870. for (var i = 0; i < danpaiList.length-2; i++) {
  871. if(danpaiList[i]+1 == danpaiList[i+1] && danpaiList[i]+2 == danpaiList[i+2]){
  872. dpszList.push([danpaiList[i],danpaiList[i+1],danpaiList[i+2]])
  873. i+=2;
  874. }
  875. }
  876. // console.warn("整理手牌 单牌可组顺子 dpszList ",JSON.stringify(dpszList));
  877. for (var i = 0; i < dpszList.length; i++) {
  878. let needRemoves = dpszList[i];
  879. let issccg = this.remove(danpaiList, needRemoves);//删除手牌结果
  880. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  881. console.error("删除出错了444 ",JSON.stringify(danpaiList),needRemoves);
  882. return [];
  883. }
  884. }
  885. // console.warn("整理手牌 剔除可组顺子的单牌 danpaiList ",JSON.stringify(danpaiList));
  886. for (var j = 0; j < chengshuangList.length; j++) {
  887. let sfcj = false;//是否可以成绞
  888. for (var i = 0; i < danpaiList.length; i++) {
  889. if(this.getValue(danpaiList[i]) == this.getValue(chengshuangList[j][0])){
  890. dphdzkzjpList.push(danpaiList[i])
  891. jiaoList.push([danpaiList[i],chengshuangList[j][0],chengshuangList[j][1]]);
  892. sfcj = true;
  893. break;
  894. }
  895. }
  896. if(!sfcj) duiziList.push(chengshuangList[j])
  897. }
  898. // console.warn("整理手牌 dphdzkzjpList ",JSON.stringify(dphdzkzjpList));
  899. if(dphdzkzjpList.length > 0){
  900. let issccg = this.remove(danpaiList, dphdzkzjpList);//删除手牌结果
  901. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  902. console.error("删除出错了555 ",JSON.stringify(danpaiList),dphdzkzjpList);
  903. return [];
  904. }
  905. }
  906. // console.warn("整理手牌 最hou的单牌 danpaiList ",JSON.stringify(danpaiList));
  907. danpaiList = _.chunk(danpaiList, 3);
  908. // console.warn("手牌中的提刻列表 ",tikeList);//
  909. // console.warn("手牌中的大顺列表 ",dashunList);//
  910. // console.warn("手牌中的小顺列表 ",xiaoshunList);//
  911. // console.warn("手牌中的普通顺子列表 ",dpszList);//
  912. // console.warn("手牌中的绞牌列表 ",jiaoList);//
  913. // console.warn("手牌中的对子列表 ",duiziList);//
  914. // console.warn("手牌中的单牌列表 ",danpaiList);//
  915. res = res.concat(tikeList);//
  916. res = res.concat(dashunList);//
  917. res = res.concat(xiaoshunList);//
  918. res = res.concat(dpszList);//
  919. res = res.concat(jiaoList);//
  920. res = res.concat(duiziList);//
  921. res = res.concat(danpaiList);//
  922. // console.warn("整理之后的牌 res ",res);
  923. //下面是检验整理之后的牌和原来的牌有没有发生变化
  924. let zlzh = [];//整理之后的牌
  925. for (var i = 0; i < res.length; i++) {
  926. zlzh = zlzh.concat(res[i].reverse());
  927. }
  928. zlzh = _.sortBy(zlzh);///降序排序
  929. let lygdys = _.difference(zlzh,CardsCopy);////另一个队员的人数
  930. let lygdys2 = _.difference(CardsCopy,zlzh);////另一个队员的人数
  931. if(lygdys.length > 0 || lygdys2.length > 0 || zlzh.length != CardsCopy.length){
  932. console.error("111==============================这把派出错了 =======",JSON.stringify(zlzh));
  933. console.error("222==============================这把派出错了 =======",JSON.stringify(CardsCopy));
  934. return [];
  935. }
  936. return res;
  937. };
  938. //得到一组手牌的块类型 (_cards是数组,元素也是数组)
  939. proto.getCardStyle = function (_cards) {
  940. // console.warn("得到一组手牌的块类型 ",JSON.stringify(_cards));
  941. let res = [];
  942. for (var i = 0; i < _cards.length; i++) {
  943. let lie = _.cloneDeep(_cards[i]);//第i列的牌
  944. lie = this.sort(lie);
  945. // console.warn("这列牌 ",lie);
  946. let style = -1;
  947. let zlpzs = lie.length;//这列牌张数
  948. let zlqczh = _.uniq(lie);//这列牌去重之后
  949. let zlqchzs = zlqczh.length;//这列牌去重之后的张数
  950. // console.warn("得到一组手牌的块类型2222 ","zlpzs",zlpzs,"zlqchzs",zlqchzs);
  951. if(zlpzs == 4){
  952. // console.warn("得到一组手牌的块类型 getCardStyle 调用anGangAnalyze ");
  953. let res = this.anGangAnalyze(lie);
  954. if(res.length > 0) style = STYLE.ANGANG;
  955. else style = STYLE.NULL;
  956. }
  957. else if(zlpzs == 3){
  958. if(zlqchzs == 1) style = STYLE.KE;
  959. else if(zlqchzs == 2) {
  960. if(this.getValue(lie[0]) == this.getValue(lie[1]) && this.getValue(lie[0]) == this.getValue(lie[2])) style = STYLE.JIAO;
  961. else style = STYLE.NULL;
  962. }
  963. else if(zlqchzs == 3) {
  964. let ress = this.shunAnalyze(lie, lie[0]);
  965. if(ress.length > 0) {
  966. if(ress[0].type <= 4) style = STYLE.SUN;
  967. else if(ress[0].type >= 5 && ress[0].type <= 6) style = STYLE.JIAO;
  968. else style = STYLE.NULL;
  969. }
  970. else style = STYLE.NULL;
  971. }
  972. }
  973. else if(zlpzs < 3){
  974. style = STYLE.NULL;
  975. }
  976. if(style < 0) console.error("这里判断手牌的块出错了");
  977. res[i] = style;
  978. }
  979. return res;
  980. };
  981. //得到一组手牌的胡息类型(_cards是数组,元素也是数组)
  982. proto.getCardHXType = function (_cards,_styles) {
  983. let res = [];
  984. if(_cards.length != _styles.length){
  985. console.error("这里判断手牌的块出错了");
  986. return res;
  987. }
  988. for (var i = 0; i < _cards.length; i++) {
  989. let lie = _.cloneDeep(_cards[i]);//第i列的牌
  990. lie = this.sort(lie);
  991. let dyzhs = this.getType(lie[0]);//第一张花色
  992. let dyzpz = this.getValue(lie[0]);//第一张牌值
  993. let hxlx = -1;//胡息类型
  994. if(_styles[i] < 0) hxlx = HUXITYPE.NULL;
  995. else if(_styles[i] == STYLE.SUN) {
  996. let dezpz = this.getValue(lie[1]);//第二张牌值
  997. // console.warn("得到一组手牌的胡息类型,",lie,_styles[i]);
  998. if(dyzhs == this.xiaoType && (dyzpz == 1 || (dyzpz == 2 && dezpz == 7))) hxlx = HUXITYPE.XIAOSHUN;
  999. else if(dyzhs == this.daType && (dyzpz == 1 || (dyzpz == 2 && dezpz == 7))) hxlx = HUXITYPE.DASHUN;
  1000. else hxlx = HUXITYPE.NULL;
  1001. }
  1002. else if(_styles[i] == STYLE.KE) {
  1003. if(dyzhs == this.xiaoType) hxlx = HUXITYPE.XIAOKE;
  1004. else if(dyzhs == this.daType) hxlx = HUXITYPE.DAKE;
  1005. else hxlx = HUXITYPE.NULL;
  1006. }
  1007. else if(_styles[i] == STYLE.PENG) {
  1008. if(dyzhs == this.xiaoType) hxlx = HUXITYPE.XIAOPENG;
  1009. else if(dyzhs == this.daType) hxlx = HUXITYPE.DAPENG;
  1010. else hxlx = HUXITYPE.NULL;
  1011. }
  1012. else if(_styles[i] == STYLE.WEI) {
  1013. if(dyzhs == this.xiaoType) hxlx = HUXITYPE.XIAOWEI;
  1014. else if(dyzhs == this.daType) hxlx = HUXITYPE.DAWEI;
  1015. else hxlx = HUXITYPE.NULL;
  1016. }
  1017. else if(_styles[i] == STYLE.GANG || _styles[i] == STYLE.ZMGANG) {
  1018. if(dyzhs == this.xiaoType) hxlx = HUXITYPE.XIAOPAO;
  1019. else if(dyzhs == this.daType) hxlx = HUXITYPE.DAPAO;
  1020. else hxlx = HUXITYPE.NULL;
  1021. }
  1022. else if(_styles[i] == STYLE.ANGANG) {
  1023. if(dyzhs == this.xiaoType) hxlx = HUXITYPE.XIAOTI;
  1024. else if(dyzhs == this.daType) hxlx = HUXITYPE.DATI;
  1025. else hxlx = HUXITYPE.NULL;
  1026. }
  1027. else hxlx = HUXITYPE.NULL;
  1028. res[i] = hxlx;
  1029. }
  1030. return res;
  1031. };
  1032. //得到一组手牌的胡息息数(参数是手牌的胡息类型数组)
  1033. proto.getCardHXValue = function (_hxtypes) {
  1034. let res = [];
  1035. for (var i = 0; i < _hxtypes.length; i++) {
  1036. res[i] = HUXIVALUE[_hxtypes[i]];
  1037. }
  1038. return res;
  1039. };
  1040. // 是否无牌
  1041. proto.isNoCards = function () {
  1042. return this.lastCount <= 0;
  1043. };
  1044. // 摸牌函数
  1045. proto.moCard = function () {
  1046. var card = 0;
  1047. if (this.lastCount > 0) {
  1048. card = this.cardsPool[this.castCount++];
  1049. this.lastCount -= 1;
  1050. }
  1051. return { card: card };
  1052. };
  1053. // 是否将牌
  1054. proto.isJang = function (lcard, rcard) {
  1055. return lcard == rcard;
  1056. };
  1057. // 可否碰牌
  1058. proto.isPeng = function (_handCards, card) {
  1059. let handCards = this.arr2ToArr1(_handCards,true);
  1060. if(this.getACardCount(handCards,card) >= 3) return false;
  1061. for (let i = 0; i < handCards.length - 1; ++i) {
  1062. if (handCards[i] == card && handCards[i + 1] == card) {
  1063. return true;
  1064. }
  1065. }
  1066. return false;
  1067. };
  1068. // 碰牌 TL++参数chirID为了判断圈风和位风
  1069. proto.peng = function (chirID,handCards, card) {
  1070. if (this.isPeng(handCards, card)) {
  1071. return { style: STYLE.PENG, cards: [card, card, card], card: card };
  1072. }
  1073. };
  1074. // 得到吃牌需要删除的所有牌
  1075. proto.getChiAllNeedRemoves = function (card, type, cdpsszs, bi1Type, bi2Type) {
  1076. let res = [];
  1077. res = this.getChiOtherCards(card, type);
  1078. if (cdpsszs == 1) {
  1079. res = res.concat(this.getChiResCards(card, bi1Type));
  1080. }
  1081. if (cdpsszs == 2) {
  1082. res = res.concat(this.getChiResCards(card, bi1Type));
  1083. res = res.concat(this.getChiResCards(card, bi2Type));
  1084. }
  1085. // console.warn("得到吃牌需要删除的所有牌",res);
  1086. return res;
  1087. };
  1088. //通过吃的牌和吃的类型得到需要删除的牌
  1089. proto.getChiOtherCards = function (card, type) {
  1090. let res = [];
  1091. let ctype = this.getType(card);
  1092. let value = this.getValue(card);
  1093. if (type == 1) {
  1094. res = [card + 1, card + 2];
  1095. } else if (type == 2) {
  1096. res = [card - 1, card + 1];
  1097. } else if (type == 3){
  1098. res = [card - 2, card - 1];
  1099. } else if (type == 4) {//二七十顺
  1100. if(ctype == 1) {
  1101. if(value == 2) res = [107,110];
  1102. else if(value == 7) res = [102,110];
  1103. else if(value == 10) res = [102,107];
  1104. }
  1105. else if(ctype == 2) {
  1106. if(value == 2) res = [207,210];
  1107. else if(value == 7) res = [202,210];
  1108. else if(value == 10) res = [202,207];
  1109. }
  1110. } else if (type == 5) {//绞情况1
  1111. if(ctype == 1) res = [card+100,card+100];
  1112. else if(ctype == 2) res = [card-100,card-100];
  1113. } else if (type == 6) {//绞情况2
  1114. if(ctype == 1) res = [card,card+100];
  1115. else if(ctype == 2) res = [card,card-100];
  1116. }
  1117. return res;
  1118. }
  1119. // 吃牌,1-@**左吃, 2-*@*中吃, 3-@**右吃
  1120. proto.getChiResCards = function (card, type) {
  1121. let res = [];
  1122. let ctype = this.getType(card);
  1123. let value = this.getValue(card);
  1124. if (type == 1) {
  1125. res = [card, card + 1, card + 2];
  1126. } else if (type == 2) {
  1127. res = [card - 1, card, card + 1];
  1128. } else if (type == 3){
  1129. res = [card - 2, card - 1, card];
  1130. } else if (type == 4) {//二七十顺
  1131. if(ctype == 1) res = [102,107,110];
  1132. else if(ctype == 2) res = [202,207,210];
  1133. } else if (type == 5) {//绞情况1
  1134. if(ctype == 1) res = [card,card+100,card+100];
  1135. else if(ctype == 2) res = [card,card-100,card-100];
  1136. } else if (type == 6) {//绞情况2
  1137. if(ctype == 1) res = [card,card,card+100];
  1138. else if(ctype == 2) res = [card,card,card-100];
  1139. }
  1140. return res;
  1141. };
  1142. // 吃牌,1-@**左吃, 2-*@*中吃, 3-@**右吃
  1143. proto.chi = function (_handCards, card, type, cdpsszs, bi1Type, bi2Type, outerId, chairId) {
  1144. // console.warn("roto.chi = funct card, type, cdpsszs, bi1Type, bi2Type, outerId, chairId ",card, type, cdpsszs, bi1Type, bi2Type, outerId, chairId);
  1145. let res = [];
  1146. let needRemoves = this.getChiAllNeedRemoves(card,type, cdpsszs, bi1Type, bi2Type);
  1147. let handCards = this.arr2ToArr1(_handCards,false);
  1148. var length = needRemoves.length;
  1149. // console.warn("roto.chi = funct 1111 ",handCards,needRemoves)
  1150. for (let i = 0; i < length; ++i) {
  1151. let pos = handCards.indexOf(needRemoves[i]);
  1152. // console.warn("roto.chi = funct 2222 ",pos,needRemoves[i])
  1153. if (pos == -1) return res;
  1154. }
  1155. for (let i = 0; i < length; ++i) {
  1156. let pos = handCards.indexOf(needRemoves[i]);
  1157. // console.warn("roto.chi = funct 3333 ",pos,needRemoves[i])
  1158. if (pos == -1) return res;
  1159. handCards.splice(pos, 1);
  1160. }
  1161. let resItem = { style: STYLE.CHI, type: type, card: card, origin:outerId};
  1162. resItem.cards = this.getChiResCards(card, type);
  1163. res[res.length] = resItem;
  1164. if (cdpsszs == 1 && type != 6) {
  1165. let bi1Item = { style: STYLE.CHI, type: bi1Type, card: card, origin:chairId};
  1166. bi1Item.cards = this.getChiResCards(card, bi1Type);
  1167. res[res.length] = bi1Item;
  1168. }
  1169. if (cdpsszs == 2) {
  1170. let bi1Item = { style: STYLE.CHI, type: bi1Type, card: card, origin:chairId};
  1171. bi1Item.cards = this.getChiResCards(card, bi1Type);
  1172. // console.warn("roto.chi = funct ????? ",bi1Item)
  1173. res[res.length] = bi1Item;
  1174. if(bi2Type > 0){
  1175. let resItem2 = { style: STYLE.CHI, type: bi2Type, card: card, origin:chairId};
  1176. resItem2.cards = this.getChiResCards(card, bi2Type);
  1177. res[res.length] = resItem2;
  1178. }
  1179. }
  1180. // console.warn("roto.chi = funct res ",res,resItem)
  1181. return res;
  1182. };
  1183. // 杠牌,1-普通杠, 2-暗杠, 3-自摸明杠 TL++参数chirID为了判断圈风和位风
  1184. proto.gang = function (chirID,_handCards, huCards, card, type) {
  1185. let handCards = this.arr2ToArr1(_handCards,true);
  1186. // 结果
  1187. var res = null;
  1188. // 明杠
  1189. if (type == 1) {
  1190. let gang = this.gangAnalyze(handCards, card);
  1191. if (gang) {
  1192. res = { style: STYLE.GANG, card: card, cards: [card, card, card, card] };
  1193. }
  1194. }
  1195. // 暗杠
  1196. else if (type == 2) {
  1197. handCards.push(card);
  1198. let anGang = this.anGangAnalyze(handCards, card);
  1199. if (anGang[0]) {
  1200. res = { style: STYLE.ANGANG, card: card, cards: [card, card, card, card] };
  1201. }
  1202. }
  1203. else if ((type == 4 || type == 5) && card) {
  1204. let angang = this.zmGangAnalyze2(handCards, huCards, card);
  1205. if (angang[0]) {
  1206. res = { style: STYLE.ANGANG, card: card, cards: [card, card, card, card] };
  1207. }
  1208. }
  1209. return res;
  1210. };
  1211. // 杠牌2仅在发牌时候调用,因为发牌时可能有多个提,应该返回个数组,1-普通杠, 2-暗杠, 3-自摸明杠
  1212. proto.gang2 = function (chirID,_handCards, huCards, card, type) {
  1213. let handCards = this.arr2ToArr1(_handCards,true);
  1214. // 结果
  1215. var res = [];
  1216. // 明杠
  1217. if (type == 1) {
  1218. let gang = this.gangAnalyze(handCards, card);
  1219. if (gang) {
  1220. res.push({ style: STYLE.GANG, card: card, cards: [card, card, card, card] });
  1221. }
  1222. }
  1223. // 暗杠
  1224. else if (type == 2) {
  1225. let anGang = this.anGangAnalyze(handCards);
  1226. if (anGang[0]) {
  1227. for (var i = 0; i < anGang.length; i++) {
  1228. res.push({style: STYLE.ANGANG, card: anGang[i].card, cards: [anGang[i].card, anGang[i].card, anGang[i].card, anGang[i].card] });
  1229. }
  1230. }
  1231. }
  1232. else if ((type == 4 || type == 5) && card) {
  1233. let angang2 = this.zmGangAnalyze2(handCards, huCards, card);
  1234. if (angang2[0]) {
  1235. res.push({ style: STYLE.ANGANG, card: card, cards: [card, card, card, card] });
  1236. }
  1237. }
  1238. return res;
  1239. };
  1240. // 自摸明杠,1-普通杠, 2-暗杠, 3-自摸明杠 TL++参数chirID为了判断圈风和位风
  1241. proto.zmGang = function (chirID,_handCards, huCards, card) {
  1242. let handCards = this.arr2ToArr1(_handCards,true);
  1243. var zmGang = this.zmGangAnalyze(handCards, huCards, card);
  1244. if (zmGang[0]) {
  1245. return { style: STYLE.ZMGANG, cards: [card, card, card, card] };
  1246. }
  1247. else{
  1248. zmGang = this.zmGangAnalyze2(handCards, huCards, card);
  1249. if (zmGang[0]) {
  1250. return { style: STYLE.ZMGANG, cards: [card, card, card, card] };
  1251. }
  1252. }
  1253. };
  1254. //检查吃牌的数据是否合法
  1255. proto.checkChiData = function (type,cdpsszs,bi1Type,bi2Type) {
  1256. if(cdpsszs == 0) {
  1257. return true;
  1258. }
  1259. else if(cdpsszs == 1 ){
  1260. if(type == 6){
  1261. return true;
  1262. }
  1263. else{
  1264. if(bi1Type >= 1 && bi1Type <= 6) return true;
  1265. }
  1266. }
  1267. else if(cdpsszs == 2){
  1268. if(type == 6){
  1269. if(bi1Type >= 1 && bi1Type <= 6) return true;
  1270. }
  1271. else{
  1272. if(bi1Type == 6){
  1273. return true;
  1274. }
  1275. else{
  1276. if(bi1Type >= 1 && bi1Type <= 6 && bi2Type >= 1 && bi2Type <= 6) return true;
  1277. }
  1278. }
  1279. }
  1280. return false;
  1281. }
  1282. //删除数组中重复出现3次以上的元素
  1283. proto.sccx3cysdys = function (_Cards) {
  1284. let Cards = _.cloneDeep(_Cards);
  1285. let gc = _.groupBy(Cards, function (n) { return n})
  1286. let vc = _.values(gc);
  1287. var sortVal = _.sortBy(vc, function (n) { return n.length }).reverse();
  1288. for (var i = 0; i < sortVal.length; i++) {
  1289. if(sortVal[i].length >= 3){
  1290. let issccg = this.remove(Cards,sortVal[i]);//从手牌中剔除可组成刻提的牌
  1291. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  1292. console.error("删除出错了 ",JSON.stringify(Cards));
  1293. return [];
  1294. }
  1295. // console.error(" Cards222 ",JSON.stringify(Cards));
  1296. }
  1297. }
  1298. return Cards;
  1299. }
  1300. proto.getACardCount = function (array, element) {
  1301. let cout = array.filter(e => e === element).length;
  1302. // console.warn("得到出现的次数 ",cout, element,JSON.stringify(array));
  1303. return cout;
  1304. }
  1305. // 吃牌分析,1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传
  1306. proto.chiAnalyze = function (_handCards, card, type) {
  1307. // console.warn("吃牌分析,1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传111 ",_handCards, card, type);
  1308. let handCards = this.arr2ToArr1(_handCards,true);
  1309. handCards = this.sccx3cysdys(handCards);
  1310. // console.warn("吃牌分析,1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传222 ",JSON.stringify(handCards), card, type);
  1311. // 结果集合
  1312. var result = [];
  1313. // 排除手牌不够情况
  1314. if (handCards.length <= 2) return result;
  1315. if(this.getACardCount(handCards,card) >= 3) return result;
  1316. // 牌面值
  1317. let value = this.getValue(card);
  1318. // 查找句子
  1319. var length = handCards.length;
  1320. for (let i = 0; i < length; ++i) {
  1321. // @**左吃,吃类型(可以不指定)
  1322. if (!type || type == 1) {
  1323. if (value < 9 && i < length - 1) {
  1324. if (handCards[i] == card + 1 && handCards[i + 1] == card + 2) {
  1325. result.push({ type: 1, card: card });
  1326. }
  1327. }
  1328. }
  1329. // *@*中吃,吃类型(可以不指定)
  1330. if (!type || type == 2) {
  1331. if (value > 1 && value < 10 && i < length - 1) {
  1332. if (handCards[i] == card - 1 && handCards[i + 1] != card - 1) {
  1333. for (let j = i + 1; j < i + 5 && j < length; ++j) {
  1334. if (handCards[j] == card + 1) {
  1335. result.push({ type: 2, card: card });
  1336. break;
  1337. }
  1338. }
  1339. }
  1340. }
  1341. }
  1342. // **@右吃,吃类型(可以不指定)
  1343. if (!type || type == 3) {
  1344. if (value > 2 && i < length - 1) {
  1345. if (handCards[i] == card - 2 && handCards[i + 1] == card - 1) {
  1346. result.push({ type: 3, card: card });
  1347. }
  1348. }
  1349. }
  1350. }
  1351. let ctype = this.getType(card);
  1352. //上面是吃普通顺子,下面是吃2,7,10顺子
  1353. if (!type || type == 4) {
  1354. let pos = [2,7,10].indexOf(value);
  1355. if (pos != -1) {
  1356. // let eqsszpzs = [,];//二七十顺的牌值列表
  1357. let needList = [];//想吃手牌需要存在的牌
  1358. if(ctype == 1) needList = [102,107,110];
  1359. else if(ctype == 2) needList = [202,207,210];
  1360. needList.splice(pos, 1);
  1361. if (handCards.indexOf(needList[0]) != -1 && handCards.indexOf(needList[1]) != -1) {
  1362. result.push({ type: 4, card: card });
  1363. }
  1364. }
  1365. }
  1366. //下面是吃绞牌
  1367. let pzhsxtzs = 0;//自己手牌中与要吃的那张牌的牌值相同花色相同的张数
  1368. let pzxthsbtzs = 0;//自己手牌中与要吃的那张牌的牌值相同花色不同的张数
  1369. for (let i = 0; i < length; ++i) {
  1370. if(this.getValue(handCards[i]) == value){
  1371. if(this.getType(handCards[i]) == ctype) pzhsxtzs++;
  1372. else pzxthsbtzs++;
  1373. }
  1374. }
  1375. // console.warn("下面是吃绞牌 "," pzhsxtzs ",pzhsxtzs," pzxthsbtzs ",pzxthsbtzs);
  1376. if (!type || type == 5) {//自己两牌花色和吃的那张牌的花色相同数量为0
  1377. if(pzxthsbtzs == 2){
  1378. result.push({ type: 5, card: card });
  1379. }
  1380. }
  1381. if (!type || type == 6) {//自己两牌花色和吃的那张牌的花色相同数量为1
  1382. if(pzhsxtzs >= 1 && pzxthsbtzs >= 1 && pzhsxtzs <= 2 && pzxthsbtzs <= 2){
  1383. result.push({ type: 6, card: card });
  1384. }
  1385. }
  1386. // console.warn("chiAnalyze result ",result);
  1387. return result;
  1388. };
  1389. //得到吃牌时吃比数据
  1390. proto.getChiBiArr = function (_handCards, card) {
  1391. let chiBiArr = {cdpsszs:-1,chi:[],bi1:[],bi2:[]}; //吃牌时吃比数据
  1392. //chi是{}类型的数组,bi1是[{},{}...]类型的数组,bi2是[[{},{}...],[{},{}...]...]要吃的牌手上只有一张时彼bi2为空数组
  1393. let handCards = this.arr2ToArr1(_handCards,true);
  1394. handCards = this.sccx3cysdys(handCards);
  1395. // 排除手牌不够情况
  1396. if (handCards.length <= 2) return chiBiArr;
  1397. // console.warn("得到吃牌时吃比数据 ks",JSON.stringify(_handCards), card,JSON.stringify(handCards));
  1398. let cdpsszs = this.getACardCount(handCards,card);//要吃的牌手上张数
  1399. chiBiArr.cdpsszs = cdpsszs;
  1400. if(cdpsszs >= 3) return chiBiArr;
  1401. let chiArr = this.chiAnalyze(_handCards, card);
  1402. // console.warn("得到吃牌时吃比数据 chiArr ",cdpsszs,chiArr);
  1403. if(cdpsszs == 0) {
  1404. for (var i = 0; i < chiArr.length; i++) {
  1405. let chiItem = chiArr[i];
  1406. chiBiArr.chi.push(chiItem);
  1407. }
  1408. return chiBiArr;
  1409. }
  1410. else if(cdpsszs == 1){
  1411. for (var i = 0; i < chiArr.length; i++) {
  1412. let chiItem = chiArr[i];
  1413. let copyCards = _.cloneDeep(handCards);
  1414. // console.warn("得到吃牌时吃比数据 删除之前的 copyCards ",JSON.stringify(copyCards));
  1415. let needRemoves = this.getChiOtherCards(chiItem.card,chiItem.type);
  1416. let issccg = this.remove(copyCards, needRemoves);//删除手牌结果
  1417. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  1418. console.error("删除出错了111 ",JSON.stringify(copyCards));
  1419. return chiBiArr;
  1420. }
  1421. // console.warn("得到吃牌时吃比数据 删除之后的 copyCards ",JSON.stringify(copyCards)," needRemoves ",needRemoves);
  1422. if(chiItem.type == 6){
  1423. //这种绞牌的吃牌会消耗掉手上那张要吃的牌,所以这种类型的就不用比了
  1424. chiBiArr.chi.push(chiItem);
  1425. chiBiArr.bi1.push([]);
  1426. }
  1427. else{
  1428. let bi1 = [];
  1429. if(copyCards.indexOf(card) != -1){
  1430. this.remove(copyCards, [card]);//删除手牌结果//把下面判断bi1需要消耗的手牌card先删掉,这样才能正确的判断出bi1
  1431. bi1 = this.chiAnalyze(copyCards, card);
  1432. }
  1433. let bi1Item = [];
  1434. for (var j = 0; j < bi1.length; j++) {
  1435. bi1Item.push(bi1[j])
  1436. }
  1437. // console.warn("得到吃牌时吃比数据 bi1 ",bi1Item,bi1);
  1438. if(bi1Item.length > 0){
  1439. chiBiArr.chi.push(chiItem);
  1440. chiBiArr.bi1.push(bi1Item);
  1441. }
  1442. }
  1443. }
  1444. }
  1445. else if(cdpsszs == 2){
  1446. for (var i = 0; i < chiArr.length; i++) {
  1447. // if(i == 2) break;
  1448. let zzcfsfky = false;//这种吃法是否可以
  1449. let chiItem = chiArr[i];
  1450. let copyCards = _.cloneDeep(handCards);
  1451. // console.warn("得到吃牌时吃比数据 删除之前的222 copyCards ",i,JSON.stringify(copyCards));
  1452. let needRemoves = this.getChiOtherCards(chiItem.card,chiItem.type);
  1453. //getChiOtherCards返回值是2个,适合得到吃牌需要删除的牌,getChiResCards适合得到比牌需要删除的牌,这里是删除上面吃牌消耗掉的2张手牌
  1454. //采用getChiResCards的目的是替下面的比牌判断先删掉一张card(相当于正常吃时候别人打下来的那张牌)
  1455. // let needRemoves = this.getChiResCards(chiItem.card,chiItem.type);
  1456. let issccg = this.remove(copyCards, needRemoves);//删除手牌结果
  1457. if(!issccg){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  1458. console.error("删除出错了222 ",JSON.stringify(copyCards));
  1459. return chiBiArr;
  1460. }
  1461. if(copyCards.indexOf(card) != -1){
  1462. this.remove(copyCards, [card]);//删除手牌结果//把下面判断bi1需要消耗的手牌card先删掉,这样才能正确的判断出bi1
  1463. }
  1464. // console.warn("得到吃牌时吃比数据 删除之后的222 copyCards ",i,JSON.stringify(copyCards)," needRemoves ",needRemoves);
  1465. let bi1 = this.chiAnalyze(copyCards, card);
  1466. // console.warn("得到吃牌时吃比数据 222 bi1 ",JSON.stringify(bi1));
  1467. // let bi1Item = [];
  1468. for (var j = 0; j < bi1.length; j++) {
  1469. if(chiItem.type == 6 && bi1[j].type == 6) continue;
  1470. let ccd = chiBiArr.chi.length;//吃数组长度
  1471. // console.warn("?????????????????????? ",ccd,bi1.length);
  1472. let b1cd = chiBiArr.bi1.length;//比1数组长度
  1473. let b2cd = chiBiArr.bi2.length;//比2数组长度
  1474. // console.warn("各长度111=== ",i,j,ccd,b1cd,b2cd,JSON.stringify(chiBiArr));
  1475. if(chiItem.type == 6){
  1476. zzcfsfky = true;
  1477. // chiBiArr.bi1[ccd]=bi1;
  1478. if(!chiBiArr.bi1[ccd]) chiBiArr.bi1[ccd]=[];
  1479. chiBiArr.bi1[ccd].push(bi1[j]);
  1480. if(!chiBiArr.bi2[ccd]) chiBiArr.bi2[ccd]=[];
  1481. chiBiArr.bi2[ccd].push([]);
  1482. }
  1483. else{
  1484. let zzbfsfky = false;//这种比法是否可以
  1485. let needRemoves2 = this.getChiOtherCards(bi1[j].card,bi1[j].type);
  1486. let copyCards2 = _.cloneDeep(copyCards);
  1487. // console.warn("得到吃牌时吃比数据 删除之前的333 copyCards2 ",JSON.stringify(copyCards2));
  1488. let issccg2 = this.remove(copyCards2, needRemoves2);//删除手牌结果
  1489. if(!issccg2){//是否删除成功,不成功说明手牌中没有,据此可知代码出错了
  1490. console.error("删除出错了333 ",JSON.stringify(copyCards)," needRemoves ",needRemoves2);
  1491. return chiBiArr;
  1492. }
  1493. // console.warn("得到吃牌时吃比数据 删除之后的333 copyCards2 ",JSON.stringify(copyCards2)," needRemoves ",needRemoves2);
  1494. // let bi2 = this.chiAnalyze(copyCards2, card);
  1495. let bi2 = [];
  1496. let bi2sfbxy = false;//bi2是否必须有
  1497. if(copyCards2.indexOf(card) != -1){
  1498. bi2sfbxy = true;
  1499. this.remove(copyCards2, [card]);//删除手牌结果//把下面判断bi2需要消耗的手牌card先删掉,这样才能正确的判断出bi2
  1500. bi2 = this.chiAnalyze(copyCards2, card);
  1501. }
  1502. // console.warn("==========================================111 ",i,j,bi2sfbxy,chiArr[i].type,bi1[j].type);
  1503. if(!bi2sfbxy){
  1504. let xhgs = 0;//消耗个数
  1505. if(chiArr[i].type == 6) xhgs += 2;//这种类型消耗2张
  1506. else xhgs += 1;//这种类型消耗1张
  1507. if(bi1[j].type == 6) xhgs += 2;//这种类型消耗2张
  1508. else xhgs += 1;//这种类型消耗1张
  1509. if(xhgs < cdpsszs + 1) bi2sfbxy = true;//前面的吃比没有消耗完card,所以必须要有比2不然这种吃法就不行
  1510. // console.warn("==========================================222 ",xhgs,chiArr[i].type,bi1[j].type);
  1511. }
  1512. // console.warn("得到吃牌时吃比数据 333 bi2 ",JSON.stringify(copyCards2),JSON.stringify(bi2));
  1513. if(bi2.length > 0){
  1514. zzcfsfky = true;
  1515. // chiBiArr.bi1[ccd]=bi1;
  1516. if(!chiBiArr.bi1[ccd]) chiBiArr.bi1[ccd]=[];
  1517. chiBiArr.bi1[ccd].push(bi1[j]);
  1518. if(!chiBiArr.bi2[ccd]) chiBiArr.bi2[ccd]=[];
  1519. chiBiArr.bi2[ccd].push(bi2);
  1520. // console.warn("各长度222=== ",i,j,ccd,b1cd,b2cd,JSON.stringify(chiBiArr));
  1521. }
  1522. else{
  1523. // console.warn("得到吃牌时吃比数据 444 bi2 ",bi2sfbxy,JSON.stringify(copyCards2),JSON.stringify(bi2));
  1524. if(!bi2sfbxy){
  1525. zzcfsfky = true;
  1526. // chiBiArr.bi1[ccd]=bi1;
  1527. if(!chiBiArr.bi1[ccd]) chiBiArr.bi1[ccd]=[];
  1528. chiBiArr.bi1[ccd].push(bi1[j]);
  1529. if(!chiBiArr.bi2[ccd]) chiBiArr.bi2[ccd]=[];
  1530. chiBiArr.bi2[ccd].push([]);
  1531. }
  1532. else {
  1533. //至此是比2必须有且bi2.length = 0 则此种比法不行
  1534. // if()
  1535. // bi1[j] = null;
  1536. }
  1537. // console.warn("得到吃牌时吃比数据 555 bi2 ",bi2sfbxy,zzcfsfky)
  1538. }
  1539. }
  1540. }
  1541. if(zzcfsfky){
  1542. chiBiArr.chi.push(chiItem);
  1543. }
  1544. }
  1545. }
  1546. // console.warn("吃比数据分析 ",JSON.stringify(chiBiArr));
  1547. return chiBiArr;
  1548. }
  1549. // 顺子分析,1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传
  1550. proto.shunAnalyze = function (handCards, card, type) {
  1551. // console.warn("吃牌分析,1-@**左吃, 2-*@*中吃, 3-@**右吃, type可以不传 ",handCards, card, type);
  1552. // 结果集合
  1553. var result = [];
  1554. // 排除手牌不够情况
  1555. if (handCards.length < 2) return result;
  1556. // 牌面值
  1557. let value = this.getValue(card);
  1558. // 查找句子
  1559. var length = handCards.length;
  1560. for (let i = 0; i < length; ++i) {
  1561. // @**左吃,吃类型(可以不指定)
  1562. if (!type || type == 1) {
  1563. if (value < 9 && i < length - 1) {
  1564. if (handCards[i] == card + 1 && handCards[i + 1] == card + 2) {
  1565. result.push({ type: 1, card: card });
  1566. }
  1567. }
  1568. }
  1569. // *@*中吃,吃类型(可以不指定)
  1570. if (!type || type == 2) {
  1571. if (value > 1 && value < 10 && i < length - 1) {
  1572. if (handCards[i] == card - 1 && handCards[i + 1] != card - 1) {
  1573. for (let j = i + 1; j < i + 5 && j < length; ++j) {
  1574. if (handCards[j] == card + 1) {
  1575. result.push({ type: 2, card: card });
  1576. break;
  1577. }
  1578. }
  1579. }
  1580. }
  1581. }
  1582. // **@右吃,吃类型(可以不指定)
  1583. if (!type || type == 3) {
  1584. if (value > 2 && i < length - 1) {
  1585. if (handCards[i] == card - 2 && handCards[i + 1] == card - 1) {
  1586. result.push({ type: 3, card: card });
  1587. }
  1588. }
  1589. }
  1590. }
  1591. let ctype = this.getType(card);
  1592. //上面是吃普通顺子,下面是吃2,7,10顺子
  1593. if (!type || type == 4) {
  1594. let pos = [2,7,10].indexOf(value);
  1595. if (pos != -1) {
  1596. // let eqsszpzs = [,];//二七十顺的牌值列表
  1597. let needList = [];//想吃手牌需要存在的牌
  1598. if(ctype == 1) needList = [102,107,110];
  1599. else if(ctype == 2) needList = [202,207,210];
  1600. needList.splice(pos, 1);
  1601. if (handCards.indexOf(needList[0]) != -1 && handCards.indexOf(needList[1]) != -1) {
  1602. result.push({ type: 4, card: card });
  1603. }
  1604. }
  1605. }
  1606. //下面是吃绞牌
  1607. let pzhsxtzs = 0;//自己手牌中与要吃的那张牌的牌值相同花色相同的张数
  1608. let pzxthsbtzs = 0;//自己手牌中与要吃的那张牌的牌值相同花色不同的张数
  1609. for (let i = 0; i < length; ++i) {
  1610. if(this.getValue(handCards[i]) == value){
  1611. if(this.getType(handCards[i]) == ctype) pzhsxtzs++;
  1612. else pzxthsbtzs++;
  1613. }
  1614. }
  1615. if (!type || type == 5) {//自己两牌花色和吃的那张牌的花色相同数量为0
  1616. if(pzxthsbtzs == 2){
  1617. result.push({ type: 5, card: card });
  1618. }
  1619. }
  1620. if (!type || type == 6) {//自己两牌花色和吃的那张牌的花色相同数量为1
  1621. if(pzhsxtzs >= 1 && pzxthsbtzs >= 1 && pzhsxtzs <= 2 && pzxthsbtzs <= 2){
  1622. result.push({ type: 6, card: card });
  1623. }
  1624. }
  1625. // console.warn("shunAnalyze result ",result);
  1626. return result;
  1627. };
  1628. // 判断摸牌者能否偎牌
  1629. proto.weiAnalyze = function (_handCards, card, zzpsfgd) {
  1630. let handCards = this.arr2ToArr1(_handCards,false);
  1631. //if(this.lastCount <= 0) return null;//红中不能碰杠
  1632. let count = 0;
  1633. for (var i = 0; i < handCards.length; i++) {
  1634. if(handCards[i] == card) count++;
  1635. }
  1636. // console.warn("判断摸牌者能否偎牌 ",card,count,JSON.stringify(handCards));
  1637. if(count == 2) {
  1638. let type = 1;//偎牌
  1639. if(zzpsfgd) type = 2;//臭偎 忍碰之后的偎牌称为“臭偎”
  1640. return { style: STYLE.WEI,type:type,card: card, cards: [card, card, card] };
  1641. }
  1642. return null;
  1643. }
  1644. // 普通杠分析,1-普通杠, 2-暗杠, 3-自摸明杠
  1645. proto.gangAnalyze = function (_handCards, card) {
  1646. let handCards = this.arr2ToArr1(_handCards,true);
  1647. // console.warn("普通杠分析,1 ",card,handCards,JSON.stringify(_handCards));
  1648. // 结果对象
  1649. var result = null;
  1650. var length = handCards.length;
  1651. if (length < 3) return result;
  1652. // 查找同牌
  1653. for (let i = 0; i < length - 2; ++i) {
  1654. if (handCards[i] == card && handCards[i + 1] == card && handCards[i + 2] == card) {
  1655. result = { type: 1, card: card };
  1656. break;
  1657. }
  1658. }
  1659. return result;
  1660. };
  1661. // 暗杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
  1662. proto.anGangAnalyze = function (handCards, card) {
  1663. // console.warn("暗杠分析,1 ",card,JSON.stringify(handCards));
  1664. // 结果对象
  1665. var result = [];
  1666. // if(this.lastCount <= 0) return result;//红中不能碰杠
  1667. var length = handCards.length;
  1668. if (length < 4) return result;
  1669. // 查找同牌
  1670. var moCard = handCards[length - 1];
  1671. for (let i = 0; i < length - 3; ++i) {
  1672. // 指定杠牌,可以不指定
  1673. if (card && handCards[i] != card) continue;
  1674. // 判定是否刻子
  1675. if (handCards[i] == handCards[i + 1] && handCards[i] == handCards[i + 2]) {
  1676. // 手上有杠牌
  1677. if (handCards[i] == handCards[i + 3]) {
  1678. result.push({ type: 2, card: handCards[i] });
  1679. }
  1680. // 刚摸到杠牌
  1681. else if (handCards[i] == moCard) {
  1682. result.push({ type: 2, card: moCard });
  1683. }
  1684. }
  1685. }
  1686. // console.warn("暗杠分析,5================ ",result)
  1687. return result;
  1688. };
  1689. // 自摸杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
  1690. proto.zmGangAnalyze = function (handCards, huCards, card) {
  1691. // 返回结果
  1692. var result = [];
  1693. // if(this.lastCount <= 0) return result;//红中不能碰杠
  1694. // 遍历句子
  1695. for (let i = 0; i < huCards.length; ++i) {
  1696. if (huCards[i].style != STYLE.PENG) {
  1697. continue;
  1698. }
  1699. let _card = huCards[i].card;
  1700. if (card && _card != card) continue;
  1701. let pos = handCards.indexOf(_card);
  1702. if (pos != -1) {
  1703. result.push({ type: 3, card: _card });
  1704. if (card) break;
  1705. }
  1706. }
  1707. return result;
  1708. };
  1709. // 自摸杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
  1710. proto.zmGangAnalyze1 = function (_handCards, huCards, card) {
  1711. let handCards = _.cloneDeep(_handCards)
  1712. if(card) handCards.push(card);
  1713. // 返回结果
  1714. var result = [];
  1715. // 遍历句子
  1716. for (let i = 0; i < huCards.length; ++i) {
  1717. if (huCards[i].style != STYLE.WEI) {
  1718. continue;
  1719. }
  1720. let _card = huCards[i].card;
  1721. if (card && _card != card) continue;
  1722. let pos = handCards.indexOf(_card);
  1723. if (pos != -1) {
  1724. result.push({ type: 5, card: _card });
  1725. if (card) break;
  1726. }
  1727. }
  1728. return result;
  1729. };
  1730. // 自摸杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
  1731. proto.zmGangAnalyze2 = function (_handCards, huCards, card) {
  1732. let handCards = _.cloneDeep(_handCards)
  1733. if(card) handCards.push(card);
  1734. // 返回结果
  1735. var result = [];
  1736. // 遍历句子
  1737. for (let i = 0; i < huCards.length; ++i) {
  1738. if (huCards[i].style != STYLE.WEI) {
  1739. continue;
  1740. }
  1741. let _card = huCards[i].card;
  1742. if (card && _card != card) continue;
  1743. let pos = handCards.indexOf(_card);
  1744. if (pos != -1) {
  1745. result.push({ type: 2, card: _card });
  1746. if (card) break;
  1747. }
  1748. }
  1749. return result;
  1750. };
  1751. // 自摸杠分析,1-普通杠, 2-暗杠, 3-自摸明杠, card可以不传
  1752. proto.zmGangAnalyze3 = function (_handCards, huCards, card) {
  1753. let handCards = _.cloneDeep(_handCards)
  1754. if(card) handCards.push(card);
  1755. // 返回结果
  1756. var result = [];
  1757. // 遍历句子
  1758. for (let i = 0; i < huCards.length; ++i) {
  1759. if (huCards[i].style != STYLE.WEI) {
  1760. continue;
  1761. }
  1762. let _card = huCards[i].card;
  1763. if (card && _card != card) continue;
  1764. let pos = handCards.indexOf(_card);
  1765. if (pos != -1) {
  1766. result.push({ type: 4, card: _card });
  1767. if (card) break;
  1768. }
  1769. }
  1770. return result;
  1771. };
  1772. // TL++得到某张牌是否合法
  1773. proto.getACardIsHeFa = function (card) {
  1774. if(card >= 101 && card <= 110) return true;
  1775. if(card >= 201 && card <= 210) return true;
  1776. return false;
  1777. };
  1778. // TL++,得到默认手牌中百搭牌当的数组 cards: 手牌去除百搭之后的牌
  1779. proto.getBDMakeList = function (cards) {
  1780. let reslist = [
  1781. 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
  1782. 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, //壹到拾
  1783. ]
  1784. let group1 = _.groupBy(cards, function (n) { return n })
  1785. let res = [];
  1786. for (var i = 0; i < reslist.length; i++) {
  1787. let card = reslist[i];
  1788. let ckey = card.toString();
  1789. if(group1[ckey] && group1[ckey].length == 4){
  1790. continue;
  1791. }
  1792. res.push(card);
  1793. }
  1794. return res;
  1795. // return _.uniq(makeList);
  1796. };
  1797. // 三提五坎分析 chairID(TL++这个参数) 手牌 加上这张判断能不能胡 手上的吃碰杠牌 是否自己摸上来的
  1798. proto.stwkAnalyze = function (chairID,_handCards, card, huCards, isTouch,isAdd) {
  1799. let handCards = this.arr2ToArr1(_handCards,true);
  1800. if(isAdd){
  1801. handCards = handCards.concat(card);/////不是自己抓的就把这张牌拼接进手牌中
  1802. handCards = this.sort(handCards);
  1803. }
  1804. this.addThisIsHu = card//////TL++,胡牌判断时加上这张能不能胡
  1805. // 手牌不全
  1806. var length = handCards.length;
  1807. let cphx = this.getCPHX(huCards);//此人吃碰胡息
  1808. let result = null;
  1809. //下面是判断3提5坎
  1810. let spcards2 = this.groupCards(handCards);
  1811. let spcards = [];
  1812. if(spcards2.length + huCards.length > 7){
  1813. let zhzq = [];//整合之前
  1814. for (var i = 0; i < spcards2.length; i++) {
  1815. if(spcards2[i].length < 3){
  1816. zhzq = zhzq.concat(spcards2[i]);
  1817. }
  1818. else{
  1819. spcards.push(spcards2[i]);
  1820. }
  1821. }
  1822. let zhzqs = _.chunk(zhzq, 3);
  1823. for (var i = 0; i < zhzqs.length; i++) {
  1824. spcards.push(zhzqs[i]);
  1825. }
  1826. }
  1827. else{
  1828. spcards = spcards2;
  1829. }
  1830. // console.warn("只有在发牌阶段判断3提5坎呢????222 ",chairID,spcards);
  1831. let styles = this.getCardStyle(spcards);
  1832. let hxTypes = this.getCardHXType(spcards,styles);
  1833. // let gbjg = this.sfxygbhxlx(isTouch,card,spcards,hxTypes);
  1834. // if(gbjg) {
  1835. // //胡息类型在此改变了
  1836. // console.warn("胡息类型在此改变了 ",gbjg);
  1837. // hxTypes[gbjg[0]] = gbjg[1];
  1838. // }
  1839. let hxValues = this.getCardHXValue(hxTypes);
  1840. let sphxs = _.sum(hxValues);//手牌胡息数
  1841. let crzhx = sphxs+cphx;//此人总胡息
  1842. if(this.isSANTIWUKAN(spcards,huCards,[],[])){
  1843. result = {
  1844. zdhx: crzhx,
  1845. card: card,
  1846. cards: spcards,
  1847. hxInfos: {
  1848. styles: styles,
  1849. hxTypes: hxTypes,
  1850. hxValues: hxValues
  1851. }
  1852. }
  1853. }
  1854. // console.warn("胡牌分析返回值2222 ",result);
  1855. return result;
  1856. };
  1857. // 胡牌分析 chairID(TL++这个参数) 手牌 加上这张判断能不能胡 手上的吃碰杠牌 是否自己摸上来的
  1858. proto.huAnalyze = function (chairID,_handCards, card, huCards, isTouch,isAdd) {
  1859. // console.error("sscc777胡牌分析card",card);
  1860. // console.error("sscc777胡牌分析handCards",_handCards);
  1861. // console.error("sscc777胡牌分析huCards",huCards);
  1862. // console.error("sscc777胡牌分析isTouch,card",chairID,isTouch,card);
  1863. let handCards = this.arr2ToArr1(_handCards,true);
  1864. // console.error("sscc777胡牌分析handCards",chairID,JSON.stringify(handCards),_handCards);
  1865. // let tempAllTai = 0/////临时总台数
  1866. // let tempres = {}///本函数的返回值
  1867. this.addThisIsHu = card//////TL++,胡牌判断时加上这张能不能胡
  1868. if(isAdd){
  1869. handCards = handCards.concat(card);/////不是自己抓的就把这张牌拼接进手牌中
  1870. handCards = this.sort(handCards);
  1871. }
  1872. // 手牌不全
  1873. var length = handCards.length;
  1874. if (length % 3 == 1) return;//2个提还未补张,牌数不对无法胡排
  1875. // console.warn("============================================== ",chairID,JSON.stringify(handCards),card);
  1876. let khzh = this.getIsHu(handCards,card);//满足可胡牌型的所有手牌组合
  1877. let cphx = this.getCPHX(huCards);//此人吃碰胡息
  1878. let result = null;
  1879. // console.warn("此人吃碰胡息 ",cphx,huCards," khzh ",khzh);
  1880. let mqzdhx = 0;//目前最大胡息
  1881. for (var i = 0; i < khzh.length; i++) {
  1882. if(khzh[i].length + huCards.length < 7) continue;
  1883. let spcards = this.pai2weiTo3Wei(khzh[i]);//把2位数手牌转换为3位数
  1884. let styles = this.getCardStyle(spcards);
  1885. let hxTypes = this.getCardHXType(spcards,styles);
  1886. let gbjg = this.sfxygbhxlx(isTouch,card,spcards,hxTypes);
  1887. if(gbjg) {
  1888. //胡息类型在此改变了
  1889. // console.warn("胡息类型在此改变了2 ",hxTypes[gbjg[0]],gbjg);
  1890. hxTypes[gbjg[0]] = gbjg[1];
  1891. }
  1892. let hxValues = this.getCardHXValue(hxTypes);
  1893. let sphxs = _.sum(hxValues);//手牌胡息数
  1894. let crzhx = sphxs+cphx;//此人总胡息
  1895. // console.warn(" khzh[i] ",JSON.stringify(khzh[i])," spcards ",spcards," styles ",styles," hxTypes ",hxTypes," hxValues ",hxValues);
  1896. if(crzhx >= this.khMinHX){
  1897. if(crzhx > mqzdhx){
  1898. mqzdhx = crzhx;
  1899. result = {
  1900. zdhx: mqzdhx,
  1901. card: card,
  1902. cards: spcards,
  1903. hxInfos: {
  1904. styles: styles,
  1905. hxTypes: hxTypes,
  1906. hxValues: hxValues
  1907. }
  1908. }
  1909. }
  1910. }
  1911. }
  1912. // console.warn("胡牌分析返回值2222 ",result);
  1913. return result;
  1914. };
  1915. // 摸牌时得到摸牌者是否能跑
  1916. proto.getMPZSFNP = function (card, _huCards) {
  1917. // console.error("摸牌时得到摸牌者是否能跑 ",_huCards);
  1918. let huCards = this.deepCloneTL(_huCards);
  1919. let kpIndex = -1;//可跑下标
  1920. for (let i = 0; i < huCards.length; ++i) {
  1921. if(huCards[i].card != card) continue;
  1922. if (huCards[i].style == STYLE.PENG) {
  1923. kpIndex = i;
  1924. break;
  1925. }
  1926. }
  1927. return kpIndex > -1;
  1928. }
  1929. // 摸牌时得到是否能跑
  1930. proto.getMPSSFNP = function (card, _huCards,isTouch) {
  1931. // console.error("摸牌时得到摸牌者是否能跑 ",_huCards);
  1932. let huCards = this.deepCloneTL(_huCards);
  1933. let kpIndex = -1;//可跑下标
  1934. for (let i = 0; i < huCards.length; ++i) {
  1935. if(huCards[i].card != card) continue;
  1936. if (huCards[i].style == STYLE.PENG) {
  1937. kpIndex = i;
  1938. break;
  1939. }
  1940. else if (huCards[i].style == STYLE.WEI) {
  1941. if(!isTouch) {
  1942. kpIndex = i;
  1943. break;
  1944. }
  1945. }
  1946. }
  1947. return kpIndex > -1;
  1948. }
  1949. // 跑胡牌分析 这个是判断那种跑之后是否可胡 和上面的区别是上面是判断跑之前是否可胡
  1950. // chairID(TL++这个参数) 手牌 加上这张判断能不能胡 手上的吃碰杠牌 是否自己摸上来的
  1951. proto.paohuAnalyze = function (chairID,_handCards, card, _huCards, isTouch,isAdd) {
  1952. console.error("sscc777胡牌分析paohuCards",chairID,isTouch,_huCards);
  1953. let huCards = this.deepCloneTL(_huCards);
  1954. let kpIndex = -1;//可跑下标
  1955. for (let i = 0; i < huCards.length; ++i) {
  1956. if(huCards[i].card != card) continue;
  1957. if (huCards[i].style == STYLE.PENG) {
  1958. kpIndex = i;
  1959. break;
  1960. }
  1961. else if (huCards[i].style == STYLE.WEI) {
  1962. if(!isTouch) {
  1963. kpIndex = i;
  1964. break;
  1965. }
  1966. }
  1967. }
  1968. if(kpIndex < 0) return;
  1969. else{
  1970. huCards[kpIndex].style = STYLE.ZMGANG;
  1971. huCards[kpIndex].cards.push(card);
  1972. }
  1973. // console.error("sscc777胡牌分析card",card);
  1974. // console.error("sscc777胡牌分析handCards",_handCards);
  1975. // console.error("sscc777胡牌分析huCards",huCards);
  1976. // console.error("sscc777胡牌分析isTouch,card",chairID,isTouch,card);
  1977. let handCards = this.arr2ToArr1(_handCards,true);
  1978. // console.error("sscc777胡牌分析handCards",chairID,JSON.stringify(handCards),_handCards);
  1979. // let tempAllTai = 0/////临时总台数
  1980. // let tempres = {}///本函数的返回值
  1981. this.addThisIsHu = card//////TL++,胡牌判断时加上这张能不能胡
  1982. // if(isAdd){
  1983. // handCards = handCards.concat(card);/////不是自己抓的就把这张牌拼接进手牌中
  1984. // handCards = this.sort(handCards);
  1985. // }
  1986. // 手牌不全
  1987. var length = handCards.length;
  1988. if (length % 3 == 1) return;//2个提还未补张,牌数不对无法胡排
  1989. // console.warn("============================================== ",chairID,JSON.stringify(handCards),card);
  1990. let khzh = this.getIsHu(handCards,card);//满足可胡牌型的所有手牌组合
  1991. let cphx = this.getCPHX(huCards);//此人吃碰胡息
  1992. let result = null;
  1993. // console.warn("此人吃碰胡息222 ",cphx,huCards," khzh ",khzh);
  1994. let mqzdhx = 0;//目前最大胡息
  1995. for (var i = 0; i < khzh.length; i++) {
  1996. if(khzh[i].length + huCards.length < 7) continue;
  1997. let spcards = this.pai2weiTo3Wei(khzh[i]);//把2位数手牌转换为3位数
  1998. let styles = this.getCardStyle(spcards);
  1999. let hxTypes = this.getCardHXType(spcards,styles);
  2000. let gbjg = this.sfxygbhxlx(isTouch,card,spcards,hxTypes);
  2001. if(gbjg) {
  2002. //胡息类型在此改变了
  2003. // console.warn("胡息类型在此改变了2 ",hxTypes[gbjg[0]],gbjg);
  2004. hxTypes[gbjg[0]] = gbjg[1];
  2005. }
  2006. let hxValues = this.getCardHXValue(hxTypes);
  2007. let sphxs = _.sum(hxValues);//手牌胡息数
  2008. let crzhx = sphxs+cphx;//此人总胡息
  2009. // console.warn(" khzh[i] ",JSON.stringify(khzh[i])," spcards ",spcards," styles ",styles," hxTypes ",hxTypes," hxValues ",hxValues);
  2010. if(crzhx >= this.khMinHX){
  2011. if(crzhx > mqzdhx){
  2012. mqzdhx = crzhx;
  2013. result = {
  2014. zdhx: mqzdhx,
  2015. card: card,
  2016. cards: spcards,
  2017. hxInfos: {
  2018. styles: styles,
  2019. hxTypes: hxTypes,
  2020. hxValues: hxValues
  2021. }
  2022. }
  2023. }
  2024. }
  2025. }
  2026. // console.warn("胡牌分析返回值2222 ",result);
  2027. return result;
  2028. };
  2029. // // TL++是否需要改变胡息类型,比如手上2个小三,别人摸小三自己胡了,算胡息时这三个小三不能按照“小刻”算胡息,应该按照“小碰”算胡息
  2030. proto.sfxygbhxlx = function (isTouch,card,handCards, hxTypes) {
  2031. // console.warn("是否需要改变胡息类型 isTouch,card,handCards, hxTypes ",isTouch,card,handCards, hxTypes);
  2032. if(isTouch) return false;
  2033. let res = [];//[修改的下标,修改之后的值]
  2034. for (var i = 0; i < handCards.length; i++) {
  2035. if(handCards[i].indexOf(card) != -1){
  2036. // if(hxTypes[i] != HUXITYPE.XIAOKE && hxTypes[i] != HUXITYPE.DAKE) return false;
  2037. // console.warn("????????????????????mmmm111 ",i,hxTypes[i] ,HUXITYPE.XIAOKE,HUXITYPE.XIAOPENG);
  2038. if(hxTypes[i] == HUXITYPE.XIAOKE) {
  2039. res = [i,HUXITYPE.XIAOPENG];
  2040. // console.warn("????????111");
  2041. }
  2042. if(hxTypes[i] == HUXITYPE.DAKE) {
  2043. res = [i,HUXITYPE.DAPENG];
  2044. // console.warn("????????222");
  2045. }
  2046. if(hxTypes[i] == HUXITYPE.XIAOTI) {
  2047. res = [i,HUXITYPE.XIAOPAO];
  2048. // console.warn("????????111");
  2049. }
  2050. if(hxTypes[i] == HUXITYPE.DATI) {
  2051. res = [i,HUXITYPE.DAPAO];
  2052. // console.warn("????????222");
  2053. }
  2054. // console.warn("????????????????????mmmm222 ",i,hxTypes[i] ,HUXITYPE.XIAOKE,HUXITYPE.XIAOPENG,res);
  2055. }
  2056. }
  2057. if(res.length == 2) return res;
  2058. // console.warn("????????????????????mmmm333 ",card,res);
  2059. return false;
  2060. };
  2061. //更新胡牌提示里面的剩余张数 _cards需要更新的牌,因为吃需要更新好几张所以这参数是数组
  2062. proto.updateSYPS = function (chairID,_cards,handCards,allhuCards,outCards,hutip) {
  2063. if(!hutip.kehuData || hutip.kehuData.length < 1) return;
  2064. let cards = _.cloneDeep(_cards);
  2065. cards = _.uniq(cards);
  2066. let syzs = -1;
  2067. for (var x = 0; x < cards.length; x++) {
  2068. let card = cards[x];
  2069. for (var i = 0; i < hutip.kehuData.length; i++) {
  2070. for (var j = 0; j < hutip.kehuData[i].tpxq.length; j++) {
  2071. if(hutip.kehuData[i].tpxq[j][0] == card){
  2072. if(syzs < 0) syzs = this.getACardSYZS(chairID,card,handCards,allhuCards,outCards);
  2073. hutip.kehuData[i].tpxq[j][2] = syzs;
  2074. }
  2075. }
  2076. }
  2077. }
  2078. };
  2079. // // TL++非当前玩家胡牌提示 和hutip方法的区别是这个是判断非当前玩家(当前玩家手牌是够的非当前玩家手牌少一张)
  2080. proto.hutip2 = function (chairID,_handCards, _huCards,_allhuCards,outCards) {
  2081. // console.error("hutip777胡牌提示handCards222 ",chairID,_handCards,_huCards,this.yscs);
  2082. // console.error("sscc777胡牌提示huCards",_huCards);
  2083. let hutipRes = [];
  2084. let handCards = this.arr2ToArr1(_handCards,true);
  2085. // if(chairID == 1) console.warn("sscc777胡牌提示handCards ",chairID,_handCards);
  2086. let huCardsys = this.deepCloneTL(_huCards);
  2087. let allhuCards = _.clone(_allhuCards);////用统计胡牌提示的剩余牌
  2088. let dangPaiList = this.getBDMakeList(handCards);
  2089. let cphxys = this.getCPHX(huCardsys);//此人吃碰胡息
  2090. // console.warn("hutip777胡牌提示 dangPaiList ",dangPaiList.length,JSON.stringify(dangPaiList));
  2091. let kdcdp = 100;////可打出的牌 就是打出这个牌之后可以胡
  2092. let dczhkhdp = [];////打出 kdcdp 之后可以胡的牌
  2093. let yjpdgdsp = 0;////已经判断过的手牌 为了减少运算量判断过的手牌就不再判断了
  2094. let isSTWK = false;//是否3提5坎;
  2095. for (var k = 0; k < dangPaiList.length; k++) {
  2096. this.yscs++;
  2097. let handCards1 = _.cloneDeep(handCards);
  2098. // let isneedLog = (chairID == 1 && dangPaiList[k] == 209)
  2099. // if(isneedLog) console.warn("111=============胡牌提示huCards",i,handCards[i] , dangPaiList[k],handCards,huCardsys);
  2100. let huCards = this.deepCloneTL(huCardsys);
  2101. let cphx = cphxys;//此人吃碰胡息
  2102. let resKind = this.getNewHuCards(chairID,handCards1,huCards,dangPaiList[k]);
  2103. if(resKind == 0){
  2104. handCards1.push(dangPaiList[k]);
  2105. handCards1 = this.sort(handCards1);
  2106. }
  2107. else {
  2108. //至此当的牌dangPaiList[k]已经放入碰杠牌中,故被替换的原来牌在这里删除
  2109. // this.remove(handCards1,[yjpdgdsp])
  2110. cphx = this.getCPHX(huCards);//此人吃碰胡息
  2111. }
  2112. let sfbnxcwt = resKind != 1 && resKind != 3 && resKind != 4;//是否不能形成偎提
  2113. // if(isneedLog) console.warn("222===============胡牌提示 handCards1 ",resKind,handCards1.length,JSON.stringify(handCards1));
  2114. let khzh = this.getIsHu(handCards1,dangPaiList[k]);//满足可胡牌型的所有手牌组合
  2115. // if(isneedLog) console.warn("222===============胡牌提示222222 khzh",khzh,resKind)
  2116. if(khzh.length == 0 && resKind > 0){
  2117. // if(isneedLog) console.warn("222===============到这里了2 khzh",khzh,resKind)
  2118. //放入吃碰牌不能胡时在这里判断不放入吃碰牌情况
  2119. let handCards2 = _.cloneDeep(handCards);
  2120. cphx = cphxys;//此人吃碰胡息
  2121. handCards2.push(dangPaiList[k]);
  2122. handCards2 = this.sort(handCards2);
  2123. huCards = this.deepCloneTL(huCardsys);
  2124. khzh = this.getIsHu(handCards2,dangPaiList[k]);//满足可胡牌型的所有手牌组合
  2125. // if(isneedLog) console.warn("222===============到这里了333 khzh",khzh)
  2126. }
  2127. let result = null;
  2128. let crzdpxfs = 0;//此人最大牌型番数 如果牌型和胡息不满足此值不会被赋值
  2129. if(khzh.length > 0) {
  2130. // if(isneedLog) console.warn("至此这手牌牌型满足要求了 此人吃碰胡息 ",cphx,khzh.length,huCards);
  2131. let mqzdpxfs = 0;//目前最大牌型番数
  2132. for (var x = 0; x < khzh.length; x++) {
  2133. let crzhx = 0;//此人总胡息
  2134. if(khzh[x].length + huCards.length < 7) continue;
  2135. let spcards = this.pai2weiTo3Wei(khzh[x]);//把2位数手牌转换为3位数
  2136. let styles = this.getCardStyle(spcards);
  2137. let hxTypes = this.getCardHXType(spcards,styles);
  2138. let hxValues = this.getCardHXValue(hxTypes);
  2139. let sphxs = _.sum(hxValues);//手牌胡息数
  2140. crzhx = sphxs+cphx;//此人总胡息
  2141. // if(isneedLog) console.warn(" khzh[x] ",x,JSON.stringify(khzh[x])," spcards ",spcards," styles ",styles," hxTypes ",hxTypes," hxValues ",hxValues);
  2142. if(crzhx >= this.khMinHX){
  2143. // let typeInfo = this.isGetCardTypeTL(chairID,spcards,huCards,[],true,dangPaiList[k])
  2144. // let allFan = typeInfo.allFan;//第X种组合方式的总番数
  2145. // if(allFan > mqzdpxfs){
  2146. // if(isneedLog) console.warn("在这里刷新 目前最大牌型番数 mqzdpxfs, allFan ",mqzdpxfs, allFan);
  2147. // mqzdpxfs = allFan;//刷新目前最大牌型番数
  2148. // }
  2149. crzdpxfs = 1;//如果要计算番数需要把这行去掉
  2150. break;
  2151. }
  2152. }
  2153. // 在这里对最高胡息最终结果进行赋值
  2154. // if(mqzdpxfs > crzdpxfs) crzdpxfs = mqzdpxfs;
  2155. }
  2156. if(crzdpxfs > 0){//如果牌型和胡息不满足此值不会被赋值
  2157. let syzs = this.getACardSYZS(chairID,dangPaiList[k],_handCards,allhuCards,outCards);
  2158. // console.warn("111????????????????????????????????",dangPaiList[k],syzs);
  2159. dczhkhdp[dczhkhdp.length] = [dangPaiList[k],crzdpxfs,syzs];////[可胡的牌,最大胡息,剩余张数]
  2160. }
  2161. }
  2162. if(dczhkhdp.length > 0 || isSTWK){
  2163. ////至此表示打出handCards[i]之后可以听牌
  2164. let data = {
  2165. "canTing": kdcdp,////表示打出这张牌之后可以听牌
  2166. "youjin": isSTWK,////打出这张牌之后是不是游金(抛搭)这个在象山麻将里面没有用
  2167. "tpxq": dczhkhdp
  2168. }
  2169. hutipRes[hutipRes.length] = data;
  2170. }
  2171. // console.warn("333????????????????????????????????",JSON.stringify(hutipRes));
  2172. // console.warn("222sscc777胡牌提示handCards ",chairID,_handCards,_huCards,this.yscs);
  2173. let kehuData3 = {
  2174. kehuData:hutipRes/////对象数组,分别表示打哪张可胡哪几张数据
  2175. };
  2176. return kehuData3;
  2177. // return hutipRes;
  2178. };
  2179. //在胡牌提示里将 当的牌 放入碰杠牌里
  2180. proto.getNewHuCards = function (chairID,_handCards, _huCards,card) {
  2181. // console.warn("在胡牌提示里将当的牌放入碰杠牌里 _handCards",JSON.stringify(_handCards));
  2182. let resKind = 0;//处理类型,0表示加入手牌,1表示需和手牌组成偎提,2表示可以和吃碰牌组成提跑
  2183. let sameList = _handCards.filter(e => e === card);
  2184. let addType = 0;//新增的吃碰类型
  2185. if(sameList.length == 2) addType = STYLE.WEI;
  2186. else if(sameList.length == 3) addType = STYLE.ANGANG;
  2187. if(addType > 0){
  2188. sameList.push(card);//手牌和当的牌成提牌
  2189. let addItem = { style: addType, cards: sameList, card: card, origin: chairID}
  2190. _huCards.push(addItem)
  2191. this.remove(_handCards,sameList)
  2192. // console.warn("在胡牌提示里将当的牌放入碰杠牌里 2222 ",JSON.stringify(sameList),JSON.stringify(_handCards));
  2193. resKind = 1;
  2194. if(addType = STYLE.WEI) resKind = 4;
  2195. }
  2196. else{
  2197. for (let i = 0; i < _huCards.length; ++i) {
  2198. if(_huCards[i].card != card) continue;
  2199. if (_huCards[i].style == STYLE.PENG) {//跑牌情况2:当自己从墩上摸得牌,是自己已经碰的牌
  2200. _huCards[i].style = STYLE.ZMGANG;
  2201. _huCards[i].cards.push(card);
  2202. resKind = 2;
  2203. break;
  2204. }
  2205. else if (_huCards[i].style == STYLE.WEI) {//提牌情况3:当自己从墩上摸得牌,是自己已经“偎牌”的牌
  2206. _huCards[i].style = STYLE.ANGANG;
  2207. _huCards[i].cards.push(card);
  2208. resKind = 3;
  2209. break
  2210. }
  2211. }
  2212. }
  2213. return resKind;
  2214. }
  2215. // // TL++胡牌提示 chairID(TL++这个参数) 手牌 手上的吃碰杠牌 这个是胡牌提示里不统计台数
  2216. proto.hutip = function (chairID,_handCards, _huCards,_allhuCards,outCards) {
  2217. // return {
  2218. // kehuData:[]/////对象数组,分别表示打哪张可胡哪几张数据
  2219. // };////服务端运算量太大放到前端去计算了
  2220. this.yscs = 0
  2221. // console.error("hutip777胡牌提示handCards ",chairID,_handCards,_huCards,this.yscs);
  2222. // console.error("sscc777胡牌提示huCards",_huCards);
  2223. let hutipRes = [];
  2224. let handCards = this.arr2ToArr1(_handCards,true);
  2225. // if(chairID == 1) console.warn("sscc777胡牌提示handCards ",chairID,_handCards);
  2226. let huCardsys = this.deepCloneTL(_huCards);
  2227. let allhuCards = _.clone(_allhuCards);////用统计胡牌提示的剩余牌
  2228. let dangPaiList = this.getBDMakeList(handCards);
  2229. let cphxys = this.getCPHX(huCardsys);//此人吃碰胡息
  2230. // console.warn("hutip777胡牌提示 dangPaiList ",dangPaiList.length,JSON.stringify(dangPaiList));
  2231. let kdcdp = 0;////可打出的牌 就是打出这个牌之后可以胡
  2232. let dczhkhdp = [];////打出 kdcdp 之后可以胡的牌
  2233. let yjpdgdsp = 0;////已经判断过的手牌 为了减少运算量判断过的手牌就不再判断了
  2234. // let yjpdgdzh = [];////已经判断过的组合 把28换成26 和 把26换成28 的结果是一样的所以可以剔除以减少运算
  2235. // console.warn("=============胡牌提示 开始运算 ",handCards.length,JSON.stringify(handCards));
  2236. for (var i = 0; i < handCards.length; i++) {
  2237. kdcdp = handCards[i];////可打出的牌
  2238. dczhkhdp = [];
  2239. if(handCards[i] == yjpdgdsp) continue;////已经判断过的手牌 为了减少运算量判断过的手牌就不再判断了
  2240. if(this.getACardCount(handCards,handCards[i]) >= 3) continue;
  2241. yjpdgdsp = handCards[i];////已经判断过的手牌 为了减少运算量判断过的手牌就不再判断了
  2242. let isSTWK = false;//是否3提5坎;
  2243. for (var k = 0; k < dangPaiList.length; k++) {
  2244. this.yscs++;
  2245. let handCards1 = _.cloneDeep(handCards);
  2246. // let isneedLog = (handCards1[i] == 208 && dangPaiList[k] == 107)
  2247. // if(isneedLog) console.warn("111=============胡牌提示huCards",i,handCards[i] , dangPaiList[k],handCards,huCardsys);
  2248. let huCards = this.deepCloneTL(huCardsys);
  2249. let cphx = cphxys;//此人吃碰胡息
  2250. let resKind = this.getNewHuCards(chairID,handCards1,huCards,dangPaiList[k]);
  2251. if(resKind == 4 && yjpdgdsp == dangPaiList[k]) resKind = 0;
  2252. if(resKind == 0){
  2253. if(handCards1[i] != dangPaiList[k]){
  2254. handCards1[i] = dangPaiList[k];
  2255. handCards1 = this.sort(handCards1);
  2256. }
  2257. }
  2258. else {
  2259. //至此当的牌dangPaiList[k]已经放入碰杠牌中,故被替换的原来牌在这里删除
  2260. this.remove(handCards1,[yjpdgdsp])
  2261. cphx = this.getCPHX(huCards);//此人吃碰胡息
  2262. }
  2263. // if(isneedLog) console.warn("222===============胡牌提示 handCards1 ",resKind,handCards1.length,JSON.stringify(handCards1));
  2264. let khzh = this.getIsHu(handCards1,dangPaiList[k]);//满足可胡牌型的所有手牌组合
  2265. let sfbnxcwt = resKind != 1 && resKind != 3;//是否不能形成偎提
  2266. if(khzh.length == 0 && sfbnxcwt){
  2267. //放入吃碰牌不能胡时在这里判断不放入吃碰牌情况
  2268. let handCards2 = _.cloneDeep(handCards);
  2269. cphx = cphxys;//此人吃碰胡息
  2270. if(handCards2[i] != dangPaiList[k]){
  2271. handCards2[i] = dangPaiList[k];
  2272. handCards2 = this.sort(handCards2);
  2273. }
  2274. huCards = this.deepCloneTL(huCardsys);
  2275. khzh = this.getIsHu(handCards2,dangPaiList[k]);//满足可胡牌型的所有手牌组合
  2276. }
  2277. let result = null;
  2278. let crzdpxfs = 0;//此人最大牌型番数 如果牌型和胡息不满足此值不会被赋值
  2279. if(khzh.length > 0) {
  2280. // if(isneedLog) console.warn("至此这手牌牌型满足要求了 此人吃碰胡息 ",cphx,khzh.length,huCards);
  2281. let mqzdpxfs = 0;//目前最大牌型番数
  2282. for (var x = 0; x < khzh.length; x++) {
  2283. let crzhx = 0;//此人总胡息
  2284. if(khzh[x].length + huCards.length < 7) continue;
  2285. let spcards = this.pai2weiTo3Wei(khzh[x]);//把2位数手牌转换为3位数
  2286. let styles = this.getCardStyle(spcards);
  2287. let hxTypes = this.getCardHXType(spcards,styles);
  2288. let hxValues = this.getCardHXValue(hxTypes);
  2289. let sphxs = _.sum(hxValues);//手牌胡息数
  2290. crzhx = sphxs+cphx;//此人总胡息
  2291. // if(isneedLog) console.warn(" khzh[x] ",x,JSON.stringify(khzh[x])," spcards ",spcards," styles ",styles," hxTypes ",hxTypes," hxValues ",hxValues);
  2292. if(crzhx >= this.khMinHX){
  2293. // let typeInfo = this.isGetCardTypeTL(chairID,spcards,huCards,[],true,dangPaiList[k])
  2294. // let allFan = typeInfo.allFan;//第X种组合方式的总番数
  2295. // if(allFan > mqzdpxfs){
  2296. // if(isneedLog) console.warn("在这里刷新 目前最大牌型番数 mqzdpxfs, allFan ",mqzdpxfs, allFan);
  2297. // mqzdpxfs = allFan;//刷新目前最大牌型番数
  2298. // }
  2299. crzdpxfs = 1;//如果要计算番数需要把这行去掉
  2300. break;
  2301. }
  2302. }
  2303. // 在这里对最高胡息最终结果进行赋值
  2304. // if(mqzdpxfs > crzdpxfs) crzdpxfs = mqzdpxfs;
  2305. }
  2306. if(crzdpxfs > 0){//如果牌型和胡息不满足此值不会被赋值
  2307. let syzs = this.getACardSYZS(chairID,dangPaiList[k],_handCards,allhuCards,outCards);
  2308. // console.warn("111????????????????????????????????",dangPaiList[k],syzs);
  2309. dczhkhdp[dczhkhdp.length] = [dangPaiList[k],crzdpxfs,syzs];////[可胡的牌,最大胡息,剩余张数]
  2310. }
  2311. }
  2312. if(dczhkhdp.length > 0 || isSTWK){
  2313. ////至此表示打出handCards[i]之后可以听牌
  2314. let data = {
  2315. "canTing": kdcdp,////表示打出这张牌之后可以听牌
  2316. "youjin": isSTWK,////打出这张牌之后是不是游金(抛搭)这个在象山麻将里面没有用
  2317. "tpxq": dczhkhdp
  2318. }
  2319. hutipRes[hutipRes.length] = data;
  2320. }
  2321. }
  2322. // console.warn("333????????????????????????????????",JSON.stringify(hutipRes));
  2323. // console.warn("222sscc777胡牌提示handCards ",chairID,_handCards,_huCards,this.yscs);
  2324. let kehuData3 = {
  2325. kehuData:hutipRes/////对象数组,分别表示打哪张可胡哪几张数据
  2326. };
  2327. return kehuData3;
  2328. // return hutipRes;
  2329. };
  2330. ////TL++得到一张牌的剩余张数 用于胡牌提示提示该张的剩余张数
  2331. proto.getACardSYZS = function (chairID,card,_handCards,allhuCards,outCards) {
  2332. ////下面这段是适合胡牌提示里用的剩余牌数统计
  2333. let yjcxdzs = 0;////该牌已经出现的张数
  2334. for (var i = 0; i < _handCards.length; i++) {
  2335. for (var j = 0; j < _handCards[i].length; j++) {
  2336. if(card == _handCards[i][j]) yjcxdzs++;////该牌已经出现在自己手牌里的张数
  2337. }
  2338. }
  2339. // console.warn("111得到一张牌的剩余张数 手牌张数",yjcxdzs,_handCards);
  2340. for (var i = 0; i < allhuCards.length; i++) {
  2341. if(allhuCards[i].length == 0) continue;////该玩家没有碰杠牌
  2342. // console.warn("222得到一张牌的剩余张数 碰杠杠牌",card,yjcxdzs,i,JSON.stringify(_.clone(allhuCards[i])));
  2343. for (var k = 0; k < allhuCards[i].length; k++) {
  2344. let pgs = allhuCards[i][k].style;//碰杠类型
  2345. let pgt = allhuCards[i][k].type;
  2346. if(chairID != i && pgs == STYLE.WEI && pgt == 1) continue;//偎牌对于别人是暗牌所以不能统计
  2347. let pgp = allhuCards[i][k].cards;
  2348. if(!pgp) continue;
  2349. for (var j = 0; j < pgp.length; j++) {
  2350. if(card == pgp[j]) yjcxdzs++;////该牌已经出现在自己手牌里的张数
  2351. }
  2352. }
  2353. }
  2354. for (var i = 0; i < outCards.length; i++) {
  2355. if(outCards[i].length == 0) continue;////该玩家没有出牌
  2356. // console.warn("2得到一张牌的剩余张数 打出去的牌",card,yjcxdzs,i,JSON.stringify(_.clone(outCards[i])));
  2357. for (var k = 0; k < outCards[i].length; k++) {
  2358. if(card == outCards[i][k]) yjcxdzs++;////该牌已经出现在所有打出去牌里的张数;
  2359. }
  2360. }
  2361. let zs = 4
  2362. let syzs = zs - yjcxdzs;////剩余张数 = 总张数 - 已经出现的张数
  2363. // console.warn("333得到一张牌的剩余张数",card,syzs);
  2364. if(syzs < 0) syzs = 0;
  2365. return syzs;
  2366. };
  2367. ////二维数组转换为一维数组
  2368. proto.arr2ToArr1 = function (_handCards,isSort) {
  2369. let handCards = [];//_handCards.flat();
  2370. for (var i = 0; i < _handCards.length; i++) {
  2371. handCards = handCards.concat(_handCards[i]);
  2372. }
  2373. if(isSort) handCards = this.sort(handCards);
  2374. return handCards;
  2375. };
  2376. //根据过掉的吃碰牌(yGDCPP)获得本次是否可以吃碰card
  2377. proto.getIsCanCP = function (yGDCPP,card,type) {
  2378. let kind = null;//过掉的类型=1表滑动打出,=2表过吃,=3表过碰,=4表过吃碰
  2379. for (var i = 0; i < yGDCPP.length; i++) {
  2380. if(yGDCPP[i][0] == card){
  2381. kind = yGDCPP[i][1];
  2382. break;
  2383. }
  2384. }
  2385. if(!kind) return true;//没有过掉或打掉过此牌故可吃碰
  2386. if(type == 1){//判断是否可吃
  2387. if(kind == 3) return true;//过碰可以吃
  2388. } else if(type == 2){//判断是否可碰
  2389. }
  2390. return false;
  2391. };
  2392. //根据过掉的吃碰牌(yGDCPP)获得card是否过掉用于判断臭偎(过吃肯定过碰)
  2393. proto.getIsGuo = function (yGDCPP,card,type) {
  2394. let kind = null;//过掉的类型=1表滑动打出,=2表过吃,=3表过碰,=4表过吃碰
  2395. for (var i = 0; i < yGDCPP.length; i++) {
  2396. if(yGDCPP[i][0] == card){
  2397. kind = yGDCPP[i][1];
  2398. break;
  2399. }
  2400. }
  2401. if(!kind) return false;//没有过掉或打掉过此牌故可吃碰
  2402. if(kind == 2 || kind == 3 || kind == 4) return true;//过碰可以吃
  2403. return false;
  2404. };
  2405. //获得手牌张数
  2406. proto.getSPZS = function (_handCards) {
  2407. let count = 0;
  2408. for (var i = 0; i < _handCards.length; i++) {
  2409. count += _handCards[i].length;
  2410. }
  2411. return count;
  2412. };
  2413. //获得吃碰牌里提跑的个数
  2414. proto.getCPPLTPGS = function (huCards) {
  2415. let gwjdtgs = 0;//该玩家的提个数
  2416. for (var i = 0; i < huCards.length; i++) {
  2417. if(huCards[i].style == STYLE.ANGANG || huCards[i].style == STYLE.GANG) gwjdtgs++;
  2418. }
  2419. return gwjdtgs;
  2420. };
  2421. //获得是否需要不出牌(进子)
  2422. proto.isNeedJinZi = function (_handCards,huCards) {
  2423. let gwjdtgs = 0;//该玩家的提个数
  2424. for (var i = 0; i < huCards.length; i++) {
  2425. if(huCards[i].style == STYLE.ANGANG) gwjdtgs++;
  2426. }
  2427. if(gwjdtgs < 2) return false;
  2428. let count = this.getSPZS(_handCards)
  2429. if(count%3 == 0) return true;
  2430. return false;
  2431. };
  2432. //获得吃碰之后是否需要不出牌(进子)在吃碰偎提跑之后调用,如果不需要出牌就给下家摸牌
  2433. proto.isXYBCP = function (_handCards,huCards) {
  2434. let gwjdtgs = 0;//该玩家的提个数
  2435. for (var i = 0; i < huCards.length; i++) {
  2436. if(huCards[i].style == STYLE.ANGANG) gwjdtgs++;
  2437. }
  2438. if(gwjdtgs < 2) return false;
  2439. let count = this.getSPZS(_handCards)
  2440. if(count%3 == 1) return true;
  2441. return false;
  2442. };
  2443. //庄家手牌里增加一张牌(仅仅在发牌时无人可胡情况下才会调用)
  2444. proto.addACardToCards = function (_handCards,card) {
  2445. let handCards = this.arr2ToArr1(_handCards,false);
  2446. handCards.push(card);
  2447. return this.changeRes2(handCards);
  2448. };
  2449. //是否是发牌阶段
  2450. proto.isFPJD = function () {
  2451. return (this.moPaiCountTL == 1 && this.chuPaiCountTL == 0);
  2452. };
  2453. // 获得吃碰牌的胡息总数
  2454. proto.getCPHX = function (huCards) {
  2455. // console.warn("获得吃碰牌的胡息数 ",JSON.stringify(huCards));
  2456. let res = 0
  2457. for (let i = 0; i < huCards.length; ++i) {
  2458. let phs = this.getType(huCards[i].card);
  2459. if (huCards[i].style == STYLE.CHI) {
  2460. let type = huCards[i].type;
  2461. if(type >= 1 && type <=3){
  2462. if(type == this.getValue(huCards[i].card)){
  2463. if(phs == 1) res += HUXIVALUE[HUXITYPE.XIAOSHUN];//小顺
  2464. else if(phs == 2) res += HUXIVALUE[HUXITYPE.DASHUN];//大顺
  2465. }
  2466. }
  2467. else if(type == 4){
  2468. if(phs == 1) res += HUXIVALUE[HUXITYPE.XIAOSHUN];//小顺
  2469. else if(phs == 2) res += HUXIVALUE[HUXITYPE.DASHUN];//大顺
  2470. }
  2471. }
  2472. else if (huCards[i].style == STYLE.PENG) {
  2473. if(phs == 1) res += HUXIVALUE[HUXITYPE.XIAOPENG];//小碰
  2474. else if(phs == 2) res += HUXIVALUE[HUXITYPE.DAPENG];//大碰
  2475. }
  2476. else if (huCards[i].style == STYLE.WEI) {
  2477. if(phs == 1) res += HUXIVALUE[HUXITYPE.XIAOWEI];//小偎
  2478. else if(phs == 2) res += HUXIVALUE[HUXITYPE.DAWEI];//大偎
  2479. }
  2480. else if (huCards[i].style == STYLE.GANG || huCards[i].style == STYLE.ZMGANG) {
  2481. if(phs == 1) res += HUXIVALUE[HUXITYPE.XIAOPAO];//小跑
  2482. else if(phs == 2) res += HUXIVALUE[HUXITYPE.DAPAO];//大跑
  2483. }
  2484. else if (huCards[i].style == STYLE.ANGANG) {
  2485. if(phs == 1) res += HUXIVALUE[HUXITYPE.XIAOTI];//小提
  2486. else if(phs == 2) res += HUXIVALUE[HUXITYPE.DATI];//大提
  2487. }
  2488. }
  2489. return res;
  2490. };
  2491. // 获得吃碰牌的胡息数组
  2492. proto.getCPHXInfo = function (huCards) {
  2493. // console.warn("获得吃碰牌的胡息数组 ",JSON.stringify(huCards));
  2494. let styles = [];
  2495. let hxTypes = [];
  2496. let hxValues = [];
  2497. for (let i = 0; i < huCards.length; ++i) {
  2498. let phs = this.getType(huCards[i].card);
  2499. console.warn("");
  2500. if (huCards[i].style == STYLE.CHI) {
  2501. let type = huCards[i].type;
  2502. if(type >= 1 && type <=3){
  2503. if(type == this.getValue(huCards[i].card)){//小顺或者大顺
  2504. if(phs == 1) {
  2505. hxValues.push(HUXIVALUE[HUXITYPE.XIAOSHUN]);//小顺
  2506. hxTypes.push(HUXITYPE.XIAOSHUN);
  2507. }
  2508. else if(phs == 2) {
  2509. hxValues.push(HUXIVALUE[HUXITYPE.DASHUN]);//大顺
  2510. hxTypes.push(HUXITYPE.DASHUN);
  2511. }
  2512. styles.push(STYLE.SUN);
  2513. }
  2514. else{//普通顺子
  2515. styles.push(STYLE.SUN);
  2516. hxValues.push(HUXIVALUE[HUXITYPE.NULL]);
  2517. hxTypes.push(HUXITYPE.NULL);
  2518. }
  2519. }
  2520. else if(type == 4){//二七十或贰柒拾
  2521. if(phs == 1) {
  2522. hxValues.push(HUXIVALUE[HUXITYPE.XIAOSHUN]);//小顺
  2523. hxTypes.push(HUXITYPE.XIAOSHUN);
  2524. }
  2525. else if(phs == 2) {
  2526. hxValues.push(HUXIVALUE[HUXITYPE.DASHUN]);//大顺
  2527. hxTypes.push(HUXITYPE.DASHUN);
  2528. }
  2529. styles.push(STYLE.SUN);
  2530. }
  2531. else{//绞牌
  2532. styles.push(STYLE.JIAO);
  2533. hxValues.push(HUXIVALUE[HUXITYPE.NULL]);
  2534. hxTypes.push(HUXITYPE.NULL);
  2535. }
  2536. }
  2537. else if (huCards[i].style == STYLE.PENG) {
  2538. if(phs == 1) {
  2539. hxValues.push(HUXIVALUE[HUXITYPE.XIAOPENG]);//小碰
  2540. hxTypes.push(HUXITYPE.XIAOPENG);
  2541. }
  2542. else if(phs == 2) {
  2543. hxValues.push(HUXIVALUE[HUXITYPE.DAPENG]);//大碰
  2544. hxTypes.push(HUXITYPE.DAPENG);
  2545. }
  2546. styles.push(STYLE.PENG);
  2547. }
  2548. else if (huCards[i].style == STYLE.WEI) {
  2549. if(phs == 1) {
  2550. hxValues.push(HUXIVALUE[HUXITYPE.XIAOWEI]);//小偎
  2551. hxTypes.push(HUXITYPE.XIAOWEI);
  2552. }
  2553. else if(phs == 2) {
  2554. hxValues.push(HUXIVALUE[HUXITYPE.DAWEI]);//大偎
  2555. hxTypes.push(HUXITYPE.DAWEI);
  2556. }
  2557. styles.push(STYLE.WEI);
  2558. }
  2559. else if (huCards[i].style == STYLE.GANG || huCards[i].style == STYLE.ZMGANG) {
  2560. if(phs == 1) {
  2561. hxValues.push(HUXIVALUE[HUXITYPE.XIAOPAO]);//小跑
  2562. hxTypes.push(HUXITYPE.XIAOPAO);
  2563. }
  2564. else if(phs == 2) {
  2565. hxValues.push(HUXIVALUE[HUXITYPE.DAPAO]);//大跑
  2566. hxTypes.push(HUXITYPE.DAPAO);
  2567. }
  2568. if (huCards[i].style == STYLE.GANG) styles.push(STYLE.GANG);
  2569. else if (huCards[i].style == STYLE.ZMGANG) styles.push(STYLE.ZMGANG);
  2570. }
  2571. else if (huCards[i].style == STYLE.ANGANG) {
  2572. if(phs == 1) {
  2573. hxValues.push(HUXIVALUE[HUXITYPE.XIAOTI]);//小提
  2574. hxTypes.push(HUXITYPE.XIAOTI);
  2575. }
  2576. else if(phs == 2) {
  2577. hxValues.push(HUXIVALUE[HUXITYPE.DATI]);//大提
  2578. hxTypes.push(HUXITYPE.DATI);
  2579. }
  2580. styles.push(STYLE.ANGANG);
  2581. }
  2582. else{
  2583. styles.push(STYLE.NULL);
  2584. hxValues.push(HUXIVALUE[HUXITYPE.NULL]);
  2585. hxTypes.push(HUXITYPE.NULL);
  2586. }
  2587. }
  2588. let res = {
  2589. styles: styles,
  2590. hxTypes: hxTypes,
  2591. hxValues: hxValues,
  2592. }
  2593. return res;
  2594. };
  2595. //得到满足可胡的所有手牌搭配 kksfkc坎坷是否可拆
  2596. proto.getIsHu = function (cards,addCard) {
  2597. // let map1 = [1,1,2,2,3,3,11,11,12,12,13,13,4,4]
  2598. let map1 = this.pai3weiTo2Wei(cards);
  2599. // console.warn("getIsHu cards,map1 ",cards.length,JSON.stringify(cards),map1.length,JSON.stringify(map1));
  2600. let map = _.fill(Array(this.MAX_PAI_CNT), 0);//[];
  2601. let mapDeep = _.fill(Array(this.MAX_PAI_CNT), 0);//[];
  2602. // console.warn("getIsHu map.length ",map.length,JSON.stringify(map));
  2603. for (let i = 0; i < map1.length; i++) {
  2604. map[map1[i]]++;//为了统计每个牌值的张数
  2605. mapDeep[map1[i]]++;//为了统计每个牌值的张数
  2606. }
  2607. // console.warn("getIsHu map ",JSON.stringify(map));
  2608. this.paiArr = _.cloneDeep(map);
  2609. this.fromPaiId = 0;
  2610. this.lastNum = map1.length;
  2611. this.duiList = [];//保存所有可以胡的牌型,数组元素是long类型,有几个元素代表有几种不同的可组合成胡的方式
  2612. // this.duiList[0] = item;
  2613. this.laiZiList = [];//所有可以胡牌型用到的癞子保存在这里,元素代表本次可胡方式中百搭当的牌
  2614. this.takeOffAll();
  2615. // console.warn("下面是this.duiList ",this.byteType,this.bytePai,this.duiList.length,this.duiList);
  2616. if(this.duiList.length == 0){
  2617. this.paiArr = _.cloneDeep(mapDeep);
  2618. this.fromPaiId = 0;
  2619. this.lastNum = map1.length;
  2620. this.takeOffAllTL(addCard);
  2621. }
  2622. // console.warn("下面是this.duiListTL ",this.byteType,this.bytePai,this.duiList.length,this.duiList);
  2623. let result = [];
  2624. for (var i = 0; i < this.duiList.length; i++) {
  2625. let dui = bigInt(this.duiList[i]);
  2626. let item = [];
  2627. // console.warn("这是 dui "+dui.toString() + " "+ typeof dui+ " "+ typeof this.duiList[i]);
  2628. while (dui > 0) {
  2629. // console.warn("这是000 dui "+dui.toString()+" bitPai "+this.bitPai+" bitType "+this.bitType +" type "+ typeof dui);
  2630. // item.push(this.popDui(dui.toString()));
  2631. item.unshift(this.popDui(dui.toString()));
  2632. // console.warn("这是111 dui "+dui.toString()+" bitPai "+this.bitPai+" bitType "+this.bitType+" type "+ typeof dui);
  2633. let he = this.bitPai+this.bitType;//bigInt(this.bitPai).add(bigInt(this.bitType));
  2634. let duiItem = dui;
  2635. dui = duiItem.shiftRight(he);
  2636. // console.warn("这是222 dui "+dui.toString()+" bitPai "+this.bitPai+" bitType "+this.bitType + " type "+typeof dui);
  2637. }
  2638. result.push(item)
  2639. }
  2640. // console.warn("getIsHu result ",result);
  2641. return result;
  2642. };
  2643. // 拆分手牌(无参数,只在huanaly中调用)
  2644. proto.takeOffAll = function () {
  2645. let dui = bigInt("0").toString();//long类型
  2646. // console.warn("dui的类型 ",typeof dui);
  2647. // console.warn("拆分手牌(无参数,只在huanaly中调用)111-----------------------------------------");
  2648. // 手里有3张以上相同的不可以拆
  2649. for (let i = this.XIAO_YI; i <= this.DA_SHI; i++) {
  2650. if (this.paiArr[i] >= 3 && i != this.fromPaiId) {
  2651. // if(this.paiArr[i] == 4) console.warn("takeOffAll ",i,dui,this.paiArr[i],JSON.stringify(this.paiArr));
  2652. dui = this.pushDui(dui, i, this.paiArr[i] == 3 ? this.KAN : this.TI);
  2653. this.lastNum -= this.paiArr[i];
  2654. this.paiArr[i] = 0;
  2655. }
  2656. }
  2657. // console.warn("拆分手牌(无参数,只在huanaly中调用)222-----------------------------------------");
  2658. //处理好提坎之后从最小的牌开始拆分
  2659. this.takeOffAll2(this.XIAO_YI, dui);
  2660. };
  2661. // 牌1:[209,209,209,109,109,109,104,204,204,101,102,103,202,207,210,108,108,108,106,107],
  2662. // 牌2:[209,209,209,109,109,109,104,204,204,101,102,103,202,207,210,108,108,108,106,106],
  2663. //上面takeOffAll()判断牌1会出错所以增加了下,面这个takeOffAllTL(),为了实现亮张不必非和手牌组成坎刻不可拆
  2664. // (因为两手牌对108使用不一样,一个是和自己手上3个108组成提一个是和手上的106,107组成6,7,8顺子)
  2665. // 拆分手牌(无参数,只在huanaly中调用)
  2666. proto.takeOffAllTL = function (addCard) {
  2667. let dui = bigInt("0").toString();//long类型
  2668. // 手里有3张以上相同的不可以拆
  2669. let addCardZHZH = this.pai3weiTo2Wei([addCard])[0];//胡的那张牌转换之后
  2670. for (let i = this.XIAO_YI; i <= this.DA_SHI; i++) {
  2671. let count = this.paiArr[i];
  2672. if(count >= 3 && addCardZHZH == i) {
  2673. count--;
  2674. }
  2675. if (count >= 3 && i != this.fromPaiId) {
  2676. dui = this.pushDui(dui, i, count == 3 ? this.KAN : this.TI);
  2677. this.lastNum -= count;
  2678. this.paiArr[i] -= count;
  2679. }
  2680. }
  2681. // for (let i = this.XIAO_YI; i <= this.DA_SHI; i++) {
  2682. // if (this.paiArr[i] == 3 && i != this.fromPaiId) {
  2683. // dui = this.pushDui(dui, i, this.paiArr[i] == 3 ? this.KAN : this.TI);
  2684. // this.lastNum -= this.paiArr[i];
  2685. // this.paiArr[i] = 0;
  2686. // }
  2687. // else if (this.paiArr[i] == 4 && i != this.fromPaiId) {
  2688. // // dui = this.pushDui(dui, i, this.paiArr[i] == 3 ? this.KAN : this.TI);
  2689. // dui = this.pushDui(dui, i,this.KAN);
  2690. // this.lastNum -= 3;
  2691. // this.paiArr[i] = 1;
  2692. // }
  2693. // }
  2694. //处理好提坎之后从最小的牌开始拆分
  2695. this.takeOffAll2(this.XIAO_YI, dui);
  2696. };
  2697. // 拆分手牌(无参数,只在huanaly中调用)
  2698. proto.takeOffAll2 = function (paiId, dui) {
  2699. // console.warn("takeOffAll参数 lastNum "+this.lastNum+" dui "+dui+" paiId "+paiId);
  2700. // 剩余3张以内的 0是拆完了可以胡 1不能胡 2刚好是个将就可以胡
  2701. if (this.lastNum < 3) {
  2702. if (this.lastNum == 0) {
  2703. // 能胡
  2704. // console.warn("这种方式能胡 ",dui,typeof dui);
  2705. this.duiList.push(dui);
  2706. // 保存癞子代替的牌
  2707. let laizi = 0;
  2708. for (let i = this.XIAO_YI; i <= this.DA_SHI; i++) {
  2709. for (let j = this.paiArr[i]; j < 0; j++) {
  2710. // laizi = laizi << this.bitPai | i;
  2711. }
  2712. }
  2713. this.laiZiList.push(laizi);
  2714. } else if (this.lastNum == 2) {
  2715. // 剩余2张能否做将
  2716. this.takeOffJiang(dui);
  2717. }
  2718. return;
  2719. }
  2720. for (let i = paiId; i <= this.DA_SHI; i++) {
  2721. // 4张相同的
  2722. this.takeOffSame(i, 4, this.TI, dui);
  2723. // 3张相同的
  2724. this.takeOffSame(i, 3, this.KAN, dui);
  2725. // 2710
  2726. this.takeOff2710(i, dui);
  2727. // 拆顺子
  2728. this.takeOff123(i, dui);
  2729. // 拆混对
  2730. this.takeOffHunDui(i, dui);
  2731. }
  2732. };
  2733. //拆分将,剩余2张能否做将
  2734. proto.takeOffJiang = function (dui) {
  2735. // console.warn("拆分将,剩余2张能否做将 dui "+dui);
  2736. let wangNum = this.paiArr[this.WANG];
  2737. let paiNum = 2 - wangNum;
  2738. this.paiArr[this.WANG] = 0;
  2739. for (let i = this.XIAO_YI; i <= this.DA_SHI; i++) {
  2740. if (this.paiArr[i] == paiNum) {
  2741. this.paiArr[i] -= 2;
  2742. this.lastNum -= 2;
  2743. this.takeOffAll2(this.MAX_PAI_CNT, this.pushDui(dui, i, this.DUIZI));
  2744. this.paiArr[i] += 2;
  2745. this.lastNum += 2;
  2746. }
  2747. }
  2748. this.paiArr[this.WANG] += wangNum;
  2749. };
  2750. //拆分相同的,
  2751. proto.takeOffSame = function (i, cnt, type, dui) {
  2752. // console.warn("拆分相同的, "+" i "+i+" cnt "+cnt+" type "+type+" dui "+dui);
  2753. if (this.paiArr[i] + this.paiArr[this.WANG] >= cnt) {
  2754. let needWang = cnt - this.paiArr[i];
  2755. if (needWang > 0) {
  2756. this.paiArr[this.WANG] -= needWang;
  2757. }
  2758. this.paiArr[i] -= cnt;
  2759. this.lastNum -= cnt;
  2760. this.takeOffAll2(i, this.pushDui(dui, i, type));
  2761. this.lastNum += cnt;
  2762. this.paiArr[i] += cnt;
  2763. if (needWang > 0) {
  2764. this.paiArr[this.WANG] += needWang;
  2765. }
  2766. }
  2767. };
  2768. //拆分普通顺子
  2769. proto.takeOff123 = function (i, dui) {
  2770. // console.warn("拆分普通顺子 "+" i "+i+" dui "+dui);
  2771. // 处理边界的问题,9 10 11变成顺子的问题
  2772. let border = (i - 1) % 10;
  2773. if (border > 7) {
  2774. return;
  2775. }
  2776. let i1 = i + 1;
  2777. let i2 = i + 2;
  2778. this.takeOffSingle(i, i1, i2, this.SHUNZI, dui);
  2779. };
  2780. //拆分2710
  2781. proto.takeOff2710 = function (i, dui) {
  2782. // 只有是2的时候才组合2710
  2783. if (i % 10 != 2) {
  2784. return;
  2785. }
  2786. let i1 = i + 5;
  2787. let i2 = i + 8;
  2788. // console.warn("takeOff2710 "+" i "+i+" i1 "+i1+" i2 "+i2+" S2710 "+this.S2710+" dui "+dui);
  2789. this.takeOffSingle(i, i1, i2, this.S2710, dui);
  2790. };
  2791. //拆分单牌
  2792. proto.takeOffSingle = function (i, i1, i2, type, dui) {
  2793. let needWang = 0;
  2794. if (this.paiArr[i] < 1)
  2795. needWang++;
  2796. if (this.paiArr[i1] < 1)
  2797. needWang++;
  2798. if (this.paiArr[i2] < 1)
  2799. needWang++;
  2800. // console.warn("takeOffSingle111 "+" needWang "+needWang+" this.paiArr[this.WANG] "+this.paiArr[this.WANG]+" WANG "+this.WANG+" dui "+dui+" i "+i+" i1 "+i1+" i2 "+i2);
  2801. if (this.paiArr[this.WANG] >= needWang) {
  2802. this.paiArr[this.WANG] -= needWang;
  2803. this.paiArr[i]--;
  2804. this.paiArr[i1]--;
  2805. this.paiArr[i2]--;
  2806. this.lastNum -= 3;
  2807. // console.warn("takeOffSingle222 "+" needWang "+needWang+" this.paiArr[this.WANG] "+this.paiArr[this.WANG]+" this.WANG "+this.WANG+" dui "+dui+" i "+i+" type "+type);
  2808. this.takeOffAll2(i, this.pushDui(dui, i, type));
  2809. this.lastNum += 3;
  2810. this.paiArr[i]++;
  2811. this.paiArr[i1]++;
  2812. this.paiArr[i2]++;
  2813. this.paiArr[this.WANG] += needWang;
  2814. }
  2815. };
  2816. //拆分混对(绞牌)和下面的参数不一样
  2817. proto.takeOffHunDui = function (i, dui) {
  2818. // console.warn("拆分混对(绞牌) "+" i "+i+" dui "+dui);
  2819. let relatePai = i > 10 ? i - 10 : i + 10;
  2820. if (this.paiArr[i] >= 1 && this.paiArr[relatePai] >= 1) {
  2821. if (this.paiArr[i] >= 2) {
  2822. this.takeOffHunDui2(i, relatePai, dui);
  2823. } else if (this.paiArr[this.WANG] >= 1) {
  2824. this.paiArr[this.WANG]--;
  2825. this.takeOffHunDui2(i, relatePai, dui);
  2826. this.paiArr[this.WANG]++;
  2827. }
  2828. }
  2829. };
  2830. //拆分混对(绞牌)和上面的参数不一样
  2831. proto.takeOffHunDui2 = function (i, relatePai, dui) {
  2832. // console.warn("拆分混对(绞牌)222 "+" i "+i+" relatePai "+relatePai+" dui "+dui);
  2833. this.paiArr[i] -= 2;
  2834. this.paiArr[relatePai]--;
  2835. this.lastNum -= 3;
  2836. this.takeOffAll2(i, this.pushDui(dui, i, this.HUNDUN));
  2837. this.lastNum += 3;
  2838. this.paiArr[i] += 2;
  2839. this.paiArr[relatePai]++;
  2840. };
  2841. //
  2842. proto.pushDui = function (dui, paiId, type) {
  2843. // let typeStr = ""+type+"(";
  2844. // if(type == 1) typeStr += "顺子";
  2845. // else if(type == 2) typeStr += "混对";
  2846. // else if(type == 3) typeStr += "坎";
  2847. // else if(type == 4) typeStr += "提";
  2848. // else if(type == 5) typeStr += "对子";
  2849. // else if(type == 6) typeStr += "27十";
  2850. // typeStr += ")";
  2851. let x1 = bigInt(dui);
  2852. let x2 = bigInt(paiId);
  2853. let x3 = bigInt(type);
  2854. // //上面是对参数进行类型转换,下面是运算
  2855. let a1 = x1.shiftLeft(this.bitType);
  2856. let a2 = a1.or(x3);//a1.or(bigInt("6"));
  2857. let a3 = a2.shiftLeft(this.bitPai);
  2858. let a4 = a3.or(x2);//a3.or(bigInt("12"));
  2859. let res = a4.toString();
  2860. // console.warn("pushDui res "+res+" dui "+dui+" paiId "+paiId+" type "+typeStr+" bitType "+this.bitType+" bitPai "+this.bitPai);
  2861. return res;
  2862. };
  2863. proto.popDui = function (dui) {
  2864. // let paiId = parseInt(dui & this.bytePai);
  2865. // let type = parseInt(dui >>> this.bitPai & this.byteType);
  2866. let x1 = bigInt(dui);
  2867. let x2 = bigInt(this.bytePai);
  2868. let x3 = bigInt(this.byteType);
  2869. let a1 = x1.and(x2);
  2870. let paiId = a1.valueOf();
  2871. let a2 = x1.shiftRight(this.bitPai);
  2872. let a3 = a2.and(x3);
  2873. let type = a3.valueOf();
  2874. // console.warn("popDui000 dui "+dui+" bitPai "+this.bitPai+" bytePai "+this.bytePai+" byteType "+this.byteType);
  2875. // console.warn("popDui111 type "+type+" paiId "+paiId);
  2876. switch (type) {
  2877. case this.TI:
  2878. return [paiId, paiId, paiId, paiId];
  2879. case this.KAN:
  2880. return [paiId, paiId, paiId];
  2881. case this.SHUNZI:
  2882. return [paiId, paiId + 1, paiId + 2];
  2883. case this.S2710:
  2884. return [paiId, paiId + 5, paiId + 8];
  2885. case this.HUNDUN:
  2886. let relatePai = paiId > 10 ? paiId - 10 : paiId + 10;
  2887. return [paiId, paiId, relatePai];
  2888. case this.DUIZI:
  2889. return [paiId, paiId];
  2890. }
  2891. return [];
  2892. };
  2893. //3位数字的牌转换为2位数字的牌
  2894. proto.pai3weiTo2Wei = function (cards) {
  2895. // let pai2WeiList = [0n,1n,2n,3n,4n,5n,6n,7n,8n,9n,10n,11n,12n,13n,14n,15n,16n,17n,18n,19n,20n,21n,22n,23n];
  2896. let res = [];
  2897. for (var i = 0; i < cards.length; i++) {
  2898. let ct = this.getType(cards[i]);
  2899. if(ct == 1) res[i] = cards[i]-100;
  2900. else if(ct == 2) res[i] = cards[i]-200+10;
  2901. }
  2902. return res;
  2903. };
  2904. //2位数字的牌转换为3位数字的牌 参数是数组,数组元素也是数组
  2905. proto.pai2weiTo3Wei = function (cardsArr) {
  2906. let res = [];
  2907. for (var i = 0; i < cardsArr.length; i++) {
  2908. let item = [];
  2909. for (var j = 0; j < cardsArr[i].length; j++) {
  2910. let card = cardsArr[i][j];
  2911. let ct = Math.ceil(card / 10);
  2912. if(ct == 1) item[j] = card+100;
  2913. else if(ct == 2) item[j] = card-10+200;
  2914. }
  2915. res.push(item);
  2916. }
  2917. return res;
  2918. };
  2919. //得到偎完之后是否无牌可出
  2920. proto.getSFWPKC2 = function (handCards,handHXInfos) {
  2921. let kcdp = [];//可出的牌
  2922. for (var i = 0; i < handHXInfos.hxTypes.length; i++) {
  2923. if(handHXInfos.hxTypes[i] < 3) {//因为杂牌小顺大顺,这些牌都是能出的
  2924. kcdp = kcdp.concat(handCards[i]);
  2925. }
  2926. }
  2927. // console.warn("得到吃碰偎之后是否无牌可出 ",kcdp.length,JSON.stringify(handHXInfos),JSON.stringify(handCards),kcdp);
  2928. return kcdp.length <= 0;
  2929. }
  2930. //得到吃碰偎之后是否无牌可出(手上都是刻子的时候就无牌可出了)
  2931. proto.getSFWPKC = function (handCards,handHXInfos) {
  2932. let kcdp = [];//可出的牌
  2933. for (var i = 0; i < handHXInfos.hxTypes.length; i++) {
  2934. if(handHXInfos.hxTypes[i] < 3) {//因为杂牌小顺大顺,这些牌都是能出的
  2935. kcdp = kcdp.concat(handCards[i]);
  2936. }
  2937. }
  2938. // console.warn("得到吃碰偎之后是否无牌可出 ",kcdp.length,JSON.stringify(handHXInfos),JSON.stringify(handCards),kcdp);
  2939. return kcdp.length <= 2;
  2940. }
  2941. //得到剩余牌堆
  2942. proto.getSYPaiDui = function () {
  2943. // console.warn("得到剩余牌堆 ",this.castCount,this.cardsPool.length);
  2944. let res = this.cardsPool.slice(this.castCount, this.cardsPool.length);
  2945. // console.warn("得到剩余牌堆222 ",this.castCount,this.cardsPool.length);
  2946. return res;
  2947. };
  2948. // 获得赢家每列牌的类型和胡息数
  2949. proto.getWinerHXInfo = function (huResItem,huCards) {
  2950. // console.warn("获得赢家每列牌的类型和胡息数 huResItem ",JSON.stringify(huResItem));
  2951. // console.warn("获得赢家每列牌的类型和胡息数 huCards ",JSON.stringify(huCards));
  2952. let cphxInfo = this.getCPHXInfo(huCards);
  2953. let styles = cphxInfo.styles;
  2954. let hxTypes = cphxInfo.hxTypes;
  2955. let hxValues = cphxInfo.hxValues;
  2956. // console.warn("获得赢家每列牌的类型和胡息数 cphxInfo ",JSON.stringify(cphxInfo));
  2957. if(huResItem && huResItem.hxInfos){
  2958. styles = styles.concat(huResItem.hxInfos.styles);
  2959. hxTypes = hxTypes.concat(huResItem.hxInfos.hxTypes);
  2960. hxValues = hxValues.concat(huResItem.hxInfos.hxValues);
  2961. }
  2962. let hxInfos = {
  2963. styles: styles,
  2964. hxTypes: hxTypes,
  2965. hxValues: hxValues
  2966. }
  2967. return hxInfos;
  2968. };
  2969. ////////////////////下面是计算牌型相关的////////////////////
  2970. //计算牌型和番数
  2971. proto.isGetCardTypeTL = function (chairID,handCards, huCards, hures,isMoHu,addcard) {
  2972. let tempsatisfyType = []////临时胡牌牌型
  2973. for (let i = 0; i < this.huTypeTL.length; ++i) {
  2974. tempsatisfyType[i] = 0
  2975. }
  2976. let curTaiShu = 0/////临时总台数
  2977. let shoupai = []/////手牌
  2978. for (var i = 0; i < handCards.length; i++) {
  2979. shoupai = shoupai.concat(handCards[i])
  2980. }
  2981. let PGPai = huCards/////手上的碰杠牌
  2982. let chengju = hures/////整理好之后成句的牌(包括碰杠牌)
  2983. // let chengju = hures/////整理好之后成句的牌(包括碰杠牌)
  2984. // console.warn("计算牌型和番数 PGPai ",JSON.stringify(PGPai));
  2985. let geLeiCount = [0,0,0,0];//各类型个数[大子个数,小子个数,红牌个数,黑牌个数]
  2986. for (var i = 0; i < handCards.length; i++) {
  2987. for (var j = 0; j < handCards[i].length; j++) {
  2988. let type = this.getType(handCards[i][j]);
  2989. let value = this.getValue(handCards[i][j]);
  2990. if(type == 1) geLeiCount[1]++;
  2991. else if(type == 2) geLeiCount[0]++;
  2992. if(value == 2 || value == 7 || value == 10) geLeiCount[2]++;
  2993. else geLeiCount[3]++;
  2994. }
  2995. }
  2996. for (var i = 0; i < PGPai.length; i++) {
  2997. for (var j = 0; j < PGPai[i].cards.length; j++) {
  2998. let ic = PGPai[i].cards[j];
  2999. let type = this.getType(ic);
  3000. let value = this.getValue(ic);
  3001. if(type == 1) geLeiCount[1]++;
  3002. else if(type == 2) geLeiCount[0]++;
  3003. if(value == 2 || value == 7 || value == 10) geLeiCount[2]++;
  3004. else geLeiCount[3]++;
  3005. }
  3006. }
  3007. // console.warn("各类型个数[大子个数,小子个数,红牌个数,黑牌个数] ",JSON.stringify(geLeiCount));
  3008. this.hongHuCount = 0;
  3009. this.daHuCount = 0;
  3010. this.xiaoHuCount = 0;
  3011. this.datuanyuanCount = 0;
  3012. this.wxdldgs = 0;//无息的列的个数,用来判断真假行息
  3013. this.isBeiKaoBei = false;//是否满足背靠背,用来判断真假背靠背
  3014. for (let i = 0; i < this.huTypeTL.length; ++i) {
  3015. if(this.xypddpxlb.indexOf(i) == -1) continue;//需要判断的牌型列表
  3016. if( i == 0 && this.isTIANHU(handCards,PGPai,chengju,geLeiCount,isMoHu)){
  3017. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3018. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3019. }
  3020. else if( i == 1 && this.isDIHU(handCards,PGPai,chengju,geLeiCount,isMoHu)){
  3021. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3022. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3023. }
  3024. else if( i == 2 && this.isHAIHU(handCards,PGPai,chengju,geLeiCount)){
  3025. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3026. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3027. }
  3028. else if( i == 3 && this.isTINGHU(handCards,PGPai,chengju,geLeiCount,chairID)){
  3029. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3030. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3031. }
  3032. else if( i == 4 && this.isHONGHU(handCards,PGPai,chengju,geLeiCount)){
  3033. curTaiShu = curTaiShu + this.huTypeTL[i][1] + this.hongHuCount*1;
  3034. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3035. }
  3036. else if( i == 5 && this.isHEIHU(handCards,PGPai,chengju,geLeiCount)){
  3037. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3038. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3039. }
  3040. else if( i == 6 && this.isDIANHU(handCards,PGPai,chengju,geLeiCount)){
  3041. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3042. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3043. }
  3044. else if( i == 7 && this.isDAHU(handCards,PGPai,chengju,geLeiCount)){
  3045. curTaiShu = curTaiShu + this.huTypeTL[i][1] + this.daHuCount*1;
  3046. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3047. }
  3048. else if( i == 8 && this.isXIAOHU(handCards,PGPai,chengju,geLeiCount)){
  3049. curTaiShu = curTaiShu + this.huTypeTL[i][1] + this.xiaoHuCount*1;
  3050. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3051. }
  3052. else if( i == 9 && this.isSHUAHOU(handCards,PGPai,chengju,geLeiCount,addcard)){
  3053. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3054. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3055. }
  3056. else if( i == 10 && this.isHUANGFAN(handCards,PGPai,chengju,geLeiCount)){
  3057. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3058. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3059. }
  3060. else if( i == 11 && this.isDUIZIHU(handCards,PGPai,chengju,geLeiCount)){
  3061. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3062. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3063. }
  3064. else if( i == 12 && this.isDATUANYUAN(handCards,PGPai,chengju,geLeiCount)){
  3065. curTaiShu = curTaiShu + this.huTypeTL[i][1]*this.datuanyuanCount;
  3066. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3067. }
  3068. else if( i == 13 && this.isZHENHANGXI(handCards,PGPai,chengju,geLeiCount)){
  3069. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3070. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3071. }
  3072. else if( i == 14 && this.isJIAHANGXI(handCards,PGPai,chengju,geLeiCount)){
  3073. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3074. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3075. }
  3076. else if( i == 15 && this.isSIQIHONG(handCards,PGPai,chengju,geLeiCount)){
  3077. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3078. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3079. }
  3080. else if( i == 16 && this.isZHENBEIKAOBEI(handCards,PGPai,chengju,geLeiCount,isMoHu)){
  3081. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3082. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3083. }
  3084. else if( i == 17 && this.isJIABEIKAOBEI(handCards,PGPai,chengju,geLeiCount)){
  3085. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3086. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3087. }
  3088. else if( i == 18 && this.isSANTIWUKAN(handCards,PGPai,chengju,geLeiCount)){
  3089. //满足3提5坎肯定满足天胡和听胡
  3090. // curTaiShu = curTaiShu + this.huTypeTL[3][1]
  3091. // tempsatisfyType[3] = 1/////设置胡牌满足的牌型
  3092. let itemIndex = 3;
  3093. if(this.gameKindTL == 3) itemIndex = 18
  3094. curTaiShu = curTaiShu + this.huTypeTL[itemIndex][1]
  3095. tempsatisfyType[itemIndex] = 1/////设置胡牌满足的牌型
  3096. }
  3097. else if( i == 19 && this.isJINWUCANGJIAO(handCards,PGPai,chengju,geLeiCount,isMoHu)){
  3098. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3099. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3100. }
  3101. else if( i == 20 && this.isDING(handCards,PGPai,chengju,geLeiCount)){
  3102. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3103. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3104. }
  3105. else if( i == 21 && this.isZHUOXIAOSAN(handCards,PGPai,chengju,geLeiCount,isMoHu)){
  3106. curTaiShu = curTaiShu + this.huTypeTL[i][1]
  3107. tempsatisfyType[i] = 1;/////设置胡牌满足的牌型
  3108. }
  3109. }
  3110. let hutype = [];
  3111. let allFan = 0;
  3112. for (var i = 0; i < tempsatisfyType.length; i++) {
  3113. if(tempsatisfyType[i] < 1) continue;
  3114. if(i == 10) continue;//黄番在计算完之后算,因为比较特殊放在牌型最后一个
  3115. let fanitem = this.huTypeTL[i][1];//此牌型的番数
  3116. if(i == 4) fanitem = this.huTypeTL[i][1] + this.hongHuCount*1;
  3117. else if(i == 7) fanitem = this.huTypeTL[i][1] + this.daHuCount*1;
  3118. else if(i == 8) fanitem = this.huTypeTL[i][1] + this.xiaoHuCount*1;
  3119. else if(i == 12) fanitem = this.huTypeTL[i][1] * this.datuanyuanCount*1;
  3120. allFan += fanitem;//黄番最后*2,所以这里不加黄番的番数
  3121. hutype.push([this.huTypeTL[i][0],fanitem]);
  3122. }
  3123. if(allFan == 0) allFan = 1;//没有牌型的话算1番
  3124. if(tempsatisfyType[10]){//黄番最后*2
  3125. hutype.push([this.huTypeTL[10][0],this.huTypeTL[10][1]]);
  3126. allFan = allFan*2;
  3127. }
  3128. // console.warn("计算牌型和番数 tempsatisfyType ",JSON.stringify(tempsatisfyType)," hutype ",hutype);
  3129. return{hutype:hutype,allFan:allFan}
  3130. }
  3131. //是否 "天胡",
  3132. proto.isTIANHU = function (shoupai,PGPai,chengju,geLeiCount,isMoHu) {
  3133. let isstwk = this.isSANTIWUKAN(shoupai,PGPai,chengju,geLeiCount);
  3134. if(isstwk && isMoHu) return true;
  3135. return this.chuPaiCountTL == 0 && isMoHu;
  3136. };
  3137. //是否 "地胡",
  3138. proto.isDIHU = function (shoupai,PGPai,chengju,geLeiCount,isMoHu) {
  3139. let isstwk = this.isSANTIWUKAN(shoupai,PGPai,chengju,geLeiCount);
  3140. if(isstwk && (!isMoHu) && this.chuPaiCountTL == 0) return true;
  3141. return (this.chuPaiCountTL == 0 && !isMoHu);
  3142. };
  3143. //是否 "海胡",
  3144. proto.isHAIHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3145. return this.lastCount == 0;
  3146. };
  3147. //是否 "听胡", 这个判断方法的前提是3提五坎不能点过
  3148. proto.isTINGHU = function (shoupai,PGPai,chengju,geLeiCount,chairId) {
  3149. let tpgs = 0;//提跑个数
  3150. for (var i = 0; i < PGPai.length; i++) {
  3151. if(PGPai[i].style == STYLE.ANGANG || PGPai[i].style == STYLE.ZMGANG || PGPai[i].style == STYLE.GANG){
  3152. tpgs++;
  3153. }
  3154. }
  3155. // console.warn("===================听胡 ",chairId,tpgs,this.everyCPGS);
  3156. let cpgsyq = 0;//出牌个数要求
  3157. if(this.chBanker == chairId) cpgsyq = 1;
  3158. if(tpgs >= 2 || this.everyCPGS[chairId] > cpgsyq) return false;
  3159. return true;
  3160. };
  3161. //是否 "红胡",
  3162. proto.isHONGHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3163. if(geLeiCount[2] >= 10){
  3164. this.hongHuCount = geLeiCount[2] - 10;
  3165. return true;
  3166. }
  3167. return false;
  3168. };
  3169. //是否 "黑胡",
  3170. proto.isHEIHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3171. return geLeiCount[2] == 0;
  3172. };
  3173. //是否 "点胡",
  3174. proto.isDIANHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3175. return geLeiCount[2] == 1;
  3176. };
  3177. //是否 "大胡",
  3178. proto.isDAHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3179. if(geLeiCount[0] >= 18){
  3180. this.daHuCount = geLeiCount[0] - 18;
  3181. return true;
  3182. }
  3183. return false;
  3184. };
  3185. //是否 "小胡",
  3186. proto.isXIAOHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3187. if(geLeiCount[1] >= 16){
  3188. this.xiaoHuCount = geLeiCount[1] - 16;
  3189. return true;
  3190. }
  3191. return false;
  3192. };
  3193. //是否 "耍猴",
  3194. proto.isSHUAHOU = function (shoupai,PGPai,chengju,geLeiCount,addcard) {
  3195. // console.warn("------------------------ 耍猴 ",count ,sp,addcard,shoupai);
  3196. let sp = [];
  3197. for (var i = 0; i < shoupai.length; i++) {
  3198. sp = sp.concat(shoupai[i])
  3199. }
  3200. return sp.length == 2 && sp[0] == addcard;
  3201. };
  3202. //是否 "黄番",
  3203. proto.isHUANGFAN = function (shoupai,PGPai,chengju,geLeiCount,dengshuValue) {
  3204. // console.warn("是否 黄番 ",dengshuValue);
  3205. return this.dengshuValue > 0;
  3206. };
  3207. //是否 "对子胡",
  3208. proto.isDUIZIHU = function (shoupai,PGPai,chengju,geLeiCount) {
  3209. for (var i = 0; i < PGPai.length; i++) {
  3210. let style = PGPai[i].style;
  3211. if(style == STYLE.CHI || style == STYLE.SUN || style == STYLE.JIAO || style == STYLE.BIPAI) return false;
  3212. }
  3213. let dzgs = 0;//对子个数
  3214. for (var i = 0; i < shoupai.length; i++) {
  3215. for (var j = 0; j < shoupai[i].length; j++) {
  3216. if(shoupai[i][0] != shoupai[i][j]) return false;
  3217. }
  3218. if(shoupai[i].length == 2 && shoupai[i][0] == shoupai[i][1]) dzgs++;
  3219. }
  3220. if(dzgs > 1) return false;
  3221. return true;
  3222. };
  3223. //是否 "大团圆",
  3224. proto.isDATUANYUAN = function (shoupai,PGPai,chengju,geLeiCount) {
  3225. let gpzptgs = _.fill(Array(10), 0);//各牌值跑提个数
  3226. for (var i = 0; i < PGPai.length; i++) {
  3227. let style = PGPai[i].style;
  3228. if(style == STYLE.GANG || style == STYLE.ANGANG || style == STYLE.ZMGANG){
  3229. let pz = this.getValue(PGPai[i].card)%10;
  3230. gpzptgs[pz]++;
  3231. }
  3232. }
  3233. for (var i = 0; i < shoupai.length; i++) {
  3234. if(shoupai[i].length == 4){//起手时闲家的三提五坎
  3235. if(shoupai[i][0] == shoupai[i][1] && shoupai[i][0] == shoupai[i][2] && shoupai[i][0] == shoupai[i][3]) {
  3236. let pz = this.getValue(shoupai[i][0])%10;
  3237. gpzptgs[pz]++;
  3238. }
  3239. }
  3240. }
  3241. // return gpzptgs.indexOf(2) != -1;
  3242. // console.warn("大团圆 ",JSON.stringify(PGPai),gpzptgs);
  3243. for (var i = 0; i < gpzptgs.length; i++) {
  3244. if(gpzptgs[i] == 2) this.datuanyuanCount++;
  3245. }
  3246. return this.datuanyuanCount > 0;
  3247. };
  3248. //是否 "真行息",
  3249. proto.isZHENHANGXI = function (shoupai,PGPai,chengju,geLeiCount) {
  3250. let wxgs = 0;//无息的列的个数
  3251. for (var i = 0; i < PGPai.length; i++) {
  3252. let style = PGPai[i].style;
  3253. if(style == STYLE.GANG || style == STYLE.ANGANG || style == STYLE.ZMGANG) continue;
  3254. if(style == STYLE.PENG || style == STYLE.WEI) continue;
  3255. if(style == STYLE.CHI){
  3256. let zgcdhxInfo = this.getCPHXInfo([PGPai[i]]);//这个吃的胡息信息
  3257. if(zgcdhxInfo.hxValues[0] > 0) continue;
  3258. }
  3259. wxgs++
  3260. if(wxgs>= 2) break;
  3261. }
  3262. if(wxgs>= 2) {
  3263. this.wxdldgs = wxgs;//无息的列的个数,用来判断真假行息
  3264. return false;
  3265. }
  3266. for (var i = 0; i < shoupai.length; i++) {
  3267. let zlspcd = shoupai[i].length;
  3268. let lie = [];//第i列的牌
  3269. if(zlspcd == 3 || zlspcd == 4){
  3270. // lie = _.cloneDeep(shoupai[i]);//第i列的牌
  3271. lie = this.sort(shoupai[i]);
  3272. }
  3273. if(zlspcd <= 2){
  3274. wxgs++;
  3275. }
  3276. else if(zlspcd == 3){
  3277. if(lie[0] == lie[1] && lie[0] == lie[2]) continue;
  3278. if(this.getValue(lie[0]) == 1 && lie[0] + 1 == lie[1] && lie[0]+2 == lie[2]) continue;
  3279. if(this.getValue(lie[0]) == 2 && this.getValue(lie[1]) == 7 && this.getValue(lie[2]) == 10){
  3280. if(this.getType(lie[0]) == this.getType(lie[1]) && this.getType(lie[0]) == this.getType(lie[2])) continue;
  3281. }
  3282. wxgs++;
  3283. }
  3284. else if(zlspcd == 4){
  3285. if(lie[0] == lie[1] && lie[0] == lie[2] && lie[0] == lie[3]) continue;
  3286. wxgs++;
  3287. }
  3288. if(wxgs>= 2) break;
  3289. }
  3290. this.wxdldgs = wxgs;//无息的列的个数,用来判断真假行息
  3291. return this.wxdldgs == 0;
  3292. };
  3293. //是否 "假行息",
  3294. proto.isJIAHANGXI = function (shoupai,PGPai,chengju,geLeiCount) {
  3295. let jiang = 0;
  3296. for (var i = 0; i < shoupai.length; i++) {
  3297. if(shoupai[i].length == 2 && shoupai[i][0] == shoupai[i][1]) {
  3298. jiang = shoupai[i][0];
  3299. break;
  3300. }
  3301. }
  3302. // console.warn("是否 假行息, ",this.wxdldgs , jiang);
  3303. return this.wxdldgs == 1 && jiang;
  3304. };
  3305. //是否 "四七红",
  3306. proto.isSIQIHONG = function (shoupai,PGPai,chengju,geLeiCount) {
  3307. return geLeiCount[2] == 4 || geLeiCount[2] == 7;
  3308. };
  3309. //是否 "真背靠背",
  3310. proto.isZHENBEIKAOBEI = function (shoupai,PGPai,chengju,geLeiCount,isMoHu) {
  3311. // console.warn("真背靠背 shoupai,PGPai ",this.addThisIsHu,shoupai,PGPai);
  3312. let jiang = 0;
  3313. for (var i = 0; i < shoupai.length; i++) {
  3314. if(shoupai[i].length == 2 && shoupai[i][0] == shoupai[i][1]) {
  3315. if(jiang > 0) {
  3316. // console.error("真背靠背判断出错了 ",JSON.stringify(shoupai));
  3317. return false;
  3318. }
  3319. jiang = shoupai[i][0];
  3320. }
  3321. }
  3322. // console.warn("真背靠背 jiang ",jiang)
  3323. if(!jiang) return false;//没将
  3324. let jiangV = this.getValue(jiang);
  3325. if(jiangV != this.getValue(this.addThisIsHu)) return false;
  3326. //自摸胡的话会先偎掉所以应该用碰杠牌判断,非自摸胡才会用手牌判断
  3327. if(isMoHu){
  3328. for (var i = 0; i < PGPai.length; i++) {
  3329. if(PGPai[i].style == STYLE.WEI && PGPai[i].card == this.addThisIsHu) {
  3330. this.isBeiKaoBei = true;//是否满足背靠背,用来判断真假背靠背
  3331. }
  3332. }
  3333. }
  3334. else{
  3335. for (var i = 0; i < shoupai.length; i++) {
  3336. if(shoupai[i].length == 3 && shoupai[i][0] == shoupai[i][1] && shoupai[i][0] == shoupai[i][2]) {
  3337. if(jiangV == this.getValue(shoupai[i][0]) && this.addThisIsHu == shoupai[i][0]) {
  3338. this.isBeiKaoBei = true;//是否满足背靠背,用来判断真假背靠背
  3339. }
  3340. }
  3341. }
  3342. }
  3343. let spcount = 0;
  3344. if(this.isBeiKaoBei){
  3345. for (var i = 0; i < shoupai.length; i++) {
  3346. spcount += shoupai[i].length;
  3347. }
  3348. }
  3349. else{
  3350. return false;
  3351. }
  3352. return (isMoHu && spcount == 2)|| (!isMoHu && spcount == 5);
  3353. };
  3354. //是否 "假背靠背",
  3355. proto.isJIABEIKAOBEI = function (shoupai,PGPai,chengju,geLeiCount) {
  3356. let spcount = 0;
  3357. if(this.isBeiKaoBei){
  3358. for (var i = 0; i < shoupai.length; i++) {
  3359. spcount += shoupai[i].length;
  3360. }
  3361. }
  3362. else{
  3363. return false;
  3364. }
  3365. return spcount > 5;
  3366. };
  3367. //是否 "三提五坎",
  3368. proto.isSANTIWUKAN = function (shoupai,PGPai,chengju,geLeiCount) {
  3369. let tiKanGS = [0,0];//提和坎分别的个数
  3370. for (var i = 0; i < PGPai.length; i++) {
  3371. let style = PGPai[i].style;
  3372. if(style == STYLE.ANGANG) tiKanGS[0]++;
  3373. }
  3374. for (var i = 0; i < shoupai.length; i++) {
  3375. if(shoupai[i].length == 3){
  3376. if(shoupai[i][0] == shoupai[i][1] && shoupai[i][0] == shoupai[i][2]) tiKanGS[1]++;
  3377. }
  3378. else if(shoupai[i].length == 4){//起手时闲家的三提五坎
  3379. if(shoupai[i][0] == shoupai[i][1] && shoupai[i][0] == shoupai[i][2] && shoupai[i][0] == shoupai[i][3]) tiKanGS[0]++;
  3380. }
  3381. }
  3382. // console.warn("是否 三提五坎, ",tiKanGS,shoupai,PGPai);
  3383. return (tiKanGS[0] >= 3 || tiKanGS[1] >= 5 || (tiKanGS[0]+tiKanGS[1]) >= 5)
  3384. };
  3385. //是否 "金屋藏娇",
  3386. proto.isJINWUCANGJIAO = function (shoupai,PGPai,chengju,geLeiCount,isMoHu) {
  3387. let sffpjd = this.isFPJD();//是否发牌阶段
  3388. // console.warn("金屋藏娇 shoupai,PGPai ",sffpjd,this.addThisIsHu,isMoHu,shoupai,PGPai);
  3389. for (var i = 0; i < PGPai.length; i++) {
  3390. if(PGPai[i].style == STYLE.ANGANG && PGPai[i].card == 103) return true;
  3391. }
  3392. if(sffpjd){//发牌阶段
  3393. for (var i = 0; i < shoupai.length; i++) {
  3394. if(shoupai[i].length == 4){//起手时闲家的三提五坎
  3395. if(shoupai[i][0] == shoupai[i][1] && shoupai[i][0] == shoupai[i][2] && shoupai[i][0] == shoupai[i][3]){
  3396. if(shoupai[i][0] == 103) {
  3397. if(isMoHu) return true;
  3398. else{
  3399. if(this.addThisIsHu != 103) return true;
  3400. }
  3401. }
  3402. }
  3403. }
  3404. }
  3405. }
  3406. return false;
  3407. };
  3408. //是否 "丁", 需要满足胡牌牌型
  3409. proto.isDING = function (shoupai,PGPai,chengju,geLeiCount) {
  3410. let jiang = 0;
  3411. for (var i = 0; i < shoupai.length; i++) {
  3412. if(shoupai[i].length == 2 && shoupai[i][0] == shoupai[i][1]) {
  3413. if(jiang > 0) {
  3414. console.error("丁判断出错了 ",JSON.stringify(shoupai));
  3415. return false;
  3416. }
  3417. jiang = shoupai[i][0];
  3418. }
  3419. }
  3420. return this.addThisIsHu == jiang;
  3421. };
  3422. //是否 "捉小三",
  3423. proto.isZHUOXIAOSAN = function (shoupai,PGPai,chengju,geLeiCount,isMoHu) {
  3424. // console.warn("捉小三 ",this.addThisIsHu , isMoHu);
  3425. return (this.addThisIsHu == 103 && isMoHu);//////TL++,胡牌判断时加上这张能不能胡
  3426. };