實驗要求九宮格的拼圖遊戲,上下左右鍵用來移動,採用貼圖實現。程式思路
將一張圖片分成三行三列進行貼圖,首先需要打亂順序,但保證右下角為空白。然後上下左右可以進行圖片的移動,將空白處和四周進行交換,如果最終達到完整的圖片則遊戲結束,獲得勝利。
關鍵之處是,如果一個拼圖隨意打亂順序是否可以還原。這是不一定的。在網上查閱資料表明,將貼表徵圖號順序排成一列,最後的位置是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