我的網頁麻將 mj.fantasymaya.org 仿照以前的 QKMJ,電腦對手由一個規則式 AI(RuleBot)負責:算向聽、算進張、查表估計放槍危險度,再依牌風參數做攻守取捨。它已經不算弱,但我想知道:能不能做出「高手看了也覺得有挑戰」的 AI?
這篇整理最近幾天和 Claude、Codex 一起做的引擎研究。先講結論:
- 讀牌可以做得更準:用「待牌形狀+歷程」的讀牌模型,找安全牌的誤判約減半,在四批獨立資料上都成立。
- 但更準的讀牌沒有直接變成更多分數:放槍少了約 7%,胡牌也跟著變少,淨分與原版打平。
- 原因可以量化:在台灣 16 張「自摸三家付」的規則下,胡一把的價值約是放一槍額外損失的 3.4 倍,防守的邊際價值天生偏低。
- 對一般玩家,積極進攻比小心防守更賺,這正好呼應「高手吃一般玩家籌碼」的現象。
- 最後把研究成果轉成產品:三位強者角色、四級強度分級、單機覆盤。
規矩:每一關都先寫好停損條件
麻將運氣成分很大,單看幾十將的輸贏什麼都證明不了。所以每個實驗都遵守同一套規矩:
- 先寫預先登記:參數、種子範圍、通過門檻,在跑之前寫進檔案,跑完不准改。
- 成對對局:同一副牌、座位輪換,讓「新 AI」和「原版」面對完全相同的牌運,只比決策差異。
- 至少 3,000 個種子,用按種子分組的信賴區間判斷;區間下界大於 0 才算通過。
- 沒過就停,不在同一批種子上反覆調參數。
這套規矩讓後面好幾個「看起來很有希望」的結果被證明只是雜訊,也讓真正有效的東西站得住。
第一步:把高手的讀牌經驗變成可以檢驗的模型
我翻了 PTT 上流傳很久的〈打麻將猜牌和實用技巧〉,裡面有很多經驗:「河裡絕少五、六萬,則四、七萬危險」、「先打一後打二,緊防三、六」、「跟打」等等。原本的 RuleBot 危險度表只分「中張/二八/么九/字牌 × 現物 × 筋」,完全沒看牌山剩幾張,也沒用到「放過」的資訊。
新的讀牌模型做兩件事:
- 待牌形狀組合數:一張牌的危險度,等於「能等這張牌的所有形狀」(兩面、嵌張、邊張、對碰、單騎)在未見牌中的組合數總和。這就是高手說的「看壁」。
- 歷程重新加權:對手自己打過的牌、以及他上次捨牌後別人打出而他沒胡的牌(放過窗),都會壓低含那張牌的形狀。
後者有一條很乾淨的性質:台灣 16 張沒有振聽,一律榮胡的牌手,放過窗裡的牌不可能是他當下的待牌。在 3.5 萬個樣本中檢查,違反次數是 0。
| 驗證資料(我方有危險候選的局面) | 危險牌 Top-3 命中 | 選最低風險牌仍踩到待牌 |
|---|---|---|
| 預先登記的全新種子 A | 57.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.9 | 16.8% → 15.8% | 24.8% → 24.2% |
| 讀牌排序+原本的防守總量 | −1.5 | −9.8~+6.8 | 16.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 |
| 個性派 | 金枝婆婆、林老師、阿財伯 |
想燒腦的可以開「三強切磋桌」,想打快樂麻將的選一般角色。因為大家通常只打一兩局就離開,排行也改成以「局」即時結算的單人籌碼。
最後一塊是單機覆盤:一局結束後,引擎用算盤姨的打法逐手對照你的決定,標出「小失誤/大失誤」並估計約損失多少分期望值,也會說明理由,例如「這張在下家上次打牌後已被放過,對他是安全牌」。放槍時還會分辨是「運氣不好」(你打的已經是最安全的牌之一),還是「可以避免」;聽牌沒胡時,告訴你聽的牌還剩幾張在牌山、幾張在誰手上。
這次學到的事
- 準確不等於有用。 讀牌模型在每一項離線指標上都贏,但決策的價值取決於計分規則;在台灣 16 張,放槍的真實成本比直覺小得多。
- 量尺要選對。 強 AI 互打時差距被運氣淹沒,「一強三弱」才看得出牌力,也更接近真實玩家的處境。
- 預先登記救了我好幾次。 刀仔第一次測出 +48、顯著勝出,我差點就要把它改成所有 AI 的預設;用全新種子再測只剩 +18、不顯著。沒有這條規矩,就會把運氣當成進步。
- 沒過的實驗也有價值。 失敗的讀牌接入,換來了損失模型;失敗的推收模組,換來了「對弱者要積極」的方向,以及現在的三強角色與覆盤。
下一步想做的,是在網站上保存單機牌局紀錄(去除個資),累積真人資料後,讓讀牌從「讀 AI」變成「讀人」,也用真人的勝率來驗證強者到底夠不夠強。
完整的研究紀錄、原始數據與預先登記,都保存在遊戲專案的研究資料夾裡。有興趣的話,歡迎到 mj.fantasymaya.org 找算盤姨切磋。