用React寫一個數字華容道,你須要知道的祕密

還在上班?很無聊? 數字華容道暢玩地址 css

開發源碼地址html

這個叫前言

年底了。哦,不,要過年了。之前只能一路站到公司的我,今早竟然是坐着過來的。新的一年,總要學一個新東西來迎接新的將來吧,因此選擇了一直未碰的那個聽說是全宇宙最牛逼的前端框架-React,在上下班的地鐵上看了兩天官方教程,so what。光看不練假把式,因而就想着作個什麼,偶然看到一個妹妹發了一條關於玩數字華容道,根本停不下來的朋友圈,這遊戲我在今年的最強大腦看過,可是就看兩小天才在滑呀滑呀滑,感受還不錯,程序猿就該多玩益智類,少玩什麼跳一跳。因而去商店下了個,玩着還行,就是廣告太多,並且只能玩五階如下的,看起不難,一個想法涌於腦上,何不拿這練練手作個Demo,畢竟咱們屬於智慧家族的。閒話扯完,進入正題。本文包含但不只包括如下內容:前端

  • Demo 開發環境
  • 數字華容道里的祕密
  • 講一講裏面算法的實現
  • React的使用感覺及易錯點

Demo 開發環境

React:16.2.0react

react-router-dom:4.2.2webpack

webpack:3.8.1git

JS:ES6github

CSS:Scssweb

React教程算法

項目目錄結構(很適合React入門 若是感受不錯,請留下你的star):以下圖所示
項目目錄結構編程

數字華容道里的祕密

玩法說明

數字華容道里在外國被稱爲puzzle,譯爲數字推盤,最經典的就是puzzle15的高價懸賞13 15 14局面的解。怎麼玩,一圖勝千言,簡單來講就是講左圖的亂選狀態,滑成右圖所示的順序狀態。我的感受和小時候玩的推箱子有點相似。

clipboard.png

你不知道的祕密

其實寫完Demo的隨機序列頁面生成和操做交互就用了一個周天和一個週一夜,但到如今線上這個樣子,又多用了三四個晚上(白天上班,晚上學習,這就是前端的平常),爲何?由於當時基礎版部署到線上,讓女友試一把,結果玩個三階的,都兩分鐘了還在折騰(我最快是18秒還原),怎麼這麼笨,因而搶過來,搗騰,再搗騰,怎麼回事,感受無解啊,因而去百度了一下,在知乎裏看到了這個問題,還真的有無解的狀況,問題地址。看圖,後面還要講(敲黑板)

clipboard.png

噢,原來是醬紫。光隨機生成一個亂序數列是不夠的,還得保證這個數列的逆序數爲偶數,嗦嘎。因而在隨機序列的生成中又多加一個過程,判斷序列逆序數奇偶性,並調整。早上地鐵上本身又不斷玩玩測試,三階ok,四階ok,五階ok,而後又一遍,一遍。原覺得這樣就大功告成了,可是出現了這樣的畫面。有圖。誰剛說的四階ok。。。。因而又測試了屢次,發現三階,五階確實ok,但四階確實有Bug。Why,後面本身每開一局,截一張圖,無解的,標記下來,下面就是幾張立功的圖片。
立了大功的幾張圖
下午年會,領導上面講,本身下面睡。睡得天昏地暗,時過境遷,竟然還在叨叨叨,本身就拿起酒店提供的紙和筆找這些數字間的祕密。首先,這幾組數字都是偶逆序列,前一晚寫的調整奇逆序列爲偶逆序列的代碼是沒有問題的,那問題出在哪了。抓腦摳鼻,抖腳搖頭,那一組圖片來回翻閱,靈光一閃,水哥附身,原來是這樣:空格項都出如今第三行,哦,不,應該是奇數行。爲何呢?又去百度,又看到了上面知乎和豆瓣的正經說瞎話的大神(今生最討此類人,害死個仙人),看到這,我開始懷疑,這句話的正確性。奇數階,格子上下左右移動確實不會改變數列的逆序奇偶性。但偶數階,格子的上下移動是會改變序列的奇偶性的,簡單總結一下:

奇數階(3x3,5x5):上移或下移一個數字,其調換的位置是偶數,因此不改變數列逆序數的奇偶性,因此奇數階,生成的初始隨機數列的逆序數必須爲偶數;

偶數階(4x4,6x6):上移或下移一個數字,其調換的位置是奇數,因此會改變數列逆序數的奇偶性,上下交換一次改變一次奇偶性,交換兩次就回到初始狀態。因此能夠大體這樣理解,偶數的平方仍然爲偶數,其有數字的滑塊個數爲奇數個,因此有一個數字必然會和空滑塊產生位置交換,若是空滑塊位於奇數行(空滑塊是不參於數字序列的逆序數計算的),就會產生2n-1次交換,其會改變數列逆序數的奇偶性;而位於偶數行,就會產生2n次交換,不會改變數列逆序數的奇偶性,因此用一個公式總結就是:(數列初始狀態是否爲偶數) === (空行是否爲偶數),簡單來說就是求這兩個數的異或。最後的代碼流程的實現
clipboard.png

講一講裏面算法的實現

生成一個亂序不重複的1~n數組數列

方法有不少,但我知道的兩種,這裏分享一下,有知道其餘的,請留言作個評論,讓你們一塊兒進步。
順序數組隨機性調換
思路基本就是,先生成一個順序的1~n的順序數組,而後再經過一個1~n的循環來打亂這個數組,其時間複雜度是O(n)。代碼以下。

export const disorganize = (length) => {
        const arr = [];
        let temp;
        for (var i = 1; i < length; i++) {
            arr.push(i);
        }
        for (i = 0; i < length; i++) {
            let random = Math.round(Math.random() * (length - 2));
            temp = arr[random];
            arr[random] = arr[i];
            arr[i] = temp;
        }
        return arr;
    };

隨機數生成亂序數組
生成一個隨機數,並判斷其是否在目標數組中已存在,當數組個數爲n時,目的達到。其時間複雜度我不知,代碼以下:

export const randomArr = (length) => {
        const arr = [];
        let temp;
        while(arr.length<(length-1)){
            let random = 1+Math.round(Math.random() * (length - 2));
            if(random<length && arr.indexOf(random)===-1){
                arr.push(random);
            }
        }
        return arr;
    };

雖然代碼看起比上面的簡單,但其時間複雜度最由是O(f(n),最差狀況未知,因此,方法一更推薦,若是說的有什麼毛病,還請及時指出。

數列逆序性的奇偶性判斷

最早想到的就是冒泡排序,由於冒泡排序過程就是判斷兩個數是否逆序,是,就交換,不是,繼續下一組判斷。因此,咱們直接將交換的次數,記爲數列逆序數個數,就達到了想要的效果。固然這個題用其餘排序方法也能達到目的,理解了其排序的原理,就很容易計算數列的逆序性,我這裏是直接用的之前冒泡排序的算法。

const bubbleOrder=(arr)=>{
    let i ,j ,count=0;
    const swap=(tar,lastIndex,newIndex)=>{
        let temp = tar[lastIndex];
        tar[lastIndex] =tar[newIndex];
        tar[newIndex] = temp;
        count++;
    }
    for(i=0;i<arr.length;i++){
        for(j=arr.length-1;j>i;j--){
            (arr[j]<arr[j-1])&&swap(arr,j-1,j);
        }
    }
    return count;
}

JS寫一個秒錶

秒錶是啥,start-pause-stop-reset,中間的步驟不是必須的,但先後兩步必須。固然方法有不少,但都離不開setTimeout或則setInterval兩個方法,requestAnimation應該也能夠。這裏提供一個本身寫的,固然思路來源於網上,只是用本身的思路表達出來。基於setInterval和Date對象。源碼以下:

const timer=(offsetTime)=>{  //offsetTime爲0時,表示從0開始計,不爲0,表示是暫停後繼續計時
    const formatter=(t)=>{
        const res =t>9 ? t : '0'+t
        return res;
    }
    let startTime = new Date().getTime(),tPass=0,tOffset=offsetTime||0;
    this.interId = setInterval(()=> {   //this.interId 是組件下面建的一個保持定時器值的,用於暫停和中止
        let tNew = new Date().getTime(),ms,sec,min,timeStr;
        tPass = tOffset +tNew - startTime;
        ms = Math.floor(tPass/10 % 100);
        sec = Math.floor((tPass / 1000) % 60);
        min = Math.floor((tPass / 1000 / 60) % 60);
        timeStr = formatter(min)+':'+formatter(sec)+':'+formatter(ms);
        this.tick(timeStr,tPass);
    },100)
}

tick(timeStr,tPass){  //這是遊戲頁面一個react組件中的一個用於更新顯示dom的觸發器
    this.setState({
        timePass:tPass,
        time:timeStr
    })
}

React的使用感覺及易錯點(大神留步)

用Vue與用React的區別,抱頭痛哭,Vue半年沒上項目了,忘得差很少了,我的觀點(非喜勿噴)。

  • 直接感覺就是,Vue確實比React容易上手,其模板,js,css的組件式的開發方式,更接近咱們之前工做中經常使用的template + requireJs的開發模式。
  • React更強調js的編程和項目的總體架構,state放在哪一級,哪一級經過props來控制,固然route 4.0的組件化設計更強調這一點;
  • React在國內仍是不像Vue那麼大衆,比方說,出了問題,不少只有在stackOverflow有相關答案;
  • 不過,不管是Vue仍是React,熟悉ES6和麪向對象的編程,二者上手都是很快的(裝個逼,別打我)。

在整個學習過程當中,將不少教程中敲黑板指出來的坑,又結結實實踩了一遍,如今能夠說影響深入。本身整理了一下,作個小分享,願和我同樣剛入門的,碰見下面的錯誤,不會那麼迷茫
不要在

Cannot read property 'setState' of undefined'

解釋:這個問題,主要是組件對象的構造constructor中,未在constructor綁定事件處理函數的this指向。這個在教程中是有明確說明的,解決辦法就是constructor()中添加:this.resetClick = this.resetClick.bind(this);
clipboard.png

Cannot read property 'size' of undefined'

解釋:這個問題,主要是組件對象的構造constructor中,未傳入props對象,致使整個組件對象無props屬性;其實除了理解繼承,理解React組件的生命週期也很重要。

clipboard.png

you are adding a new property in the Synthetic event Object

解釋:能夠簡單理解爲SyntheticEvent是react爲瀏覽器兼容寫的一個dom事件代理,除了她原有的那些屬性,你不能私自爲其添加屬性。
clipboard.png
clipboard.png

iphone,元素滑動,頁面會跟隨滑動

這個真的是一個讓人頭疼的東西,太影響交互體驗了。若是作過h5都知道加滑動阻止.而react有他本身的一套事件機制封裝,因此在跟元素直接添加touchmove的preventDefault是不行的。得用最原始的事件寫法寫一個阻止滑動的觸摸事件,以下所示。

componentDidMount() {
    document.addEventListener('touchmove', (e) => {
      e.preventDefault();
    }, { passive: false });
  }

其餘,後面持續補充

一個疑問

一開始個人遊戲盒子是用的flex佈局,但一考慮,盒子裏面的方塊要滑動效果,我要作滑動的緩動效果,因而又改用了絕對定位佈局,每一個
方塊計算其定位點。但事實證實,我當時確實太菜,我用了state來管理每一個方塊在盒子的位置,但我調整state時,React的virtualDom
會自動計算,並更新dom節點,那我保持整個項目,怎麼才能本身作出緩動效果呢?純CSS不行,請各位大神給點建議。

結束語

在放假的前幾個小時,把拖了幾天的文章寫完,有點趕,有不足的地方還請及時指出。關於這個項目,後期本身想繼續優化,作一些功能拓展,好比接入數據記錄,NxM階這樣的玩法,有思路的,還請能分享給我,郵箱:closertb@163.com。在最後,送給2017不曾放棄努力的本身一些鼓勵,願2018年能用更好的發展。也祝各位戰友2018新年快樂,年後再見!!!!

相關文章
相關標籤/搜索