學更好的別人,
做更好的自己,
——《微卡智享》

本文長度為2498字,預計閱讀7分鐘
前言
上一篇《C++ OpenCV生成九宮格影像》介紹了如何將圖片分割城九宮格,然后重新打亂了順序顯示出來,本篇就來說一下怎么制作一個九宮格的拼圖游戲,專案的重新創建了,和數字華容道在一個原始碼中,最后會放出鏈接,

實作效果

Q1
九宮格拼圖和數字華容道差別在哪?
其實這個問題最主要的就是兩個方面:
1.原來的資料華容道是4X4的布局,九宮格拼圖是3X3的布局,關于隨機打亂順序通過逆序數解決無解的處理方式不同,
2.數字華容道的布局格里本身就存在一個0的空格,可以直接移動,九宮格通過將圖片分割后,占格是滿的,所以要考慮處理一個單獨移來的格,這里我是將最右下的圖在固定位置處理的,
重點講解
微卡智享
01
關于隨機打亂順序無解情況
這個問題在《整活!我是如何用OpenCV做了數字華容道游戲!(附原始碼)》其中說過,因為逆序數的問題,需要做調整,計算逆序對的方法那里面已經封裝好了一個類,所以這次就直接參考進來即可,不過在九宮格中的處理方式,
在3X3的拼圖中,因為我們隨機生成的影像最后一塊是固定的,所以把這塊拋開,然后計算了前面的影像逆序數,如果逆序數為偶數時可以保證拼圖是能夠還原的,
三階拼圖
在求解拼圖的可還原性時需要把空白塊去掉,因為空白塊是可以自由移動的,計算它的逆序會增加復雜性,
對于 3 x 3 的拼圖,把每一個圖塊標號為 0,1,2,3,4,5,6,7,8,去掉空白塊8號后,保證非空白的8塊(即標號為0,1,2,3,4,5,6,7的圖塊)組成序列的逆序數為偶數即可保證拼圖可還原,

如上圖所示,圖①到圖②交換了空白塊與圖塊7,逆序數沒有變,都為0,圖②到圖③交換了空白塊與圖塊4,逆序數增加到2,圖④的逆序數為2,交換空白塊與圖塊6后變成圖⑤的情況,逆序數還是2,再將圖塊3與空白塊交換變成圖⑥的情況,逆序數還是2,
從中可以看出,在水平方向交換空白塊與其他圖塊時,整個數列(去掉空白塊后)所有數字的順序是完全不變的,只有在垂直方向上交換空白塊與其他塊時,某些數字的順序才會改變,并且只改變了三個數的順序,如圖②到圖③,由4,5,6改變為5,6,4,也就是將4往后移動了2個位置,其他數字與這三個數字的相對順序都是沒有改變的,所以數列逆序數的改變取決于這三個數內部的逆序數的變化,而這三個數的逆序數的改變取決于移動的數與另外兩個數的大小關系,在這里移動的是4,另外兩個數是5與6,都大于4,所以移動4,逆序數將會加2或減2,而圖⑤到圖⑥,由3,2,4改變成2,4,3,將3往后移動了2個位置,移動的是3,另外兩個數是2與4,一個大于3一個小于3,所以移動3之后,逆序數不變,因此我們可以得出結論:每次交換空白塊與其他圖塊時,無論是水平方向還是垂直方向,數列逆序數要么加減2,要么不變,所以三階拼圖的圖塊標號構成的數列,其逆序數為偶數,這個拼圖就是可以還原的,
————————————————
上面這段為CSDN博主「☆熱帶冰川☆」的原創文章,原文鏈接:
https://blog.csdn.net/weixin_42438777/article/details/84723308
因為我這里隨機生成后做為空白塊就是最后一個,所以不會存在空白塊與其他圖塊交換,于是這里我直接將第三行第一列和第二個第一列的影像進行交換即可解決這個無解的問題,

而計算逆序數的方式就是和數字華容道一樣那個CalcReverseNum這個類的方法即可,因此這個專案我也把路徑規劃類和計算逆序數類都單位移動到Utils下了,兩個專案共同參考就好,



02
關于右下角處理
剛開始的布局九個格都是滿的,所以加入了一個狀態的引數,當游戲未開始時,只允許點擊右下角的圖片,將其移動到整個影像的下方,留出空格來,
然后滑鼠的點擊事件就要多加一步判斷,除了計算二維陣列中的位置,還要加入點擊的右下角時是游戲開始處理還是進行程序中處理,
bool ImgPuzzles::ImageMove(int& row, int& col, int& curposition, int& newposition)
{
bool res = true;
int cols = vtsCutMat[0].size();
if (Status == 0) {
if (vtsCutMat.size() - 1 != row || vtsCutMat[vtsCutMat.size() - 1].size() - 1 != col) {
std::cout << "請先移動右下格開始" << std::endl;
res = false;
}
else {
//移開最右下角的圖片
finalCutMat = vtsCutMat[row][col];
newposition = vtsContours.size();
finalCutMat->curposition = newposition;
vtsCutMat[row][col] = nullptr;
}
}
else if (Status == 1) {
//計算可移動的區域
//0.判斷是不是結束位置
if (row == 99 && col == 99) {
int tmprow = vtsCutMat.size() - 1;
int tmpcol = vtsCutMat[tmprow].size() - 1;
if (vtsCutMat[tmprow][tmpcol] == nullptr) {
//移開最右下角的圖片
newposition = tmprow * cols + tmpcol + 1;
finalCutMat->curposition = newposition;
vtsCutMat[tmprow][tmpcol] = finalCutMat;
finalCutMat = nullptr;
}
else
{
res = false;
}
}
//1.左邊
else if (col - 1 >= 0 && vtsCutMat[row][col - 1] == nullptr) {
newposition = row * cols + col;
vtsCutMat[row][col]->curposition = newposition;
vtsCutMat[row][col - 1] = vtsCutMat[row][col];
vtsCutMat[row][col] = nullptr;
}
//2.右邊
else if (col + 1 <= vtsCutMat.size() - 1 && vtsCutMat[row][col + 1] == nullptr) {
newposition = row * cols + col + 2;
vtsCutMat[row][col]->curposition = newposition;
vtsCutMat[row][col + 1] = vtsCutMat[row][col];
vtsCutMat[row][col] = nullptr;
}
//3.上邊
else if (row - 1 >= 0 && vtsCutMat[row - 1][col] == nullptr) {
newposition = (row - 1) * cols + col + 1;
vtsCutMat[row][col]->curposition = newposition;
vtsCutMat[row - 1][col] = vtsCutMat[row][col];
vtsCutMat[row][col] = nullptr;
}
//4.下邊
else if (row + 1 <= vtsCutMat.size() - 1 && vtsCutMat[row + 1][col] == nullptr) {
newposition = (row + 1) * cols + col + 1;
vtsCutMat[row][col]->curposition = newposition;
vtsCutMat[row + 1][col] = vtsCutMat[row][col];
vtsCutMat[row][col] = nullptr;
}
else {
res = false;
}
}
return res;
}
03
關于影像顯示
原來做數字華容道時,移動位置變換后都是重新繪制影像然后全部顯示,這次個修改了只有生成游戲時一次全部繪制,點擊移動時,只針對要替換的兩個位置的影像進行交換處理,
定義了一個靜態顯示影像

滑鼠事件中curposition和newposition分別是點擊的位置和移動后的位置

通過DrawPuzzleMat方法輸入的新舊位置,將兩個區域的影像進行交換,再重新顯示,
運行影像

原始碼地址
https://github.com/Vaccae/OpenCVNumPuzzles.git
碼云地址點擊文末的原文鏈接


往期推薦



C++ OpenCV生成九宮格影像

制作一個Android Sqlite遠程運維小工具

VS2022 MAUI Hello World——Windows平臺及Android平臺效果

點個在看你最好看
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/377217.html
標籤:其他
上一篇:python做猜拳小游戲
