-- (Internal) Returns a unique key for the start and end points.
local function keyOf(start, goal)
- return string.format("%d,%d>%d,%d", start.x, start.y, goal.x, goal.y)
+ return string.format("%d,%d,%d>%d,%d,%d", start.x, start.y, start.z, goal.x, goal.y, goal.z)
end
-- (Internal) Returns the cached path for start and end points.
-- (Internal) Return the distance between two points.
-- This method doesn't bother getting the square root of s, it is faster
-- and it still works for our use.
-local function distance(x1, y1, x2, y2)
+local function distance(x1, y1, z1, x2, y2, z2)
local dx = x1 - x2
local dy = y1 - y2
- local s = dx * dx + dy * dy
+ local dz = z1 - z2
+ local s = dx * dx + dy * dy + dz * dz
return s
end
local function calculateScore(previous, node, goal)
local G = previous.score + 1
- local H = distance(node.x, node.y, goal.x, goal.y)
+ local H = distance(node.x, node.y, node.z, goal.x, goal.y, goal.z)
return G + H, G, H
end
-- (Internal) Returns true if the given list contains the specified item.
local function listContains(list, item)
for _, test in ipairs(list) do
- if test.x == item.x and test.y == item.y then
+ if test.x == item.x and test.y == item.y and test.z == item.z then
return true
end
end
-- (Internal) Returns the item in the given list.
local function listItem(list, item)
for _, test in ipairs(list) do
- if test.x == item.x and test.y == item.y then
+ if test.x == item.x and test.y == item.y and test.z == item.z then
return test
end
end
end
-- (Internal) Requests adjacent map values around the given node.
-local function getAdjacent(width, height, node, positionIsOpenFunc, includeDiagonals)
+local function getAdjacent(width, height, depth, node, positionIsOpenFunc, includeDiagonals)
local result = { }
- local positions = {
- { x = 0, y = -1 }, -- top
- { x = -1, y = 0 }, -- left
- { x = 0, y = 1 }, -- bottom
- { x = 1, y = 0 }, -- right
- }
-
- if includeDiagonals then
- local diagonalMovements = {
- { x = -1, y = -1 }, -- top left
- { x = 1, y = -1 }, -- top right
- { x = -1, y = 1 }, -- bot left
- { x = 1, y = 1 }, -- bot right
- }
-
- for _, value in ipairs(diagonalMovements) do
- table.insert(positions, value)
- end
+ local positions = {}
+
+ for x = -1, 1 do
+ for y = -1, 1 do
+ for z = -1, 1 do
+ local a = math.abs(x) + math.abs(y) + math.abs(z)
+
+ if a ~= 0 and includeDiagonals or a == 1 then
+ table.insert(positions, {x = x, y = y, z = z})
+ end
+ end
+ end
end
for _, point in ipairs(positions) do
local px = clamp(node.x + point.x, 1, width)
local py = clamp(node.y + point.y, 1, height)
- local value = positionIsOpenFunc( px, py )
+ local pz = clamp(node.z + point.z, 1, depth)
+
+ local value = positionIsOpenFunc( px, py, pz )
if value then
- table.insert( result, { x = px, y = py } )
+ table.insert( result, { x = px, y = py, z = pz } )
end
end
end
-- Returns the path from start to goal, or false if no path exists.
-function module:find(width, height, start, goal, positionIsOpenFunc, useCache, excludeDiagonalMoving)
+function module:find(width, height, depth, start, goal, positionIsOpenFunc, useCache, excludeDiagonalMoving)
if useCache then
local cachedPath = getCached(start, goal)
start.score = 0
start.G = 0
- start.H = distance(start.x, start.y, goal.x, goal.y)
- start.parent = { x = 0, y = 0 }
+ start.H = distance(start.x, start.y, start.z, goal.x, goal.y, goal.z)
+ start.parent = { x = 0, y = 0, z = 0 }
table.insert(open, start)
while not success and #open > 0 do
if not success then
- local adjacentList = getAdjacent(width, height, current, positionIsOpenFunc, not excludeDiagonalMoving)
+ local adjacentList = getAdjacent(width, height, depth, current, positionIsOpenFunc, not excludeDiagonalMoving)
for _, adjacent in ipairs(adjacentList) do
while node do
- table.insert(path, 1, { x = node.x, y = node.y } )
+ table.insert(path, 1, { x = node.x, y = node.y, z = node.z } )
node = listItem(closed, node.parent)
end
-- start is always top left (1,1)
-- goal is always bottom right (10, 10)
- local start = { x = 1, y = 1 }
- local goal = { x = 10, y = 10 }
+ local start = { x = 1, y = 1, z = 1 }
+ local goal = { x = 10, y = 10, z = 1 }
local map = nil
-- define some test maps (10 x 10)
]]
local openmapSolution = {
- { x = 1, y = 1 },
- { x = 2, y = 2 },
- { x = 3, y = 3 },
- { x = 4, y = 4 },
- { x = 5, y = 5 },
- { x = 6, y = 6 },
- { x = 7, y = 7 },
- { x = 8, y = 8 },
- { x = 9, y = 9 },
- { x = 10, y = 10 },
+ { x = 1, y = 1, z = 1 },
+ { x = 2, y = 2, z = 1 },
+ { x = 3, y = 3, z = 1 },
+ { x = 4, y = 4, z = 1 },
+ { x = 5, y = 5, z = 1 },
+ { x = 6, y = 6, z = 1 },
+ { x = 7, y = 7, z = 1 },
+ { x = 8, y = 8, z = 1 },
+ { x = 9, y = 9, z = 1 },
+ { x = 10, y = 10, z = 1 },
}
local simplemap = [[
]]
local simplemapSolution = {
- { x = 1, y = 1 },
- { x = 2, y = 2 },
- { x = 3, y = 3 },
- { x = 4, y = 4 },
- { x = 4, y = 5 },
- { x = 3, y = 6 },
- { x = 2, y = 7 },
- { x = 1, y = 8 },
- { x = 2, y = 9 },
- { x = 3, y = 10 },
- { x = 4, y = 10 },
- { x = 5, y = 10 },
- { x = 6, y = 10 },
- { x = 7, y = 10 },
- { x = 8, y = 10 },
- { x = 9, y = 10 },
- { x = 10, y = 10 },
+ { x = 1, y = 1, z = 1 },
+ { x = 2, y = 2, z = 1 },
+ { x = 3, y = 3, z = 1 },
+ { x = 4, y = 4, z = 1 },
+ { x = 4, y = 5, z = 1 },
+ { x = 3, y = 6, z = 1 },
+ { x = 2, y = 7, z = 1 },
+ { x = 1, y = 8, z = 1 },
+ { x = 2, y = 9, z = 1 },
+ { x = 3, y = 10, z = 1 },
+ { x = 4, y = 10, z = 1 },
+ { x = 5, y = 10, z = 1 },
+ { x = 6, y = 10, z = 1 },
+ { x = 7, y = 10, z = 1 },
+ { x = 8, y = 10, z = 1 },
+ { x = 9, y = 10, z = 1 },
+ { x = 10, y = 10, z = 1 },
}
local simplemapDiagonalSolution = {
- { x = 1, y = 1 },
- { x = 1, y = 2 },
- { x = 1, y = 3 },
- { x = 1, y = 4 },
- { x = 1, y = 5 },
- { x = 1, y = 6 },
- { x = 1, y = 7 },
- { x = 1, y = 8 },
- { x = 1, y = 9 },
- { x = 2, y = 9 },
- { x = 3, y = 9 },
- { x = 4, y = 9 },
- { x = 5, y = 9 },
- { x = 6, y = 9 },
- { x = 7, y = 9 },
- { x = 8, y = 9 },
- { x = 9, y = 9 },
- { x = 9, y = 10 },
- { x = 10, y = 10 },
+ { x = 1, y = 1, z = 1 },
+ { x = 1, y = 2, z = 1 },
+ { x = 1, y = 3, z = 1 },
+ { x = 1, y = 4, z = 1 },
+ { x = 1, y = 5, z = 1 },
+ { x = 1, y = 6, z = 1 },
+ { x = 1, y = 7, z = 1 },
+ { x = 1, y = 8, z = 1 },
+ { x = 1, y = 9, z = 1 },
+ { x = 2, y = 9, z = 1 },
+ { x = 3, y = 9, z = 1 },
+ { x = 4, y = 9, z = 1 },
+ { x = 5, y = 9, z = 1 },
+ { x = 6, y = 9, z = 1 },
+ { x = 7, y = 9, z = 1 },
+ { x = 8, y = 9, z = 1 },
+ { x = 9, y = 9, z = 1 },
+ { x = 9, y = 10, z = 1 },
+ { x = 10, y = 10, z = 1 },
}
local complexmap = [[
]]
local complexmapSolution = {
- { x = 1, y = 1 },
- { x = 2, y = 1 },
- { x = 3, y = 1 },
- { x = 4, y = 1 },
- { x = 5, y = 1 },
- { x = 6, y = 1 },
- { x = 7, y = 1 },
- { x = 8, y = 1 },
- { x = 9, y = 1 },
- { x = 10, y = 2 },
- { x = 9, y = 3 },
- { x = 8, y = 3 },
- { x = 7, y = 3 },
- { x = 6, y = 3 },
- { x = 5, y = 3 },
- { x = 4, y = 3 },
- { x = 3, y = 3 },
- { x = 2, y = 3 },
- { x = 1, y = 4 },
- { x = 1, y = 5 },
- { x = 1, y = 6 },
- { x = 2, y = 7 },
- { x = 3, y = 6 },
- { x = 4, y = 5 },
- { x = 5, y = 6 },
- { x = 5, y = 7 },
- { x = 5, y = 8 },
- { x = 6, y = 9 },
- { x = 7, y = 9 },
- { x = 8, y = 8 },
- { x = 7, y = 7 },
- { x = 7, y = 6 },
- { x = 8, y = 5 },
- { x = 9, y = 6 },
- { x = 10, y = 7 },
- { x = 10, y = 8 },
- { x = 10, y = 9 },
- { x = 10, y = 10 },
+ { x = 1, y = 1, z = 1 },
+ { x = 2, y = 1, z = 1 },
+ { x = 3, y = 1, z = 1 },
+ { x = 4, y = 1, z = 1 },
+ { x = 5, y = 1, z = 1 },
+ { x = 6, y = 1, z = 1 },
+ { x = 7, y = 1, z = 1 },
+ { x = 8, y = 1, z = 1 },
+ { x = 9, y = 1, z = 1 },
+ { x = 10, y = 2, z = 1 },
+ { x = 9, y = 3, z = 1 },
+ { x = 8, y = 3, z = 1 },
+ { x = 7, y = 3, z = 1 },
+ { x = 6, y = 3, z = 1 },
+ { x = 5, y = 3, z = 1 },
+ { x = 4, y = 3, z = 1 },
+ { x = 3, y = 3, z = 1 },
+ { x = 2, y = 3, z = 1 },
+ { x = 1, y = 4, z = 1 },
+ { x = 1, y = 5, z = 1 },
+ { x = 1, y = 6, z = 1 },
+ { x = 2, y = 7, z = 1 },
+ { x = 3, y = 6, z = 1 },
+ { x = 4, y = 5, z = 1 },
+ { x = 5, y = 6, z = 1 },
+ { x = 5, y = 7, z = 1 },
+ { x = 5, y = 8, z = 1 },
+ { x = 6, y = 9, z = 1 },
+ { x = 7, y = 9, z = 1 },
+ { x = 8, y = 8, z = 1 },
+ { x = 7, y = 7, z = 1 },
+ { x = 7, y = 6, z = 1 },
+ { x = 8, y = 5, z = 1 },
+ { x = 9, y = 6, z = 1 },
+ { x = 10, y = 7, z = 1 },
+ { x = 10, y = 8, z = 1 },
+ { x = 10, y = 9, z = 1 },
+ { x = 10, y = 10, z = 1 },
}
local unsolvablemap = [[
end
-- get the value at position xy on a map
- local function mapTileIsOpen(x, y)
- return map[ ((y-1) * 10) + x ] == "0"
+ local function mapTileIsOpen(x, y, z)
+ return z == 1 and map[ ((y-1) * 10) + x ] == "0"
end
local function printSolution(path)
local luastar = require("lua-star")
makemap(openmap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen)
--printSolution(path)
assert.are.equal(10, #path)
assert.are.same(openmapSolution, path)
local luastar = require("lua-star")
makemap(simplemap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen)
--printSolution(path)
assert.are.equal(17, #path)
assert.are.same(simplemapSolution, path)
local luastar = require("lua-star")
local excludeDiagonals = true
makemap(simplemap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen, false, excludeDiagonals)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen, false, excludeDiagonals)
--printSolution(path)
assert.are.equal(19, #path)
assert.are.same(simplemapDiagonalSolution, path)
local luastar = require("lua-star")
makemap(complexmap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen)
--printSolution(path)
assert.are.equal(38, #path)
assert.are.same(complexmapSolution, path)
local luastar = require("lua-star")
makemap(unsolvablemap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen)
assert.is_false(path)
end)
local luastar = require("lua-star")
local excludeDiagonals = true
makemap(unsolvablemap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen, false, excludeDiagonals)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen, false, excludeDiagonals)
assert.is_false(path)
end)
local luastar = require("lua-star")
local excludeDiagonals = true
makemap(complexmap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen, false, excludeDiagonals)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen, false, excludeDiagonals)
assert.is_false(path)
end)
local luastar = require("lua-star")
makemap(openmap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen)
- local samepath = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen)
+ local samepath = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen)
assert.is_not.equal(path, samepath)
end)
local luastar = require("lua-star")
makemap(openmap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen, true)
- local samepath = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen, true)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen, true)
+ local samepath = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen, true)
assert.are.equal(path, samepath)
end)
local luastar = require("lua-star")
makemap(openmap)
- local path = luastar:find(mapsize, mapsize, start, goal, mapTileIsOpen, true)
+ local path = luastar:find(mapsize, mapsize, 1, start, goal, mapTileIsOpen, true)
luastar:clearCached()
assert.is_nil(luastar.cache)