Please note, I am eleven years old and a novice at Corona.
Quite a while ago, I tried to recreate the code shown in the article ‘Tutorial: The A* Algorithm’. I had fixed my original issues, but now the visuals are not working. By this, I mean that nothing visually appears. All I get is a black screen, which, of course, isn’t what should appear. It’s not like there are any error messages in terminal; it’s just that nothing appears on the simulator’s screen.
If anyone could help me with this unusual glitch, that would be great.
Here’s the code for main.lua:
display.setStatusBar( display.HiddenStatusBar )
local pathfinder = require("pathfinder")
local kLevelRows = 50 -- Rows in map
local kLevelCols = 50 -- Columns in map
local kRoadProbability = 6
local level = {}
local curGameFunction = nil
local cellWidth = display.contentWidth/kLevelCols
local cellHeight = display.contentHeight/kLevelRows
local startCell = {col = 1, row = 1}
local endCell = {col = 1, row = 1}
local cells = {}
local walker
-- builds our grid --
function buildGrid()
-- build map array --
for x = 0, kLevelCols do
level[x] = {}
for y = 0, kLevelRows do
local probability = math.random(0, 10)
if probability \<= kRoadProbability then
level[x][y] = 1
else
level[x][y] = 0
end
end
end
-- build screen now --
for x = 0, kLevelCols do
for y = 0, kLevelRows do
local cell = display.newRect(x\*cellWidth, y\*cellHeight, cellWidth, cellHeight)
cell.strokeWidth = 1
cell:setStrokeColor(0, 0, 0)
if level[x][y] == 0 then
cell:setFillColor(255, 0, 0)
end
if cells[x] == nil then
cells[x] = {}
end
cells[x][y] = cell
end
end
print2d(level)
end
-- Touch handler. Delegates call to current selected game function --
function onBoardTouched(event)
if event.phase == "began" then
curGameFunction(event)
end
end
function onStartCellSelected(event)
end
function onEndCellSelected(event)
end
function onDetermineAStar(event)
end
function onEnd(event)
end
function onEnd(event)
-- start over --
curGameFunction = function(event) onStartCellSelected(event) end
end
curGameFunction = function(event) onStartCellSelected(event) end
-- gets the display.newRect object based on x,y screen value --
function getCell(x, y)
local indices = getIndices(x, y)
return cells[indices[1]][indices[2]]
end
-- colors a cell on the grid --
function colorCell(cell, red, green, blue)
cell:setFillColor(red, green, blue)
end
local instructions = nil
-- displays instructions (albeit hard to read) to the user --
function displayInstructions(string)
if instructions ~= nil then
instructions:removeSelf()
end
instructions = display.newText(string, 0, 0, native.systemFontBold, 20)
instructions:setTextColor(0, 0, 0)
end
-- called to select the starting point on the grid --
function onStartCellSelected(event)
local indices = getIndices(event.x, event.y)
if level[indices[1]][indices[2]] == 0 then
displayInstructions("Cannot select red. Try again.")
else
startCell.col = indices[1]
startCell.row = indices[2]
displayInstructions("Select the ending cell.")
colorCell(getCell(event.x, event.y), 0, 255, 0)
curGameFunction = function(event) onEndCellSelected(event) end
end
end
-- called to select the ending point in the grid --
function onEndCellSelected(event)
local indices = getIndices(event.x, event.y)
if level[indices[1]][indices[2]] == 0 then
displayInstructions("Cannot select red. Try again")
else
endCell.col = indices[1]
endCell.row = indices[2]
colorCell(getCell(event.x, event.y), 0, 0, 255)
displayInstructions("Touch anywhere to see A\* go")
curGameFunction = function(event) onDetermineAStar(event) end
end
end
-- called when the demonstration ends (resets the grid) --
function onEnd(event)
for x = 0, kLevelCols do
for y = 0, kLevelRows do
cells[x][y]:removeSelf()
end
end
if walker then
walker:removeSelf()
walker = nil
end
cells = {}
buildGrid()
displayInstructions("Select the starting cell")
curGameFunction = function(event) onStartCellSelected(event) end
end
local path = pathfinder.pathFind(level, kLevelCols, kLevelRows, startCell.col, startCell.row, endCell.col, endCell.row)
function newWalker(path)
local walker = display.newCircle((startCell.col + 0.5) \* cellWidth, (startCell.row + 0.5) \* cellHeight, cellWidth)
walker.strokeWidth = 1
walker:setStrokeColor(0, 0, 0)
walker:setFillColor(0, 255, 255)
walker.pathIndex = 0
walker.pathLen = table\_len(path)
walker.speed = 50
function walker:go()
if self.pathIndex \< self.pathLen then
local dir = path[self.pathIndex]
self.transition = transition.to(self, { time = self.speed \* dir.count,
x = self.x + dir.dx \* dir.count \* cellWidth,
y = self.y + dir.dy \* dir.count \* cellHeight,
onComplete = function() self:go() end})
end
return walker
end
-- called to get A\* algorithm going --
function onDetermineAStar(event)
displayInstructions("")
-- run A\* --
local path = pathfinder.pathFind(level, kLevelCols, kLevelRows, startCell.col, startCell.row, endCell.col, endCell.row)
print("Path", path)
if path ~= false then
-- color the path --
local currentCell = {x=startCell.col, y=startCell.row}
for k = 0, #path do
local cellDirectionX = path[k].dx
local cellDirectionY = path[k].dy
local count = path[k].count
for l = 1, count do
currentCell.x = currentCell.x + cellDirectionX
currentCell.y = currentCell.y + cellDirectionY
if currentCell.x ~= endCell.col or currentCell.y ~= endCell.row then
colorCell(cells[currentCell.x][currentCell.y], 255, 255, 0)
end
end
end
-- create a moving object
walker = newWalker(path)
walker:go()
curGameFunction = function(event) onEnd(event) end
else
displayInstructions("Suitable path not found")
curGameFunction = function(event) onEnd(event) end
end
end
end
Here’s the code for pathfinder.lua:
[code]
– A* pathfinding module
– Author: Lerg
– Release date: 2011-08-25
– Version: 1.1
– License: MIT
– Based on the python implementation.
– USAGE:
– Import this module and use pathFind() function. Map should be zero indexed (first element index is zero).
local _M = {}
local mAbs = math.abs
local mSqrt = math.sqrt
local startX = 1
local startY = 1
– 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)
assert(the_map, mapW, mapH, startX, startY, targetX)
– 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)
print( startX )
print( startY )
print( mapW )
print( mapH )
print( n0 )
print( n0.priority )
assert(open_nodes_map)
assert(startX)
assert(startY)
assert(open_nodes_map[startY][startX])
assert(n0)
assert(n0.priority)
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
[/code] [import]uid: 82408 topic_id: 22877 reply_id: 322877[/import]