I'm making a sort of crafting system for my game, and I imagine the most efficient way to carry it out would be if I had a table of selected ingredients, and a method to see if there's 2 items in any order in the selected table. Example: I want to check if there's the string "foo1" anywhere in the table, and I also want to see if there's "bar2" in the table anywhere. Feel free to ask questions to verify what I'm asking, and thanks for your help!
First, solve the problem of finding an item in a table (we will say where it is in particular):
function find(tab, element) for index, value in pairs(tab) do if value == element then return index end end end
find
will return nil
if element
wasn't in tab
, otherwise it will return where in tab
it is, ie, tab[find(tab, element)] == element
.
Thus to check if something is in a table:
if find(tab, "foo") then -- "foo" is in `tab`
We could just string a few together to see if they have multiple things:
if find(tab, "foo") and find(tab, "bar") then -- they have both "foo" and "bar"
It's also straightforward to use a loop to check if all of the things are in the table:
local required = {"foo", "bar"} local haveAll = true -- (have all *so far*) for _, thing in pairs(required) do haveAll = haveAll and find(tab, thing) end if haveAll then -- they have all the things
The limitation of this solution is that we can't require more than one a thing.
if find(tab, "foo") and find(tab, "foo") then -- same thing as if find(tab, "foo") then -- since both calls will return the same thing
Thus we could make a "count" (or find multiple) function:
function findAll(tab, element) local where = {} for index, value in pairs(tab) do if value == element then table.insert(where, index) end end end
Now findAll
will return an empty list if it isn't in tab
, otherwise it will return a list of elements.
Thus
if #findAll(tab, "foo") >= 1 then -- they have at least one foo
And we can require 2 foo and 3 bar like this:
if #findAll(tab, "foo") >= 2 and findAll(tab, "bar") >= 3 then
To put this into a loop, we can make a bunch of pairings:
local required = { {"foo", 2}, {"bar", 3} } local haveAll = true for _, thing in pairs(required) do -- thing[1]: ingrediant -- thing[2]: quantity haveAll = haveAll and ( #findAll(tab, thing[1]) >= thing[2] ) end
Bonus option: Say you don't want to make the tables of tables like above. E.g., you want ingredients to look like this:
local required = {"foo", "foo", "bar", "bar", "bar"} -- Arguably this is a worse format because you could miscount the -- number; still, it is probably more natural.
Then we can just go through all of the elements of required
and count how many times they appear!
local haveAll = true -- (have all *so far*) for _, thing in pairs(required) haveAll = haveAll and ( #findAll(tab, thing) >= #findAll(required, thing) ) end if haveAll then
This is somewhat inefficient because it will re-check for, say, "bar" three times -- but the behavior will be correct, and this will still be very fast for small lists like ingredient lists.