【原创-更新完毕】|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载1
【原创】|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载2_zhuyi8120的博客-CSDN博客
【原创】|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载3_zhuyi8120的博客-CSDN博客
【原创】|日历拼图游戏的解决方案(C语言-进阶应用)-详解连载4_zhuyi8120的博客-CSDN博客r
【原创】|源码全文-日历拼图游戏的解决方案(C语言-进阶应用)-详解连载5_zhuyi8120的博客-CSDN博客
后续的链接见上。
问题提出:
日历拼图是一款拼图玩具。见图1所示。
图1:日历拼图(未排列之前)
图2:今天(12月6日)的日历拼图
在7×7的方格中,去掉2+4个格子,用8块不同形状的积木进行拼图,再留出2个格子表示月份和日期。因为每天所留下的空格不一样,相当于每天都要拼一次,单纯人工去拼是有一定难度的(也正是有一定难度,才吸引一些爱好者去玩)。
方案运行的结果:
本文使用C语言,运用数组表示格点,使用指针链进行搜寻满足要求的拼法。结果如下图所示(无图形界面)。
图3:求12月6日拼图的运行结果
问题分析:
(对于完成一个解决方案,前期对问题进行较完善的分析要比看到一个问题就直接写代码的方式要好很多,这是磨刀不误砍柴功。)
1、拼图底盘(以下简称底盘)有43个点,采用数组表示各节点。这是一种常规的做法,适合于解决当前问题。
2、以'-'表示空节点,这样做,相对于用空格(' ')来表示,效果比较好。因为空格有时候难以辨认。
3、根据8种方块的特点,因为长和宽都没有超过4格的,所以,可采用4×4的方格来表示。
4、注意到对于一个普通的不规则方块来说,是有8种不同的表现形式。分别是每旋转90度就是一种新形式,不翻面就有4种;在翻面之后,又有另外4种。这些会在初始化阶段完成。
5、同时也要注意到,在本游戏的这8个方块里面,其中有4种是有8种变化形式的,有3种是有4种变化,而那个3×2的方块,是只有2种变化形式的。分析到这点,对于减少后期尝试的次数有很大帮助。简化后较不作简化的方式,可以节省15/16的搜寻空间(2×2×4)。
6、在7×7的格子里,按4×4满排来计算,初始坐标为(0,0)的话,可以从(0,0)开始,向(3,3)的4×4个格子里逐个搜寻就可以了。但实际上,我们可以看到,只有一个方块的长边是有4个格子的,其他7个的长边都是只占3个格子。因此针对不同的方块要表示出他们特有的搜寻空间。以最简单的3×2方块来说,横放时的搜索空间为从(0,0)到(4,5),共需搜寻30个格子;竖放时,是从(0,0)到(5,4),虽然也是30个格子,但有几个格子是不一样的。
图4:不同变体的搜寻范围不一样
这就要求对不同的方块的不同变体要做一个标识。
7、每个方块在初始时,都会设置在左上角,这样对搜寻是很有好处的。但我们在进行旋转和翻面时,容易出现不在左上角的情况。
图5:在旋转时,出现不在左上角的情形
当图形不在左上角的时候,对搜寻是会有难度的。主要体现在初始点不容易确定。如图中的右图,以(0,0)为初始搜寻是可能会遗漏的。这就需要进行适合的规整化处理,以便于搜寻的正常进行。
8、判断一个方块是否可以放入,这个比较容易。前面我们提到了用‘-’来表示空格,然后,我们可以用不同的字母来表示(上面两图已经有用不同的字母表示不同的方块)。当底盘的某一格是空格(‘-’)时,而新加入的方块为实体(有字母)时,就表示可以加入;如果新加入的方块中有一个格是有字母的,而底盘原来也已经有字母了,这形成了一个“冲突”,就说明不能加入了,要换另一个地方放这个方块了。
图6:不能再插入方块F的例子
9、把可以加入的方块插入底盘也是比较容易的。就是把底盘的空格(‘-’)更新为字母。
图7:可以插入方块H的例子
10、如何判断一个方块不能插入底盘,这是一个难点。
首先,我们可以分析,如果在搜寻范围里(第6点提到)都搜寻过了,出现了冲突(第8点的情况),那就说明不能插入。
其次,如果简单的用搜寻到最后一个单元格来判断,是没法做到的。因为,可能最后一个就刚好是合适的(比如满足要求的排法最后一个就是合适的)。
所以,要想个合适的方式来进行判断。最后我选择了冲突次数的方式来判断。
11、当能够判断在某一种排法里,所有的可能都已经尝试过了,要进行回调时,如何处理。我一开始是打算用图示的方式来解决的,但我没想到可行的方式(或许有更好的方式可以,但我不知道)。最后,我还是选择了用指针和链表来实现。
附:笔者的尝试:
自2021年11月27日(星期五)开始尝试写代码,到2021年12月4日中午总算完成。这是当时完成的截图(尝试了211万多次)和拼好的拼图。