added pathfind

This commit is contained in:
Max Cahill 2023-12-05 10:56:42 +11:00
parent 1b396037a2
commit 4a3740cf53
2 changed files with 110 additions and 0 deletions

View File

@ -40,6 +40,7 @@ local _batteries = {
pretty = require_relative("pretty"), pretty = require_relative("pretty"),
measure = require_relative("measure"), measure = require_relative("measure"),
make_pooled = require_relative("make_pooled"), make_pooled = require_relative("make_pooled"),
pathfind = require_relative("pathfind"),
} }
--assign aliases --assign aliases

109
pathfind.lua Normal file
View File

@ -0,0 +1,109 @@
--[[
general pathfinding with a general goal
]]
local path = (...):gsub("pathfind", "")
local sort = require(path .. "sort")
--helper to generate a constant value for default weight, heuristic
local generate_constant = function() return 1 end
--find a path in a weighted graph
-- uses A* algorithm internally
--returns a table of all the nodes from the start to the goal,
-- or false if no path was found
--arguments table requires the following fields
-- start - the value of the node to start from
-- alias: start_node
-- is_goal - a function which takes a node, and returns true is a goal
-- alias: goal
-- neighbours - a function which takes a node, and returns a table of neighbour nodes
-- alias: generate_neighbours
-- distance - a function which given two nodes, returns the distance between them
-- defaults to a constant distance, which means all steps between nodes have the same cost
-- alias: weight, g
-- heuristic - a function which given a node, returns a heuristic indicative of the distance to the goal;
-- can be used to speed up searches at the cost of accuracy by returning some proportion higher than the actual distance
-- defaults to a constant value, which means the A* algorithm degrades to breadth-first search
-- alias: h
function pathfind(args)
local start = args.start or args.start_node
local is_goal = args.is_goal or args.goal
local neighbours = args.neighbours or args.generate_neighbours
local distance = args.distance or args.weight or args.g or generate_constant
local heuristic = args.heuristic or args.h or generate_constant
local predecessor = {}
local seen = {}
local f_score = {[start] = 0}
local g_score = {[start] = 0}
local function search_compare(a, b)
return
(f_score[a] or math.huge) >
(f_score[b] or math.huge)
end
local to_search = {}
local current = start
while current and not is_goal(current) do
seen[current] = true
for i, node in ipairs(neighbours(current)) do
if not seen[node] then
local tentative_g_score = (g_score[current] or math.huge) + distance(current, node)
if g_score[node] == nil then
if tentative_g_score < (g_score[node] or math.huge) then
predecessor[node] = current
g_score[node] = tentative_g_score
f_score[node] = tentative_g_score + heuristic(node)
end
table.insert(to_search, node)
sort.insertion_sort(to_search, search_compare)
end
end
end
current = table.remove(to_search)
end
--didn't make it to the goal
if not current or not is_goal(current) then
return false
end
--build up the ordering
local path = {}
while current do
table.insert(path, 1, current)
current = predecessor[current]
end
--return a path
return path
end
-- Based on https://github.com/lewtds/pathfinder.lua/blob/master/pathfinder.lua
-- with modification to allow for generalised goals/heuristics
-- Modified 2022 Max Cahill
-- Original License:
-- Copyright © 2016 Trung Ngo
-- Permission is hereby granted, free of charge, to any person obtaining a
-- copy of this software and associated documentation files (the \"Software\"),
-- to deal in the Software without restriction, including without limitation
-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
-- and/or sell copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.
return pathfind