亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

利用Python破解斗地主殘局詳解

 更新時間:2017年06月30日 14:17:25   作者:Tim  
斗地主應該對大家來說都不陌生,下面這篇文章主要跟大家分享了關(guān)于利用Python破解斗地主殘局的相關(guān)資料,文中介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。

前言

相信大家都玩過斗地主,規(guī)則就不再介紹了。

直接上一張朋友圈看到的殘局圖:

這道題我剛看到時,曾嘗試用手工來破解,每次都以為找到了農(nóng)民的必勝策略時,最后都發(fā)現(xiàn)其實農(nóng)民跑不掉。由于手工破解無法窮盡所有可能性,所以這道題究竟農(nóng)民有沒有妙手跑掉呢,只能通過代碼來幫助我們運算了。

本文將簡要講述怎么通過代碼來求解此類問題,在最后會公布殘局的最后結(jié)果,并開源代碼以供大家吐槽。

minimax

代碼的核心思想是minimax。minimax可以拆解為兩部分,mini和max,分別是最小和最大的意思。

直觀的理解是什么呢?就有點像A、B兩個人下棋。A現(xiàn)在可以在N個點走棋,假設A在某個點走棋了,使得A的這一步的盤面評估分數(shù)最高;但是輪到B下的時候,就一定會朝著讓A最不利的方向走,使得A的下一步必然按照B設定的軌跡來,而沒法達到A在第一步時估算到這一步的最高盤面評分。

在牌局中是一樣的,如果農(nóng)民的一手牌,讓地主無論如何應對都不能贏的話,那么可以說農(nóng)民有必勝策略;否則,農(nóng)民必輸。

核心邏輯

我們可以用一個函數(shù)hand_out來模擬一個人的出牌過程。在現(xiàn)實生活中,一個人想要出牌的話,必然需要知道自己手上的所有牌:me_pokers,也需要知道上一手的出的牌:last_hand。如果我們要用這個函數(shù)來模擬兩個人的出牌,則還需要知道對手當前的所有牌:enemy_pokers。

這個函數(shù)的返回值,是輪到我me_pokers出牌時,是否能夠必贏牌。如果能贏則返回真,否則返回假。

def hand_out(me_pokers, enemy_pokers, last_hand)

假設輪到我出牌時,如果我手上的牌都出完了,那么我將立刻知道我贏了;反之如果對手的牌都出完了,而我沒有,則我失敗了。

if not me_pokers:
 return True
if not enemy_pokers:
 return False

因為現(xiàn)在輪到我出牌,所以我首先需要知道我現(xiàn)在能出的所有手牌組合。注意:這個組合中,包括過牌(即不出牌)的策略。

all_hands = get_all_hands(me_pokers)

現(xiàn)在我們要對所有可能的手牌組合進行遍歷。

首先我需要知道,上一手對方出的牌是什么。

  • 如果對方上一手選擇過牌,或者沒有上一手牌,那么我這一輪必須不能過牌,但是我可以出任意的牌
  • 如果對手上一手出了牌,則我必須要出一個比它更大的牌或者選擇這一輪直接過牌(不出牌)

關(guān)鍵點來了,在出完我的牌或選擇過牌后,我們需要用一個遞歸調(diào)用來模擬對手下一步的行為。如果對手的下一次出牌不能獲勝的話,則我這一次的出牌必勝;否則,對于我的每一個出牌選擇,對手都能獲勝的話,則我必敗。

全部代碼如下:

def hand_out(me_pokers, enemy_pokers, last_hand, cache):
 if not me_pokers:
  # 我全部過牌,直接獲勝
  return True
 if not enemy_pokers:
  # 對手全部過牌,我失敗
  return False
 # 獲取我當前可以出的所有手牌組合,包括過牌
 all_hands = get_all_hands(me_pokers)
 # 遍歷我的所有出牌組合,進行模擬出牌
 for hand in all_hands:
  # 如果上一輪對手出了牌,則這一輪我必須要出比對手更大的牌 或者 對手上一輪選擇過牌,那么我只需出任意牌,但是不能過牌
  if (last_hand and can_comb2_beat_comb1(last_hand, hand)) or (not last_hand and hand['type'] != COMB_TYPE.PASS):
   # 模擬對手出牌,如果對手不能取勝,則我必勝
   if not hand_out(enemy_pokers, make_hand(me_pokers, hand), hand, cache):
    return True
  # 如果上一輪對手出了牌,但我這一輪選擇過牌
  elif last_hand and hand['type'] == COMB_TYPE.PASS:
   # 模擬對手出牌,如果對手不能取勝,則我必勝
   if not hand_out(enemy_pokers, me_pokers, None, cache):
    return True
 # 如果之前的所有出牌組合均不能必勝,則我必敗
 return False

構(gòu)建

以上核心邏輯理清楚后,構(gòu)建破解器將變得十分簡單。

首先,我們要用數(shù)字來表示牌的大小,這里我們用3表示3,11來表示J,12表示Q,依次類推……

其次,我們需要求出一個手牌的所有出牌組合,這里需要get_all_hands函數(shù),具體實現(xiàn)比較繁瑣但是很簡單,就不在此贅述。

然后,我們還需要一個牌力判斷函數(shù)can_comb2_beat_comb1(comb1, comb2) ,這個函數(shù)用于比較兩組手牌的牌力,看是否comb2可以擊敗comb1。唯一需要注意的一點,在斗地主的規(guī)則中,除了炸彈外,其他所有牌力均等,只有牌型一樣時才能去比較。

最后,我們需要一個模擬出牌函數(shù)make_hand(pokers, hand) ,用于求出在手牌為pokers的情況下打出一手牌hand后,剩下的手牌,實現(xiàn)也非常簡單,只需簡單的移除掉那些打出的牌即可。

效率

由于一副牌的可能手牌巨大,導致遞歸的分支數(shù)巨大。所以時間開銷非常大,為階乘級O(N!),根據(jù)斯特林公式,大約為O(N^N)。

由于可能會有很多重復的牌面出現(xiàn),導致了很多重復的遞歸調(diào)用。所以加一個緩存能極大提升效率。

即對我方手牌和敵方手牌和上一輪手牌的描述(str(me_pokers)+str(enemy_pokers)+str(last_hand))為鍵,將求出的結(jié)果存進緩存字典中。下一次遇到相同的局面時,即可直接從緩存字典中取出,而無需再次重復計算。時間復雜度優(yōu)化為指數(shù)級O(C^N)。

結(jié)果

代碼運算出來的結(jié)果是,農(nóng)民沒有必勝策略。換言之,只要地主會玩,農(nóng)民不可能贏。階級固化已經(jīng)如斯了么……

開源

代碼放于Github: doudizhu_solver,或者大家可以本地下載,MIT協(xié)議,隨便玩。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Python中np.percentile和df.quantile分位數(shù)詳解

    Python中np.percentile和df.quantile分位數(shù)詳解

    分位數(shù)(Quantile)亦稱分位點是指將一個隨機變量的概率分布范圍分為幾個等份的數(shù)值點,下面這篇文章主要給大家介紹了關(guān)于Python中np.percentile和df.quantile分位數(shù)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • pandas.DataFrame的pivot()和unstack()實現(xiàn)行轉(zhuǎn)列

    pandas.DataFrame的pivot()和unstack()實現(xiàn)行轉(zhuǎn)列

    這篇文章主要介紹了pandas.DataFrame的pivot()和unstack()實現(xiàn)行轉(zhuǎn)列,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-07-07
  • Python實現(xiàn)用戶登錄注冊

    Python實現(xiàn)用戶登錄注冊

    這篇文章主要為大家詳細介紹了Python實現(xiàn)用戶登錄注冊,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • pyinstaller封裝exe的操作

    pyinstaller封裝exe的操作

    這篇文章主要介紹了pyinstaller封裝exe的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • python中將字典改造為對象的方法

    python中將字典改造為對象的方法

    這篇文章主要介紹了python中將字典改造為對象的方法,在實際項目中,當使用json模塊加載一個深度很深的字典類型的json文件時,使用字典的訪問方式,將會出現(xiàn)很多中括號,即不直觀也不美觀,可以將這個字典轉(zhuǎn)化為對象,使得可以用.的方式訪問,需要的朋友可以參考下
    2023-11-11
  • python中cv2.projectPoints的用法小結(jié)

    python中cv2.projectPoints的用法小結(jié)

    這篇文章主要介紹了python中cv2.projectPoints的用法,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-12-12
  • 詳解python的argpare和click模塊小結(jié)

    詳解python的argpare和click模塊小結(jié)

    這篇文章主要介紹了詳解python的argpare和click模塊小結(jié),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • Pycharm配置lua編譯環(huán)境過程圖解

    Pycharm配置lua編譯環(huán)境過程圖解

    這篇文章主要介紹了Pycharm配置lua編譯環(huán)境過程圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • Tensorflow實現(xiàn)卷積神經(jīng)網(wǎng)絡用于人臉關(guān)鍵點識別

    Tensorflow實現(xiàn)卷積神經(jīng)網(wǎng)絡用于人臉關(guān)鍵點識別

    這篇文章主要介紹了Tensorflow實現(xiàn)卷積神經(jīng)網(wǎng)絡用于人臉關(guān)鍵點識別,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • Python2.7編程中SQLite3基本操作方法示例

    Python2.7編程中SQLite3基本操作方法示例

    這篇文章主要介紹了Python2.7編程中SQLite3基本操作方法,涉及Python2.7操作sqlite3數(shù)據(jù)庫的增刪改查及防注入等相關(guān)技巧,需要的朋友可以參考下
    2017-08-08

最新評論