当前位置:网站首页>撲克牌遊戲程序——人機對抗

撲克牌遊戲程序——人機對抗

2022-07-06 13:45:00 編程林黛玉

編寫撲克牌遊戲程序,初步設定玩家是人機對抗,每人各抓12張牌,遊戲規則是每次只能出一張牌,投骰子决定誰先出牌。點數大的管住點數小的。如果管不住對方時就不出牌,讓對方出牌。先出完牌的一方為贏。權值大小規定如下(從大到小):13,12,11,10,9,8,7,6,5,4,3,2,1。花色:方片,紅桃,黑桃,梅花。

總體流程圖如下:

輪流出牌流程圖如下:

全局變量如下:

jk=[]       #一副撲克牌

cp=[]       #計算機手中的牌

ps=[]       #人手中的牌

dt=[]       #已經打到桌上的牌

n=random.randint(0,1)  #該誰出牌,0:計算機出牌,1:人出牌

m=0         #出牌方式,0首次出牌,1:跟牌

函數模塊定義如下:

函數名稱

功能描述

參數

返回值

其它說明

createjk()

產生一副撲克牌並洗牌,將生成的牌放到列錶jk中

dest()

發牌函數,每方發12張牌,發到列錶cp(機方)和ps(人方)中。

psplay()

人出一次牌的函數

cpplay()

計算機出一次牌的函數

play()

出牌的總體函數,和判輸贏

主函數

調用上面的函數完成遊戲

代碼(Python): 

改進前的代碼:

import random 
#全局變量不可設太多,必要的即可,否則很難維護
jk=[]  # 一副撲克牌
cp=[]  #計算機手中的牌
ps=[]  #人手中的牌
dt=[]  #已經打到桌子上的牌
n=random.randint(0,1)  #該誰出牌,0:計算機出牌  1:人出牌
m=0  #出牌方式,0首次出牌,1跟牌
def createjk():  #產生一副撲克牌並洗牌
    color=['紅心','方片','黑桃','梅花']
    for c in color:   #產生一副牌,用雙層循環,每個花色產生1-13的數字
        for j in range(1,14):
            jk.append((c,j))  #將產生的牌加到列錶中
    for i in range(10000):  #洗牌
        x1=random.randint(0,51) #產生0-51一共52個隨機數字
        x2=random.randint(0,51)
        while (x1==x2):  #先判斷x1和x2是否相等,若相等則再給x2賦另外的值,直到他們不相等
            x2=random.randint(0,51)
        jk[x1],jk[x2]=jk[x2],jk[x1]  #將x1和x2交換,即洗牌
def dest():   #發牌函數,destribute分發
    for i in range(12):  #雖然有52張牌,但是只發了24張
        x=jk.pop()  #pop()方法用於移除列錶中的一個元素(默認為最後一個元素),並且返回該元素的值
        y=jk.pop()  #注意是先移除,再返回
        if len(cp)==0:  #為電腦發牌
            cp.append(x)
        else:  #保序插入,插入列錶,且按順序插到合適比特置
            k=len(cp)-1  #長度减1即為列錶中最後一個元素的下標
            while(k>=0 and cp[k][1]>x[1]):  #cp[k][1]和x[1]中的[1]都不可以省,因為在列錶中的每一個元組中又包含了兩個元素,第0元素是花色,第1個元素是牌上的數字
                k=k-1
            cp.insert(k+1,x)  #退出循環,即k<0或cp[k][1]<=x[1],若k<0,即-1,說明x就是最小的,便插到cp[0]處;若cp[k][1]<=x[1],因為k=k-1,則再加回去
        if len(ps)==0:  #為人發牌,與為計算機發牌的算法相同
            ps.append(y)  
        else:
            k=len(ps)-1
            while(k>=0 and ps[k][1]>y[1]):
                k=k-1
            ps.insert(k+1,y)
def psplay(): #人出牌
    global m  #務必申明是全局變量,否則下面的m和n都會被認為是局部變量 
    global n
    print("*****************************************")
    print("已打出的牌:")
    print(dt)  #輸出桌面上的牌
    print("您手中的牌:")
    L=len(ps)
    s=''
    for i in range(L): #循環將人手中的牌加入s字符串中
        s+='%d:%s%d,'%(i+1,ps[i][0],ps[i][1]) #其實也可以直接輸出ps,但那樣便沒有標號了
    s+='0:要不起'
    print(s)  #將s輸出
    while(True):
        k=int(input('請出牌'))
        if k==0:  #若輸入0,即要不起,則令m=0,首次出牌,並退出循環
            m=0
            break
        elif (k>0 and k<=L) and (m==0 or (m==1 and ps[k-1][1]>dt[-1][1])):
        #否則,若k輸入在合理範圍內,且是首次出牌或者是跟牌且k對應的牌比桌面上對應的最後一張牌大,進行以下操作
        #注意,必須判斷m的值,因為要判斷是跟牌還是首次出牌,若為首次出牌,則不會再加“比桌面上最後一張牌大這一條件”
            dt.append(ps[k-1])  #若符合條件即出牌,則把這張牌加到桌面列錶中
            del(ps[k-1])  #並在人手中删除這張牌
            m=1  #並改變m=的值,使下一步變為跟牌
            break
def cpplay():
    global m
    global n
    k=0
    if m==0:  #判斷m的值,若為首次出牌
        print("計算機出")  #則告知人
        print(cp[0])  #且將計算機手中的第一張牌,即最小的牌輸出,即告知人
        dt.append(cp[0])  #將出手的牌加到桌面列錶中
        del(cp[0])  #並在計算機手中删除這張牌
        m=1  #並改變m=的值,使下一步變為跟牌
    else:  #若為跟牌
        k=0  #令k=0,從第一張牌開始尋找比桌面上的牌大的牌
        while(k<len(cp) and dt[-1][1]>=cp[k][1]):  #確定k在範圍內且尋找比桌面上最後一張牌大的牌
            k+=1
        if k==len(cp):  #判斷退出循環的條件是否是沒有找到符合條件的牌
            print("計算機要不起")
            m=0
        else:   #判斷退出循環的條件是找到了符合條件的牌
            print("計算機出了")
            print(cp[k])
            dt.append(cp[k])
            m=1
def play():  #開始玩
    global m
    global n
    n=random.randint(0,1)  #决定誰先出牌
    m=0  #首次出牌
    while(True):
        if n==0:  #如果n==0,計算機先出牌
            cpplay()
            if len(cp)==0:  #如果計算機出完牌,長度為0,說明計算機先出完牌,即計算機贏
                print("計算機贏了")
                break
        else:  #否則n==1,人出牌
            psplay()
            if len(ps)==0:  #如果計算機出完牌,長度為0,說明計算機先出完牌,即計算機贏
                print("人贏了")
                break
        n=(n+1)%2  #每次出完牌,將n的值換掉,換另一方出牌
    #如果退出了循環,說明有一方贏了,則將撲克牌、計算機、人、桌面的牌全部清空
    jk.clear()  #每局遊戲結束後都要將程序的各個列錶都清空,以免影響下一局遊戲
    cp.clear()
    ps.clear()
    dt.clear()
#主程序
c='y'  #初始化c,以進入下面循環開始第一局
while c=='y':
    createjk()  #產生牌並洗牌
    dest()    #發牌
    play()    #開始玩
    c=input('是否再來一局?(y/n)')   
print("遊戲結束,再見!")

運行結果: 

 改進後的代碼: 

import random
jk=[]       #一副撲克牌
cp=[]       #計算機手中的牌
ps=[]       #人手中的牌
dt=[]       #已經打到桌上的牌
n=0         #該誰出牌,0:計算機出牌,1:人出牌
m=0         #出牌方式,0首次出牌,1:跟牌
def showjk(x):  #顯示列錶x中的所有撲克牌
    s=''
    for i in x:
        s+=i[0]+str(i[1])+' '
    return s
def createjk(): #產生一副撲克牌,並洗牌
    color=['','','','']
    for c in color:     #生成一副撲克牌
        for j in range(1,14):
            jk.append((c,j))
    
    for i in range(10000):      #洗牌
        x1=random.randint(0,51)  #隨機抽兩張牌
        x2=random.randint(0,51)
        while(x1==x2):      #如果這兩張牌相同,則重新抽取,直到這兩張牌不同為止
            x2=random.randint(0,51)
        jk[x1],jk[x2]=jk[x2],jk[x1]     #交換這兩張牌
        
def dest():       #發牌函數,destribution 分發,即發牌的意思
    for i in range(12):
        x=jk.pop()      #抽一張牌給變量x,將發給機器方手中的牌——列錶cp中
        y=jk.pop()      #抽一張牌給變量y,將發給機器方手中的牌——列錶ps中
        #以下是保序插入,按由小到大順序插入每一張牌
        if len(cp)==0:  #如果列錶cp是空的,則直接插入
            cp.append(x)
        else:
            k=len(cp)-1  #找到列錶cp的最大下標比特置
            while(k>=0 and cp[k][1]>x[1]):# 從後向前依次查找,找到第一個點數小於x的點數的下標,如果沒找到則k的值為-1
                k=k-1
            cp.insert(k+1, x)   #將x插入到k+1那個比特置
            #以下是將y插入到人手中的牌,算法和上面相同
        if len(ps)==0:
            ps.append(y)
        else:
            k=len(ps)-1
            while(k>=0 and ps[k][1]>y[1]):
                k=k-1
            ps.insert(k+1, y) 
def psplay(): #人打一張牌
    global m
    global n
    print()
    print('桌面:'+showjk(dt))
    print('您手中的牌:')
    L=len(ps)
    s=''
    for i in range(L):
        s=s+'%d:%s%d, '%(i+1,ps[i][0],ps[i][1]) #顯示人手中所有的牌,每張牌前面有序號
    s+='0:要不起'   
    print(s)            #顯示菜單
    while(True):        #該循環控制出牌並保證出牌的合法性
        k=int(input('請您出牌'))  #接收用戶的輸入
        if k==0:        #錶示用戶不出牌
            m=0         #m=0是為下一輪循環時,讓下計算機方按首次出牌方式出牌
            break
        #下面語句判斷打出的牌是否合法,0<k<=L控制用戶輸入的k必須在可選範圍內
        #若m==0可以隨意出牌,如果m==1則出的牌必須大於桌面上最後一張牌
        elif (0<k<=L) and (m==0 or (m==1 and ps[k-1][1]>dt[-1][1])): 
            dt.append(ps[k-1])  #將合法打出的牌加入已打出牌的列錶dt中
            del(ps[k-1])        #在ps中删除打出的牌
            m=1                 #為下一輪循環須知准備,讓計算機方跟牌
            break
def cpplay():   #計算機打一張牌
    global m
    global n
    k=0
    if m==0:    #首次出牌,可以隨意出牌,一般出手中最小的牌
        #下面顯示打出的牌,因為是首次出牌,所以打出的是最小的牌    
        print('計算機出:'+showjk([cp[0]]))      
        dt.append(cp[0])#將打出的牌添加到已打出牌的列錶dt中
        del(cp[0])      #從cp列錶將打出牌删除
        m=1             #設置下一輪人打牌的出牌方式為跟牌
    else:               #此分支為m==1的情况,計算機要跟牌
        k=0
        #下面的循環是在計算機手中的牌裏找到大於上輪出的牌的最小牌
        while(k<len(cp) and dt[-1][1]>=cp[k][1]): #k從0開始依次向後查找
            k+=1
        if k==len(cp):  #如果超範圍了說明沒找到大於上輪剛打出的牌
            print('計算機要不起')
            m=0
        else:
            print('計算機出了'+showjk([cp[k]]))
            #print(cp[k])        #將找到的牌打出
            dt.append(cp[k])    #添加到已打出牌的列錶dt中   
            del(cp[k])          #從cp列錶删除
            m=1                 #設置m的為1,讓下一輪人方按“跟牌”的方式出牌

def play():
    global n
    global m
    n=random.randint(0, 1)  #n用0到1之間的隨機數來賦值,隨機决定誰先出牌
    m=0
    print('***************************')
    print('             遊戲開始')                     #設置初始出牌方式為“首次出牌”
    if n==0:
        print('本次遊戲計算機先出牌')
    else:
        print('人先出牌')
    while(True):
        if n==0:            #計算機出牌
            cpplay()        #計算機打出一張牌
            if len(cp)==0:  #如果計算機手中牌為0,則計算機贏了
                print('計算機贏了!!!')
                break
        else:
            psplay()        #此時n==1,人打出一張牌
            if len(ps)==0:  #如果人手中的牌為0,則人贏了
                print('人贏了!!!')
                break
        n=(n+1)%2           #翻轉n的值,原來是1則翻轉後為0,原來值為0則翻轉後為1
    jk.clear()
    cp.clear()
    ps.clear()
    dt.clear()
#主程序
c='y'
while c=='y':        
    createjk()
    dest()
    play()
    c=input('是否再來一局?(y/n)')
print('遊戲結束,再見!') 

這是一個比較有意思的程序,人和計算機輪流出牌,先出完牌的一方獲勝,但還是比較簡單,它只是雙方出牌比較大小,沒有我們平時玩的撲克牌遊戲比如鬥地主中的許多規則。而且,在上面的程序中,計算機不懂玩牌的“戰術”,它只懂得手裏有比我大的牌就一直出。

但想必大家都知道著名的人機對抗——2017年5月,在中國烏鎮圍棋峰會上,它與排名世界第一的世界圍棋冠軍柯潔對戰,以3比0的總比分獲勝。圍棋界公認阿爾法圍棋的棋力已經超過人類職業圍棋頂尖水平,在GoRatings網站公布的世界職業圍棋排名中,其等級分曾超過排名人類第一的棋手柯潔。

阿爾法圍棋(AlphaGo)此前的版本,結合了數百萬人類圍棋專家的棋譜,以及强化學習進行了自我訓練。AlphaGoZero的能力則在這個基礎上有了質的提昇。最大的區別是,它不再需要人類數據。也就是說,它一開始就沒有接觸過人類棋譜。研發團隊只是讓它自由隨意地在棋盤上下棋,然後進行自我博弈。據阿爾法圍棋團隊負責人大衛·席爾瓦(Dave Sliver)介紹,AlphaGoZero使用新的强化學習方法,讓自己變成了老師。系統一開始甚至並不知道什麼是圍棋,只是從單一神經網絡開始,通過神經網絡强大的搜索算法,進行了自我對弈。隨著自我博弈的增加,神經網絡逐漸調整,提昇預測下一步的能力,最終贏得比賽。更為厲害的是,隨著訓練的深入,阿爾法圍棋團隊發現,AlphaGoZero還獨立發現了遊戲規則,並走出了新策略,為圍棋這項古老遊戲帶來了新的見解。

不僅如此,6月1日,中國首個原創虛擬學生“華智冰”在北京正式亮相並進入清華大學計算機科學與技術系知識工程實驗室學習,並相繼由一首《男孩》而走紅網絡。與一般的虛擬數字人不同,華智冰擁有持續的學習能力,能够逐漸“長大”,不斷“學習”數據中隱含的模式,包括文本、視覺、圖像,視頻等,就像人類能够不斷從身邊經曆的事情中來學習行為模式一樣。隨著時間的推移,華智冰針對新場景學到的新能力,將有機地融入自己的模型中,從而變得越來越聰明。

人機對抗作為人工智能研究的前沿方向,已成為國內外智能領域研究的熱點,未來行業領域對於人工智能專業人才的需求量會逐漸增大,感興趣的同學可以多多了解一些有關人機對抗或者人工智能方面的知識哦!

原网站

版权声明
本文为[編程林黛玉]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060917046291.html