純lua版A*算法優化測試

寫了個A*算法的lua版本,也參考了不少網上資料還有開源的代碼,關於寫這個的原因,只是在做一個rts的尋路,寫了個lua版本也是圖省事,想盡快看效果。出於程序員的好奇和執着,修改了好幾個版本,優化算法,提高了一些效率,當然這裏還有優化的餘地,就不做進一步深入了,畢竟時間有限,優化的結果也是很明顯的。

關於A*算法的基本原理,可以參考一個很好教程:http://www.policyalmanac.org/games/aStarTutorial.htm


優化

關於A*算法的優化,無非以下二點:

  • openlist、closelist的查找
  • openlist中獲得最優節點

這裏優化的方向有針對性,不同需要有不同的優化策略。這裏我們研究的是針對2維地圖的優化方案。
解決第一點,是要有好的查找效率,簡單的可以用hash來加速查找。當然lua裏直接map就可以提升不少速度:

  local idx = self:getIndex(row, col)
  local curNode = self:getNode(node, row, col, (row ~= node.row and col ~= node.col))
  local openNode = self.open_list[idx]
  local closeNode = self.close_list[idx]

  if not openNode and not closeNode then
      -- 不在OPEN表和CLOSE表中
      self.open_list[idx] = curNode
  elseif openNode then
      -- 在OPEN表
      if openNode.f > curNode.f then
          -- 更新OPEN表中的節點
          self.open_list[idx] = curNode
      end
  else
      -- 在CLOSE表中
      if closeNode.f > curNode.f then
          self.open_list[idx] = curNode
          self.close_list[idx] = nil
      end
  end

解決第二點,是要讓openlist更快的查找最小f值節點,這裏使用更快的heap,代碼參考https://github.com/Yonaba/Binary-Heaps

  -- start node
  local idx = self:getIndex(self.startPoint.row, self.startPoint.col)
  self.open_list:push(self.nodes[idx])

  -- walkable 
  local check = function(row, col)
      ...
  end

  local dir = self.four_dir and four_dir or eight_dir
  while not self.open_list:empty() do
      -- check
      local node = self.open_list:pop()
      node.state = 2

      if node.row == self.endPoint.row and node.col == self.endPoint.col then
          -- found
      end

      for i = 1, #dir do
          local row = node.row + dir[i][1]
          local col = node.col + dir[i][2]
          local idx = self:getIndex(row, col)

          if check(row, col) then
              local g, h, f = self:getCost(node, row, col, ...)
              local newNode = self.nodes[idx]

              if newNode.state == 0 then
                  -- add new node
                  newNode.father = node
                  newNode.g = g
                  newNode.h = h
                  newNode.f = f
                  newNode.state = 1
                  self.open_list:push(newNode)
              elseif newNode.state == 1 then
                  -- alreay in openlist
                  if newNode.f > f then
                      -- a better way then update 
                      newNode.father = node
                      newNode.g = g
                      newNode.h = h
                      newNode.f = f
                  end
              else
                  -- in closelist
              end
          end
      end
  end

測試結果

測試環境使用cocos2dx2.2.6-lua,地圖tilemap,大小50*50,地圖信息如下:
地圖信息

PC - Experiment1 實驗結果:

Experiment1([1, 1] -> [1, 50]) time (s)
A* - array 0.0960~0.1030
A* - map 0.0290~0.0300
A* - map & heap sort 0.0020~0.0030

PC - Experiment2 實驗結果:

Experiment2([1, 1] -> [6, 5]) time (s)
A* - array 0.1360~0.1450
A* - map 0.0420~0.0460
A* - map & heap sort 0.0020~0.0030

Mobile - Experiment1 實驗結果:

Experiment1([1, 1] -> [1, 50]) time (s)
A* - array 1.050~1.110
A* - map 0.500~0.600
A* - map & heap sort 0.040~0.050

Mobile - Experiment2 實驗結果:

Experiment2([1, 1] -> [6, 5]) time (s)
A* - array 1.260~1.300
A* - map 0.640~0.700
A* - map & heap sort 0.024~0.027

Repository

github:adamwu

往後可能寫個c++版本的,畢竟lua效率要差些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章