├── README.md ├── StandardLibraries ├── BinarySearch.lua ├── Graph.lua ├── Heap.lua ├── PriorityQueue.lua └── SortedList.lua └── UtilityLibraries └── Pathfinder.lua /README.md: -------------------------------------------------------------------------------- 1 | # Wiki-Lua-Libraries 2 | -------------------------------------------------------------------------------- /StandardLibraries/BinarySearch.lua: -------------------------------------------------------------------------------- 1 | -- BinarySearch contains the following functions: 2 | -- :Search(tableToSearch, toFind, compareFunction) - Search the provided table for the requested value 3 | -- comparator: Uses this function to compare values. If none is given, will assume the largest value is table[1] 4 | -- Comparator should accept two values and return 1 if a > b, -1 if a < b, and 0 if a == b 5 | -- :FindInsertPoint(tableToSearch, toInsert, comparator) - Returns the index of an insertion point for the provided value in the provided table 6 | -- comparator: Uses this function to compare values. If none is given, will assume the largest value is table[1] 7 | -- Comparator should accept two values and return 1 if a > b, -1 if a < b, and 0 if a == b 8 | -- BinarySearch assumes a sorted table and returns nil if there is no valid value/index 9 | 10 | local BinarySearch = {} 11 | 12 | function BinarySearch.DefaultCompare(a, b) 13 | if b > a then 14 | return -1 15 | elseif b < a then 16 | return 1 17 | else 18 | return 0 19 | end 20 | end 21 | 22 | function BinarySearch:Search(tableToSearch, toFind, comparator) 23 | if not comparator then 24 | comparator = BinarySearch.DefaultCompare 25 | end 26 | 27 | local minIndex = 1 28 | local maxIndex = #tableToSearch 29 | 30 | while minIndex <= maxIndex do 31 | local mid = math.floor((maxIndex+minIndex)/2) 32 | 33 | local compareVal = comparator(tableToSearch[mid], toFind) 34 | if compareVal == 0 then 35 | return mid 36 | elseif compareVal > 0 then 37 | minIndex = mid + 1 38 | else 39 | maxIndex = mid - 1 40 | end 41 | end 42 | 43 | return nil 44 | end 45 | 46 | function BinarySearch:FindInsertPoint(tableToSearch, toInsert, comparator) 47 | if #tableToSearch == 0 then 48 | return 1 49 | end 50 | 51 | if not comparator then 52 | comparator = BinarySearch.DefaultCompare 53 | end 54 | 55 | local minIndex = 1 56 | local maxIndex = #tableToSearch 57 | local mid 58 | 59 | while true do 60 | mid = math.floor((maxIndex+minIndex)/2) 61 | local compareVal = comparator(tableToSearch[mid], toInsert) 62 | if compareVal == 0 then 63 | return mid 64 | elseif compareVal > 0 then 65 | minIndex = mid+1 66 | if minIndex > maxIndex then 67 | return mid+1 68 | end 69 | else 70 | maxIndex = mid-1 71 | if minIndex > maxIndex then 72 | return mid 73 | end 74 | end 75 | end 76 | end 77 | 78 | return BinarySearch 79 | -------------------------------------------------------------------------------- /StandardLibraries/Graph.lua: -------------------------------------------------------------------------------- 1 | -- Graph contains the following Enums: 2 | -- Graph.GraphType - Values: OneWay, TwoWay 3 | -- OneWay: The Graph will assume connections are one-way (Connect('A', 'B', 1) will connect 'A' to 'B', but not 'B' to 'A' 4 | -- TwoWay: The Graph will assume connections are two-way (Conenct('A', 'B', 1) will connect 'A' to 'B' and 'B' to 'A' 5 | -- Graph contains the following functions: 6 | -- .new(graphType) - Creates a new Graph 7 | -- graphType - Enum value Graph.GraphType 8 | -- A Graph object has the following functions: 9 | -- :AddVertex(newVertex) - Adds a vertex to the graph 10 | -- :ReplaceVertex(oldVertex, newVertex) - Replaces the oldVertex with the newVertex in all connections in the Graph 11 | -- :RemoveVertex(vertex) - Removes the vertex, all of its connections, and all connections to it 12 | -- :Connect(vertex1, vertex2, cost) - Connects vertex1 to vertex2 with a connection value cost - This also functions as SetConnectionCost 13 | -- cost: Values of false are treated as nil, defaults to true if no cost is given 14 | -- :GetConnectionCost(vertex1, vertex2) - Gets the connection value between vertex1 and vertex2 15 | -- :Disconnect(vertex1, vertex2) - Removes the connection between vertex1 and vertex2 16 | -- :Adjacent(vertex1, vertex2) - Returns true if the two vertices are connected, false if not. If TwoWay Graph, the connection must be both ways to return true 17 | -- :Neighbors(vertex) - Return a table of all connected vertices to vertex. Returns nil if empty. If TwoWay Graph, the connection must be both ways to be returned 18 | -- :Clone() - Creates and returns a new copy of the Graph 19 | 20 | local Graph = {} 21 | Graph.__index = Graph 22 | 23 | Graph.GraphType = { OneWay = 1, TwoWay = 2 } 24 | 25 | -- Assumes costs are non-false values 26 | -- This is an adjacency matrix 27 | function Graph.new(graphType) 28 | if not graphType then 29 | graphType = Graph.GraphType.TwoWay 30 | end 31 | 32 | local newGraph = { } 33 | newGraph.Type = graphType 34 | setmetatable(newGraph, Graph) 35 | return newGraph 36 | end 37 | 38 | function Graph:AddVertex(newVertex) 39 | if not self[newVertex] then 40 | self[newVertex] = { } 41 | end 42 | end 43 | 44 | function Graph:ReplaceVertex(oldVertex, newVertex) 45 | if not self[oldVertex] then 46 | self:AddVertex(newVertex) 47 | else 48 | self[newVertex] = self[oldVertex] 49 | self[oldVertex] = nil 50 | 51 | for v, e in pairs(self) do 52 | if v ~= "Type" and e[oldVertex] then 53 | e[newVertex] = e[oldVertex] 54 | e[oldVertex] = nil 55 | end 56 | end 57 | end 58 | end 59 | 60 | function Graph:RemoveVertex(vertex) 61 | if self[vertex] then 62 | self[vertex] = nil 63 | end 64 | 65 | for v, e in pairs(self) do 66 | if v ~= "Type" and e[vertex] then 67 | e[vertex] = nil 68 | end 69 | end 70 | end 71 | 72 | function Graph:Connect(vertex1, vertex2, cost) 73 | if not cost then 74 | cost = true 75 | end 76 | 77 | if self[vertex1] and self[vertex2] then 78 | self[vertex1][vertex2] = cost 79 | if self.Type == Graph.GraphType.TwoWay then 80 | self[vertex2][vertex1] = cost 81 | end 82 | end 83 | end 84 | 85 | function Graph:GetConnectionCost(vertex1, vertex2) 86 | if self[vertex1] and self[vertex2] then 87 | if self.Type == Graph.GraphType.TwoWay then 88 | if self[vertex2][vertex1] and self[vertex1][vertex2] then 89 | return self[vertex1][vertex2] 90 | else 91 | return nil 92 | end 93 | else 94 | if self[vertex1][vertex2] then 95 | return self[vertex1][vertex2] 96 | else 97 | return nil 98 | end 99 | end 100 | end 101 | end 102 | 103 | function Graph:Disconnect(vertex1, vertex2) 104 | if self[vertex1] then 105 | self[vertex1][vertex2] = nil 106 | 107 | if self.Type == Graph.GraphType.TwoWay and self[vertex2] then 108 | self[vertex2][vertex1] = nil 109 | end 110 | end 111 | end 112 | 113 | function Graph:Adjacent(vertex1, vertex2) 114 | if self[vertex1] and self[vertex2] then 115 | if self[vertex1][vertex2] then 116 | if self.Type == Graph.GraphType.TwoWay then 117 | if self[vertex2][vertex1] then 118 | return true 119 | end 120 | else 121 | return true 122 | end 123 | end 124 | end 125 | return false 126 | end 127 | 128 | function Graph:Neighbors(vertex) 129 | if self[vertex] then 130 | local neighbors = { } 131 | for v, c in pairs(self[vertex]) do 132 | if self.Type == Graph.GraphType.OneWay then 133 | table.insert(neighbors, v) 134 | else 135 | if self[v] and self[v][vertex] then 136 | table.insert(neighbors, v) 137 | end 138 | end 139 | end 140 | 141 | if #neighbors > 0 then 142 | return neighbors 143 | end 144 | end 145 | 146 | return nil 147 | end 148 | 149 | function Graph:Clone() 150 | local newGraph = { } 151 | for k, v in pairs(self) do 152 | newGraph[k] = v 153 | if k ~= "Type" then 154 | for k2, v2 in pairs(v) do 155 | newGraph[k][k2] = v2 156 | end 157 | end 158 | end 159 | setmetatable(newGraph, Graph) 160 | return newGraph 161 | end 162 | 163 | return Graph 164 | -------------------------------------------------------------------------------- /StandardLibraries/Heap.lua: -------------------------------------------------------------------------------- 1 | -- Heap contains the following functions: 2 | -- .new(comparator) - Creates a new Heap 3 | -- comparator: Uses this function to compare values. If none is given, will assume values are numbers and will find smallest value 4 | -- Comparator should accept two values and return true if a should be further up the heap than b and false otherwise 5 | -- :Heapify(oldTable, comparator) - Converts a table to a Heap - Will destroy the provided table 6 | -- comparator: The comparator to pass to Heap.new(comparator) 7 | -- :Meld(heap1, heap2) - Creates a new Heap using the two provided Heaps 8 | -- A Heap object has the following functions: 9 | -- :Insert(newValue) - Adds an element to the Heap 10 | -- :Pop() - Removes the first element in the Heap and returns it 11 | -- :Peek() - Returns the first element in the Heap but does not remove it 12 | -- :GetAsTable() - Returns a table of the elements in the Heap 13 | -- :Clear() - Removes all values from the Heap 14 | -- :Print() - Prints out all the values in the Heap 15 | -- :Size() - Returns the size of the Heap 16 | -- :Clone() - Creates and returns a new copy of the Heap 17 | 18 | Heap = {} 19 | Heap.__index = Heap 20 | 21 | local Floor = math.floor 22 | local function DefaultCompare(a, b) 23 | if a > b then 24 | return true 25 | else 26 | return false 27 | end 28 | end 29 | 30 | local function SiftUp(heap, index) 31 | local parentIndex 32 | if index ~= 1 then 33 | parentIndex = Floor(index/2) 34 | if heap.Compare(heap[parentIndex], heap[index]) then 35 | heap[parentIndex], heap[index] = heap[index], heap[parentIndex] 36 | SiftUp(heap, parentIndex) 37 | end 38 | end 39 | end 40 | 41 | local function SiftDown(heap, index) 42 | local leftChildIndex, rightChildIndex, minIndex 43 | leftChildIndex = index * 2 44 | rightChildIndex = index * 2 + 1 45 | if rightChildIndex > #heap then 46 | if leftChildIndex > #heap then 47 | return 48 | else 49 | minIndex = leftChildIndex 50 | end 51 | else 52 | if not heap.Compare(heap[leftChildIndex], heap[rightChildIndex]) then 53 | minIndex = leftChildIndex 54 | else 55 | minIndex = rightChildIndex 56 | end 57 | end 58 | 59 | if heap.Compare(heap[index], heap[minIndex]) then 60 | heap[minIndex], heap[index] = heap[index], heap[minIndex] 61 | SiftDown(heap, minIndex) 62 | end 63 | end 64 | 65 | function Heap.new(comparator) 66 | local newHeap = { } 67 | setmetatable(newHeap, Heap) 68 | if comparator then 69 | newHeap.Compare = comparator 70 | else 71 | newHeap.Compare = DefaultCompare 72 | end 73 | 74 | return newHeap 75 | end 76 | 77 | function Heap:Insert(newValue) 78 | table.insert(self, newValue) 79 | 80 | if #self <= 1 then 81 | return 82 | end 83 | 84 | SiftUp(self, #self) 85 | end 86 | 87 | function Heap:Pop() 88 | if #self > 0 then 89 | local toReturn = self[1] 90 | self[1] = self[#self] 91 | table.remove(self, #self) 92 | if #self > 0 then 93 | SiftDown(self, 1) 94 | end 95 | return toReturn 96 | else 97 | return nil 98 | end 99 | end 100 | 101 | function Heap:Peek() 102 | if #self > 0 then 103 | return self[1] 104 | else 105 | return nil 106 | end 107 | end 108 | 109 | function Heap:GetAsTable() 110 | local newTable = { } 111 | for i = 1, #self do 112 | table.insert(newTable, self[i]) 113 | end 114 | return newTable 115 | end 116 | 117 | function Heap:Clear() 118 | for k in pairs(self) do 119 | self[k] = nil 120 | end 121 | end 122 | 123 | function Heap:Print() 124 | local out = "" 125 | for i = 1, #self do 126 | out = out .. tostring(self[i]) .. " " 127 | end 128 | print(out) 129 | end 130 | 131 | function Heap:Size() 132 | return #self 133 | end 134 | 135 | function Heap:Clone() 136 | local newHeap = Heap.new(self.Compare) 137 | for i = 1, #self do 138 | table.insert(newHeap, self[i]) 139 | end 140 | return newHeap 141 | end 142 | 143 | -- Functions that are not self-referential 144 | function Heap:Heapify(oldTable, comparator) 145 | local newHeap = Heap.new(comparator) 146 | for i = #oldTable, 1, -1 do 147 | newHeap:Insert(oldTable[i]) 148 | table.remove(oldTable, i) 149 | end 150 | return newHeap 151 | end 152 | 153 | function Heap:Meld(heap1, heap2, comparator) 154 | if not comparator then 155 | comparator = heap1.Compare or heap2.Compare 156 | end 157 | local newHeap = Heap.new(comparator) 158 | for i = #heap1, 1, -1 do 159 | newHeap:Insert(heap1[i]) 160 | end 161 | for i = #heap2, 1, -1 do 162 | newHeap:Insert(heap2[i]) 163 | end 164 | return newHeap 165 | end 166 | 167 | return Heap 168 | -------------------------------------------------------------------------------- /StandardLibraries/PriorityQueue.lua: -------------------------------------------------------------------------------- 1 | -- PriorityQueue contains the following functions: 2 | -- .new(comparator) - Create a new PriorityQueue 3 | -- comparator: Uses this function to compare values. If none is given, will assume priorities are numbers and will find highest priority value 4 | -- Comparator should accept two values and return true if a should be further up the queue than b and false otherwise 5 | -- :CreateFromTables(table1, table2, comparator) - Creates a new Priority queue in the format (table1[n] - priority table2[n]) - Tables will remain unchanged 6 | -- table1: The table to use as the values 7 | -- table2: The table to use as the priorities 8 | -- comparator: The comparator to pass to PriorityQueue.new(comparator) 9 | -- :Merge(queue1, queue2) - Creates a new PriorityQueue using the two provided PriorityQueues 10 | -- A PriorityQueue object has the following functions: 11 | -- :Add(newValue, priority) - Adds an element to the PriorityQueue 12 | -- newValue: The value to be added 13 | -- priority: The priority to give the newValue 14 | -- :Pop() - Removes the highest priority value from the queue and returns it 15 | -- :Peek() - Returns the highest priority value from the queue but does not remove it, nil if queue is empty 16 | -- :GetAsTable() - Returns the values from the queue as a table 17 | -- :Clear() - Clears all elements from the PriorityQueue 18 | -- :Print(withPriorities) - Prints out all the elements in the PriorityQueue 19 | -- withPriorities: If true, will print out the priorities, if false, will only output the values 20 | -- :Size() - Gets the number of elements in the PriorityQueue 21 | -- :Clone() - Creates and returns a new copy of the PriorityQueue 22 | 23 | PriorityQueue = {} 24 | PriorityQueue.__index = PriorityQueue 25 | 26 | local Floor = math.floor 27 | local function DefaultCompare(a, b) 28 | if a < b then 29 | return true 30 | else 31 | return false 32 | end 33 | end 34 | 35 | local function SiftUp(queue, index) 36 | local parentIndex 37 | if index ~= 1 then 38 | parentIndex = Floor(index/2) 39 | if queue.Compare(queue.Priorities[parentIndex], queue.Priorities[index]) then 40 | queue.Values[parentIndex], queue.Priorities[parentIndex], queue.Values[index], queue.Priorities[index] = 41 | queue.Values[index], queue.Priorities[index], queue.Values[parentIndex], queue.Priorities[parentIndex] 42 | SiftUp(queue, parentIndex) 43 | end 44 | end 45 | end 46 | 47 | local function SiftDown(queue, index) 48 | local lcIndex, rcIndex, minIndex 49 | lcIndex = index*2 50 | rcIndex = index*2+1 51 | if rcIndex > #queue.Values then 52 | if lcIndex > #queue.Values then 53 | return 54 | else 55 | minIndex = lcIndex 56 | end 57 | else 58 | if not queue.Compare(queue.Priorities[lcIndex], queue.Priorities[rcIndex]) then 59 | minIndex = lcIndex 60 | else 61 | minIndex = rcIndex 62 | end 63 | end 64 | 65 | if queue.Compare(queue.Priorities[index], queue.Priorities[minIndex]) then 66 | queue.Values[minIndex], queue.Priorities[minIndex], queue.Values[index], queue.Priorities[index] = 67 | queue.Values[index], queue.Priorities[index], queue.Values[minIndex], queue.Priorities[minIndex] 68 | SiftDown(queue, minIndex) 69 | end 70 | end 71 | 72 | function PriorityQueue.new(comparator) 73 | local newQueue = { } 74 | setmetatable(newQueue, PriorityQueue) 75 | if comparator then 76 | newQueue.Compare = comparator 77 | else 78 | newQueue.Compare = DefaultCompare 79 | end 80 | 81 | newQueue.Values = { } 82 | newQueue.Priorities = { } 83 | 84 | return newQueue 85 | end 86 | 87 | function PriorityQueue:Add(newValue, priority) 88 | table.insert(self.Values, newValue) 89 | table.insert(self.Priorities, priority) 90 | 91 | if #self.Values <= 1 then 92 | return 93 | end 94 | 95 | SiftUp(self, #self.Values) 96 | end 97 | 98 | function PriorityQueue:Pop() 99 | if #self.Values <= 0 then 100 | return nil, nil 101 | end 102 | 103 | local returnVal, returnPriority = self.Values[1], self.Priorities[1] 104 | self.Values[1], self.Priorities[1] = self.Values[#self.Values], self.Priorities[#self.Priorities] 105 | table.remove(self.Values, #self.Values) 106 | table.remove(self.Priorities, #self.Priorities) 107 | if #self.Values > 0 then 108 | SiftDown(self, 1) 109 | end 110 | 111 | return returnVal, returnPriority 112 | end 113 | 114 | function PriorityQueue:Peek() 115 | if #self.Values > 0 then 116 | return self.Values[1], self.Priorities[1] 117 | else 118 | return nil, nil 119 | end 120 | end 121 | 122 | function PriorityQueue:GetAsTable() 123 | if not self.Values or #self.Values < 1 then 124 | return nil, nil 125 | end 126 | 127 | local vals = { } 128 | local pris = { } 129 | 130 | for i = 1, #self.Values do 131 | table.insert(vals, self.Values[i]) 132 | table.insert(pris, self.Priorities[i]) 133 | end 134 | 135 | return vals, pris 136 | end 137 | 138 | function PriorityQueue:Clear() 139 | for k in pairs(self.Values) do 140 | self.Values[k] = nil 141 | end 142 | for k in pairs(self.Priorities) do 143 | self.Priorities[k] = nil 144 | end 145 | for k in pairs(self) do 146 | self[k] = nil 147 | end 148 | end 149 | 150 | function PriorityQueue:Print(withPriorities) 151 | if not withPriorities then 152 | local out = "" 153 | for i = 1, #self.Values do 154 | out = out .. tostring(self.Values[i]) .. " " 155 | end 156 | print(out) 157 | else 158 | local out = "" 159 | for i = 1, #self.Values do 160 | out = out .. tostring(self.Values[i]) .. "(" .. tostring(self.Priorities[i]) .. ") " 161 | end 162 | print(out) 163 | end 164 | end 165 | 166 | function PriorityQueue:Size() 167 | return #self.Values 168 | end 169 | 170 | function PriorityQueue:Clone() 171 | local newQueue = PriorityQueue.new(self.Compare) 172 | for i = 1, #self.Values do 173 | table.insert(newQueue.Values, self.Values[i]) 174 | table.insert(newQueue.Priorities, self.Priorities[i]) 175 | end 176 | return newQueue 177 | end 178 | 179 | -- Functions that are not self-referential 180 | function PriorityQueue:CreateFromTables(table1, table2, comparator) 181 | local newQueue = PriorityQueue.new(comparator) 182 | for i = #table1, 1, -1 do 183 | if table2[i] then 184 | newQueue:Add(table1[i], table2[i]) 185 | else 186 | return 187 | end 188 | end 189 | end 190 | 191 | function PriorityQueue:Merge(queue1, queue2, comparator) 192 | if not comparator then 193 | comparator = queue1.Compare or queue2.Compare 194 | end 195 | local newQueue = PriorityQueue.new(comparator) 196 | for i = #queue1.Values, 1, -1 do 197 | newQueue:Add(queue1.Values[i], queue1.Priorities[i]) 198 | end 199 | for i = #queue2.Values, 1, -1 do 200 | newQueue:Add(queue2.Values[i], queue2.Priorities[i]) 201 | end 202 | return newQueue 203 | end 204 | 205 | return PriorityQueue 206 | -------------------------------------------------------------------------------- /StandardLibraries/SortedList.lua: -------------------------------------------------------------------------------- 1 | -- SortedList contains the following functions: 2 | -- .new(comparator) - Creates a new SortedList 3 | -- comparator: Uses this function to compare values. If none is given, will assume the largest value is table[1] 4 | -- Comparator should accept two values and return 1 if a > b, -1 if a < b, and 0 if a == b 5 | -- :CreateFromTable(t, comparator) - Converts a table to a SortedList 6 | -- comparator: The comparator to pass to SortedList.new(comparator) 7 | -- :Merge(list1, list2) - Creates a new SortedList using the two provided SortedLists 8 | -- A SortedList object has the following functions: 9 | -- :Add(newValue) - Adds an element to the SortedList 10 | -- :Pop(numValues) - Removes the first elements in the SortedList and returns them 11 | -- numValues: The number of items to Pop(), defaults to 1 12 | -- :Peek(numValues) - Returns the first elements in the SortedList but does not remove them 13 | -- numValues: The number of items to Peek(), defaults to 1 14 | -- :GetAsTable() - Returns a table of the elements in the SortedList 15 | -- :SetComparator(comparator) - Sets the tables comparator to the new value, then sorts it 16 | -- :Clear() - Removes all values from the SortedList 17 | -- :Print() - Prints out all the values in the SortedList 18 | -- :Size() - Returns the size of the SortedList 19 | -- :Clone() - Creates and returns a new copy of the SortedList 20 | -- :Contains(value) - Checks if the requested value is in the list. Returns the index in the list if it is, or nil if it's not. Assumes the list is sorted 21 | -- :Sort() - Sorts the list using the comparator 22 | 23 | local SortedList = {} 24 | SortedList.__index = SortedList 25 | 26 | local function DefaultCompare(a, b) 27 | if b > a then 28 | return -1 29 | elseif b < a then 30 | return 1 31 | else 32 | return 0 33 | end 34 | end 35 | 36 | local function SortCompare(node1, node2, comparator) 37 | if comparator(node1, node2) > 0 then 38 | return true 39 | else 40 | return false 41 | end 42 | end 43 | 44 | local function FindInsertionPoint(listToSearch, toInsert) 45 | if #listToSearch == 0 then 46 | return 1 47 | end 48 | 49 | local minIndex = 1 50 | local maxIndex = #listToSearch 51 | local mid 52 | 53 | while true do 54 | mid = math.floor((maxIndex+minIndex)/2) 55 | local compareVal = listToSearch.Compare(listToSearch[mid], toInsert) 56 | if compareVal == 0 then 57 | return mid 58 | elseif compareVal > 0 then 59 | minIndex = mid+1 60 | if minIndex > maxIndex then 61 | return mid+1 62 | end 63 | else 64 | maxIndex = mid-1 65 | if minIndex > maxIndex then 66 | return mid 67 | end 68 | end 69 | end 70 | end 71 | 72 | function SortedList.new(comparator) 73 | local newList = { } 74 | setmetatable(newList, SortedList) 75 | if comparator then 76 | newList.Compare = comparator 77 | else 78 | newList.Compare = DefaultCompare 79 | end 80 | 81 | return newList 82 | end 83 | 84 | function SortedList:Add(newValue) 85 | table.insert(self, FindInsertionPoint(self, newValue), newValue) 86 | end 87 | 88 | function SortedList:Pop(numValues) 89 | if numValues == nil or numValues == 1 then 90 | return table.remove(self, 1) 91 | end 92 | 93 | local returnVals = { } 94 | for i = 1, numValues do 95 | table.insert(returnVals, self:Pop()) 96 | end 97 | 98 | return unpack(returnVals) 99 | end 100 | 101 | function SortedList:Peek(numValues) 102 | if numValues == nil or numValues == 1 then 103 | return self[1] 104 | end 105 | 106 | local returnVals = { } 107 | for i = 1, numValues do 108 | table.insert(returnVals, self[i]) 109 | end 110 | 111 | return unpack(returnVals) 112 | end 113 | 114 | function SortedList:Print() 115 | print(table.concat(self, " ")) 116 | end 117 | 118 | function SortedList:GetAsTable() 119 | local newTable = { } 120 | for i = 1, #self do 121 | table.insert(newTable, self[i]) 122 | end 123 | return newTable 124 | end 125 | 126 | function SortedList:SetComparator(comparator) 127 | if comparator then 128 | self.Compare = comparator 129 | end 130 | 131 | self:Sort() 132 | end 133 | 134 | function SortedList:Clear() 135 | for i = #self, 1, -1 do 136 | table.remove(self, i) 137 | end 138 | end 139 | 140 | function SortedList:Clone() 141 | local newList = SortedList.new(self.Compare) 142 | for i = 1, #self do 143 | table.insert(newList, self[i]) 144 | end 145 | return newList 146 | end 147 | 148 | function SortedList:Size() 149 | return #self 150 | end 151 | 152 | function SortedList:Contains(value) 153 | local ret 154 | local success, message = pcall(function() 155 | local minIndex = 1 156 | local maxIndex = #self 157 | 158 | while minIndex <= maxIndex do 159 | local mid = math.floor((maxIndex+minIndex)/2) 160 | 161 | local compareVal = self.Compare(self[mid], value) 162 | if compareVal == 0 then 163 | ret = mid 164 | return true 165 | elseif compareVal > 0 then 166 | minIndex = mid + 1 167 | else 168 | maxIndex = mid - 1 169 | end 170 | end 171 | end) 172 | 173 | return ret 174 | end 175 | 176 | function SortedList:Sort() 177 | table.sort(self, function(a, b) return SortCompare(a, b, self.Compare) end) 178 | end 179 | 180 | -- Functions that are not self-referential 181 | function SortedList:Merge(list1, list2, comparator) 182 | if not comparator then 183 | comparator = list1.Compare or list2.Compare 184 | end 185 | 186 | local newList = SortedList.new(comparator) 187 | for i = 1, #list1 do 188 | newList:Add(list1[i]) 189 | end 190 | for i = 1, #list2 do 191 | newList:Add(list2[i]) 192 | end 193 | return newList 194 | end 195 | 196 | function SortedList:CreateFromTable(t, comparator) 197 | if not comparator then 198 | comparator = DefaultCompare 199 | end 200 | local newList = SortedList.new(comparator) 201 | for i = 1, #t do 202 | newList:Add(t[i]) 203 | end 204 | end 205 | 206 | return SortedList 207 | -------------------------------------------------------------------------------- /UtilityLibraries/Pathfinder.lua: -------------------------------------------------------------------------------- 1 | -- Pathfinder contains the following functions: 2 | -- .new(graph, estimateDistance, comparator) - Creates a new node-based Pathfinder 3 | -- graph: The map of nodes that will be traversed 4 | -- Graph must have the following functions: 5 | -- :Neighbors(node) - Returns a table of all adjacent nodes 6 | -- :GetConnectionCost(node1, node2) - Returns how "far" node2 is from node1 7 | -- estimateDistance(node1, node2): A function that estimates how far node1 is from node2 8 | -- Must be admissible - Can never overestimate "distance" from node1 to node2 9 | -- comparator(node1, node2): A function that compares total cost of two nodes 10 | -- Should return -1 if node1 is further than node2, 1 if node1 is shorter than node2, and 0 if they are even 11 | -- Should accept nodes as format node = {nodeData, costSoFarTable, estimatedCostTable} where costSoFarTable and estiamtedCostTable are indexed by nodeData 12 | -- A Pathfinder object has the following functions: 13 | -- :FindPath(start, goal) - Find a path between the start and goal nodes. Returns nil if no path is found, otherwise returns path in reverse order 14 | 15 | local SortedList = require(303863742) -- This is the Roblox Wiki asset id for the SortedList module 16 | 17 | local Pathfinder = {} 18 | Pathfinder.__index = Pathfinder 19 | 20 | -- Should accept nodes as format node = {nodeData, costSoFarTable, estimatedCostTable} 21 | -- where costSoFarTable and estiamtedCostTable are indexed by nodeData 22 | local function Compare(node1, node2) 23 | if node1[3][node1[1]] > node2[3][node2[1]] then 24 | return -1 25 | elseif node1[3][node1[1]] < node2[3][node2[1]] then 26 | return 1 27 | else 28 | return 0 29 | end 30 | end 31 | 32 | local function reconstructPath(cameFrom, current) 33 | local path = { current } 34 | while cameFrom[current] do 35 | current = cameFrom[current] 36 | table.insert(path, current) 37 | end 38 | return path 39 | end 40 | 41 | function Pathfinder.new(graph, estimateDistance, comparator) 42 | local newPathfinder = { } 43 | newPathfinder.Graph = graph 44 | newPathfinder.EstimateDistance = estimateDistance 45 | if not comparator then 46 | comparator = Compare 47 | end 48 | setmetatable(newPathfinder, Pathfinder) 49 | newPathfinder.Open = SortedList.new(comparator) 50 | return newPathfinder 51 | end 52 | 53 | function Pathfinder:FindPath(start, goal) 54 | local closed = { } 55 | local open = self.Open 56 | open:Clear() 57 | local cameFrom = { } 58 | local estimateDistance = self.EstimateDistance 59 | 60 | local gs = { } 61 | local fs = { } 62 | 63 | gs[start] = 0 64 | fs[start] = estimateDistance(start, goal) 65 | 66 | open:Add({start, gs, fs}) 67 | 68 | while open:Size() > 0 do 69 | local current = open:Pop() 70 | 71 | if current[1] == goal then 72 | return reconstructPath(cameFrom, current[1]) 73 | end 74 | 75 | closed[current[1]] = true 76 | 77 | local neighbors = self.Graph:Neighbors(current[1]) 78 | for i = 1, #neighbors do 79 | local n = neighbors[i] 80 | if not closed[n] then 81 | local ng = gs[current[1]] + self.Graph:GetConnectionCost(current[1], n) 82 | local contains = open:Contains({n, gs, fs}) 83 | if not contains or ng < gs[n] then 84 | local nh = estimateDistance(n, goal) 85 | gs[n] = ng 86 | fs[n] = ng+nh 87 | cameFrom[n] = current[1] 88 | if not contains then open:Add({n, gs, fs}) 89 | else open:Sort() end 90 | end 91 | end 92 | end 93 | end 94 | 95 | return nil 96 | end 97 | 98 | return Pathfinder 99 | --------------------------------------------------------------------------------