今天我们用tkinter的canvas画布实现扑克牌的随机发牌:将54张牌随机发给四位牌手,在屏幕上显示每位牌手的牌,利用Canvas将发牌结果显示出来。

需要准备的是54张扑克牌的图片(gif或者png,jpg的话要经过额外的转换),为了保证最终的牌序是按照由小到大排列的,图片编号要按照AAAA、2222……KKKK、大小王来排列(也许A和2应该更大,但是请忽略这一点)。

下面是完整代码:

from tkinter import *
import random

n=54
(player1,player2,player3,player4)=([],[],[],[])
(p1,p2,p3,p4)=([],[],[],[])
pocker=[i for i in range(n)]
imgs=[]
root=Tk()
cv=Canvas(root,bg="White",width=700,height=600)

#洗牌(用随机交换打乱牌序)
def gen_pocker(n):
    x=100
    while(x>0):
        x=x-1
        p1=random.randint(0,n-1)
        p2=random.randint(0,n-1)
        t=pocker[p1]
        pocker[p1]=pocker[p2]
        pocker[p2]=t
    return pocker
    
pocker=gen_pocker(n)  #打乱后的牌组编号
#将图片放入imgs列表
for i in range(1,55):
    imgs.insert(i,PhotoImage(file="imgs/"+str(i)+".gif"))
#发牌,
for m in range(0,54,4):
    try:
        p1.append(pocker[m])
        p2.append(pocker[m+1])
        p3.append(pocker[m+2])
        p4.append(pocker[m+3])
    except:
        break
#由小到大排序
p1.sort()
p2.sort()
p3.sort()
p4.sort()
#逐一显示图片,其中1号和2号会发到14张牌
for x in range(0,14):
    try:
        img=imgs[p1[x]]
        player1.append(cv.create_image((230+20*x,80),image=img))  #参数控制图片的位置和间隔
        img=imgs[p2[x]]
        player2.append(cv.create_image((100,150+25*x), image=img))
        img = imgs[p3[x]]
        player3.append(cv.create_image((230+20*x,500), image=img))
        img = imgs[p4[x]]
        player4.append(cv.create_image((600,150+25*x), image=img))
    except:
        pass    

cv.pack()
root.mainloop()

图片在百度网盘

链接: https://pan.baidu.com/s/1yHoQCTZ1sde-J3d3qnb4DQ?pwd=m28h 
提取码: m28h  

模拟结果如图:

稍微改一改也可以用来模拟斗地主:

再改一改也可以模拟狼人杀,逻辑比扑克牌简单多了,不需要排序,而且通常只有12张牌。成果如下:

还可以改变一下布局,既然用的是网易的图片,那就和网易狼人杀的位置一样吧:

如果想看发牌的动画效果,请参考下面的代码:

from tkinter import *
import random
from PIL import Image, ImageTk
import time
n=12
player2,player4=[],[],[],[]
p1,p2,p3,p4=[],[],[],[]
pocker=[i for i in range(n)]
imgs=[]
root=Tk()
cv=Canvas(root,bg="White",width=400,height=600)
for i in range(1,13):
    imgs.insert(i,ImageTk.PhotoImage(file="img/"+str(i)+".gif"))

#洗牌(用随机交换打乱牌序)
def gen_pocker(n):
    x=10
    while(x>0):
        x=x-1
        p1=random.randint(0,n-1)
        p2=random.randint(0,n-1)
        t=pocker[p1]
        pocker[p1]=pocker[p2]
        pocker[p2]=t
    return pocker
#发牌    
def mm():
    global p2,p4  #list本身虽然是全局变量,但如果不用global仅能修改元素,不能重新定义列表
    cv.deldete('all')
    p2,p4=[],[]
    pocker=gen_pocker(n)  #打乱后的牌组编号
    for m in range(0,12,2):
        try:
            p2.append(pocker[m])
            p4.append(pocker[m+1])
        except:
            break

#先更新左边一列
def ks(x):	    
    if x==0:
        mm()  	
    img=imgs[p2[x]]
    player2.append(cv.create_image((100,50+100*x), image=img))
    x+=1
    #从第七张开始更新右边一列
    if x==6:
        x2=0
        root.after(500,ks2,x2)
        return
    root.after(500,ks,x)
#更新右侧一列
def ks2(x2):
    img = imgs[p4[x2]]
    player4.append(cv.create_image((320,50+100*x2), image=img))
    x2+=1
    #最后一张之后结束
    if x2==6:
        return
    root.after(500,ks2,x2)

button = Button(root, text ="发牌", width=10,height=1,command = lambda:ks(0))
button.place(x=170, y=260) 

cv.pack()
root.mainloop()

  

这样就能实现动态发牌的效果了:

 

什么?边角位真的总有狼?

有了这个程序,手上没牌也能模拟发牌、研究位置学了。

 

删除canvas创建的组件方法:在定义时加一个tags属性,然后用delete删除,例如:

cv.create_image((100,100), image=img,tags='o')
cv.delete('o')

  

最后为喜欢玩狼人杀的小伙伴们送上一则位置学攻略吧——

好好听发言。