summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-03-14 22:22:20 -0500
committersanine <sanine.not@pm.me>2023-03-14 22:22:20 -0500
commit7516044247663dabccc7db110703cfcf8545d95f (patch)
tree77b06170bdafad99e34a22447568fde557611b15
parent344d6e68bee7f286f7c4b4b25518367c595b4619 (diff)
add ecs.Filter
-rw-r--r--honey/ecs.lua100
-rw-r--r--honey/ecs.test.lua51
2 files changed, 151 insertions, 0 deletions
diff --git a/honey/ecs.lua b/honey/ecs.lua
new file mode 100644
index 0000000..ef682b5
--- /dev/null
+++ b/honey/ecs.lua
@@ -0,0 +1,100 @@
+local module = {}
+setmetatable(module, {__index=_G})
+setfenv(1, module)
+
+
+--===== filters =====--
+
+
+Filter = {}
+
+-- base filter creation
+function Filter.new(_, operation, tbl)
+ local self = {}
+ self.keys = {}
+ self.filters = {}
+ self.op = operation
+
+ for _, v in ipairs(tbl) do
+ if type(v) == "function" then
+ table.insert(self.filters, v)
+ elseif type(v) == "string" then
+ table.insert(self.keys, v)
+ end
+ end
+
+ return function(entity)
+ return Filter.check(self, entity)
+ end
+end
+setmetatable(Filter, {__call=Filter.new})
+
+
+-- base filter checking
+function Filter.check(self, entity)
+ local funcname = "check" .. self.op
+ return Filter[funcname](self, entity)
+end
+
+
+-- AND filter (all keys and subfilters must match)
+function Filter.AND(tbl)
+ return Filter("AND", tbl)
+end
+function Filter.checkAND(self, entity)
+ for _, subfilter in ipairs(self.filters) do
+ if not subfilter(entity) then return false end
+ end
+ for _, key in ipairs(self.keys) do
+ if entity[key] == nil then return false end
+ end
+ return true
+end
+
+
+-- OR filter (at least one key or subfilter must match)
+function Filter.OR(tbl)
+ return Filter("OR", tbl)
+end
+function Filter.checkOR(self, entity)
+ for _, subfilter in ipairs(self.filters) do
+ if subfilter(entity) then return true end
+ end
+ for _, key in ipairs(self.keys) do
+ if entity[key] ~= nil then return true end
+ end
+ return false
+end
+
+
+-- NAND filter (at least one key or subfilter must NOT match)
+function Filter.NAND(tbl)
+ return Filter("NAND", tbl)
+end
+function Filter.checkNAND(self, entity)
+ for _, subfilter in ipairs(self.filters) do
+ if not subfilter(entity) then return true end
+ end
+ for _, key in ipairs(self.keys) do
+ if entity[key] == nil then return true end
+ end
+ return false
+end
+
+
+-- NOR filter (no keys or subfilters may match)
+function Filter.NOR(tbl)
+ return Filter("NOR", tbl)
+end
+function Filter.checkNOR(self, entity)
+ for _, subfilter in ipairs(self.filters) do
+ if subfilter(entity) then return false end
+ end
+ for _, key in ipairs(self.keys) do
+ if entity[key] ~= nil then return false end
+ end
+ return true
+end
+
+
+return module
diff --git a/honey/ecs.test.lua b/honey/ecs.test.lua
new file mode 100644
index 0000000..7814097
--- /dev/null
+++ b/honey/ecs.test.lua
@@ -0,0 +1,51 @@
+local function test(msg, f)
+ local success, error = pcall(f)
+ if success then
+ print(msg .. "\t\t[OK]")
+ else
+ print(msg .. "\t\t[FAIL]")
+ print(error)
+ end
+end
+
+
+local ecs = require 'ecs'
+local Filter = ecs.Filter
+
+
+test("Filter.AND correctly matches basic keys", function()
+ local filter = Filter.AND{"hello", "world"}
+
+ assert(filter{hello=true} == false)
+ assert(filter{world=true} == false)
+ assert(filter{hello=true, world=true} == true)
+ assert(filter{asm=true, hello=true, world=true} == true)
+end)
+test("Filter.AND correctly matches subfilters", function()
+ local subfilter = Filter.AND{"hello"}
+ local filter = Filter.AND{subfilter, "world"}
+
+ assert(filter{hello=true} == false)
+ assert(filter{world=true} == false)
+ assert(filter{hello=true, world=true} == true)
+ assert(filter{asm=true, hello=true, world=true} == true)
+end)
+
+
+test("Filter.OR correctly matches basic keys", function()
+ local filter = Filter.OR{"hello", "world"}
+
+ assert(filter{hello=true} == true)
+ assert(filter{world=true} == true)
+ assert(filter{hello=true, world=true} == true)
+ assert(filter{asm=true} == false)
+end)
+test("Filter.OR correctly matches subfilters", function()
+ local subfilter = Filter.OR{"hello"}
+ local filter = Filter.OR{subfilter, "world"}
+
+ assert(filter{hello=true} == true)
+ assert(filter{world=true} == true)
+ assert(filter{hello=true, world=true} == true)
+ assert(filter{asm=true} == false)
+end)