Can't get A* Pathfinder to work

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....

views:1686 update:2012/1/3 13:02:13
corona forums © 2003-2011