From f7f1bb853707c2920c62d950e01974e24a24a714 Mon Sep 17 00:00:00 2001 From: notify Date: Sat, 24 Jun 2023 12:29:20 +0800 Subject: [PATCH] Ltest (#208) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 借助单元测试修复了exppattern存在的诸多bug --- lua/core/card.lua | 25 ++++++++++++- lua/core/exppattern.lua | 77 +++++++++++++++++++++++++++++++++++++---- lua/core/util.lua | 4 +++ test/lua/pattern.lua | 46 +++++++++++++++++------- 4 files changed, 132 insertions(+), 20 deletions(-) diff --git a/lua/core/card.lua b/lua/core/card.lua index a2734350..972f4b52 100644 --- a/lua/core/card.lua +++ b/lua/core/card.lua @@ -79,7 +79,7 @@ function Card:initialize(name, suit, number, color) self.is_derived = true end - local name_splited = name:split("__") + local name_splited = self.name:split("__") self.trueName = name_splited[#name_splited] if suit == Card.Spade or suit == Card.Club then @@ -280,6 +280,29 @@ function Card:getTypeString() return "notype" end +local subtype_string_table = { + [Card.SubtypeArmor] = "armor", + [Card.SubtypeWeapon] = "weapon", + [Card.SubtypeTreasure] = "treasure", + [Card.SubtypeDelayedTrick] = "delayed_trick", + [Card.SubtypeDefensiveRide] = "defensive_ride", + [Card.SubtypeOffensiveRide] = "offensive_ride", +} + +function Card:getSubtypeString() + local t = self.sub_type + local ret = subtype_string_table[t] + if ret == nil then + if self.type == Card.TypeTrick then + return "normal_trick" + elseif self.type == Card.TypeBasic then + return "basic" + end + else + return ret + end +end + --- 获取卡牌点数并返回点数文字描述(仅限A/J/Q/K)。 local function getNumberStr(num) if num == 1 then diff --git a/lua/core/exppattern.lua b/lua/core/exppattern.lua index 83ff0749..67f29890 100644 --- a/lua/core/exppattern.lua +++ b/lua/core/exppattern.lua @@ -32,6 +32,8 @@ ---@field public cardType string[] ---@field public id integer[] +-- v0.2.6改动: cardType会被解析为trueName数组和name数组,而不是自己单独成立 + local numbertable = { ["A"] = 1, ["J"] = 11, @@ -44,14 +46,34 @@ local placetable = { [Card.PlayerEquip] = "equip", } +local card_type_table = {} + +local function fillCardTypeTable() + local tmp = {} + for _, cd in ipairs(Fk.cards) do + local t = cd:getTypeString() + local st = cd:getSubtypeString() + local tn = cd.trueName + -- TODO: local n = cd.name + + if not tmp[tn] then + card_type_table[t] = card_type_table[t] or {} + card_type_table[st] = card_type_table[st] or {} + table.insertIfNeed(card_type_table[t], tn) + table.insertIfNeed(card_type_table[st], tn) + tmp[tn] = true + end + end +end + local function matchSingleKey(matcher, card, key) local match = matcher[key] if not match then return true end local val = card[key] if key == "suit" then val = card:getSuitString() - elseif key == "cardType" then - val = card:getTypeString() + -- elseif key == "cardType" then + -- val = card:getTypeString() elseif key == "place" then val = placetable[Fk:currentRoom():getCardArea(card.id)] if not val then @@ -90,10 +112,31 @@ local function matchCard(matcher, card) and matchSingleKey(matcher, card, "suit") and matchSingleKey(matcher, card, "place") and matchSingleKey(matcher, card, "name") - and matchSingleKey(matcher, card, "cardType") + -- and matchSingleKey(matcher, card, "cardType") and matchSingleKey(matcher, card, "id") end +local function hasNegIntersection(a, b) + local neg_pass = false + + -- 第一次比较: 比较neg和正常值,如有不同即认为可以匹配 + -- 比如 ^jink 可以匹配 slash,jink + for _, neg in ipairs(a.neg or Util.DummyTable) do + for _, e in ipairs(b) do + if type(neg) == "table" then + neg_pass = not table.contains(neg, e) + else + neg_pass = neg ~= e + end + if neg_pass then return true end + end + end + + -- 第二次比较: 比较双方neg + -- 比如 ^jink 可以匹配 ^slash + -- 暂时想不出好方案 +end + local function hasIntersection(a, b) if a == nil or b == nil then return true @@ -109,9 +152,9 @@ local function hasIntersection(a, b) end end - -- TODO: 判断含有neg的两个matcher + local neg_pass = hasNegIntersection(a, b) or hasNegIntersection(b, a) - return false + return neg_pass end ---@param a Matcher @@ -123,7 +166,7 @@ local function matchMatcher(a, b) "suit", "place", "name", - "cardType", + -- "cardType", "id", } @@ -260,7 +303,27 @@ local function parseMatcher(str) ret.suit = not table.contains(t[3], ".") and t[3] or nil ret.place = not table.contains(t[4], ".") and t[4] or nil ret.name = not table.contains(t[5], ".") and t[5] or nil - ret.cardType = not table.contains(t[6], ".") and t[6] or nil + -- ret.cardType = not table.contains(t[6], ".") and t[6] or nil + if table.empty(card_type_table) then + fillCardTypeTable() + end + for _, ctype in ipairs(t[6]) do + for _, n in ipairs(card_type_table[ctype] or Util.DummyTable) do + if not ret.trueName then ret.trueName = {} end + table.insertIfNeed(ret.trueName, n) + end + end + for _, neg in ipairs(t[6].neg or Util.DummyTable) do + if type(neg) ~= "table" then neg = { neg } end + if not ret.trueName then ret.trueName = {} end + if not ret.trueName.neg then ret.trueName.neg = {} end + + local temp = {} + for _, ctype in ipairs(neg) do + table.insertTable(temp, card_type_table[ctype] or Util.DummyTable) + end + table.insert(ret.trueName.neg, temp) + end if not table.contains(t[7], ".") then ret.id = parseRawNumTable(t[7]) diff --git a/lua/core/util.lua b/lua/core/util.lua index a90511e0..f0c4bed7 100644 --- a/lua/core/util.lua +++ b/lua/core/util.lua @@ -250,6 +250,10 @@ function table:assign(targetTbl) end end +function table.empty(t) + return next(t) == nil +end + -- allow a = "Hello"; a[1] == "H" local str_mt = getmetatable("") str_mt.__index = function(str, k) diff --git a/test/lua/pattern.lua b/test/lua/pattern.lua index e2b0dbb7..23543c22 100644 --- a/test/lua/pattern.lua +++ b/test/lua/pattern.lua @@ -1,22 +1,44 @@ -local exp1 = Exppattern:Parse("slash,jink") -local exp2 = Exppattern:Parse("peach,jink") -local exp3 = Exppattern:Parse(".|.|.|.|.|trick") -local exp4 = Exppattern:Parse("peach,ex_nihilo") - -local slash = Fk:cloneCard("slash") - TestExppattern = { testMatchExp = function() - assert(exp1:matchExp(exp2)) + local exp1 = Exppattern:Parse("slash,jink") + lu.assertTrue(exp1:matchExp("peack,jink")) end, testEasyMatchCard = function() - assert(exp1:match(slash)) - assert(not exp2:match(slash)) + local exp1 = Exppattern:Parse("slash,jink") + local exp2 = Exppattern:Parse("peach,jink") + local slash = Fk:cloneCard("slash") + lu.assertTrue(exp1:match(slash)) + lu.assertFalse(exp2:match(slash)) end, testMatchWithType = function() - assert(not exp3:matchExp(exp1)) - assert(exp3:matchExp(exp4)) + local exp3 = Exppattern:Parse(".|.|.|.|.|normal_trick") + lu.assertFalse(exp3:matchExp("slash,jink")) + lu.assertTrue(exp3:matchExp("peach,ex_nihilo")) + + local basic = Exppattern:Parse(".|.|.|.|.|basic") + lu.assertFalse(basic:matchExp("nullification")) + lu.assertTrue(basic:matchExp("slash,vine")) + lu.assertTrue(Exppattern:Parse(".|.|.|.|.|armor"):matchExp("slash,vine")) + end, + + testMatchNeg = function() + lu.assertError(function() Exppattern:Parse("^(a,|1)") end) + local not_nul = Exppattern:Parse("^nullification") + local not_slash_jink = Exppattern:Parse("^(slash,jink)") + local not_basic = Exppattern:Parse(".|.|.|.|.|^basic") + local slash_jink = Exppattern:Parse("slash,jink") + local slash = Fk:cloneCard("slash") + + lu.assertFalse(not_nul:matchExp("nullification")) + lu.assertTrue(not_basic:matchExp("nullification")) + lu.assertFalse(not_slash_jink:matchExp("jink")) + lu.assertTrue(not_nul:match(slash)) + lu.assertFalse(not_slash_jink:match(slash)) + lu.assertFalse(not_basic:match(slash)) + lu.assertTrue(not_nul:matchExp("peach")) + lu.assertFalse(not_slash_jink:matchExp(not_basic)) + lu.assertFalse(slash_jink:matchExp(not_slash_jink)) end, }