js版九宮格拼圖與啓發式搜索(A*算法)

  九宮格拼圖遊戲你們都很熟悉,這裏給你們如介紹何應用狀態空間搜索的方式求解拼圖的最佳路徑和一個遊戲dome及自動求解方法;git

本文分web版遊戲的實現和啓發式搜索算法兩部分;github

先看dome,直接鼠標點擊要移動的方塊開始遊戲,點擊 提示 開始最佳路徑搜索(啓發式)直到最後一步;web

(若是提示無解,則表示沒有找到最佳路點擊重置從新試一次,可經過console查看所有搜索的每一步節點狀態,或在js/main.js中打斷點看每一步結果,詳細內容見下文)

項目地址: github.com/pangyongshe…


1、遊戲的實現方法

  首先咱們考慮如何用數據表示拼圖遊戲的狀態,即將拼圖遊戲視圖與數據綁定;算法

  以下圖所示:
數組

    

  (1)以左上角爲原點,創建座標系,藍色數字表示位置序號,markdown

     則該位置div(拼圖塊)的left和right(向左和向下的偏移距離)等於爲其左上角綠點的座標(x,y),即:dom

      left = x * 小方塊邊長函數

      right = y* 小方塊邊長
oop

  (2)這樣的話,咱們就能夠用一個長度爲9數組表示當前拼圖的狀態空間spa

     如 [2,0,1,5,4,6,7,8,3] 可表示一號方塊在2號位置

二號方塊在0號位置...
以下圖所示:

自此咱們就實現了視圖與數據的對應關係,把拼圖問題轉化成爲一個數組排列組合問題;

  (3)對於任意號位置a的座標c咱們可經過創建一個以下二維數組來獲取, 

      var place= [
        [0, 0],[1, 0],[2, 0],

        [0, 1],[1, 1],[2, 1],   

        [0, 2],[1, 2],[2, 2]

                      ]

    位置序號與座標則有以下關係

      c=place[a]

    由以上可知獲取a座標方法

    

   初始化每個小方塊位置方法(block爲所有小方塊dom,這裏借用數組方法forEach遍歷div)

    

   (4)對於任意兩個座標的距離咱們能夠表示爲

      d=| x1 - x2 | + | y1-y2 |

     代碼以下

    

  (5)那麼咱們能夠求得當前狀態和目標狀態的所有距離爲,(每一個小方塊距離目標的距離求和),f(x)第x方塊距離目標位置的距離

    

    代碼以下

    

  (6)如何判斷點擊的方塊是否能移動,首先咱們將最後一個方塊隱藏若是點擊的方塊距離最後(8號)方塊距離爲1則表示能夠移動,及兩個狀態能夠轉化,

     這個方法也能夠看作兩個狀態的數組可否相互轉化,可做爲後面啓發式搜索判斷節點擴展的方法;

    

    以上代碼爲每一個方塊添加點擊事件

    至此遊戲的基本實現方式介紹完畢,詳細看代碼

2、啓發式搜索

  啓發式搜索就是在狀態空間中的搜索對每個搜索的位置進行評估,獲得最好的位置,再從這個位置進行搜索直到目標。這樣能夠省略大量無謂的搜索路徑,提升了效率。在啓發式搜索中,對位置的估價是十分重要的。採用了不一樣的估價能夠有不一樣的效果

它把到達節點的耗散g(n)和從該節點到目標節點的消耗h(n)結合起來對節點進行評價:f(n)=g(n)+h(n)

  簡單的說就是擴展當前狀態節點的全部可能下一步節點,經過一個方式來估算那個節點最快能到到目標,不斷重複知道實現達到目標狀態;

  咱們這裏的估計方法爲 當前狀態的所有距離+走的步數;

  

  搜索過程可能描述以下:

(1)把初始節點S0放入Open表中,f(S0)=g(S0)+h(S0);

(2)若是Open表爲空,則問題無解,失敗退出;

(3)把Open表的第一個節點取出放入Closed表,並記該節點爲n;

(4)考察節點n是否爲目標節點。如果,則找到了問題的解,成功退出;

(5)若節點n不可擴展,則轉到第(2)步;

(6)擴展節點n,生成子節點ni(i=1,2,……),計算每個子節點的估價值f(ni) (i=1,2,……),併爲每個子節點設置指向父節點的指針,而後將這些子節點放入Open表中;

(7)根據各節點的估價函數值,對Open表中的所有節點按從小到大的順序從新進行排序;

(8)轉第(2)步。

  代碼太長 截圖不夠,詳細仍是看代碼吧:O(∩_∩)O

  


  

  我是把一步的搜索結果直接展現在視圖中的,因此closed表中沒有保留節點狀態,單經過console.log輸出,你們能夠點擊F12在調試模式下查看所有節點;

  若但願查看每一步視圖狀態,則能夠在searchA方法的while循環中打斷點查看效果;

  網上找個圖說明一下搜索的方法:容易明白

  


 其實這裏仍是有個兩問題需注意:

(1)並非每次都能找到目標狀態 ,由於個人數組打亂是徹底隨機的,而尋路的限制是只能兩個相鄰節點互換位置;

(2)在每次擴展節點的時候需考慮這個節點是否已經出如今closed表中(即尋路的過程當中出現過改節點),這樣會致使尋路的過程在幾個節點中轉圈,陷入死循環; 但也不能徹底限制closed的表中的數據不能重複出現,這樣會下降尋路成功的可能性;這裏我取了一個比較合理的值256,即在最近的256次中沒有出現過改節點;

相關文章
相關標籤/搜索