九、鬥地主 癩子演算法
癩子演算法是鬥地主里大家都比較關心的,如何實現一個正確的癩子演算法呢?
我們在實現山西運城鬥地主時,裡面有一個花牌的概念,可以用作萬能牌,也就是癩子;在實現摜蛋遊戲時,當前等級的紅桃牌也可以當作任意牌,也是癩子。
現在加入癩子,那麼在手牌掃描時,遇到癩子,只統計個數,別的牌照舊處理;在計算手牌的key, powerValue, scanTable時,都需要標明是否處理癩子;還是不匹配,按本身的值處理,比如摜蛋里打2時,紅桃2就是一個2,而不會匹配到3組成炸彈。我們需要這個標誌,避免多次重複展開癩子。
修改後,各函數為:
// 獲得正確的keystd::string LordCards::getKey(bool checkFlower, int &leastValue, int &maxCount) { if (checkFlower) { // 計算帶癩子的key ... } else { // 不計算癩子的key ... } return key;}// 掃描手牌,記錄癩子個數int LordCards::scanToTable(bool checkFlower){ int countAny = 0; ... for (int i=0; i<theCards.size(); i++) { int card = theCards[i]; if (checkFlower && card為癩子) { ++countAny; continue; } ... } return countAny;}// 展開癩子, 剩餘癩子個數,從哪個數值開始展開// 如果多個癩子,肯定是 1 < x < y < 13, 所以後面的癩子肯定比之前的大,避免重複計算OneHand LordCards::calcPowerValue_expandAny(int count, int cardIndex) { if (count <= 0) { // 癩子展開完畢,照舊計算權重 return calcPowerValue_noFlower(); } OneHand bestHand; std::vector<int> old = theCards; // 保存當前手牌 for (int card = cardIndex; card < kCard_ValueA; ++card) { // 展開癩子 int one = getCardExpand(card); // 加入手牌 theCards.push_back(one); OneHand leftBest = calcPowerValue_expandAny(count - 1, card); if (bestHand.totalPower < leftBest.totalPower) { // 更新權重 } theCards = old; } theCards = old; restoreAnyMatch(bestHand.bestNode.cards); return bestHand;}// 計算權重 不帶癩子OneHand calcPowerValue_noFlower() { // 照舊計算權重}// 計算權重OneHand calcPowerValue(bool checkFlower) // 是否處理花牌/癩子{ int countAny = scanToTable(); // 去掉癩子 removeAnyCardsInHand(); OneHand exp = calcPowerValue_expandAny(countAny, 1); if (exp.totalPower > hand.totalPower) { // 更新權重 } // 返回權重}
計算權重時,按上述原則修改完畢。在獲得出牌列表時,也是類似原則,把癩子從頭到尾遍歷一次,獲得所有可以打出的牌,再去重。由於我們計算權重,展開癩子,獲得出牌列表時的效率都很高,遍歷一遍也沒有什麼特別大的負擔,幾乎沒有什麼遲鈍。
癩子就是這麼簡單。
推薦閱讀: