-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample.lua
More file actions
122 lines (106 loc) · 4.37 KB
/
example.lua
File metadata and controls
122 lines (106 loc) · 4.37 KB
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
local aStar = require "AStar"
-- local Utils = require "Utils" -- Uncomment if wanting to test the dead end without empty neighbors
-- local compose = Utils.compose
-- local fromNil = Utils.fromNil
-- Here we define our graph. A simple table containing for each key (node)
-- an array of node
local graph = {}
graph["A"] = {"B", "C"}
graph["B"] = {"D"}
graph["C"] = {"A", "E"}
graph["E"] = {"C", "D"}
graph["D"] = {"E", "F"}
graph["F"] = {"D", "G"}
graph["G"] = {"F", "H", "E"}
graph["H"] = {"E"}
-- So we currently have the following representation
-- A ↔ C ↔ E ← H
-- ↓ ⤢ ↖ ↑
-- B → D ↔ F ↔ G
-- First we need to define a function that, given a node, returns an array `{node} / {index = node } / {key = node}`
-- of the nodes linked to the given node
-- Since our graph is simply defined, it will be straightforward.
local function expand(n)
return graph[n]
-- return fromNil({})(graph[n])
end
-- fromNil is not needed as our graph is fully defined, but in case we wanted to be lazy
-- and define a dead end, simply doing something like
-- graph["B"] = {"D", "I"}
-- would suffice with the `expand` function above. That would also mean that any mistake
-- in our graph would be hidden.
-- Now we need to define a function that, given two nodes, return the cost of going
-- from the first to the second.
-- Again, we want to keep it simple. Our graph does not hold these value, so we will
-- simply return `1` every time.
-- As needed, we will define a curried function.
local function cost(from)
return function(to)
return 1
end
end
-- Now we need to define a function that, given a node, return the estimated cost
-- of the path left to reach the goal.
-- As always, since our graph is so simple, we will content ourselves with returning `0`
-- every time. This will make our `aStar` equivalent to a `dijkstra`.
local function heuristic(n)
return 0
end
-- Last, but not least, we need to define that, given a node, return whether we have
-- reached our goal or not. As the first example, we will want to find the path
-- from `A` to `D`.
local goalD = function(n)
return n == "D"
end
-- To avoid repeated call of the same functions, we will define a `simpleAStar`
-- in order to concern ourselves only with the goal and the start.
local simpleAStar = aStar(expand)(cost)(heuristic)
-- Because aStar is curried, you must pass each argument separatly.
-- Just make sure to apply the arguments in the correct order.
-- We can now ask `simpleAStar` to find the path from `A` to `D`.
local path = simpleAStar(goalD)("A")
-- The only thing left to do is display the path we found.
-- We do need a function to convert our path to something printable.
local function pathToString(path)
if path == nil then
return "No path found"
else
local ret = table.remove(path, 1)
for _, n in ipairs(path) do
ret = ret .. " → " .. n
end
return ret
end
end
-- If everything worked fine, it should display `A → B → D`.
print(pathToString(path))
-- Now we want the path to go from `D` to `A`.
-- For future reuse, we will define a curried function that simply check
-- if our node is in an array of node.
local function goal(targets)
return function(current)
for _, target in pairs(targets) do
if current == target then
return true
end
end
return false
end
end
-- This time we cannot pass by B, it should display `D → E → C → A`.
print(pathToString(simpleAStar(goal({"A"}))("D")))
-- We could now considere that both `B` and `D` are points of interest,
-- and we want to get to one of them, we do not particularly care
-- but we want the one with the shorter travel. Our graph is too simple
-- to present something meaningful here, but by changing the starting
-- node, we can show how the path differe.
-- Starting with `C` should give us either `C → A → B` or `C → E → D`.
print(pathToString(simpleAStar(goal({"B", "D"}))("C")))
-- Starting with `A` should give us `A → B`.
print(pathToString(simpleAStar(goal({"B", "D"}))("A")))
-- Starting with `E` should give us `E → D`.
print(pathToString(simpleAStar(goal({"B", "D"}))("E")))
-- Starting with `H` should give us `H → E → D`.
print(pathToString(simpleAStar(goal({"B", "D"}))("H")))
-- Starting with `A` should give `A → B → D → F → G → H`
print(pathToString(simpleAStar(goal({"H"}))("A")))