文章目录

  • 前言
  • 速算24点
    • 1. 玩法简介
    • 2. 游戏流程
    • 3. 剩下的部分
      • 1). 关卡 / 分数信息
        • IntVar类
      • 2). 提示按钮
        • 图片
        • 按钮
      • 3). 重新发牌
    • 4. 让电脑计算24点
      • 1). 表达式的特征
      • 2). 代码的实现
        • 将扑克牌转换成数字
        • 数字的排列组合
        • 添加运算符
        • 插入小括号
        • 返回运算结果
      • 3). 调用计算函数
    • 5. 知识点回顾
  • 完整代码

前言

大家好,最近实在是有点忙,下篇迟迟没有动笔。上一篇文章也结束得很匆忙,实在抱歉。代码部分其实早已写好,但是问哥还是想尽力将其拆解、讲解清楚,所以并不是直接分享代码。当然,如果想跳过问哥啰嗦的废话,直接参考代码,也可以跳到文章末尾查阅。

废话不多说,马上进入我们剩下的部分:

上篇 —— 游戏界面搭建
下篇 —— 功能代码实现


速算24点

1. 玩法简介

游戏规则比较简单:找一副扑克牌,去掉大小王,52张牌,每次随机抽取四张牌,运用加减乘除四种计算方法,看看谁能最快计算出24点。虽说是单机游戏,没有比赛的压力,但还是增加了计时功能:如果90秒内未能找出答案,则视为当前测试失败,自动进入下一关。同时,为了降低难度,提供了提示功能:闯关过程中,共有三次机会获得提示参考答案。

游戏截图:

2. 游戏流程

速算24点的简易流程图如下:

#mermaid-svg-iP7e0V6RVPS6jQVj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj .error-icon{fill:#552222;}#mermaid-svg-iP7e0V6RVPS6jQVj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iP7e0V6RVPS6jQVj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-iP7e0V6RVPS6jQVj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iP7e0V6RVPS6jQVj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iP7e0V6RVPS6jQVj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iP7e0V6RVPS6jQVj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iP7e0V6RVPS6jQVj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iP7e0V6RVPS6jQVj .marker.cross{stroke:#333333;}#mermaid-svg-iP7e0V6RVPS6jQVj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iP7e0V6RVPS6jQVj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj .cluster-label text{fill:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj .cluster-label span{color:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj .label text,#mermaid-svg-iP7e0V6RVPS6jQVj span{fill:#333;color:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj .node rect,#mermaid-svg-iP7e0V6RVPS6jQVj .node circle,#mermaid-svg-iP7e0V6RVPS6jQVj .node ellipse,#mermaid-svg-iP7e0V6RVPS6jQVj .node polygon,#mermaid-svg-iP7e0V6RVPS6jQVj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iP7e0V6RVPS6jQVj .node .label{text-align:center;}#mermaid-svg-iP7e0V6RVPS6jQVj .node.clickable{cursor:pointer;}#mermaid-svg-iP7e0V6RVPS6jQVj .arrowheadPath{fill:#333333;}#mermaid-svg-iP7e0V6RVPS6jQVj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iP7e0V6RVPS6jQVj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iP7e0V6RVPS6jQVj .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-iP7e0V6RVPS6jQVj .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-iP7e0V6RVPS6jQVj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iP7e0V6RVPS6jQVj .cluster text{fill:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj .cluster span{color:#333;}#mermaid-svg-iP7e0V6RVPS6jQVj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iP7e0V6RVPS6jQVj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

No
Yes
Yes
No
Yes
No 更换下一组
No
Yes
游戏开始
牌堆是否为0
抽取4张牌
计算该组合是否有解
倒计时开始
玩家输入答案
判断答案是否正确
停止倒计时
是否进入下一局
游戏结束
洗牌
有三次机会使用提示
时间用完,自动进入下一局

3. 剩下的部分

上篇内容里讲解了大部分界面的搭建,还有两个小地方,一个是关卡信息,一个是提示按钮。

1). 关卡 / 分数信息

IntVar类

上篇我们引入了StringVar类,实例了变量后,用来和标签绑定,可以直接将变量的内容显示在标签上,从而省去了不少麻烦。而StringVar是针对字符串,对于纯粹的数字,tkinter还提供了类似功能的IntVar类。同样地,将其和标签或其他组件绑定,即可直接将IntVar类实例的值显示出来。与StringVar相区别的是,IntVar是整数(int),可以直接参与计算,所以,用来记录玩家已测试的关卡数,以及通过的关卡(分数),也是十分方便的。

于是,我们先定义两个变量,实例化IntVar类:

level=IntVar()
score=IntVar()

再创建4个标签,两个用来显示固定字符“已测试”、“已通过”,另外两个用来和变量level、score绑定,以动态显示当前的关卡和分数。

cv.create_text(600,350,text='已测试:',font =('方正楷体简体',16,'bold'))
cv.create_text(600,400,text='已通过:',font =('方正楷体简体',16,'bold'))
level_lable = Label(root,text='',font=('微软雅黑',15),textvariable=level,bg='lightyellow')
cv_level = cv.create_window(670,350,window=level_lable)
score_lable = Label(root,text='',font=('微软雅黑',15),textvariable=score,bg='lightyellow')
cv_score = cv.create_window(670,400,window=score_lable)

实现效果如下图:

2). 提示按钮

提示按钮区域还分了两部分,一部分是按钮,另一部分是小灯泡的图片,用来表示玩家还剩多少次机会。

图片

问哥从网上找到的小灯泡的图片,将它改成png格式,然后命名为idea。

先定义一个常量HINT,用来表示玩家可以有多少次提示机会。默认是3次,当然你也可以改成5次,更多或更少。然后再通过for循环,在指定的位置绘制3个灯泡图片,并将这些绘制的组件放在一个列表ideas里。

HINT = 3
idea = PhotoImage(file=r"image\poker\idea.png")
ideas = []
for i in range(HINT):ideas.append(cv.create_image(450+i*25,450,image = idea))

为什么放进列表呢?因为可以方便我们在按下提示按钮后,自动减少图片并在画布上刷新。

按钮

按钮的创建很简单,我们之前也介绍过。问哥为了加速完成这个小项目,就不使用特效了,于是默认使用tkinter的Button组件。

btn = Button(root,text='提示',width=5,command=hint)
cv_btn = cv.create_window(400,450,window=btn)

关键是我们需要为这个按钮绑定一个回调函数,起名叫hint,在该函数里,我们要完成以下三个功能:

  1. 获取正确答案right_answer,并显示在标签上
  2. 将按钮禁用,以防止玩家不小心点击了多次,从而浪费了提示次数
  3. 减少一个“小灯泡”

实现代码如下:

def hint():answer.set(right_answer)btn['state']=DISABLEDidea = ideas.pop()cv.delete(idea)

同样的道理,为了防止玩家误操作(你永远无法完全预见玩家或用户大开脑洞的操作),也为了减少程序的可能,我们规定该按钮只允许在被点击的时候启用,换句话说,除了倒计时开始,玩家开始答题时,而且提示次数没有用完(ideas>0),按钮的状态是NORMAL,其他时间该按钮都应该处于DISABLED状态。

于是,我们分别在其他位置做以下更新:

def initialize():# 省略代码btn['state']=DISABLEDdef draw_card():# 省略代码if len(ideas)>0: btn['state']=NORMAL

测试效果基本OK:

3). 重新发牌

代码写到这里,除了还没有让电脑替我们找出正确答案,已经实现基本的输入、判断等功能了。试着运行一下:

发现回答正确、在选择继续下一局后,桌面上的发牌混乱了,不知道新的四张牌跑到哪里去了,这是怎么回事?

原来,我们在上篇用了一个数组cv_card来储存抽出来的四张牌,并使用for循环将这四张牌显示在桌面上,但是没有考虑到当一局游戏结束,需要重新抽四张牌的情况。在这种情况下,我们需要把桌面上的4张牌从画布上擦去,用代码的话主要是两步:

  1. 删除图片
  2. 清空列表

为了避免重复操作,我们再定义一个子程序来完成清除clear的操作:

def clear(cv_card):for i in cv_card:cv.delete(i)cv_card.clear()

然后,我们再把该函数放在抓牌函数的开始,这样,每次开始抓牌前,我们都先清除(如果有的话)上一轮的四张牌,从而保证代码对新的四张牌依然适用。

def draw_card():clear(cv_card)

同样的理由,我们在重新开始下一局后,答案标签的内容也需要清空,所以我们需要在initialize函数以及myanswer函数里添加以下代码:

def initialize():answer.set('')# 省略代码
def myanswer(event):# 省略代码if s=='BackSpace':txt=txt[:-1]elif s=='Return':if is_right(txt):root.after_cancel(cd)c = tm.askyesno(message='继续下一局吗?')if c:# 省略代码return # 添加 return,表示进入下一局后就不继续显示标签

经测试,在没有电脑帮助的情况下,已经可以成功地“自食其力”进行闯关了。可问题是,常常会出现这种情况:抽出四张牌,却很难写出答案,因为我们也无法确定是我们没有想出答案,还是这四张牌根本没有答案。所以,我们需要设计一个方法,让电脑替我们先算好答案,保存在变量right_answer里,如果没有答案的话,就自动更换下一组。有了right_answer,提示按钮也就可以发挥作用了。

4. 让电脑计算24点

算起来,这部分其实是相对独立的代码,因为我们可以把要解决的问题从这个游戏里抽离出来,转换为“给定4个数,计算出能否通过排列组合,使得这四个数组成的算式运算结果等于24。”

1). 表达式的特征

为了解决这个问题,我们观察一下四个数字组成的算术表达式的特征:

  1. 不考虑小括号的情况(也相当于一对小括号把四个数字包起来)。算式的形式为:(a+b+c+d),其中有4个数字,3个运算符(这里加号+只是代表运算符,可以是加减乘除的任意一种)。
  2. 考虑小括号的情况又分为两种:
第一种情况:一对小括号 第二种情况:两对小括号
(a+b)+c+d (a+b)+(c+d)
a+(b+c)+d ((a+b)+c)+d
a+b+(c+d) (a+(b+c))+d
(a+b+c)+d a+((b+c)+d)
a+(b+c+d) a+(b+(c+d))
  1. 再进一步观察可以发现,就像(a+b+c+d)的括号可以省略一样,(a+b+c)+d,a+(b+c+d)也一定与两对括号的情况重复:(a+b+c)+d 必定等价于 ((a+b)+c)+d 或 (a+(b+c))+d;a+(b+c+d) 必定等价于 a+((b+c)+d) 或 a+(b+(c+d))。所以最终,我们只需要考虑8种情况:
    (a+b)+c+d
    a+(b+c)+d
    a+b+(c+d)
    (a+b)+(c+d)
    ((a+b)+c)+d
    (a+(b+c))+d
    a+((b+c)+d)
    a+(b+(c+d))
    当然,这里仍然存在较大重复计算的可能性,假如三个运算符都是加法或乘法的话,上面八个表达式都是等价的,所以这并不是一个最优的算法。

2). 代码的实现

虽然存在很多重复计算,但在不考虑时间复杂度,以及运算量并不算大的情况下,完全可以让电脑进行穷举运算,把所有的可能性都检查一遍。于是我们可以把代码的实现过程分成三步:

  1. 找出4个数字所有不重复的排列组合,最多有24种可能(4!)
  2. 找出3个运算符(加减乘除)的排列组合,因为运算符可重复使用,所以是4^3=64种可能。(这里面有一些组合是不可能计算出24点的,比如连续三个减号或除号,但是如果添加小括号使得计算顺序发生改变的话,结果将有所不同。为了省事,这里就把所有排列组合都考虑了)
  3. 在算式的不同位置添加小括号。根据前面列举的,总共有8种可能。

于是,不考虑存在重复的情况下,最多总共有 24*64*8 = 12288 种可能。这点计算量对人类来说可能望而却步,但是对电脑来说简直不值一提。更何况,我们并不用找出所有正确答案,而是只要找到一个即可。

下面我们开始写代码:

将扑克牌转换成数字

我们首先要做的,就是把抽取的4张牌转化为数字。因为扑克牌的数字是从0到51,但是所代表的用于计算的数字却是从1到13,这其实可以通过简单的求余运算来实现。

于是,我们定义一个函数:

def calnum(n):global numsnums=[i%13+1 for i in n]formula=form(nums)

这个函数接收一个变量n,代表含有4张牌的列表,然后通过列表生成式(或称为推导式)将其转换成一个实际用于计算的数字的列表nums。然后再调用另一个自定义函数form()将这个列表通过排列组合转化成含有最多12288个表达式的列表,保存为formula。

需要注意的是,我们还要将nums声明成全局变量。这样做的唯一目的,是为了在判断玩家输入的时候,是否只使用了给定的4个数字。于是,我们顺便将判断玩家输入的函数 is_right() 也更新如下:

def is_right(txt):# 省略代码if sorted(txt)!=sorted(nums):tm.showinfo(message='请使用给定的数字!')return False# 省略代码

下面我们接着写form()函数。

数字的排列组合

为了不重复制造轮子,我们可以直接使用Python提供的内置模块来计算排列组合。

from itertools import permutations
def form(nums):numlist=set(permutations(nums))

从itertools模块中导入permutations函数以后,就可以使用它来计算列表的排列组合了。这个函数接收两个参数,第一个是列表等可迭代对象,第二个是数字,表示从前面的列表里取出几个元素进行排列组合,可以省略,如果省略的话,则表示默认对所有元素进行排列组合。于是在这里,我们可以省略第二个参数,直接将含有4个数字的nums列表交给permutations函数,返回一个可迭代对象。同时,为了去重,比如四张牌里有重复的数字,3,3,4,4这种,我们可以将这个结果转换为集合set,最终将结果保存在numlist里。

在控制台测试结果:

>>> from itertools import permutations
>>> nums = [1,2,3,4]
>>> numlist = permutations(nums)
>>> type(numlist)
<class 'itertools.permutations'>
>>> for i in numlist: print(i,end=' ')
(1, 2, 3, 4) (1, 2, 4, 3) (1, 3, 2, 4) (1, 3, 4, 2) (1, 4, 2, 3) (1, 4, 3, 2) (2, 1, 3, 4) (2, 1, 4, 3) (2, 3, 1, 4) (2, 3, 4, 1) (2, 4, 1, 3) (2, 4, 3, 1) (3, 1, 2, 4) (3, 1, 4, 2) (3, 2, 1, 4) (3, 2, 4, 1) (3, 4, 1, 2) (3, 4, 2, 1) (4, 1, 2, 3) (4, 1, 3, 2) (4, 2, 1, 3) (4, 2, 3, 1) (4, 3, 1, 2) (4, 3, 2, 1)
>>>

可以看到,如我们所预想的,4个不重复的数字可以组成最多24个互不重复的组合。

添加运算符

接下来,我们需要在这四个数字中间插入三个运算符,当然,我们首先要找到运算符组成的64种组合,使用三层循环即可实现,并将结果保存在列表operations里。

operations=[]
for i in '+-*/':for j in '+-*/':for k in '+-*/':operation = i+j+koperations.append(operation)

因为这个列表会被频繁调用(每发四张牌,就要插入运算符),所以我们可以将它放在主程序里,这样只要在游戏开始时运算一次,在函数里就可以一直调用(而不修改)。

接着,在函数form里,我们同样使用for循环来将3个运算符插入到4个数字之间:

def form(nums):numlist=set(permutations(nums))combo=[]for num in numlist:for i in operations:temp=[]for j in range(3):temp+=[str(num[j]),i[j]]temp.append(str(num[j+1]))combo.append(temp)

因为最终我们需要将表达式转换成字符串,所以在这里我们就可以使用str函数将数字转换成字符串,保存在列表combo里。根据之前计算的,combo里最多应该存在 24*64 = 1536 个元素,代表了1536个表达式。但是现在它们任然是单个的字符,因为我们还需要插入小括号。

插入小括号

小括号用于提升运算等级、改变运算顺序。根据前面所分析的,由于默认计算顺序为先乘除、后加减、从左到右依次运算,所以不包含小括号的情况(a+b+c+d)一定等价于某种使用小括号的情况。所以最终我们只需要考虑8种小括号的情况:三种一对小括号的情况,和五种两对小括号的情况。但是该怎样插入呢?

通过之前的计算,我们得到的combo二维列表里的表达式列表应该类似这个样子:
[‘1’, ‘+’, ‘2’, ‘+’, ‘3’, ‘+’, ‘4’]

可见,每个表达式都是包含7个字符串元素(4个数字、3个运算符)的列表。于是,我们只要事先找到需要添加小括号的位置(索引),就可以通过循环添加了。

通过比较,我们将两种情况(一对和两对小括号)的左右小括号的位置索引找到,并创建列表如下:

    one=[(3,0),(5,2),(7,4)]two=[(7,3,5,0),(5,3,0,0),(5,5,2,0),(7,5,2,2),(7,7,4,2)]

需要注意的是,我们要先插入右边的小括号,再插入左边小括号。因为插入元素以后,列表的长度改变,为了方便计算,先插入右边小括号可以最大程度保持相对位置。在两对小括号的时候也是如此(先插入右侧的括号),只需要注意其中一种情况:(a+b)+(c+d)。在这种情况下,插入两个右括号之后,夹在两个右括号中间的左括号位置发生了变化,只要记录新的位置即可。

于是,通过索引列表,我们可以将form函数补全:

def form(nums):numlist=set(permutations(nums))combo=[]for num in numlist:for i in operations:temp=[]for j in range(3):temp+=[str(num[j]),i[j]]temp.append(str(num[j+1]))combo.append(temp)one=[(3,0),(5,2),(7,4)]two=[(7,3,5,0),(5,3,0,0),(5,5,2,0),(7,5,2,2),(7,7,4,2)]formula=[]for i in combo:for j in one:temp=i[:]temp.insert(j[0],')')temp.insert(j[1],'(')formula.append(''.join(temp)) # 将列表转化为字符串for j in two:temp=i[:]temp.insert(j[0],')')temp.insert(j[1],')')temp.insert(j[2],'(')temp.insert(j[3],'(')formula.append(''.join(temp)) # 将列表转化为字符串return formula

返回运算结果

最后,我们得到包含最多12288个表达式的列表formula,并返回到函数calnum里使用eval函数进行计算。

def calnum(n):# 省略代码formula=form(nums)for i in formula:try:result = eval(i)except:continueif math.isclose(result,24):return ireturn 'None'

通过for循环遍历formula列表,依次计算结果是否等于24。如果正确,则把正确答案(表达式)返回,如果遍历所有12288种可能都没有结果,则返回字符串None。

3). 调用计算函数

按照我们之前的逻辑,在每次抓取4张牌之后,我们都需要电脑帮忙计算,看看当前4张牌能否计算出24点,如果不能,则自动进入下一局(重新抓牌),如果可以,则将答案保存在变量right_answer里,方便提示(hint)按钮调用。于是,我们更新相应部位的代码即可:

def draw_card():global cv_card, right_answer    invalid=Truewhile invalid:clear(cv_card)draw=[]if len(cardnum)==0:tm.showinfo(message='牌堆已用完,为您重新洗牌')shuffle_card()for i in range(4):draw.append(cardnum.pop())cv_card.append(cv.create_image(100,200,image=card[draw[i]]))if len(cardnum)==0:cv.delete(cv_back)for _ in range(150*(i+1)):cv.move(cv_card[i],1,0)cv.update()right_answer = calnum(draw) # 调用函数计算12288种可能if right_answer=='None':tm.showinfo(message='本组数字无解,为您自动更换下一组')else:countdown()if len(ideas)>0: btn['state']=NORMALinvalid=False

如果4张牌无法计算24点,就需要一直重新抽牌,直到可以计算为止。于是这里使用一个while循环,并给定一个标记 invalid 假定当前组合无法计算24点。只有当得到答案时(right_answer的值不是None),invalid变为False,结束while循环,开始计时等后续程序。

5. 知识点回顾

  1. Canvas的delete方法
  2. IntVar类型
  3. permutations函数

到这里,我们这个“速算24点”的小游戏就做好了。大家可以继续在里面添加其他想要的功能,改变布局、颜色等等。最终运行效果如下:


完整代码

from tkinter import *
import tkinter.messagebox as tm
import random
import math
from itertools import permutationsdef shuffle_card():global cardnum, back, cv_backcardnum = list(range(52))random.shuffle(cardnum)back = PhotoImage(file=r"image\poker\back1.png")cv_back = cv.create_image(100,200,image = back)def clear(cv_card):for i in cv_card:cv.delete(i)cv_card.clear()def draw_card():global cv_card, right_answer    invalid=Truewhile invalid:clear(cv_card)draw=[]if len(cardnum)==0:tm.showinfo(message='牌堆已用完,为您重新洗牌')shuffle_card()for i in range(4):draw.append(cardnum.pop())cv_card.append(cv.create_image(100,200,image=card[draw[i]]))if len(cardnum)==0:cv.delete(cv_back)for _ in range(150*(i+1)):cv.move(cv_card[i],1,0)cv.update()right_answer = calnum(draw)if right_answer=='None':tm.showinfo(message='本组数字无解,为您自动更换下一组')else:countdown()if len(ideas)>0: btn['state']=NORMALinvalid=Falsedef initialize():global angle,count,cv_arc,cv_inner,cv_textcount=90angle=360btn['state']=DISABLEDanswer.set('')cv_arc=cv.create_oval(100,330,200,430,fill='red',outline='yellow')cv_inner=cv.create_oval(120,350,180,410,fill='yellow',outline='yellow')cv_text=cv.create_text(150,380,text=count,font =('微软雅黑',20,'bold'),fill='red')draw_card()def countdown():global angle,count,cv_arc,cv_inner,cv_text,cdif angle == 360:angle -= 1else:cv.delete(cv_arc)cv.delete(cv_inner)cv.delete(cv_text)cv_arc=cv.create_arc(100,330,200,430,start=90,extent=angle,fill="red",outline='yellow')angle -= 1if angle%4 == 0: count-=1cv_inner=cv.create_oval(120,350,180,410,fill='yellow',outline='yellow')cv_text=cv.create_text(150,380,text=count,font =('微软雅黑',20,'bold'),fill='red')if count==0:tm.showinfo(message='倒计时结束!自动进入下一局')level.set(int(level.get())+1)cv.delete(cv_arc)cv.delete(cv_inner)cv.delete(cv_text)initialize()else:cd = root.after(250,countdown)   def myanswer(event):s=event.keysymtxt=answer.get()if s=='BackSpace':txt=txt[:-1]elif s=='Return':if is_right(txt):level.set(int(level.get())+1)score.set(int(score.get())+1)root.after_cancel(cd)c = tm.askyesno(message='继续下一局吗?')if c:cv.delete(cv_arc)cv.delete(cv_inner)cv.delete(cv_text)initialize()returnelse:root.destroy()else:txt=''elif s.isnumeric():txt+=selif s in trans:txt+=trans[s]answer.set(txt)def is_right(txt):try:result = eval(txt)except:tm.showinfo(message='算式不正确,请重新输入!')return Falsefor i in '+-*/()':txt=txt.replace(i,' ')txt=[int(i) for i in txt.split()]if sorted(txt)!=sorted(nums):tm.showinfo(message='请使用给定的数字!')return Falseif math.isclose(result,24):tm.showinfo(message='恭喜您!回答正确!')return Truedef hint():answer.set(right_answer)btn['state']=DISABLEDidea = ideas.pop()cv.delete(idea)def calnum(n):global numsnums=[i%13+1 for i in n]formula=form(nums)for i in formula:try:result = eval(i)except:continueif math.isclose(result,24):return ireturn 'None'def form(nums):numlist=set(permutations(nums))combo=[]for num in numlist:for i in operations:temp=[]for j in range(3):temp+=[str(num[j]),i[j]]temp.append(str(num[j+1]))combo.append(temp)one=[(3,0),(5,2),(7,4)]two=[(7,3,5,0),(5,3,0,0),(5,5,2,0),(7,5,2,2),(7,7,4,2)]formula=[]for i in combo:for j in one:temp=i[:]temp.insert(j[0],')')temp.insert(j[1],'(')formula.append(''.join(temp))for j in two:temp=i[:]temp.insert(j[0],')')temp.insert(j[1],')')temp.insert(j[2],'(')temp.insert(j[3],'(')formula.append(''.join(temp))return formula# 游戏从这里开始
HINT = 3
operations=[]
for i in '+-*/':for j in '+-*/':for k in '+-*/':operation = i+j+koperations.append(operation)
trans={'plus':'+','minus':'-','asterisk':'*','slash':'/','parenleft':'(','parenright':')'}
root = Tk()
root.geometry('800x500+400+200')
root.resizable(0,0)
root.title('速算24点')
# 画布大小和主窗口大小一致
cv = Canvas(root,width=800,height=500)
# 背景图片
bg = PhotoImage(file=r"image\poker\bg.png")
cv_bg = cv.create_image(400,250,image = bg)
# 标题图片
title = PhotoImage(file=r"image\poker\title.png")
cv_title = cv.create_image(400,60,image = title)
# 显示答案及关卡分数等信息
answer=StringVar()
level=IntVar()
score=IntVar()
cv.create_text(400,350,text='请输入您的答案',font =('方正楷体简体',18,'bold'))
lb = Label(root,text='',font=('微软雅黑',15),textvariable=answer,bg='lightyellow')
cv_lb = cv.create_window(400,400,window=lb)
cv.create_text(600,350,text='已测试:',font =('方正楷体简体',16,'bold'))
cv.create_text(600,400,text='已通过:',font =('方正楷体简体',16,'bold'))
level_lable = Label(root,text='',font=('微软雅黑',15),textvariable=level,bg='lightyellow')
cv_level = cv.create_window(670,350,window=level_lable)
score_lable = Label(root,text='',font=('微软雅黑',15),textvariable=score,bg='lightyellow')
cv_score = cv.create_window(670,400,window=score_lable)
# 提示图片及按钮
idea = PhotoImage(file=r"image\poker\idea.png")
ideas = []
for i in range(HINT):ideas.append(cv.create_image(450+i*25,450,image = idea))
btn = Button(root,text='提示',width=5,command=hint)
cv_btn = cv.create_window(400,450,window=btn)# 绑定从键盘获取输入<Key>,并传给自定义函数myanswer
lb.bind('<Key>',myanswer)
# 使标签组件获得焦点,不然无法从键盘输入
lb.focus_set()card = [PhotoImage(file=f'image/poker/{i:0>2}.png') for i in range(1,53)]
cv_card=[]
cv.pack()
shuffle_card()
initialize()
root.mainloop()

Python写个小游戏:速算24点(下)相关推荐

  1. Python写个小游戏:蛇棋(下)

    文章目录 前言 蛇棋 1. 玩法简介 2. 实现棋子移动 前言 惭愧啊,三个月没更新下篇,问哥想起了西岳奇童...按你胃,最近确实忙得不可开交,忙着忙着,就忘记了在CSDN上继续游戏梦想了.另一方面, ...

  2. python写游戏脚本-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  3. python编程小游戏-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  4. 使用python制作聊天框解谜游戏_使用Python写一个小游戏alien invasion!

    最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下其中的 ...

  5. 手机版python3h如何自制游戏_教你如何用 Python 写一个小游戏

    教你如何用 Python 写一个小游戏 引言 最近 python 语言大火, 除了在科学计算领域 python 有用武之地之外, 在游戏后台等方面, python 也大放异彩, 本篇博文将按照正规的项 ...

  6. 关于python小游戏的毕业论文_使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  7. 用python做一个简单的游戏,用python写一个小游戏

    大家好,本文将围绕如何用python做一个简单的小游戏展开说明,python编写的入门简单小游戏是一个很多人都想弄明白的事情,想搞清楚用python做一个简单的游戏需要先了解以下几个事情. 1.Pyt ...

  8. Python写个小游戏:速算24点(上)

    文章目录 前言 速算24点 1. 玩法简介 2. 游戏流程 3. 搭建游戏界面 1). 基本界面 2). 洗牌.发牌 洗牌 发牌 3). 计时器 4). 玩家输入公式(答案) StringVar类 按 ...

  9. python能制作游戏吗_如何用python写一个小游戏

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 引言最近python语言大火,除了在科学计算领域python有用武之地之外,在游 ...

最新文章

  1. DNS,edns,httpdns杂谈
  2. 小程序tabbar这套方案全搞定!
  3. IOS – OPenGL ES 调节图像伽马线 GPUImageGammaFilter
  4. 文件包含漏洞原理/利用方式/应对方案
  5. 周末计划-20171028
  6. C#之http transfer-Encoding模式
  7. mt4 谐波_MT4指标AB=CD Dashboard — AB = CD 谐波模式仪表盘指示器
  8. 9.【Windows】如何制作原版Win10系统盘
  9. java课设迷宫游戏_Java课程设计走迷宫.doc
  10. Win10免费升级 Win11 有时间限制
  11. (十)打包和项目部署
  12. 电信:自娱自乐的全员揽装,让人心寒!
  13. uni-app动态设置原生标题左侧按钮
  14. 非局部相似性 matlab,非局部均值滤波(NLM)和MATLAB程序详解视频教程保持图像细节...
  15. MSF图形化界面Viper(炫彩蛇)下载与使用
  16. 把java项目打包成安装包,在windows下安装
  17. 叶史瓦大学计算机专业,叶史瓦的大学排名
  18. ZOJ 3381 Osaisen Choudai !
  19. 7-11 Say Hello to Integers
  20. 怎么用ai做出适量插画_如何用AI把照片制作成矢量线条插画

热门文章

  1. 计算机系统英语作文结尾万能句子,英语作文结尾万能句型整理
  2. 什么是系统的可扩展性?
  3. 不用P图!用Python给头像加圣诞帽并制作成可执行软件!
  4. windows模拟微信小程序_GitHub - ramwin/mini-program-reference: 微信小程序测试
  5. 微前端调研及简析SPA实现原理
  6. 前端隐秘角落 - HTTP请回答
  7. 微服务架构设计实践系列之九:应用架构
  8. mysql优化慕课网_性能优化之MySQL优化(慕课)
  9. ArrayMap的源码分析
  10. 金蟾论金:4.12黄金涨势不足重启跌势-晚间走势操作建议