Please note, I am eleven years old and a novice.
I'm working on recreating the code shown in the page 'Tutorial: The A* Algorithm'. I have the pathfinder.lua file and Corona's definitely trying to use it, but I'm having issues with the file. In terminal I keep getting this:
Runtime error: ...pathfinder.lua:208: attempt to index field '?' (a nil value)
Oddly, I looked at line 208 but nothing showed up. Not even an 'invisible' piece of text. Any ideas? Here's all the code in the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | local _M = {} local mAbs = math.abs local mSqrt = math.sqrt ------------------------------------------- -- Set a value to bounds ------------------------------------------- local function clamp(value, low, high) if value < low then value = low elseif high and value > high then value = high end return value end ------------------------------------------- -- Implementation of min binary heap for use as a priority queue -- each element should have 'priority' field or one that you defined -- when created a heap. ------------------------------------------- local function newHeap (priority) if not priority then priority = 'priority' end heapObject = {} heapObject.heap = {} heapObject.len = 0 -- Size of the heap function heapObject:push (newElement) -- Adds new element to the heap local index = self.len self.heap[index] = newElement -- Add to bottom of the heap self.len = self.len + 1 -- Increase heap elements counter self:heapifyUp(index) -- Maintane min heap end function heapObject:heapifyUp (index) local parentIndex = clamp(math.floor((index - 1) / 2), 0) if self.heap[index][priority] < self.heap[parentIndex][priority] then self.heap[index], self.heap[parentIndex] = self.heap[parentIndex], self.heap[index] -- Swap self:heapifyUp(parentIndex) -- Continue sorting up the heap end end function heapObject:pop (index) -- Returns the element with the smallest priority or specific one if not index then index = 0 end local minElement = self.heap[index] self.heap[index] = self.heap[self.len - 1] -- Swap -- Remove element from heap self.heap[self.len - 1] = nil self.len = self.len - 1 self:heapifyDown(index) -- Maintane min heap return minElement end function heapObject:heapifyDown (index) local leftChildIndex = 2 * index + 1 local rightChildIndex = 2 * index + 2 if (self.heap[leftChildIndex] and self.heap[leftChildIndex][priority] and self.heap[leftChildIndex][priority] < self.heap[index][priority]) or (self.heap[rightChildIndex] and self.heap[rightChildIndex][priority] and self.heap[rightChildIndex][priority] < self.heap[index][priority]) then if (not self.heap[rightChildIndex] or not self.heap[rightChildIndex][priority]) or self.heap[leftChildIndex][priority] < self.heap[rightChildIndex][priority] then self.heap[index], self.heap[leftChildIndex] = self.heap[leftChildIndex], self.heap[index] -- Swap self:heapifyDown(leftChildIndex) -- Continue sorting down the heap else self.heap[index], self.heap[rightChildIndex] = self.heap[rightChildIndex], self.heap[index] -- Swap self:heapifyDown(rightChildIndex) -- Continue sorting down the heap end end end function heapObject:root () -- Returns the root element without removing it return self.heap[0] end return heapObject end ------------------------------------------- -- Calculate number of elements in a table -- Correctly manages zero indexed tables ------------------------------------------- function table_len (t) local len = #t + 1 if len == 1 and t[0] == nil then len = 0 end return len end ------------------------------------------- -- Reverse a table ------------------------------------------- local function table_reverse (t) local r = {} local tl = table_len(t) for k,v in pairs(t) do r[tl - k - 1] = v end return r end ------------------------------------------- -- Print two dimensional arrays ------------------------------------------- function print2d(t) for r = 0, table_len(t) - 1 do local str = '' for c = 0, table_len(t[r]) - 1 do local val = t[c][r] or 0 -- Codepunk: Changed to print in [x][y] direction val = math.round(val) if val == 0 then val = ' ' end str = str .. val .. ' ' end print(str) end end -- This represents a track node (each step) local function newNode (posX, posY, distance, priority) node = {} node.posX = posX node.posY = posY node.distance = distance node.priority = priority -- Estimation function for the remaining distance to the goal. function node:estimate(destX, destY) local dx = destX - self.posX local dy = destY - self.posY -- Manhattan distance return mSqrt(dx * dx + dy * dy) --mAbs(dx) + mAbs(dy) --Euclidian Distance --return mSqrt(dx * dx + dy * dy) end function node:updatePriority(destX, destY) self.priority = self.distance + self:estimate(destX, destY) * 10 -- A* end function node:nextMove() -- give higher priority to going straight instead of diagonally --if dirs == 8 and d % 2 ~= 0 then -- self.distance = self.distance + 14 --else self.distance = self.distance + 10 end mt = { __lt = function (a, b) return { value = a.priority < b.priority } end } setmetatable(node, mt) return node end -- A-star algorithm. -- The path returned will be a table of directions and number of steps -- @param the_map 2D table of the map representation, 0 means now way, 1 means a road. Tables are 0 indexed. -- @param mapW number Width of the map -- @param mapH number Height of the map -- @param startX number Start point -- @param startY number -- @param targetX number End point -- @param targetY number -- @return table|mixed Path is returned or false if no path is found function _M.pathFind(the_map, mapW, mapH, startX, startY, targetX, targetY) -- Number of directions: 4 or 8 local dirs = 4 local dx = {} dx[0], dx[1], dx[2], dx[3] = 1, 0, -1, 0 local dy = {} dy[0], dy[1], dy[2], dy[3] = 0, 1, 0, -1 -- For 8 directions: -- dx = 1, 1, 0, -1, -1, -1, 0, 1 -- dy = 0, 1, 1, 1, 0, -1, -1, -1 local closed_nodes_map = {} -- map of closed (tried-out) nodes local open_nodes_map = {} -- map of open (not-yet-tried) nodes local dir_map = {} -- map of dirs local row = {} for i = 0, mapW - 1 do row[i] = 0 end for i = 0, mapH - 1 do -- create 2d arrays closed_nodes_map[i] = {} open_nodes_map[i] = {} dir_map[i] = {} for j = 0, mapW - 1 do closed_nodes_map[i][j] = 0 open_nodes_map[i][j] = 0 dir_map[i][j] = 0 end end local pq = {} -- priority queues of open (not-yet-tried) nodes pq[0] = newHeap() pq[1] = newHeap() local pqi = 0 -- priority queue index -- create the start node and push into list of open nodes local n0 = newNode(startX, startY, 0, 0) n0:updatePriority(targetX, targetY) pq[pqi]:push(n0) open_nodes_map[startY][startX] = n0.priority -- mark it on the open nodes map -- A* search while pq[pqi].len > 0 do -- get the current node w/ the highest priority -- from the list of open nodes local n1 = pq[pqi]:pop() -- top node local n0 = newNode(n1.posX, n1.posY, n1.distance, n1.priority) local x = n0.posX local y = n0.posY -- remove the node from the open list open_nodes_map[y][x] = 0 closed_nodes_map[y][x] = 1 -- mark it on the closed nodes map -- quit searching when the goal is reached -- form direction table if x == targetX and y == targetY then -- generate the path from finish to start -- by following the dirs local path = {} local pathIndex = 0 local function pathInsert (a_dir, dir_count) -- TODO: find a bug when zero count directions are inserted if dir_count then local rev_dir -- reverse direction if a_dir == 0 then rev_dir = 2 end if a_dir == 1 then rev_dir = 3 end if a_dir == 2 then rev_dir = 0 end if a_dir == 3 then rev_dir = 1 end local item = {dx = dx[rev_dir], dy = dy[rev_dir], count = dir_count} path[pathIndex] = item pathIndex = pathIndex + 1 end end local prev_cur local dir_count = 0 local cur_dir while not (x == startX and y == startY) do cur_dir = dir_map[y][x] if not prev_dir then prev_dir = cur_dir end if prev_dir ~= cur_dir then pathInsert(prev_dir, dir_count) dir_count = 0 end dir_count = dir_count + 1 prev_dir = cur_dir x = x + dx[cur_dir] y = y + dy[cur_dir] end pathInsert(cur_dir, dir_count) return table_reverse(path) end -- generate moves (child nodes) in all possible dirs for i = 0, dirs - 1 do local xdx = x + dx[i] local ydy = y + dy[i] if not (xdx < 0 or xdx >= mapW or ydy < 0 or ydy >= mapH or the_map[xdx][ydy] ~= 1 or closed_nodes_map[ydy][xdx] == 1) then -- generate a child node local m0 = newNode(xdx, ydy, n0.distance, n0.priority) m0:nextMove(dirs, i) m0:updatePriority(targetX, targetY) -- if it is not in the open list then add into that if open_nodes_map[ydy][xdx] == 0 then open_nodes_map[ydy][xdx] = m0.priority pq[pqi]:push(m0) -- mark its parent node direction dir_map[ydy][xdx] = (i + dirs / 2) % dirs elseif open_nodes_map[ydy][xdx] > m0.priority then -- update the priority open_nodes_map[ydy][xdx] = m0.priority -- update the parent direction dir_map[ydy][xdx] = (i + dirs / 2) % dirs -- replace the node -- by emptying one pq to the other one -- except the node to be replaced will be ignored -- and the new node will be pushed in instead while pq[pqi][0] and not (pq[pqi][0].posX == xdx and pq[pqi][0].posY == ydy) do pq[1 - pqi]:push(pq[pqi]:pop()) end pq[pqi]:pop() -- remove the target node -- empty the larger size priority queue to the smaller one if pq[pqi].len > pq[1 - pqi].len then pqi = 1 - pqi end while pq[pqi].len > 0 do pq[1-pqi]:push(pq[pqi]:pop()) end pqi = 1 - pqi pq[pqi]:push(m0) -- add the better node instead end end end end return false -- if no route found end return _M |
Hmmm, have you got all the files you need? You should have main.lua as well.
This file is just the include file with all functions. I am not sure where you picked this up, but you cant find all info within the section "share code" here in the community. I tried it a couple of days ago, and it works fine.
Joakim
Yes, I have main.lua, that's how I'm linking to it. Oddly, the terminal shows that it has an error when it reaches line 208 of pathfinder.lua. I don't see any unusual text there, though.
Well, 208 above is a comment, so it's got to be some other line causing the error....