python 斗地主发牌_tkinter模拟斗地主发牌

本文介绍如何使用Python的tkinter库和PIL模块模拟斗地主游戏的动态发牌效果。通过创建图片组件,利用tags进行动态删除和更新,实现了从牌库到三个方向的发牌动画,以及揭示底牌的功能。同时,添加了洗牌、发牌、亮牌、码牌和叫牌等按钮,使模拟更加完整。
摘要由CSDN通过智能技术生成

在上一篇文章的最后,我们留了一个小作业:花果山美男子:tkinter模拟扑克牌和狼人杀发牌​zhuanlan.zhihu.com

小作业2:模拟斗地主发牌,上方是牌库,实现从牌库到三个方向的动态发牌,最后揭示三张底牌(提示:可以用空白图片覆盖,或者删除指定组件)

今天我们就来完成一下斗地主发牌的模拟。

发牌的动态效果,原理和上一篇文章末尾狼人杀发牌是一样的。唯一的难点是牌库减少和揭示底牌的动画,方法就是每次更新图片时增加一个参数——tags标签,然后删除对应的图片。

另外还增加了“洗牌”、“发牌”、“亮牌”、“码牌”和“叫牌”的按钮,功能顾名思义,实现的逻辑也并不难。

具体方法请参考完整代码:

from tkinter import *

import random

from PIL import Image, ImageTk

import time

import tkinter.messagebox

n=54

player1,player2,player3,player4=[],[],[],[]

p1,p2,p3,p4=[],[],[],[]

pocker=[i for i in range(n)]

imgs=[]

root=Tk()

root.title('斗地主发牌模拟')

cv=Canvas(root,bg="White",width=800,heigh=650)

count=53 #牌堆剩余牌数(索引值,从0开始,用于tags标签的命名)

img0=ImageTk.PhotoImage(file="imgS/55.gif") #牌背图片

for i in range(1,55):

imgs.insert(i,ImageTk.PhotoImage(file="imgS/"+str(i)+".gif"))

lock=0 #是否叫地主的标记

#洗牌(用随机交换打乱牌序)

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

#将三个玩家和底牌分配到对应的列表

def mm():

global p1,p2,p3,p4

p1,p2,p3,p4=[],[],[],[]

pocker=gen_pocker(n) #打乱后的牌组编号

for m in range(0,51,3):

try:

p2.append(pocker[m])

p3.append(pocker[m+1])

p4.append(pocker[m+2])

except:

break

for m in range(51,54):

p1.append(pocker[m]) #p1存储的是最后三张底牌

#绘制初始的牌库

def init():

for i in range(0,54):

#tags命名一定要有字母,纯数字的话,重新洗牌后再调用delete会失效

cv.create_image((270+5*i,80), image=img0,tags='a'+str(i))

#发牌效果的实现

def ks(x):

global count

if count != 53 and x==0: #洗牌前不能发牌

return

#底牌减少的效果,每次减少三张

cv.delete('a'+str(count))

count-=1

cv.delete('a'+str(count))

count-=1

cv.delete('a'+str(count))

count-=1

if x==0:

mm()

#发牌效果,此时牌序是随机的

img1=imgs[p2[x]]

img2=imgs[p3[x]]

img3=imgs[p4[x]]

player2.append(cv.create_image((100,100+25*x), image=img1))

player3.append(cv.create_image((220+18*x,500), image=img2))

player4.append(cv.create_image((700,100+25*x), image=img3))

x+=1

if x==17:

return

root.after(100,ks,x)

#亮底牌

def ks2():

if count !=2: #发牌结束前不能亮底牌

return

cv.delete('a2')

cv.create_image((230+50*2,80), image=imgs[p1[0]],tags='a')

cv.delete('a1')

cv.create_image((230+50*3,80), image=imgs[p1[1]],tags='b')

cv.delete('a0')

cv.create_image((230+50*4,80), image=imgs[p1[2]],tags='c')

#整理手牌(码牌)

def ks3():

global p2,p3,p4

if count != 2: #发牌结束前不能码牌

return

p2.sort()

p3.sort()

p4.sort()

for x in range(0,17):

img=imgs[p2[x]]

player2.append(cv.create_image((100,100+25*x), image=img))

img = imgs[p3[x]]

player3.append(cv.create_image((220+18*x,500), image=img))

img = imgs[p4[x]]

player4.append(cv.create_image((700,100+25*x), image=img))

#如果先叫牌后码牌,地主手里应该整理20张牌

if lock==2:

for x in range(17,20):

img=imgs[p2[x]]

player2.append(cv.create_image((100,100+25*x), image=img))

elif lock==3:

for x in range(17,20):

img=imgs[p3[x]]

player3.append(cv.create_image((220+18*x,500), image=img))

elif lock==4:

for x in range(17,20):

img=imgs[p4[x]]

player4.append(cv.create_image((700,100+25*x), image=img))

#洗牌(清空屏幕,重新绘制牌库)

def ks1():

global count,p1,p2,p3,p4,lock

p1,p2,p3,p4=[],[],[],[]

count=53

lock=0

cv.delete('all')

init()

#3号叫牌

def jp3():

try: #如果发牌前就叫牌,会进入except,lock重新清零

global p2,p3,p4,lock

if lock==0:

lock=3 #用数字来标记叫地主的玩家,此后除了该玩家不能再点击叫牌

for i in p1:

p3.append(i)

p3.sort()

for x in range(0,20):

img = imgs[p3[x]]

player3.append(cv.create_image((220+18*x,500), image=img))

#如果没有亮牌就叫牌,没有亮出的底牌也要清空

cv.delete('a')

cv.delete('b')

cv.delete('c')

cv.delete('a2')

cv.delete('a1')

cv.delete('a0')

elif lock!=3:

tkinter.messagebox.showerror("错误", "已经有地主了!")

return

else:

cv.delete('a')

cv.delete('b')

cv.delete('c')

cv.delete('a2')

cv.delete('a1')

cv.delete('a0')

except:

lock=0

pass

#2号叫牌

def jp2():

try:

global p2,p3,p4,lock

if lock==0:

lock=2

for i in p1:

p2.append(i)

p2.sort()

for x in range(0,20):

img = imgs[p2[x]]

player2.append(cv.create_image((100,100+25*x), image=img))

cv.delete('a')

cv.delete('b')

cv.delete('c')

cv.delete('a2')

cv.delete('a1')

cv.delete('a0')

elif lock!=2:

tkinter.messagebox.showerror("错误", "已经有地主了!")

return

else:

cv.delete('a')

cv.delete('b')

cv.delete('c')

cv.delete('a2')

cv.delete('a1')

cv.delete('a0')

except:

lock=0

pass

#4号叫牌

def jp4():

try:

global p2,p3,p4,lock

if lock==0:

lock=4

for i in p1:

p4.append(i)

p4.sort()

for x in range(0,20):

img = imgs[p4[x]]

player4.append(cv.create_image((700,100+25*x), image=img))

cv.delete('a')

cv.delete('b')

cv.delete('c')

cv.delete('a2')

cv.delete('a1')

cv.delete('a0')

elif lock!=4:

tkinter.messagebox.showerror("错误", "已经有地主了!")

return

else:

cv.delete('a')

cv.delete('b')

cv.delete('c')

cv.delete('a2')

cv.delete('a1')

cv.delete('a0')

except:

lock=0

pass

init()

button = Button(root, text ="洗牌", font=('黑体', 10),fg='blue',width=10,height=1,command = ks1)

button.place(x=280, y=260)

button = Button(root, text ="发牌", font=('黑体', 10),fg='blue',width=10,height=1,command = lambda:ks(0))

button.place(x=400, y=260)

button = Button(root, text ="亮牌", font=('黑体', 10),fg='blue',width=10,height=1,command = ks2)

button.place(x=280, y=300)

button = Button(root, text ="码牌", font=('黑体', 10),fg='blue',width=10,height=1,command = ks3)

button.place(x=400, y=300)

button = Button(root, text ="叫牌", font=('黑体', 10),fg='blue',width=10,height=1,command = jp3)

button.place(x=350, y=390)

button = Button(root, text ="叫\n\n牌", font=('黑体', 10),fg='blue',width=1,height=4,command = jp2)

button.place(x=160, y=200)

button = Button(root, text ="叫\n\n牌", font=('黑体', 10),fg='blue',width=1,height=4,command = jp4)

button.place(x=620, y=200)

cv.pack()

root.mainloop()

注意事项:

用delete和tags标记删除指定组件时,如果直接用数字,只有第一次能够成功删除,在重新绘制同名的组件后,再调用delete就无法删除了。如果tags中包含字母则没有这个bug。例如:cv.create_image((100,100), image=img,tags='a1')

cv.delete('a1')

而不能是:cv.create_image((100,100), image=img,tags=str(1))

cv.delete(str(1))

最后的效果请看视频:https://www.zhihu.com/video/1239598801498542080

ps.视频里我留了一手牌堆区边框和地主标识的绘制,这个大家就自己研究吧

如果实在手气不好的话:https://www.zhihu.com/video/1239604032827957248

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值