• 沒有找到結果。

第三章 MJTALK 程式實作與 MAHJONGDAXIA 的演算法探討

3.3 麻將平台程式 MJT ALK 所需功能及實作

1.起莊:

i.隨機產生 1-4 的亂數,設定莊家及閒家的位置後,再隨機產生 3~18 的亂數用來模擬莊家擲骰子取牌 動作,並依東南西北順時針的順序去數,最後依莊家位置去計算開門位置。

EX:假設隨機亂數取得 1,表示 Program1 為莊家其他 Program 為閒家,記錄莊家 Program 編號,以 便之後發牌時莊家的 Program 手牌多發一張,之後亂數取得 15(骰子骰 15),則 15 數到 Program3,表 示從 Program3 的位置開始拿牌,拿完牌後取得開門位置。如下圖 3-3。

圖 3-3 起莊 2.發牌

i. 依 照 每 張 牌 的 編 號 , 隨 機 打 亂 順 序 排 好 , 共 136 張 牌 。 一 次 16 張 牌 的 方 式 , 分 別 傳 給 Program1~Program4,莊家多發一張,並儲存各個程式的手牌,以便後面判斷吃碰槓胡的情況。

ii.利用指標記錄下一張要發的牌的位置。

3.丟牌

i.傳送丟牌指令給莊家,並取得 Program 丟牌後,傳送給所有 Program。

EX:莊家為 Program1,傳送/ask throw 給 Program1,取得/throw 330,傳送/throw 1 330 指令給所有 Program。

ii.尋問是否對所丟的牌依序做胡、槓、碰、吃等動作,如有則進入胡、槓、碰、吃等動作函式。

4.摸牌

i.將指標所指的牌發給下一個玩家,並更新手牌。

ii.發送丟牌指令,接收 Program 回傳的丟牌指令,知道丟哪張牌後,更新玩家手牌。

iii.發送丟牌資訊給其他玩家。

EX:指標目前所指為 413,傳送/mo 413 指令給 Program1,並更新 Program1 的手牌,發送/ask throw 指令給 Program2,取得/throw 440,更新玩家手牌後,發送丟牌資訊給其他 Program。如下圖 3-4。

圖 3-4 摸牌 5.吃牌/pass

i.依照接收到的丟牌資訊,去判斷下家是否能吃牌。

判斷方法:依照丟的牌去搜尋下家手牌是否有此張牌+(-)2、+(-)1 的牌。

EX:丟 3 萬,搜尋下家手牌是否有 1 萬、2 萬或者 4 萬、5 萬或者 2 萬、4 萬。如下圖 3-5。

圖 3-5 吃牌 i

ii.如可以吃牌,發送吃牌指令給 Program,接收 Program 回傳的指令,如吃牌則更新手牌,並將吃牌 的資訊發送給所有 Program 後存入吃牌陣列內,以便後續作胡牌的判斷。

EX:假設 Program1 丟牌(/throw 131),發送/ask eat 指令給 Program2,取得/eat 140 150,更新 Program2 手牌,並將/eat 2 140 131 150 指令傳給所有 Program,將吃牌資訊 131 140 150 存入吃牌陣列。如圖 3-6。

圖 3-6 吃牌 ii iii.如果回傳 pass 則不動作。

6.碰牌/pass

i.依照接收到的丟牌資訊,去判斷其他三家是否能碰牌。

判斷方法:依照丟的牌去搜尋各家手牌是否有同張牌 2 張以上。

EX:丟一萬,搜尋三家手牌,哪家有 2 張以上的一萬。如圖 3-7。

圖 3-7 碰牌 i

ii.如可以碰牌,則發送碰牌指令給那家 Program,接收 Program 回傳的指令,如碰牌則更新手牌,並 將碰牌的資訊發送給所有 Program 後存入碰牌陣列內,以便後續作胡牌的判斷。

EX:假設 Program1 丟牌(/throw 131),發送/ask pong 指令給 Program2,取得/pong 130 132,更新 Program2 手牌,並將/pong 2 130 131 132 指令傳給所有 Program,將碰牌資訊 130 131 132 存入碰牌陣列。如圖 3-8。

圖 3-8 碰牌 ii iii.如果回傳 pass 則不動作。

7.槓牌/pass 暗槓:

i.Program 依照接收到的摸牌資訊,去判斷是否能暗槓牌。

判斷方法:Program 摸牌時判斷目前 Program 手牌是否有同樣牌 3 張。

EX:摸北風,搜尋自己手牌是否有 3 張北風。

ii.如果有則傳送暗槓牌指令,平台接收 Program 回傳的指令,如暗槓牌則更新手牌並將暗槓牌的資訊 發送給所有 Program 後,存入槓牌陣列內,以便後續做胡牌的判斷。

EX:Program1 摸牌(/mo 110),發送/ask gong 指令給 Program1,取得/gong 0 110 111 112 113 指令,更 新 Program1 手牌,並將/gong 1 440 441 442 443 指令傳給 Program1,將/gong 0 指令傳給其它 Program 後,將槓牌資訊 110 111 112 113 存入槓牌陣列內,以便後續作胡牌的判斷。如圖 3-9。

圖 3-9 暗槓

程式丟牌,而 Program1 程式傳送/throw 412 指令告知平台丟出牌 412。

iv.如果回傳 pass 則不動作。

明槓:

i.依照接收到的丟牌資訊,去判斷其他兩家能否槓牌(規則:不可槓上家的牌)。

判斷方法:依照丟的牌去搜尋另外兩家手牌是否有同張牌 3 張。

EX:丟 1 萬,搜尋其他家手牌是否有 3 張 1 萬。如圖 3-10。

圖 3-10 明槓 i

ii.如有則發送槓牌指令,接收 Program 回傳的指令,如槓牌則更新手牌並將槓牌的資訊發送給所有 Program 後存入槓牌陣列內,以便後續做胡牌的判斷。

EX:Program1 丟 111,發現 Program4 可以槓,發送/ask gong 給 Program4,取得/gong 4 110 112 113 指令,更新 Program4 手牌,並將/gong 4 110 111 112 113 指令傳給所有 Program 後,將槓牌資訊 110 111 112 113 存入槓牌陣列內,以便後續作胡牌的判斷。如圖 3-11。

圖 3-11 明槓 ii

iii.明槓後平台傳送/mo 410 指令告知 Program3 程式摸到牌 410,再傳送/ask throw 指令要求 Program3 程式丟牌,而 Program3 程式傳送/throw 410 指令告知平台丟出牌 410。

iv.如果回傳 pass 則不動作。

補槓:

依照接收到的摸牌資訊,去判斷是否能補槓牌。

判斷方法:Program 摸牌時判斷 Program 的碰牌陣列是否有同樣的牌。

EX:摸 112,搜尋碰牌陣列內是否有 110 111 113。

如果有則傳送補槓牌指令,接收 Program 回傳的指令,如補槓牌則更新手牌並將補槓牌的資訊發送給 所有 Program 後存入槓牌陣列內後將碰牌陣列內的值刪除,以便後續做胡牌的判斷。

EX:Program1 摸牌(/mo 112),發送/ask gong 指令給 Program1,取得/gong 1 112 指令,更新 Program1 手牌,並將/gong 1 112 指令傳給所有 Program,將槓牌資訊 110 111 112 113 存入槓牌陣列內並將碰牌 陣列內的 110 111 113 刪除,以便後續作胡牌的判斷。如圖 3-12。

圖 3-12 補槓

iii.槓牌後平台傳送/mo 220 指令告知 Program1 程式摸到牌 220,再傳送/ask throw 指令要求 Program1 程式丟牌,而 Program1 程式傳送/throw 450 指令告知平台丟出牌 450。

iv.如果回傳 pass 則不動作。

8.胡牌

胡牌判斷定理:以下引用 2017 年,威廉斯堡大學數學系的李志光教授的判斷胡牌演算法(參考打麻將 的數學冷知識(二):如何一眼就知道胡牌了沒 https://www.thenewslens.com/article/100657)。

一副牌 P,若把一個對子(俗稱眼睛)拿掉後,假設此時數字最小的牌是 x,

算出,再計算吃、碰、槓陣列內的順、刻組數,最後相加看是否總數為 5 組順、刻配 1 組眼,如達成 條件則表示胡牌。

EX:Program1 丟 9 筒,Program3 的手牌為 78 筒,11 萬,加入 9 筒後,將眼睛拿掉後(1 組眼),計算 出手牌有 1 組順,吃牌陣列內存(456 條、345 筒)2 組順,碰牌陣列內存(東東東)1 組刻,槓牌陣列內 存(9999 萬)1 組刻,最後相加滿足胡牌條件總數為 5 組順、刻配 1 組眼,則發送胡牌指令/ask hu 給 Program3 尋問是否胡牌,如回傳胡牌指令,則傳送胡牌資訊給所有 Program,程式結束。如圖 3-13。

圖 3-13 胡牌

EX:平台廣播/hu 3 390 370 380 111 112 指令告知四個 Program,Program3 胡了 390 這張牌,且 Program3 程式未現出來的牌為 111、112、370、380。

ii.自摸:發牌給 Program 時,判斷其是否有達成胡牌條件(5 組順、刻配 1 組眼),如有則傳送胡牌指 令給此 Program,程式結束。

EX:Program1 摸 330,Program1 的手牌為 110 111 340 350,加入 330 後,計算出手牌有 1 組順、1 組眼,吃牌陣列內存(240 250 260(456 條)、130 140 150(345 萬))2 組順,碰牌陣列內存(180 181 182(888 萬))1 組刻,槓牌陣列內存(190 191 192 193 (9999 萬))1 組刻,最後相加滿足胡牌條件總數為 5 組順、

刻配 1 組眼,則發送胡牌指令/ask hu 給 Program1 尋問是否胡牌,如回傳胡牌指令,則傳送胡牌資訊 給所有 Program,程式結束。如圖 3-14 自摸。

圖 3-14 自摸

iii.槓上自摸

暗槓、補槓後摸的牌去判斷是否自摸,如果自摸則台數+1。

IV.一炮三響

胡牌有優先順序下家>對家>上家,所以胡牌時時會判斷誰家先胡,如果三家都胡則都算。

9.記錄 Log 檔

會將整局的程式執行過程中的指令存入 Log 內,如圖 3-15。

圖 3-15 記錄 Log 檔 10.特殊牌型計算及聽牌數計算

特殊牌型計算:

i.計算胡牌時所有牌的張數。

EX:123456789 萬中中中發發發白白。9 張萬子 3 張紅中 3 張發財 2 張白板。

ii.再由張數去判斷各種牌型。

EX:只有萬子跟字牌達成混一色條件,由中發白組成的兩刻一眼達成小三元條件。

iii.最後為依照達成的牌行計算所得台數。

EX:混一色台數+4,小三元台數+4。如圖 3-16。

圖 3-16 特殊牌型計算 IV.全求、海底撈月、地聽判斷

全求:胡牌時計算手牌數,如果手牌數為,1 則達成全求條件,台數+2。

海底撈月:自摸時確認摸牌的指標,如果指標為最後一張牌,則達成海底撈月條件,台數+1。

地聽:正常規則下,地聽後就只能執行摸打動作,無法改變手牌,但麻將程式會根據最佳決策去改變 手牌,目前平台方面無法解決這個問題,因此沒有地聽判斷。

聽牌數計算:

由於聽牌數為 1 時(獨聽),台數會加 1,因此需計算出聽牌張數。

i.將所有牌依序加入手牌後判斷是否胡牌。

ii.如胡牌則將加入的牌存入聽牌陣列。

iii.聽牌陣列內個數為一時表示獨聽台數+1,不是獨聽台數不變。

11.得分計算

i.設定底 1000 分、台 500 分。

ii.會先將胡牌台數算出後,再依底 1000 分、一台 500 分的公式算出當局得分,再將各局得分跟總分 情況寫入 Log 檔,如圖 3-17。

EX:當局胡牌為平胡,台數為 2,當局得分為 1000+500×2 = 2000 分。

圖 3-17 得分 Log 檔 11.防呆機制

i.當 Program 回傳的指令有誤時,啟動此機制,將 Program 剔除平台代打,當輪到此 Program 動作時,

就不作任何吃、碰、槓、胡動作判斷,只執行摸打動作。

判斷方法:當 Program 有執行吃、碰、槓等動作時,檢查回傳的指令是否有錯誤,有錯誤則記錄此 Program 編號後,關閉連線。當下一回合輪到此 Program 時,只作摸打動作,直到此局結束。當下局 開始重新連線此 Program,此時 Program 又可以開始正常運行。

EX:當 Program2 丟 110,平台檢測到 Program3 可以吃後,發出/ask eat 指令後,接收回來的指令卻 是/eat 120 310,指令有誤,則不執行更新手牌與發送吃牌指令給其他 Program 的動作,直接跳入 mo 牌函式,執行摸打動作,直到此局結束。如圖 3-18。

圖 3-18 防呆啟動,執行摸打動作

3.4 比賽局數期望值計算

由於麻將遊戲的運氣成分蠻重的,有可能在棋力相差不大的情況下,最後幾把被翻盤,因此究竟 該設多少局為比賽基準,將運氣成分最小化也是一個課題。經由以下實驗可以看出來當比賽至 100 局時棋力較弱的已經平衡,分數變動不大如下圖 3-19。

圖 3-19 100 局的程式平均得分曲線 至 200 局時棋力較強的還是有小幅變動,如下圖 3-20。

圖 3-20 200 局的程式平均得分曲線 當至 300 局時 4 隻程式都已達到平衡,分數變動基本固定如下圖 3-21。

圖 3-21 300 局的程式平均得分曲線 由此可見最佳的比賽盤數應設為 250 至 300 為妥。

相關文件