火柴棍移动游戏

这是我尝试发表的第一篇博文,内容基于一次课程大作业的报告,正在学习GitHub,计划把项目发布在GitHub上。萌新上路,希望前辈多多指教,也欢迎和大家一起交流!

任务描述

根据题目已有要求,对本次任务作出如下细化和明确:

用火柴棍摆成一个两位数以内的加、减、乘的等式或不等式,即运算数和结果均不超过99和-99,移动一根或两根火柴使得等式成立,并摆出所有可能的答案。

附加功能:

  1. 在database.txt文档中建立等式库,从中随机选取题目,并且用户可自行添加新的等式。

  2. 每道题目给出相应的难度等级。

问题建模

两位数运算的等式最大长度为10,如:-12-28=-40,最小长度为5,如:1+1=2
考虑空位,可将等式分解为包含10个元素的列表,因此整个任务归纳如下:

所有元素:0,1,2,3,4,5,6,7,8,9,+,-,*,=,空位,共15个

初始状态:由10个元素组成的等式列表

目标状态所有由10个元素组成的成立的等式列表

【注】对于初始状态即成立的等式,目标状态为新的成立的等式

操作函数:对于每个元素,在确定移动1根或2根的情况下,共有3种操作,即自身改变火柴、添加火柴、减少火柴

以元素6移动1根火柴为例,自身改变火柴可变为0或9,添加火柴可变为8,减少火柴可变为5.

代价函数:到达目标状态所验证的等式数目。由于本题要求给出所有可行答案,即要求对状态空间进行遍历,因此代价函数在本任务中并无实际价值。

算法设计和实现

  1. Char class:字符类的实现

在char.py文件中对字符类进行了定义,函数及其功能说明如下:

__init__构造函数:在对象实例化时进行赋值,等于相应的数字或符号(包括空位)

self_trans1自身改变1根函数:给出每个元素自身改变1根火柴时所得到的新的元素的列表,例如元素0会返回[6, 9]。

同理有inc1、dec1、self_trans2、inc2、dec2等函数,分别给出相应变化下得到的新元素列表。

  1. 核心搜索算法的实现

在main.py文件实现了核心搜索算法,通过深度优先搜索DFS对状态空间进行遍历,从而得到所有可能解。

下面对搜索相关的所有函数进行说明:

(1)1根火柴求解pro_solve1

首先检查改变自身是否成立,对于等式列表中的10个元素,依次调用self_trans1函数,同时注意将旧元素入栈保存。

对于每一个由新元素组成的新的等式列表,调用is_equation函数判断等式是否成立,若成立则加入答案ans列表,将旧元素出栈放回等式。

然后检查**‘1+1’**型,即从一位上减1根加在另一位上。

调用move1_1函数,共两层循环,外层对于等式列表中的10个元素,依次调用dec1函数。

内层对于每一个新元素,再对新等式列表中的每个元素,依次调用inc1函数,并通过is_equation函数判断等式是否成立,若成立则加入答案ans列表。注意两次改变不能为同一位置上的元素,因为自身减一加一的操作可等同于改变自身self_trans1。

注意栈的使用,每次需把旧元素入栈后,再进行新元素的操作,最后旧元素出栈。

(2)2根火柴求解pro_solve2

两根火柴求解思路更加多样,但在具体搜索方法上与1根相同。任务按照如下顺序对所有情况进行求解:

改变自身:依次调用self_trans2函数

‘1+1’型:自身改变1根,再调用move1_1函数移动1根

’-2+2’型:即从一位上减2根加在另一位上,调用move2_2

’-1-1+2’型:即从两位上分别减1根加在另一位上,调用move1_1_2

’2-1-1’型:即从一位上减2根分别加在另两位上,调用move2_1_1

’-1-1+1+1’型:即从两位上分别减1根分别加在另两位上,调用move1_1_1_1,到这里思路都1根火柴没有太大差别,只是由于状态空间增大,循环层数变多。

  1. 一些中间处理函数的实现

在process.py文件中实现了一些必要的处理函数,以方便调用,以下为功能说明:

split_expression:将输入的表达式进行分割,拆出数字和符号

create_obj:将分割好的表达式全部实例化,创建对象列表

is_equation:判断等式成立,主要方式为找出等号,对左右两边分别计算,判断是否相等。同时对于数字和符号的规范性也进行了必要检查,例如等式中有且仅可有一个等号,“±*”等运算符不可相连等等。

UI设计和使用说明

利用Python内置的GUI库Tkinter,在Matchstick.py中实现了UI和相应的交互函数,下面通过完整的使用流程对UI进行说明。

  1. 主界面
    在这里插入图片描述

左上角为任务说明,右上角为版权声明,上下两个空白区域将作为题目和答案的显示区域。作为附加功能之一,标签“难度等级”将会随题目变化而给出相应难度。

中间部分的输入框可供用户自行输入题目。

程序启动后将会从等式库database.txt中读取所有等式存入列表eqlst中。

  1. 生成题目
    在这里插入图片描述

点击“生成题目”按钮,将从等式列表中随机选取一个显示在上半区域。等式显示是由相应的火柴图案拼成。

  1. 查看答案
    在这里插入图片描述

点击“查看答案”按钮,要求选择“1根火柴”或“2根火柴”中的任意一个,否则程序会弹出提示框,如上图所示。
在这里插入图片描述

以2根火柴为例,当我们选中“2根火柴”并点击“查看答案”后,下半区域将会显示题目的其中一个答案,注意原本的“难度等级”标签已改为“易”。

程序根据答案数量对于难度进行定义,1解为“难”,2解为“中”,3解及以上为“易”。
在这里插入图片描述

点击“切换答案”按钮可继续查看其余答案,当所有答案均已显示,将给出如上提示。注意这里的答案已经和前一张图片不同。

特别的,若题目无解,将同样给出提示,如下图所示:
在这里插入图片描述

  1. 添加等式

作为又一附加功能,用户可自行向等式库添加新的等式,中间区域的输入框已经给出范例:-12+28=0。

当然,对用户输入会进行必要检查,在一定程度上确保符合规范,同时对于成立与否均可接受。点击“添加”按钮即可成功加入等式库,同时作为新的题目显示在上半区域:
在这里插入图片描述

  1. 退出程序
    在这里插入图片描述

点击右上角X,会弹出提示窗口提醒用户,若点击“确定”,则程序会将等式列表eqlst重新写回等式库database.txt。

实验总结

  1. 算法设计

通过本次任务,对于状态表示有了更深入的理解,能够将一个看似复杂的问题,抽象建模为具体的状态空间,再通过描述相对应的操作函数,实现对于问题的降解。

搜索算法采用了经典的DFS,由于在数据结构课程中已有相应的学习,所以通过这次训练进一步增强了对其的认知。

在实际调试过程中,出现问题最多的其实是is_equation函数,也就是如何判断等式成立,在确定基本的判断原则,即左右相等后,仍有大量的边界条件或者特殊情况是我在编程时想象不到的。正是不断地通过测试程序 -> 发现漏洞 -> 思考对策 -> 修补代码 -> 测试程序这样一个循环,才让我的程序的鲁棒性和完备性逐渐增强,也让我深刻认识到实际工程中测试环节的重要性。

  1. UI设计

本次任务多数同学选择了PyQt,但是我在C++小学期中已经体验过Qt这种拖拽式,因此在这次任务选择了更为轻量的Python内置库Tkinter。

所有的窗体布局均需由代码完成,虽然缺少了一些直观性,但具有操作性和确定性。

事实上,通过这次任务,我感觉UI设计最大的挑战不在于布局的美观,况且美工设计也不是我们的特长。整个过程我都在反复思考,所有的内置函数应该如何和窗体控件配合,如何处理用户可能的交互,是否尽可能考虑到所有的操作确保程序不轻易崩溃……诸如此类的问题才是最值得我们思考的地方,也是在UI设计中令我投入了最多精力。

运行说明

本次任务基于anaconda,利用内置的Spyder进行开发,因此其中用到的库均为anaconda内置,在利用pyinstaller打包生成.exe文件后,部分库可能无法正常运行。

因此,对于其他已安装Python的机器,可通过如下方式运行:

在程序目录下运行cmd,输入指令python Matchstick.py即可。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值