24点游戏实现(合作完成)
前言
这是我进入大学的第一个制作项目,代码极其不规范且存在很多问题,请不要直接进行复制运行,存在诸如绝对路径等问题难以解决。
这是一个合作项目,合作人有:彭力韬,唐金,曹浩然
1.游戏实现的基本功能
(1)设计相应的游戏积分功能,以给出题目到游戏者输入结果之间的时间记分。超过一定的时间未给出答案则取消该轮,每轮10次,记总分(10次分相加)前十名。
(2)游戏参与者进入游戏必须先输入名称,游戏开始后,程序随机自动生成四张牌,游戏者要求尽快给出表达式,只能使用 + - × % 运算。可以使用(),不涉及小数运算。提交结果正确加十分,否则不加分并进入下一题。若无解则点击无解按钮,正确则加十分,否则不加分。如此玩10次后,计算总分并存入文件。任何一次在要求时间内不能给出表达式或无解,则进入下一题。
(3)可根据选择的难度不同,每一轮的时间也不同
(4)可判别玩家输入的算式的值是否等于24点
(5)可使用三次请求支援,给出这四个数字是否可以构成24点
2.需求分析
(1)提供人性化且简介明了的游戏开始界面。游戏开始界面需要包含用户名输入框,用户名登入按钮,难易度选择按钮、排行榜查询按钮以及开始游戏按钮。
(2)提供易于操作的游戏界面按钮。游戏界面按钮包含随机抽取的扑克牌,算式显示框、无解按钮、运算按钮、提交按钮以及只能使用3次的请求支援按钮以及结束按钮
(3)提供可根据难易度发生变化的计时器
(4)设计能够匹配包含括号的24点运算式子的算法
(5)设计能够读取写入并且自动排行的排行榜
3.概要设计
主要存储结构
# 创建开始游戏GUI界面 def start_game(): # 创建开始游戏窗口 top = Tk() top.title('开始游戏') top.geometry('480x400') # 添加背景 bg_img = PhotoImage(file=r'C:\Users\SF-TS\Desktop\Python\background2.gif') Label(image=bg_img).pack() # 添加难易度选择标签 v = IntVar() rad_easy = Radiobutton(top, text='简单', width=4, height=1, variable=v, value=1, font=('黑体', 16),command=lambda: difficultbutton('简单')) rad_normal = Radiobutton(top, text='普通', variable=v, value=2, width=4, height=1, font=('黑体', 16),command=lambda: difficultbutton('普通')) rad_hard = Radiobutton(top, text='困难', variable=v, value=3, width=4, height=1, font=('黑体', 16),command=lambda: difficultbutton('困难')) # 游戏难度标签位置 rad_easy.place(x=330, y=150) rad_normal.place(x=330, y=200) rad_hard.place(x=330, y=250) # 添加控件函数 add_widgets_1(top) # 锁定界面长宽比 top.wm_resizable(width=False, height=False) top.mainloop()
主要函数流程
(1)开始界面创建函数:用于创建游戏的开始界面,并录入游戏者的姓名以及提供积分榜前十名的查询
(2)控件添加函数函数:为界面添加控件提供相应的功能
(3)结束游戏函数:
4.源代码
from random import randint from tkinter import * from itertools import permutations from time import sleep import threading import operator # 设置全局变量 control = 0 # 点击开始游戏打开新窗口 count = 0 # 记录所做题目总数 var_time = 0 # 倒计时时间 dif = 0 # 选定困难难度 score = 0 # 记录分数 ID = None # 用户名默认为空 # 创建开始游戏GUI界面 def start_game(): # 创建开始游戏窗口 top = Tk() top.title('开始游戏') top.geometry('480x400') # 添加背景 bg_img = PhotoImage(file=r'C:\Users\SF-TS\Desktop\Python\background2.gif') Label(image=bg_img).pack() # 添加难易度选择标签 v = IntVar() rad_easy = Radiobutton(top, text='简单', width=4, height=1, variable=v, value=1, font=('黑体', 16), command=lambda: difficultbutton('简单')) rad_normal = Radiobutton(top, text='普通', variable=v, value=2, width=4, height=1, font=('黑体', 16), command=lambda: difficultbutton('普通')) rad_hard = Radiobutton(top, text='困难', variable=v, value=3, width=4, height=1, font=('黑体', 16), command=lambda: difficultbutton('困难')) # 游戏难度标签位置 rad_easy.place(x=330, y=150) rad_normal.place(x=330, y=200) rad_hard.place(x=330, y=250) # 添加控件函数 add_widgets_1(top) # 锁定界面长宽比 top.wm_resizable(width=False, height=False) top.mainloop() # 给开始界面添加控件 def add_widgets_1(frame): # 销毁开始游戏界面 global dif def new_win(): global control control = True frame.destroy() # 添加游戏题目标签 lab_name = Label(frame, text='24点小游戏闯关', width=25, height=1, font=('黑体', 25), bg='#EBF5DF', fg='#35081D') # 添加开始游戏标签 btn_start = Button(frame, text='开始游戏', command=new_win, width=11, height=1, font=('黑体', 16), state=DISABLED) # 开始游戏按钮 # 添加登陆标签 expression = Text(frame, width=20, height=1, bg='#EBF5DF', fg='#35081D', font=('楷体', 20)) btn_login = Button(frame, text='登陆用户名', width=10, height=1, font=('黑体', 16), command=lambda: input_char1('登陆用户名', expression, btn_start)) # 添加游戏说明标签 lab_exp = Label(frame, width=34, height=14, font=('黑体', 12), bg='#EBF5DF', fg='#35081D', text='输入您的用户名点击登陆,选择难度\n' '并且开始您的愉快游戏吧!\n\n' '游戏开始后您将获得四张随机的扑克\n' '牌,请利用四则运算法则将四张扑克\n' '牌数值进行组和,使算式合理并且计\n' '算的结果为24点即可获得积分,每轮\n' '游戏共有十道题为您呈现,答对一题\n' '获得10分,答错不给分。欢迎来到惊\n' '险刺激的24点游戏!') # 添加排行榜按钮 btn_score = Button(frame, text='排行榜', command=lambda: input_char1('排行榜', expression, btn_start), font=('黑体', 16)) # 标签位置 lab_name.place(x=20, y=10) expression.place(x=20, y=100) btn_login.place(x=330, y=97) lab_exp.place(x=20, y=150) btn_score.place(x=330, y=300) btn_start.place(x=330, y=350) # 游戏难度选定功能 def difficultbutton(char): global dif if char == '简单': dif = 1 elif char == '普通': dif = 2 elif char == '困难': dif = 3 # 开始游戏按钮功能 def input_char1(char, expression, the_button): global dif, ID # 将获取到的用户名写进文件里 if char == '登陆用户名': with open(r'C:\Users\SF-TS\Desktop\Python\board.txt', 'a') as f: try: if expression.get('1.0', '1.end') == r'C:\Users\SF-TS\Desktop\Python\board.txt': f.write('匿名') ID = '匿名' the_button.config(state=NORMAL) else: f.write(expression.get('1.0', '1.end')) ID = expression.get('1.0', '1.end') the_button.config(state=NORMAL) finally: f.close() expression.delete('1.0', END) expression.insert('1.end', '登入成功!') # 设置难度系数 elif char == '排行榜': score = Tk() score.title('排行榜') score.geometry('400x200') score.wm_resizable(width=False, height=False) index() with open(r'C:\Users\SF-TS\Desktop\Python\board.txt', 'r') as f: for i in range(10): line = f.readline() name = '%s' % (line) Label(score, text=name,bg = '#F5F5DC').place(x=0, y=i * 20) Label(score, text='第'+ str(i+1) +'名',bg = '#F5F5DC').place(x=150, y=i * 20) f.close() score.mainloop() else: pass # 创建游戏GUI界面 def game(): win = Tk() # 创建界面 # 给标签添加背景 bg_img = PhotoImage(file=r'C:\Users\SF-TS\Desktop\Python\background1.gif') new_game = Label(win, image=bg_img) # 在界面上添加标签,并赋予背景 new_game.pack(fill=BOTH, expand=1) # 标签填充 add_widgets_2(new_game) # 在标签上添加控件 win.title('24点小游戏闯关') # 界面标题 win.geometry('480x700') # 界面大小 win.wm_resizable(width=False, height=False) # 锁定界面长宽比 # 添加倒计时标签,时间归零自动开始下一题 lab_time = Label(new_game, text='', width=15, height=1, font=('黑体', 16)) lab_time.place(x=40, y=650) def times(): global var_time for i in range(750, -1, -1): if var_time != 0: var_time -= 1 else: add_widgets_2(win) lab_time['text'] = '剩余时间:' + str(var_time) + '秒' sleep(1) t = threading.Thread(target=times) t.start() # 循环界面 win.mainloop() # 给游戏界面添加控件 def add_widgets_2(frame): global ID poker_number = rand_number() # 扑克牌数值 poker_color = ['meihua', 'fangkuai', 'hongtao', 'heitao'] # 扑克牌花色 poker_path = [] # 扑克牌路径 for i1, i2 in zip(poker_color, poker_number): poker_path.append(r'C:\Users\SF-TS\Desktop\Python\poke\sucai\%s%d.gif' % (i1, i2)) # 构建扑克牌路径 result = test24(poker_number) # 能算出24点的算式 # 添加扑克牌图片 global img1, img2, img3, img4, lab_img1, lab_img2, lab_img3, lab_img4 img1 = PhotoImage(file=poker_path[0]) img2 = PhotoImage(file=poker_path[1]) img3 = PhotoImage(file=poker_path[2]) img4 = PhotoImage(file=poker_path[3]) lab_img1 = Label(frame, image=img1) lab_img2 = Label(frame, image=img2) lab_img3 = Label(frame, image=img3) lab_img4 = Label(frame, image=img4) # 添加显示框控件 expression = Text(frame, width=39, height=2, bg='#EBF5DF', fg='#35081D', font=('楷体', 15)) # 添加四个数值按钮 poker_1 = Button(frame, text=poker_number[0], width=6, height=2, font=('黑体', 16), command=lambda: input_char2(poker_number[0], result, expression, btn_tijiao, btn_no)) # lambda使得可以给函数input_char传递参数poker_number[],result和expression poker_2 = Button(frame, text=poker_number[1], width=6, height=2, font=('黑体', 16), command=lambda: input_char2(poker_number[1], result, expression, btn_tijiao, btn_no)) poker_3 = Button(frame, text=poker_number[2], width=6, height=2, font=('黑体', 16), command=lambda: input_char2(poker_number[2], result, expression, btn_tijiao, btn_no)) poker_4 = Button(frame, text=poker_number[3], width=6, height=2, font=('黑体', 16), command=lambda: input_char2(poker_number[3], result, expression, btn_tijiao, btn_no)) # 添加四则运算按钮 btn_jia = Button(frame, text='+', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('+', result, expression, btn_tijiao, btn_no)) btn_jian = Button(frame, text='-', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('-', result, expression, btn_tijiao, btn_no)) btn_cheng = Button(frame, text='*', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('*', result, expression, btn_tijiao, btn_no)) btn_chu = Button(frame, text='/', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('/', result, expression, btn_tijiao, btn_no)) # 添加括号、撤销、清除、重新开始、请求支援等按钮 btn_kuohao_1 = Button(frame, text='(', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('(', result, expression, btn_tijiao, btn_no)) btn_kuohao_2 = Button(frame, text=')', width=6, height=2, font=('黑体', 16), command=lambda: input_char2(')', result, expression, btn_tijiao, btn_no)) btn_delete = Button(frame, text='撤销', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('撤销', result, expression, btn_tijiao, btn_no)) btn_clear = Button(frame, text='清除', width=6, height=2, font=('黑体', 16), command=lambda: input_char2('清除', result, expression, btn_tijiao, btn_no)) btn_no = Button(frame, text='无解', width=10, height=3, font=('黑体', 16), command=lambda: input_char2('无解', result, expression, btn_tijiao, btn_no)) # 下一题按钮点击后调用addWidgets函数,重新生给界面生成新的控件,并且刷新全局变量的值 global var_time, dif if dif == 3: var_time = 25 elif dif == 2: var_time = 50 else: var_time = 75 # 设置只能点十次下一题 global count, score btn_next = Button(frame, text='下一题\n\n已做' + str(count) + '题', width=13, height=3, font=('黑体', 16), command=lambda: add_widgets_2(frame)) if count < 10: btn_next['state'] = 'normal' else: btn_next['state'] = 'disabled' if count == 10: f = open(r'C:\Users\SF-TS\Desktop\Python\board.txt', 'a') print(score) f.write(' ' + str(score) + '\n') f.close() count += 1 # 点击一次,count+1 btn_tijiao = Button(frame, text='提交', width=6, height=8, font=('黑体', 16), command=lambda: input_char2('提交', result, expression, btn_tijiao, btn_no)) # 设置只能点击后就不能提交的查看答案按钮 btn_help = Button(frame, text='查看答案', width=10, height=3, font=('黑体', 16), command=lambda: input_char2('查看答案', result, expression, btn_tijiao, btn_no)) # 用户名标签 lab_name = Label(frame, text=ID, width=15, height=1, font=('黑体', 16)) # 结束游戏的按钮 btn_end = Button(frame, text='结束游戏', width=10, height=3, font=('黑体', 16), command=lambda: end(score)) # 设置控件的布局 lab_img1.place(x=10, y=20) lab_img2.place(x=125, y=20) lab_img3.place(x=240, y=20) lab_img4.place(x=355, y=20) expression.place(x=40, y=200) poker_1.place(x=40, y=280) poker_2.place(x=120, y=280) poker_3.place(x=200, y=280) poker_4.place(x=280, y=280) btn_jia.place(x=40, y=343) btn_jian.place(x=120, y=343) btn_cheng.place(x=200, y=343) btn_chu.place(x=280, y=343) btn_kuohao_1.place(x=40, y=406) btn_kuohao_2.place(x=120, y=406) btn_delete.place(x=200, y=406) btn_clear.place(x=280, y=406) btn_tijiao.place(x=360, y=280) btn_help.place(x=40, y=490) btn_no.place(x=162, y=490) btn_next.place(x=284, y=490) lab_name.place(x=40, y=600) btn_end.place(x=317, y=600) # 结束游戏 def end(score): global count if count<11: f = open(r'C:\Users\SF-TS\Desktop\Python\board.txt', 'a') f.write(' ' + str(score) + '\n') f.close() exit() # 游戏界面按钮功能 def input_char2(char, result, expression, btn_tijiao, btn_no): global score # 清除键功能 if char == '清除': expression.delete('0.0', END) # 清除内容 # 撤销键功能 elif char == '撤销': expressionview = expression.get('1.0', '1.end') # 光标定位到最后一位 expression.delete('1.0', END) # 删除最后一位 expression.insert('1.end', expressionview[:-1]) # 光标向前移动一位 # 请求支援功能 elif char == "查看答案": if result: expression.delete('1.0', END) expression.insert('1.0', result[0][0]) else: expression.delete('1.0', END) expression.insert('1.0', '不能构成24点哦!') btn_tijiao['state'] = 'disabled' btn_no['state'] = 'disabled' # 无解功能 elif char == '无解': # answer为判断数,0为无解,1为有解 answer = 0 if result: answer = 1 if answer == 0: expression.insert('1.0', '\n恭喜您,回答正确!按重新开始进行下一轮') score += 10 else: expression.insert('1.0', '\n很遗憾,回答错误!按重新开始进入下一轮') # 排行榜功能 elif char == '排行榜': score = Tk() score.title('排行榜') score.geometry('400x200') score.wm_resizable(width=False, height=False) # 其余键功能为插入对应字符 elif char != '提交': expression.insert('1.end', char) # 等于号功能 try: if char == "提交": result = expression.get('1.0', '1.end') # 获取文本框最后一个字符的值 expression.insert('1.end', '=%d' % (eval(result))) # 在 "=" 后面插入结果,结果为eval函数计算的值 if eval(result) == 24: # 结果正确返回信息 expression.insert('1.end', '\n恭喜您,运算正确!按重新开始进行下一轮。') score += 10 else: expression.insert('1.end', '\n回答错误') btn_tijiao['state'] = 'disable' except: expression.insert('1.end', '\n输入错误!请检查你的算式是否合理') def rand_number(): # 随机四张扑克牌函数 result = [randint(1, 10) for j in range(4)] return result #排序 def index(): f = open(r'C:\Users\SF-TS\Desktop\Python\board.txt','r')#文件打开 inf_content = f.readlines()#行读取 list_temp = []#暂定列表 list_name = []#名字列表 for each in inf_content:#除去空格 list_temp.append(each.split(' ')) for i in range(len(list_temp)): list_temp[i][1] = int(list_temp[i][1]) list_temp = sorted(list_temp, key=operator.itemgetter(1), reverse=True)#排序 f.close() dict_temp = dict(list_temp)#字典 f = open(r'C:\Users\SF-TS\Desktop\Python\board.txt','w')#清空源文件 for name in dict_temp: f.write(str(name)+' '+str(dict_temp[name]) + '\n') f.close() # 24点算法 def test24(poker_number): # 4个数字和两个运算符可能组成的表达式 exps = ('%s %s %s %s %s %s %s', '(%s %s %s) %s %s %s %s', '%s %s (%s %s %s) %s %s', '%s %s %s %s (%s %s %s)', '((%s %s %s) %s %s) %s %s', '(%s %s %s) %s (%s %s %s)', '%s %s (%s %s (%s %s %s))') ops = '+-*/' result = [] # Python允许函数的嵌套定义 # 这个函数对字符串表达式进行求值并验证是否等于24 def check(exp): try: # 有可能会出现0异常,所以放到异常处理结果中 return eval(exp) == 24 except: return False # 全排列,枚举4个数的所有可能顺序 for a in permutations(poker_number): # 查找4个数的当前排列能实现24的表达式 t = [exp % (a[0], op1, a[1], op2, a[2], op3, a[3]) for op1 in ops for op2 in ops for op3 in ops for exp in exps if check(exp % (a[0], op1, a[1], op2, a[2], op3, a[3]))] if t: result.append(t) return result # 主函数 if __name__ == '__main__': # 创建开始游戏窗口 start_game() # 创建游戏窗口,同时销毁开始游戏窗口 if control: game()
5.结果