node.js——麻將演算法(七)簡易版麻將出牌AI2.0

duanhao發表於2021-09-09

*文字為上一篇部落格http://blog.csdn.net/sm9sun/article/details/77898734的部分追加最佳化


上一篇部落格已經實現了基本的出牌邏輯,大部分情況能夠給出正確的策略選擇,但經過了一些測試,仍發現了幾個嚴重的問題:

問題一:當手牌無閒牌時,偶爾會將完整的一組牌拆開打出。例如:二萬、四萬、七萬、八萬、三筒、五筒、一條、二條、三條、九條、九條

可能會打出一條。

發生該問題的原因是當計算needhun時,打出任意一張牌返回的結果是一樣的(打二萬和一條所需要的混牌數都是一樣)。並且三張牌組又有一萬(19優先策略)。故兩張牌組戰勝了三張牌組。其實這種問題只是不僅僅是兩牌和三牌的關係,更能延伸出當手上沒有單一的閒牌且needhun值相等時,當面對拆牌的情況,AI該如何抉擇。按照我們大部分打牌的邏輯,如果該牌和手上其他的牌可以關聯,那麼我們會盡量的先留著,因為這種牌擴充套件性極強。所以在needhun值相等的情況下,我們還要參考下當前手牌有多少張牌和這張牌有關係,這樣不但可以解決上述的兩張牌組戰勝了三張牌組的問題,也可以優先時牌變的緊湊。因為緊湊的牌型容易演變出聽口多的結構。


解決方案:將原有的is_nexus方法(判斷是否為單一)改成計算該牌和手牌有關係的總數

//返回單排和手牌有關係的個數exports.has_nexus = function (i, arr) {if (i > 26) {return arr[i];}else if (i % 9 == 8) {return arr[i] + arr[i - 1] + arr[i - 2];}else if (i % 9 == 7) {return arr[i] + arr[i - 1] + arr[i - 2] + arr[i + 1];}else if (i % 9 == 0) {return arr[i] + arr[i + 1] + arr[i + 2];}else if (i % 9 == 1) {return arr[i] + arr[i + 1] + arr[i + 2] + arr[i - 1];}else {return arr[i] + arr[i + 1] + arr[i + 2] + arr[i - 1] + arr[i - 2];}}


當needhun數相等時,可以優先挑出關係數最少的打出


else if (needhun == ret_needhun){if (nexus  26)//風牌優先打{ret_pai = list[k];}if ((list[k] % 9  7) && ret_pai 


由於這種策略是在保證needhun相等情況才可以的,所以整個函式判斷結構由原來的先判斷是否單一再判斷所需賴子數改成先判斷所需賴子數再考慮關係數。可見後續完整程式碼。




問題二:只考慮聽牌數並非最佳策略甚至造成死聽

這個問題上篇部落格已經說了,當可以聽牌時我們可以選擇聽口或者剩餘牌多等不同的策略,若選擇聽口多則會造成報個死聽的可能。

故新增選擇聽牌剩餘牌最多的邏輯:

exports.GetTingPaiCount = function(Tinglist, holds, game_RemainMap){var RemainMap = [];if (game_RemainMap == null)//若引數為空,即無視出牌情況只考慮自身手牌,預設都為4個計算{for (var i = 0; i 



至於維護這個RemainMap陣列也很簡單,遊戲開始時為4,出牌時-1,碰牌時-2,吃牌時內兩張牌分別-1,槓牌置0即可。

將原來的Tinglist比對改為TingPaiCount比對即可


var TingPaiCount = exports.GetTingPaiCount(Tinglist, list, RemainMap);if (TingPaiCount > 0)//至少有得胡 如果胡的牌都沒了就換牌吧{//聽牌數比對,也可以按其他方式比對,比如所聽的牌接下來的剩餘牌if (ret_tingpaicount 





上個版本的一些BUG(前篇部落格已經改正):

1.偶爾出現第一張牌總是優先打出的BUG

一開始發現這個BUG時我是懵逼的,經過了好久才找到問題。其實這是一個語法上的BUG,之前呼叫get_needhun_for_hu時為了把賴子先摘出來,將其置為了0,由於JS陣列傳遞是引用,導致了後面按賴子為0算了,這樣當手牌有賴子時,後面的返回結果都大於正確值。

解決方法:運算結束後還原陣列,或用新陣列。


2.出牌函式迴圈計算ret_needhun不正確

初始化最大值過小,我們計算needhun時一張廢牌是會需要2張混牌的 故0xf(16)在極端的情況下並不滿足最大值(東南西北中發白各一個就是14張了)。

解決方法:初始化最大值0x1a(26)


修改後的出牌方法完整程式碼:


exports.GetRobotChupai = function (list, special, hun, RemainMap) {if (hun == null) {hun = -1;}var arr = [];var Tingobj = [];for (var i = 0; i  0)//至少有得胡 如果胡的牌都沒了就換牌吧{//聽牌數比對,也可以按其他方式比對,比如所聽的牌接下來的剩餘牌if (ret_tingpaicount  26)//風牌優先打                    {                        ret_pai = list[k];                    }                    else if (list[k] % 9  7)//邊牌優先打                    {                        ret_pai = list[k];                    }                    else if (arr[list[k] + 1] == 0 && arr[list[k] - 1]==0)//主要針對夾,優先拆                    {                        ret_pai = list[k];                    }}}}arr[list[k]]++;}return ret_pai;}





圖片描述



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1343/viewspace-2804654/,如需轉載,請註明出處,否則將追究法律責任。

相關文章