diff --git a/cockatrice/src/game/filters/filter_string.cpp b/cockatrice/src/game/filters/filter_string.cpp index 4a7a58b9e..3d168d64c 100644 --- a/cockatrice/src/game/filters/filter_string.cpp +++ b/cockatrice/src/game/filters/filter_string.cpp @@ -187,15 +187,25 @@ static void setupParserRules() return QString::fromStdString(std::string(sv.sv())).toLower(); }; + search["StringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher { + // Helper function for word boundary matching + auto createWordBoundaryMatcher = [](const QString &target) { + QString pattern = QString("\\b%1\\b").arg(QRegularExpression::escape(target)); + QRegularExpression regex(pattern, QRegularExpression::CaseInsensitiveOption); + return [regex](const QString &s) { return regex.match(s).hasMatch(); }; + }; + if (sv.choice() == 0) { const auto target = std::any_cast(sv[0]); - return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); }; + return createWordBoundaryMatcher(target); } const auto target = std::any_cast(sv[0]); return [=](const QString &s) { - auto containsString = [&s](const QString &str) { return s.split(" ").contains(str, Qt::CaseInsensitive); }; + auto containsString = [&s, &createWordBoundaryMatcher](const QString &str) { + return createWordBoundaryMatcher(str)(s); + }; return std::any_of(target.begin(), target.end(), containsString); }; }; diff --git a/tests/carddatabase/carddatabase_test.cpp b/tests/carddatabase/carddatabase_test.cpp index 4818cd22e..c572dcf3b 100644 --- a/tests/carddatabase/carddatabase_test.cpp +++ b/tests/carddatabase/carddatabase_test.cpp @@ -18,8 +18,8 @@ TEST(CardDatabaseTest, LoadXml) // load dummy cards and test result db->loadCardDatabases(); - ASSERT_EQ(8, db->getCardList().size()) << "Wrong card count after load"; - ASSERT_EQ(4, db->getSetList().size()) << "Wrong sets count after load"; + ASSERT_EQ(9, db->getCardList().size()) << "Wrong card count after load"; + ASSERT_EQ(5, db->getSetList().size()) << "Wrong sets count after load"; ASSERT_EQ(3, db->getAllMainCardTypes().size()) << "Wrong types count after load"; ASSERT_EQ(Ok, db->getLoadStatus()) << "Wrong status after load"; diff --git a/tests/carddatabase/data/cards.xml b/tests/carddatabase/data/cards.xml index f235ab4f9..2c1c09ed8 100644 --- a/tests/carddatabase/data/cards.xml +++ b/tests/carddatabase/data/cards.xml @@ -11,7 +11,7 @@ G 2G 2 - Creature + Creature — Cat Creature 3/3 @@ -26,7 +26,22 @@ R 2RR 4 - Creature + Creature — Dog + Creature + 4/4 + + + + Doctor + WHO + 0 + Why did wizards introduce two-word creature types + + 222 + R + 2RR + 4 + Creature — Human Time Lord Doctor Creature 4/4 diff --git a/tests/carddatabase/filter_string_test.cpp b/tests/carddatabase/filter_string_test.cpp index b29660159..534600e84 100644 --- a/tests/carddatabase/filter_string_test.cpp +++ b/tests/carddatabase/filter_string_test.cpp @@ -21,12 +21,14 @@ protected: cat = CardDatabaseManager::getInstance()->getCardBySimpleName("Cat"); notDeadAfterAll = CardDatabaseManager::getInstance()->getCardBySimpleName("Not Dead"); truth = CardDatabaseManager::getInstance()->getCardBySimpleName("Truth"); + doctor = CardDatabaseManager::getInstance()->getCardBySimpleName("Doctor"); } // void TearDown() override {} CardData cat; CardData notDeadAfterAll; CardData truth; + CardData doctor; }; QUERY(Empty, cat, "", true) @@ -34,6 +36,9 @@ QUERY(Typing, cat, "t", true) QUERY(NonMatchingType, cat, "t:kithkin", false) QUERY(MatchingType, cat, "t:creature", true) +QUERY(MatchingCreatureType, cat, "t:cat", true) +QUERY(PartialMatchingType, cat, "t:ca", false) +QUERY(MatchingMultiWordType, doctor, "t:\"Time Lord\"", true) QUERY(Not1, cat, "NOT t:kithkin", true) QUERY(Not2, cat, "NOT t:creature", false) QUERY(NonKeyword1, cat, "not t:kithkin", false)