python 从一道作业题到制作一个图形界面的“诈金花”游戏

题目很简单:就是自己写一个程序,实现诈金花游戏的发牌、判断输赢。

规则:

一付扑克牌,去掉大小王,每个玩家发3张牌,最后比大小,看谁赢。

牌型:

豹子:一样大的三张牌,如10,10,10
顺金:又称同花顺,即3张同花色的顺子, 如红桃 5、6、7
金花:又称同花,即3张牌同花色但非顺子,如草花J、8、7
顺子:3张连续大小但花色不全相同,如红桃5、方片6、黑桃7
对子:只有2张一样大的牌,如果对子一样大就比第3张的大小
单张:3张牌不同花不成对也不成顺子,最大的是A带头的单张

版型大小顺序: 豹子>顺金>金花>顺子>对子>单张

从网上百科到的诈金花各种牌型的出现概率,一起放进代码中增加一点趣味小知识。你可能不知道豹子出现的概率比同花顺大,实际打牌时同花顺反而比豹子小;顺子出现的概率比金花小,实际打牌时顺子反而比金花小;最大牌为5、6、7的单张牌型出现的概率都要比金花和顺子小,所以有的地方额外规定同一局中拿到“235”三张牌要比同局的豹子大。

回到正题,直接上代码,主要2个函数:一个计分、一个比大小

from random import shuffle as DealCards

Players = 5  #人数
pkPacks = 1  #扑克副数
W = "单张","对子","顺子","金花","顺金","豹子"
X = 74.38, 16.94, 3.26, 4.96, 0.22, 0.24  #出现概率
Y = 0.54, 1.36, 2.44, 3.8, 5.43, 7.33, 9.5, 11.95, 14.66, 17.38 #单张概率
V = *(str(i) for i in range(2,10)),*'TJQKA' #T代表10
F = '♠', '♥', '♣', '♦'
P = [f+v for f in F for v in V]*pkPacks 

def Scores(pokers):
    f,p = [],[]
    for poker in pokers:
        f.append(F.index(poker[0])+1)
        p.append(V.index(poker[1])+2)
    t = sorted(p)
    intSame = len(set(t))
    isFlush = int(len(set(f))==1)
    isStraight = t[0]+1==t[1]==t[2]-1
    if intSame==1:
        return 500_0000+t[0] #豹子
    elif intSame==2: #对子一样大比较剩下的单张
        return (100+t[1])*10000+(t[2] if t[0]==t[1] else t[0])
    else: #顺金(同花顺)顺子 else #单张或金花
        return 200_0000*(1+isFlush)+t[2] if isStraight else ((300*isFlush+t[2])*100+t[1])*100+t[0]

def WhoWins(P):
    Pokers,Winner = [],[]
    for i in range(0,3*Players,3):
        Pokers.append(P[i:i+3])
    for i,p in enumerate(Pokers,1):
        win = Scores(p)
        idx = win//100_0000
        print(f"Player{i}: {*p,} - {W[idx]}")
        Winner.append(win)
    win = max(Winner) #没有判断“一样大”,如是则谁在前谁为大
    idx = Winner.index(win)
    big = win//10000
    win = big//100
    per = X[win] if win else Y[big-5]
    pok = W[win] if win else '单'+V[big-2]
    print(f"【Player{idx+1} win!】--> {*Pokers[idx],} {pok}({per}%)\n".replace('T','10'))
    return P[3*Players:] #去掉每一局已发的牌

if __name__ == '__main__':
    DealCards(P) #以随机洗牌来模拟发牌
    #Players = int(input('请输入参加的人数?'))
    PlayersMax = 52*pkPacks//3+1
    if not 0<Players<PlayersMax:
        print(f'请注意:参与人数的范围 0 < Players < {PlayersMax} !')
    else:
        count = 1
        while len(P)>=3*Players: #所有牌(52*PokerPairs)发不够一局为止
            print(f'第{count}局:')
            count += 1
            P = WhoWins(P)

运行结果:

第1局:
Player1: ('♥Q', '♣2', '♣8') - 单张
Player2: ('♦10', '♥7', '♠6') - 单张
Player3: ('♣4', '♠4', '♦2') - 对子
Player4: ('♠5', '♠9', '♥6') - 单张
Player5: ('♠7', '♠3', '♣5') - 单张
【Player3 win!】--> ('♣4', '♠4', '♦2') 对子(16.94%)

第2局:
Player1: ('♥2', '♥8', '♦4') - 单张
Player2: ('♦9', '♦3', '♥A') - 单张
Player3: ('♠J', '♣A', '♦K') - 单张
Player4: ('♠8', '♥9', '♥10') - 顺子
Player5: ('♣7', '♣9', '♣10') - 金花
【Player5 win!】--> ('♣7', '♣9', '♣10') 金花(4.96%)

第3局:
Player1: ('♦7', '♦J', '♠2') - 单张
Player2: ('♥J', '♦A', '♥K') - 单张
Player3: ('♥4', '♥5', '♦6') - 顺子
Player4: ('♣Q', '♣J', '♠10') - 顺子
Player5: ('♣K', '♦8', '♦5') - 单张
【Player4 win!】--> ('♣Q', '♣J', '♠10') 顺子(3.26%)

【扩展】学习要举一反三,做完题目想到把这个程序的界面图形化。为方便摆放牌堆,就只指定一付扑克四个玩家。然后,无非就是把原代码中扑克牌对应花色、大小的下标值从以下牌型图中索引出相应的图来,放到对应的Image控件上;而想要输出的文字就对应到text控件上;再整几个button控件绑定对应的事件动作,就搞定了!

牌型图: Pokers.png

初始界面:

发牌界面:

开牌界面:

PokersV1.py 完整源代码:

import tkinter as tk
from PIL import Image,ImageTk
from time import sleep
from random import shuffle as DealCards
 
W = "单张","对子","顺子","金花","顺金","豹子" #牌型
X = 74.38, 16.94, 3.26, 4.96, 0.22, 0.24  #各牌型出现概率
Y = 0.54, 1.36, 2.44, 3.8, 5.43, 7.33, 9.5, 11.95, 14.66, 17.38 #单张概率
V = *(str(i) for i in range(2,10)),*'TJQKA' #T代表10
F = '♠', '♥', '♣', '♦'
 
def loadCards():
    infile = Image.open("pokers.png")
    Images = []
    for j in range(4):
        image = []
        for i in range(15):
            box = infile.crop((i*100,j*150,i*100+100,j*150+150))
            img = ImageTk.PhotoImage(image=box)
            image.append(img)
        Images.append(image)
    infile.close()
    return Images
 
def initCards():
    global isReady,P
    isReady = True
    P = [f+v for f in F for v in V]
    DealCards(P)
 
def dealCards():
    global cv,cards,isReady,P,Pokers
    if not isReady: return
    cv.itemconfig(txt1, text="")
    cv.itemconfig(txt2, text="")
    if len(Pokers):
        for j in range(3):
            for i in range(4):
                cv.itemconfig(cards[i][j], image=Cards[0][0])
                cv.update()
        sleep(0.3)
    for j in range(3):
        for i in range(4):
            cv.itemconfig(cards[i][j], image=Cards[1][0])
            cv.update()
            sleep(0.2)
    if len(P)<12:
        initCards()
    isReady = False
 
def playCards():
    global cv,isReady,P,Pokers,cards,Cards
    if isReady: return
    P = WhoWins(P)
    for i,pok in enumerate(Pokers):
        for j,p in enumerate(pok):
            x = F.index(p[0])
            y = V.index(p[1])
            #print(x,y,'-',i,j)
            cv.itemconfig(cards[i][j], image=Cards[x][y+2])
            cv.update()
    isReady = True
 
def Scores(pokers):
    f,p = [],[]
    for poker in pokers:
        f.append(F.index(poker[0])+1)
        p.append(V.index(poker[1])+2)
    t = sorted(p)
    intSame = len(set(t))
    isFlush = int(len(set(f))==1)
    isStraight = t[0]+1==t[1]==t[2]-1
    if intSame==1:
        return 500_0000+t[0] #豹子
    elif intSame==2: #对子一样大比较剩下的单张
        return (100+t[1])*10000+(t[2] if t[0]==t[1] else t[0])
    else: #顺金(同花顺)顺子 else #单张或金花
        return 200_0000*(1+isFlush)+t[2] if isStraight else ((300*isFlush+t[2])*100+t[1])*100+t[0] 
 
def WhoWins(P):
    global cv,txt1,txt2,Pokers
    Pokers,Winner = [],[]
    for i in range(0,3*4,3):
        Pokers.append(P[i:i+3])
    for i,p in enumerate(Pokers,1):
        win = Scores(p)
        idx = win//100_0000
        print(f"Player{i}: {*p,} - {W[idx]}".replace('T','10'))
        Winner.append(win)
    win = max(Winner) #没有判断“一样大”,如是则谁在前谁为大
    idx = Winner.index(win)
    big = win//10000
    win = big//100
    per = X[win] if win else Y[big-5]
    pok = W[win] if win else '单'+V[big-2]
    text1 = f"【Player{idx+1} win!】"
    text2 = f"{pok}{*Pokers[idx],} {per}%\n".replace('T','10')
    print(text1,'--> ',text2)
    cv.itemconfig(txt1, text=text1)
    cv.itemconfig(txt2, text=text2)
    return P[3*4:] #去掉一局已发的牌
 
def test():
    global P
    initCards()
    print("测试用:",P)
 
if __name__ == '__main__':
    root = tk.Tk()
    root.geometry('1024x768')
    root.title('诈金花')
    cv = tk.Canvas(root, width=1024, height=680, bg='darkgreen')
    cv.pack()
    Pokers = []
    initCards()
    Cards = loadCards()
    cards = [[None]*3 for _ in range(4)]
    x1, x2, x3, dx = 400, 80, 730, 105
    y1, y2, y3 = 100, 550, 320
    imgxy = [[(x1,y1),(x1+dx,y1),(x1+2*dx,y1)],[(x3,y3),(x3+dx,y3),(x3+2*dx,y3)],
             [(x1,y2),(x1+dx,y2),(x1+2*dx,y2)],[(x2,y3),(x2+dx,y3),(x2+2*dx,y3)]]
    for x,lst in enumerate(imgxy):
        for y,coord in enumerate(lst):
            cards[x][y] = cv.create_image(coord, image=Cards[0][0])
            cv.create_rectangle(coord[0]-50,coord[1]-75,coord[0]+50,coord[1]+75)
        tx,ty = coord[0]-100,coord[1]+95
        cv.create_text(tx,ty, text=f'Player{x+1}', fill='white')
    btn = []
    btnCmd = ['发牌',dealCards,'发牌',playCards,'重新理牌',test]
    for i in range(3):
        btn.append(tk.Button(root,text=btnCmd[i*2],command=btnCmd[i*2+1],width=10))
        btn[i].place(y=710, x=350+i*110)
    txt1 = cv.create_text(510,300, fill='red', font=("宋体", 16))
    txt2 = cv.create_text(510,360, fill='yellow', font=("宋体", 10))
    root.mainloop()

【编译程序】

Windows的Cmd窗口中执行如下命令,Mac系统自行百度对应的编译命令:

pyinstaller -F exam.py --noconsole

注意:记得把上面的牌型图保存为Pokers.png和代码或程序放一起运行。

  • 43
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hann Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值