当前位置:网站首页>扑克牌游戏程序——人机对抗

扑克牌游戏程序——人机对抗

2022-07-06 09:21: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://blog.csdn.net/m0_62205773/article/details/124589970