j2me实现九宫格拼图

时间:2022-12-14 23:07:32

实验要求

九宫格的拼图游戏,上下左右键用来移动,采用贴图实现。

程序思路

将一张图片分成三行三列进行贴图,首先需要打乱顺序,但保证右下角为空白。然后上下左右可以进行图片的移动,将空白处和四周进行交换,如果最终达到完整的图片则游戏结束,获得胜利。

         关键之处是,如果一个拼图随意打乱顺序是否可以还原。这是不一定的。在网上查阅资料表明,将贴图标号顺序排成一列,最后的位置是0,如果两个序列的逆序数的奇偶性是一致的,则可以由一个图形得到另一个。我们期望的游戏结果是得到{1,2,3,4,5,6,7,8,0}的序列,逆序数为偶数,则判断随机生成的拼图的逆序数是否为偶数即可。

         打乱拼图的方式我采用了随机数,用一个数组保存这个序列,除了最后一位即下标为8的数外,随机获得0到7的数字,交换位置,实现打乱,如果拼图不可解则重新打乱,每次打乱五次。

         Gamecanvas中一个线程循环判断是否有上下左右按下,并进行重新贴图,每次也判断是否赢得胜利,若获胜则切换到结束界面。

拼图可解性

链接地址:http://blog.csdn.net/tailzhou/article/details/3002442 有一个3*3的矩阵,里面分别填着数字0~8,填入的时候是随机的,要求每次只能用0和和边上的一个数字交换,最终实现所要求的数字排列。
如:
随机真数字矩阵为:
1 3 5
0 2 6
4 7 8 0,可以与1,2,4交换
最终变成目标矩阵
1 2 3
8 0 4
7 6 5
中间可进行无限次0与其他数字的交换。
问:
任意给一个数字矩阵,能否证明:经过无限次的交换,一定能到达目标矩阵或者经过无限的交换也不能实现目标矩阵?

这是一个3*3的数字拼图游戏;

考虑更一般化的问题:对于两任意排列的m*n的数字矩阵(两矩阵包含有相同的元素,都为分别为0--n*m-1,但排列顺序不同)A与B,如何判断A能否经过一系列的合法的移动(每次只能用0和和边上的一个数字交换),转换成B;

这是一个经典的问题,用逆序数的奇偶性来判断数字拼图游戏的解的存在性也早有定论;

但我没找到详细的证明;因此自己尝试给出一个通俗的证明;请各位指正!

定义:

序列:m*n的数字矩阵的m*n个元素的排列方式有(m*n)!种;指定一种排列规则后,m*n的数字矩阵可以得到一个对应的序列(序列中包含有元素0);

以下的讨论中的提到的序列都假定遵循某同一种排列规则,但不局限于任何特定的排列规则;

引理1:交换序列的任意两元素,序列的逆序数的奇偶性必定发生改变;

这是教科书上的定理,不再赘述;

从定理1,交换1次改变奇偶性,再交换再次改变奇偶性,所以连续交换2次,序列的逆序数的奇偶性必定保持不变;所以有

推论1:对序列进行偶数次的交换,序列的逆序数的奇偶性保持不变;

推论2: "对于任意的m*n的数字矩阵,经过任意k次合法移动后,若0回到原来的位置,那么序列的逆序对的奇偶性保持不变" 
1)由于每次合法移动都是跟0进行的,总的移动次数 ==0的移动次数总计;
2)对于数0,由于其位置保持不变,那么必定需要经过偶数次的左右移动,以及偶数次的上下移动;
由推论1, 所以推论2得证;

推论3: "对于任意的m*n的数字矩阵,假设经过多次合法移动后,交换了两非0元素的位置,且其他元素的位置保持不变,那么必定需要经过与0偶数次的左右交换,以及偶数次的上下交换"; 
由于交换前后0的位置保持不变,由推论2,推论3得证;


然而从引理1:交换序列的任意两元素,序列的逆序数的奇偶性必定发生改变;

这与推论3是相互矛盾的,所以推论3的假设是不存在的;

推论4: "对于任意的m*n的数字矩阵,无法经过任意次的合法移动,使得:交换了两非0元素的位置,且其他元素的位置保持不变"; 即:若A与B只有两非0元素的位置不一致,其他的(包含0)元素的位置是相同的,那么A不能通过合法移动转化成B;

下面来讨论一个具体的移动策略;
对于任意的m*n的矩阵,不妨假设行m>=列n;
假设源状态矩阵为A,目标状态矩阵为B;

1) 总能将目标状态B的0移动到左上角的位置;
将新的目标状态记为B',由于每步移动都是可逆的,所以从B可以移动到B',那么必定也可以从B'移动到B;
因此,A能否移动到B 等价于 A能否移动到B';
2) 若m>n,那么总可以移动A 使得 A的最后一行与B'的最后一行的元素与位置完全相同;
假设B'的最后一行的元素从左到右依次为B'[1..n];
a) 对于A中的B'[1],总可以将该元素先移动到目标位置的上方;然后依次移动使得其下方目标位置为0;再交换 B'[1]与 0,即可将A中的B'[1]移动到目标位置;
b) 假设A的最后一行的前i个元素已放置完毕,那么对于第i+1 <n个元素,总可以用与a)同样的方法,将A中的B'[i+1]移动到目标位置;
c) 对于A中的B'[n],先将其移动到A的倒数第2行的倒数第2列,然后将0移动到A中的B'[1]的 上方,再将B'[1..n-1]各顺时针移动一个位置;将B'[n] 下移,然后再将B'[1..n]各逆时针移动一个位置;
这样就完成了 "移动A 使得 A的最后一行与B'的最后一行的元素与位置完全相同"
记 A的前m-1行组成的子矩阵为A',B'的前m-1行组成的子矩阵为新B'

那么循环执行 2) 直到 A'的行数与列数相同;

3) 若A'的行数与列数相同,那么总可以移动A' 使得 A'的最后一行与最后一列与B'的最后一行与的元素与位置完全相同;
略,方法类似与2);

那么循环执行 3),直到 A'的行数与列数都为2;

4) 对于2*2的矩阵A' 与B',设 B'中的0的对角位置的元素为c,那么总可以按照顺(或者逆)时针移动A',c移动到正确的位置,然后移动0到正确位置;
那么可以得到两个不同的状态;
A) 若 A'的另两位置的元素与B'的对应位置的元素一模一样,即 A'==B',记此情况的A'为B1(B1==A'==B');
由移动的可逆性, 那么源状态矩阵A必定可以移动到目标状态矩阵B;
B) 若 A'的另两位置的元素与B'的对应位置的元素不一样,即 该两位置的元素是颠倒的,记此情况的A'为B2;

由于B1与B2相比,除两位置的元素的位置互换外,其他元素的位置一致;
由推论4,可以得到 B1与B2是不能相互转化的;

所以有
推论5: 对于任意的特定的目标状态B,所有的源状态A可以分成不相交的两类,一类可以转化成B1(即A可以转化成B),一类可以转化成B2(即A不可以转化成B);
将源状态A中的0移动到左上角,得到新的A',那么A'与B'的0的位置是相同的;
对于A',必定要么可以转换到B1,要么可以转换到B2; 但A',B1,B2其0的位置是相同的,由推论2:其序列的逆序对的奇偶性保持不变; 又B1,B2的 逆序对的奇偶性是不同;

所以有最终的结论:

对源状态A与目标状态B进行规范化,使得两矩阵的元素0的位置相同;记为新的源状态A'与目标状态B';
若A'与B'的逆序对的奇偶性相同(即A'与B1的逆序对的奇偶性相同),则A'必定可能转化为B',即A可以转化到B;
若A'与B'的逆序对的奇偶性不同(即A'与B2的逆序对的奇偶性相同),则A'必定不可能转化为B',即A不可以转化到B;

遇到问题

1、  在语句t1 = new TiledLayer(9,9,img,img.getWidth()/3,img.getHeight()/3);中,如果后两个参数不整除会报错java.lang.IllegalArgumentException,所以要修改图片的大小,使用了210.

2、  在jdk1.3中没有封装Integer类型,而vector的element是object类型,所以没办法将int放进去,本来想随机获得一个值,然后删掉,在依次随机获得值并顺序贴图,后来就自己使用数组的交换实现了。

3、  本来使用t1.paint(g)来重画,这样就算之前用矩形填充也无法达到清屏的目的,后来借鉴之前实验的方法,采用myGameCanvas对象的repaint方法,将重新设置贴图标号的值放在了paint函数中,实现清屏和更新。

源码地址

http://download.csdn.net/detail/felicitia/5317955