9

I am making a game using Lua and I need to use Breadth-first search to implement a fast path-finding algorithm which finds the shortest path between the enemy AI and the player.

I will have up to 3 enemies using this algorithm at once and map is a 2-dimensional tile-based maze. I have already implemented collision detection so now all that is left to do is get a way for the enemies to find the shortest path to the player in a way that can be done quickly and and preferably about 80-90 times per second per enemy.

When I previously implemented breadth-first search, I used a queue in C++. From what I have read about Lua, stack implementations using tables are very efficient since the elements don't need to be shifted after push() (AKA table.insert) and pop() (table.remove) operations. However, I have theorized that queues with large sizes can be very inefficient in Lua, because if you want to insert/remove something from index 1 of a table, all other elements in the table must be shifted either upward or downward in the table.

Thus, I would like to ask some simple questions for those experienced in Lua. What is the simplest and/or fastest implementation of a queue in this language? Is it even possible to have a fast queue in Lua or is it just generally accepted that Lua will always be forced to "shift" all elements for a queue operation such as pop() or append()?

EDIT: I understand that the underlying implementation of "shifting elements" in Lua tables is written in C and is therefore quite optimized. I just would like to know if I have any better options for queues before I begin to write a simple table implementation.

Shashank
  • 13,713
  • 5
  • 37
  • 63

1 Answers1

16

A fast implementation of queue(actually double queue) in Lua is done by the book Programming in Lua:

List = {}
function List.new ()
  return {first = 0, last = -1}
end

It can insert or remove an element at both ends in constant time, the key is to store both first and last.

function List.pushleft (list, value)
  local first = list.first - 1
  list.first = first
  list[first] = value
end

function List.pushright (list, value)
  local last = list.last + 1
  list.last = last
  list[last] = value
end

function List.popleft (list)
  local first = list.first
  if first > list.last then error("list is empty") end
  local value = list[first]
  list[first] = nil        -- to allow garbage collection
  list.first = first + 1
  return value
end

function List.popright (list)
  local last = list.last
  if list.first > last then error("list is empty") end
  local value = list[last]
  list[last] = nil         -- to allow garbage collection
  list.last = last - 1
  return value
end
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • 12
    Turns out I just put one on GitHub two days ago that's usable as a library. I deal with indices slightly differently but otherwise it's the same thing. MIT license (not in the repo yet but I will add it tonight), feel free to use it :) https://github.com/catwell/cw-lua/tree/master/deque – catwell Sep 17 '13 at 08:14
  • @catwell Thank you, your implementation works perfectly. I stripped it of a few methods which I dont need (rotate,remove). But I especially found your `contents` method useful for doing fast `ipairs` iteration which I believe is faster than doing `pairs` iteration. I'll add the MIT license to my code. – Shashank Sep 17 '13 at 08:47
  • @ShashankGupta Actually there is a way better solution to iterate such a queue which I have just added (I already had the code somewhere, just not committed :p) https://github.com/catwell/cw-lua/commit/bd29a81a7a748f65040c9db0ce6374d8acab2545 I have also added the license file. – catwell Sep 17 '13 at 09:48
  • @catwell Keep in mind though that this is a time bomb if you use it as a FIFO queue without rotating it after pop operations, as the contents of the queue will move consistently up or down the numerical range. While the theoretical numerical limit of single floats is somewhere along 10^38 and thus quite safe, the mantissa is only 23 Bits long (around 10^6), which means after those are up, resolution goes down (10^20 + 1 = 10^20) – dualed Sep 25 '13 at 13:52
  • 1
    @dualed Actually Lua numbers are doubles by default, so you only get that behavior above 2^52 (between 10^15 and 10^16). That is still quite high IMO. If you do a million operations per second it will take more than 140 years until it becomes a problem... Note that it would be worse with 32 bit integers. – catwell Sep 25 '13 at 15:56
  • @catwell Hmm, I was under the strong impression Lua used singles, but the manual also says that double is the default. No idea where I got that idea now... So, you are right, it's quite improbable any application would run long enough to crash on this, if doubles are used, sorry. – dualed Sep 26 '13 at 17:35
  • 4
    Do you mind showing the implementation for calling these functions and initializing a list/queue? – Timothy Swan Oct 21 '14 at 16:21