百吉饼小游戏

参考文章:  Python 小型项目大全 1~5_python 实验项目

环境:

  1. Python 3.10.3
  2. tkinter 包
  3. ttkbootstrap 包

界面样式:百吉饼_哔哩哔哩_bilibili

百吉饼

 代码部分:

import tkinter as tk
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from PIL import Image, ImageTk
from tkinter import messagebox
import re
import random
import time 
from threading import Thread

# ------------
# 常见配置
# ------------

# 猜的数字个数【暂时只能为3,且取值范围(3,9)】
NUM_DIGITS = 3 
# 可以猜的次数
MAX_GUESSES = 4 
# 游戏中 OR 新游戏 [不允许修改]
isNewGame = True
# 提示内容: 存储
label_list = []
# 初始化猜数字次数
numGuesses = 1
# 获取秘密数
secretNum = 0


def main():
    root = ttk.Window(title="百吉饼",size=(635,470))
    #防止用户调整尺寸
    root.resizable ( 0, 0) 

    # ------------
    # 放置图标(帮助、设置)
    # ------------
    placeImage(imgUrl='./help.png',imgSize=(30,30),ix=400,iy=25)
    help_text = ttk.Label(text="帮助",font=('黑体',12));
    help_text.place(x=440,y=30)

    placeImage(imgUrl='./setting.png',imgSize=(30,30),ix=500,iy=23)
    setting_text = ttk.Label(text="设置",font=('黑体',12));
    setting_text.place(x=535,y=30)

    # ------------
    #  猜测总数/当前猜测次数
    #-------------
    # TODO
    global currentGuess_text
    
    totalGuess_text = tk.StringVar()
    totalGuess_text.set("总次数:" + str(MAX_GUESSES))
    totalGuess = ttk.Label(textvariable=totalGuess_text,font=('黑体',12))
    totalGuess.place(x=40,y=35)

    
    currentGuess_text = tk.StringVar()
    currentGuess_text.set("当前次数:" + str(numGuesses))
    currentGuess = ttk.Label(textvariable=currentGuess_text,font=('黑体',12))
    currentGuess.place(x=150,y=35)

    # ------------
    #  放置3个文本框
    #-------------

    #全局,游戏主体需要 
    global entry1_text
    global entry1
    entry1_text = tk.StringVar()
    entry1 = tk.Entry(width=4,font=('黑体',50),textvariable=entry1_text,validate="key",validatecommand=(root.register(limitNumber),"%P"))
    entry1.grid(row=0,column=0,padx=(40,8),pady=(90,0),ipady=80)
    entry1.config(fg='#616161',justify="center")
    entry1.bind("<Return>",lambda event : focusEntry(event,1))

    global entry2_text
    global entry2
    entry2_text = tk.StringVar()
    entry2 = tk.Entry(width=4,font=('黑体',50),textvariable=entry2_text,validate="key",validatecommand=(root.register(limitNumber),"%P"))
    entry2.grid(row=0,column=1,padx=8,pady=(90,0),ipady=80)
    # 设置文本颜色和居中代码
    entry2.config(fg='#616161',justify="center")
    entry2.bind("<Return>",lambda event : focusEntry(event,2))
    entry2.bind("<BackSpace>", lambda event : backEntry(event,2))

    global entry3_text
    global entry3
    entry3_text = tk.StringVar()
    entry3 = tk.Entry(width=4,font=('黑体',50),textvariable=entry3_text,validate="key",validatecommand=(root.register(limitNumber),"%P"))
    entry3.grid(row=0,column=2,padx=(8,25),pady=(90,0),ipady=80)
    entry3.config(fg='#616161',justify="center")
    entry3.bind('<Return>',startGame)
    entry3.bind("<BackSpace>", lambda event : backEntry(event,3))

    root.mainloop()


def backEntry(event,num):
    """ 回到上一个文本框 """

    if num == 2:
        # 判断文本框是否输入了数字
        # 当为空时,才跳到上一个文本框
        if entry2_text.get() == "":
            entry1.focus()
    elif num == 3:
        if entry3_text.get() == "":
            entry2.focus()
    else:
        # 处于第1个文本框
        pass


def focusEntry(event,num): 
    """ 聚焦其他输入框 """

    if num == 1:
        # 处于第1个文本框
        entry2.focus()
    elif num == 2:
        # 处于第2个文本框
        entry3.focus()
    else: 
        # 处于第三个文本框
        # 已经进行处理
        pass


def startGame(event):
    """ 当全部输入数字后,在最后数字上回车,开始游戏 """

    if entry1_text.get() != "" and entry2_text.get() != "" and entry3_text.get() != "":
        # ------------
        #  游戏开始
        #-------------
        gameBody(isNewGame)


def bind_adaptor(fun,**kwds):
    # 没有用....
    print("中介...")
    print(fun)
    return lambda event,fun,kwds=kwds : fun(event,**kwds)


def limitNumber(s: str) -> bool:
    """ 限制输入数字 """
    s = s.strip()

    flag = False
    
    # 长度为 0,无需匹配
    if len(s) == 0:
        return True

    # 匹配规则 [匹配输入的只有1个字符,且在0-9之间]
    match_result = re.match(r"[0-9]",s)
    if match_result is not None:
        # 调用 group() 是匹配成功的字符
        # 与原字符相比
        # print("验证: ",match_result.group())
        # print("验证结果: ",match_result.group() == s)
        flag = match_result.group() == s

    return flag


def placeImage(imgUrl,imgSize,ix,iy):

    # 解决 bug 
    # 图片不显示问题: 全局变量 垃圾不回收
    # 多张图片不显示 引用
    global tk_icon

    # 从本地加载图片
    load_image = Image.open(imgUrl)
    # 设置图片大小
    load_image = load_image.resize(imgSize)
    ref = ttk.Label()
    # 加入到 tk 中
    ref.tk_icon = ImageTk.PhotoImage(load_image)
    # 放到 label 标签里面显示
    tk_label = ttk.Label(image=ref.tk_icon)
    # 图片放入位置
    tk_label.place(x=ix,y=iy)

    return tk_label



def getSecretNum():
   """返回唯一随机数字组成的字符串。"""

   numbers = list('0123456789')
   # 打乱顺序 
   random.shuffle(numbers)  

   # 获取秘密数字
   secretNum = ''
   for i in range(NUM_DIGITS):
       secretNum += str(numbers[i])
   return secretNum


def getClues(guess, secretNum):
   """提示 red yellow green"""

    # ------------
    #  放置提示文本
    #
    # red       输入数字都不正确
    # yellow    输入数字正确但位置不对
    # green     输入数字正确且位置正确
    #
    # 两个绿色圈 ===> 有2个输入数字正确且位置正确 【其余情况同理】
    #-------------

    # 示例 一般动态去创建(根据用户输入数字的情况)
    # placeImage(imgUrl='./green.png',imgSize=(30,30),ix=160,iy=398)
    # placeImage(imgUrl='./green.png',imgSize=(30,30),ix=185,iy=398)
    # clue_text = ttk.Label(text="输入数字都正确",font=('黑体',12))
    # clue_text.place(x=230,y=402)

   clues = []

   for i in range(len(guess)):
       if guess[i] == secretNum[i]:
           # 正确的数字在正确的位置。
           clues.append("green.png")
       elif guess[i] in secretNum:
           # 正确的数字在错误的位置。
           clues.append("yellow.png")
   if len(clues) == 0:
        # 没有正确的数字。
        return ["red.png"]  
   else:
       # 排序,避免泄露信息
       clues.sort()
       # 用空格将其拼接起来
       return clues


def gaveClue(clues):

    # 线索放置位置
    cluesX = [180,210,240]
    tipX = 0

    for item in label_list:
        item.place_forget()

    for i,clue in enumerate(clues):
        
        tk_label = placeImage(imgUrl=clue,imgSize=(30,30),ix=cluesX[i],iy=380)
        label_list.append(tk_label)
        # load_image = Image.open(clue)
        # load_image = load_image.resize((30,30))
        # tk_icon = ImageTk.PhotoImage(load_image)
        # tk_label = ttk.Label(image=tk_icon)
        # tk_label.place(x=cluesX[i],y=380)

        tipX = cluesX[i] + 35


    tip_text = ""    
    
    if "red.png" in clues: 
        tip_text = "没有数字匹配到"
    else:
        tip_text = "匹配到了相关数字"

    tip_label = ttk.Label(text=tip_text,font=('黑体',12))
    tip_label.place(x=tipX,y=385)
    label_list.append(tip_label)

def gameBody(isNew: bool):

    # 全局变量
    global numGuesses
    global isNewGame
    global secretNum
    global currentGuess_text

    if isNew:
        # 在游戏中
        isNewGame = False
        # 开始游戏时,创建随机数字
        secretNum = getSecretNum()

    # 用户输入的数字
    guess = ""

    guess = entry1_text.get() + entry2_text.get() + entry3_text.get()

    while numGuesses <= MAX_GUESSES:

        # 判断是否胜利
        if guess == secretNum:
            # 处理胜利页面,如弹窗,新页面
            resultFlag = True
            break

        # 获取提示
        clues = getClues(guess,secretNum)
        # 给出提示
        gaveClue(clues)

        # 如果失败了,将猜测数增加1
        numGuesses += 1

        # 更新当前次数
        currentGuess_text.set("当前次数:" + str(numGuesses))

        if numGuesses > MAX_GUESSES:
            print("失败了,正确答案: ",secretNum)  
            resultFlag = False 
            break

        # 这一回合结束,当重新输入3个数,并在最后1个数输入回车时,将会继续下一回合
        return;

    # 本轮游戏结束
    # 清空文本框
    gameOver(resultFlag)
    entry1_text.set("")
    entry2_text.set("")
    entry3_text.set("")  
    # 重新初始化 isNew
    isNewGame = True
    numGuesses = 1
    currentGuess_text.set("当前次数:" + str(numGuesses))
    for item in label_list:
        item.place_forget()


def gameOver(isSucc: bool):
    """
        游戏结束
        @param isSucc 是否成功! true : 成功
    """

    fg = ""
    bg = ""
    imgUrl = ""
    txt = tk.StringVar()

    if isSucc:
        # 成功处理逻辑
        fg = "#67c23a"
        bg = "#f0f9eb"
        imgUrl = "tip_succ_msg.png"
        txt.set("游戏胜利")
    else:
        fg = "#f56c6c"
        bg = "#fef0f0"
        imgUrl = "tip_fail_msg.png"
        txt.set("游戏失败")


    global img
    load_image = Image.open(imgUrl)
    load_image = load_image.resize((25,25))
    img = ImageTk.PhotoImage(load_image)

    pop_tip_label = tk.Label(text=txt.get(),width=160,justify="left",anchor="nw",compound="left",image=img)
    pop_tip_label.config(fg=fg,bg=bg,padx=20,pady=10)
    pop_tip_label.place(x=210,y=-50)

    def move():
        for i in range(25):
            pop_tip_label.place(x=210,y=i)
            time.sleep(0.011)

        time.sleep(1.5)    

        for i in range(24,-50,-1):
            pop_tip_label.place(x=210,y=i)
            time.sleep(0.011)

    t1 = Thread(target=move)
    t1.start()
    

if __name__ == '__main__':  
    main()

运行代码:

在一个目录下,创建 python 文件,并粘贴上述代码

cmd 中运行 python 代码 python 文件名.py

 好了,就可以玩了

posted @ 2023-04-12 19:38  辰梦starDream  阅读(8)  评论(0编辑  收藏  举报  来源