我的網頁麻將 mj.fantasymaya.org 仿照以前的 QKMJ,電腦對手由一個規則式 AI(RuleBot)負責:算向聽、算進張、查表估計放槍危險度,再依牌風參數做攻守取捨。它已經不算弱,但我想知道:能不能做出「高手看了也覺得有挑戰」的 AI?

這篇整理最近幾天和 Claude、Codex 一起做的引擎研究。先講結論:

  • 讀牌可以做得更準:用「待牌形狀+歷程」的讀牌模型,找安全牌的誤判約減半,在四批獨立資料上都成立。
  • 但更準的讀牌沒有直接變成更多分數:放槍少了約 7%,胡牌也跟著變少,淨分與原版打平。
  • 原因可以量化:在台灣 16 張「自摸三家付」的規則下,胡一把的價值約是放一槍額外損失的 3.4 倍,防守的邊際價值天生偏低。
  • 對一般玩家,積極進攻比小心防守更賺,這正好呼應「高手吃一般玩家籌碼」的現象。
  • 最後把研究成果轉成產品:三位強者角色、四級強度分級、單機覆盤。

規矩:每一關都先寫好停損條件

麻將運氣成分很大,單看幾十將的輸贏什麼都證明不了。所以每個實驗都遵守同一套規矩:

  1. 先寫預先登記:參數、種子範圍、通過門檻,在跑之前寫進檔案,跑完不准改。
  2. 成對對局:同一副牌、座位輪換,讓「新 AI」和「原版」面對完全相同的牌運,只比決策差異。
  3. 至少 3,000 個種子,用按種子分組的信賴區間判斷;區間下界大於 0 才算通過。
  4. 沒過就停,不在同一批種子上反覆調參數。

這套規矩讓後面好幾個「看起來很有希望」的結果被證明只是雜訊,也讓真正有效的東西站得住。

第一步:把高手的讀牌經驗變成可以檢驗的模型

我翻了 PTT 上流傳很久的〈打麻將猜牌和實用技巧〉,裡面有很多經驗:「河裡絕少五、六萬,則四、七萬危險」、「先打一後打二,緊防三、六」、「跟打」等等。原本的 RuleBot 危險度表只分「中張/二八/么九/字牌 × 現物 × 筋」,完全沒看牌山剩幾張,也沒用到「放過」的資訊。

新的讀牌模型做兩件事:

  • 待牌形狀組合數:一張牌的危險度,等於「能等這張牌的所有形狀」(兩面、嵌張、邊張、對碰、單騎)在未見牌中的組合數總和。這就是高手說的「看壁」。
  • 歷程重新加權:對手自己打過的牌、以及他上次捨牌後別人打出而他沒胡的牌(放過窗),都會壓低含那張牌的形狀。

後者有一條很乾淨的性質:台灣 16 張沒有振聽,一律榮胡的牌手,放過窗裡的牌不可能是他當下的待牌。在 3.5 萬個樣本中檢查,違反次數是 0。

驗證資料(我方有危險候選的局面)危險牌 Top-3 命中選最低風險牌仍踩到待牌
預先登記的全新種子 A57.6% → 61.5%2.47% → 1.53%
全新種子 B(沒見過的牌風)60.4% → 63.6%2.92% → 1.63%

模型只有 8 個參數,全部在訓練資料上擬合一次;兩批新資料的六項指標信賴區間都不跨零。讀牌確實變準了。

第二步:讀牌變準,為什麼沒有變強?

把讀牌模型接進 AI 的出牌決策,打了三組各 3,000 種子的成對對局:

版本淨分差(分/座位/將)95% 區間放槍率胡牌率
只修正舊表的小錯誤−0.7−3.6~+2.1不變不變
換上讀牌模型−3.2−11.4~+4.916.8% → 15.8%24.8% → 24.2%
讀牌排序+原本的防守總量−1.5−9.8~+6.816.8% → 15.5%24.6% → 24.2%

放槍少了約 7%,胡牌也少了,淨分打平。後來又試了「用讀牌結果改進晚局搜尋」、「對莊家連莊加強防守」,也都沒過。

於是我回頭做損失分析:自我對局 2,581 將、66,501 局,統計每次放槍到底賠多少、能不能事先看出來。再用 2,400 將、265 萬個出牌決策,統計「這樣出牌之後最後會胡牌的機率」:

項目閒家莊家
胡一把的價值(相對「不胡也不放槍」)約 315 分約 377 分(連莊 4 次以上約 700)
放一槍的額外損失約 92 分約 111 分

放槍的額外損失看起來很小,是因為就算不放槍,平均也會被自摸或流局分攤掉約 55 分。胡一把大約抵三次多的放槍,這就是「自摸三家付」的規則下,防守邊際價值偏低的原因。多躲的那幾槍,本來就不太值錢。

另一個發現:在 AI 自己打的世界裡,6 台以上的大牌只佔 0.2%,真正會讓人大賠的幾乎都是莊家連莊。這和真人對局很不一樣——真人會做清一色、大三元,所以自我對打會低估防守的價值。

第三步:推收期望值,以及「一強三弱」的尺規

既然瓶頸是「推還是收」,就把 RuleBot 固定的攻守換算,改成期望值:

推的價值 = (1 − 放槍率) × 和率 × 胡牌價值 − 預期放槍損失

主判定仍然沒過(對一般對手 −13.6,區間約 −32~+5),但出現了清楚的方向:

  • 對強對手:讀牌加推收能少放 14% 的槍,淨分持平,是「穩」的打法。
  • 對一般對手:同樣的保守化顯著虧錢(−39,區間 −65~−14)。

這時我提出一個想法:全是高手的桌子看不出差異,應該用「一個強 AI 對三個一般 AI」來量。先驗證這把尺有用:零失誤的 AI 對上犯錯率 10% 的同一個 AI,每座位每將多贏 +235 分,差距非常明顯。「少犯錯」本身就是最大的優勢,頂尖高手的戰績很可能就是這樣從一般玩家身上累積出來的。

探索性試驗也顯示,把防守權重減半,對一般對手反而更賺。對弱者應該更積極,而不是更小心。

第四步:從研究到產品——強者、分級與覆盤

研究沒有做出「全面碾壓」的新 AI,但留下了三種不比基準弱、打法又明顯不同的強者:

角色打法對原版(強池,4 圈,1,000 種子)特色
董仔魔王:坐莊狂推、看比分收放+17(約 −5~+40)原本就有的尾牙大魔王
刀仔(新)快刀:防守減半、敢推+48(+28~+69);全新種子確認 +18(−4.5~+39)胡得快、放槍略多
算盤姨(新)鐵壁:讀牌+推收期望值+14.5(−19.6~+47.6)放槍率最低(約 14.6%)

另外還有一位隱藏角色阿妙,專聽你以為安全的牌。

所有角色也都實測了強度,結果發現原本的難度標示和實際牌力對不上:金枝婆婆、林老師、阿財伯標「普通」,其實比標「輕鬆」的新手還弱,因為他們的個性(極度保守、堅持門清、只拚大牌)本身就吃虧。最後定案為四級:

分級角色
強者董仔、刀仔、算盤姨(隱藏:阿妙)
高手志明、阿雄、春嬌姐
輕鬆小芸、Emma、Kenji、Lucas
個性派金枝婆婆、林老師、阿財伯

想燒腦的可以開「三強切磋桌」,想打快樂麻將的選一般角色。因為大家通常只打一兩局就離開,排行也改成以「局」即時結算的單人籌碼。

最後一塊是單機覆盤:一局結束後,引擎用算盤姨的打法逐手對照你的決定,標出「小失誤/大失誤」並估計約損失多少分期望值,也會說明理由,例如「這張在下家上次打牌後已被放過,對他是安全牌」。放槍時還會分辨是「運氣不好」(你打的已經是最安全的牌之一),還是「可以避免」;聽牌沒胡時,告訴你聽的牌還剩幾張在牌山、幾張在誰手上。

這次學到的事

  1. 準確不等於有用。 讀牌模型在每一項離線指標上都贏,但決策的價值取決於計分規則;在台灣 16 張,放槍的真實成本比直覺小得多。
  2. 量尺要選對。 強 AI 互打時差距被運氣淹沒,「一強三弱」才看得出牌力,也更接近真實玩家的處境。
  3. 預先登記救了我好幾次。 刀仔第一次測出 +48、顯著勝出,我差點就要把它改成所有 AI 的預設;用全新種子再測只剩 +18、不顯著。沒有這條規矩,就會把運氣當成進步。
  4. 沒過的實驗也有價值。 失敗的讀牌接入,換來了損失模型;失敗的推收模組,換來了「對弱者要積極」的方向,以及現在的三強角色與覆盤。

下一步想做的,是在網站上保存單機牌局紀錄(去除個資),累積真人資料後,讓讀牌從「讀 AI」變成「讀人」,也用真人的勝率來驗證強者到底夠不夠強。

完整的研究紀錄、原始數據與預先登記,都保存在遊戲專案的研究資料夾裡。有興趣的話,歡迎到 mj.fantasymaya.org 找算盤姨切磋。