逆序數-拼圖遊戲必備知識
近兩天準備出個拼圖遊戲的教程,準備時候遇到一些問題,收集保存分享下來,一來自己用,二來漲點知識,三來有需要的小夥伴剛好也看看。
事情起因很簡單,比如下面這個拼圖(矩陣):
1 2
3 空
這樣一個2*2矩陣,是標準原始矩陣,但是變一下:
3 1
2 空
這樣是可還原的(空和附近的可以換位置,空2,空3,空1,空2即可)。
但下面這個呢?
1 3
2 空
你還能還原嗎?
這個不是怪你,是真的沒法還原。
當時腦子裏面過了一下,就發現不行,然後查了查資料,真的有前輩搞過相關的問題,並且有了靠譜的答案。
假如兩個矩陣,從左到右,從上到下排序,兩個矩陣的逆序數+空所在行+空所在列數的值的奇偶性相同,則可以還原或者說等價;若不同,那就無法還原。
又上面說的問題,嘗試去玩了幾個拼圖遊戲,發現這些家夥都是雞賊,各個塊可以直接點擊與任意位置的互換,那就不存在無解的問題了啊……
問題是,這樣遊戲本身的難度就幾乎沒了,遊戲性簡直不能看……
哦,對了,忘了說啥是“逆序數”,逆序數解釋如下:
比如1234排列是基準,然後兩個排序分別是4321、3214。
逆序數說的是在原有排序基礎上,在新排列出現與基礎排序順序前後順序不同則算一個逆序數。
那麽分析那4321。43、42、41三個,32、31兩個,21一個,所以逆序數是1 + 2 + 3 = 6個;而3214,31、32兩個,21一個,所以逆序數是2 + 1 = 3個。1234為基準,即0個。所以4321與1234是等價的,可解;3214與1234不等價,不可解。
那最上面的對比,123是基準,312逆序數為2,132逆序數為1,所以132不可解。
算起來很復雜,但是通過雙層循環來處理,只要不是太多的數字都還可以接受,畢竟是給人玩得遊戲,就算10*10的矩陣,時間復雜度頂天了也就O^2,優化成OlgO也不是不行,所以還是可以搞搞的。
具體算法代碼如下:
// 基準就是自然排序,遞增是正常的,就是後面一定比前面的都大。 const cal = num => { let arr if(typeof num === ‘number‘) { let strs = String(num) arr = Array.from(strs, str => Number(str) || 0) // 安全處理,轉換錯誤給0 } else arr = num let length = arr.length let reverse = 0 for(let i = 0; i < length - 1; i++) { let n = arr[i] for(let j = i + 1; j < length; j++) { let m = arr[j] if(n > m) reverse += 1 } } console.log(reverse) } cal(4321) // 6 cal(3214) // 3 cal(312) // 2 cal(132) // 1 cal(54321) // 10 cal([10, 14, 21, 7, 3, 12, 0]) // 15
這樣就好了,下次要寫拼圖遊戲時候,只要去專註業務邏輯就行,遊戲初始化的問題就用這個代碼拓展即可。
還有一種方法就是初始化的時候按照標準序模擬合法移動N次,然後在逆向工程即可。但是這個方案拓展性幾乎為0,所以放棄。
寫業務要註意這種陷進邏輯哈,祝你玩的開心。
喜歡文章的話,請關註一波,定期更新技術文章,滿滿的都是幹貨。
逆序數-拼圖遊戲必備知識