From 02c8f33d18ce81a71bc4f0739480950457704e26 Mon Sep 17 00:00:00 2001 From: Christian Daniel Jensen Date: Mon, 2 Nov 2026 05:49:04 -0700 Subject: [PATCH] Define the 15 canonical Continuum Universes Essence symbols and safe parsing --- .../libcockatrice/card/essence_types.h | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 libcockatrice_card/libcockatrice/card/essence_types.h diff --git a/libcockatrice_card/libcockatrice/card/essence_types.h b/libcockatrice_card/libcockatrice/card/essence_types.h new file mode 100644 index 000000000..5dbd6f79f --- /dev/null +++ b/libcockatrice_card/libcockatrice/card/essence_types.h @@ -0,0 +1,64 @@ +#ifndef CONTINUUMATRICE_ESSENCE_TYPES_H +#define CONTINUUMATRICE_ESSENCE_TYPES_H + +#include +#include +#include +#include +#include + +namespace Essence +{ +// Canonical Continuum Universes CCG essence symbols. N is colorless. +// The other codes have no names, colors or game mechanics assigned here. +inline const QString &symbols() +{ + static const QString value = QStringLiteral("MSOGEAQCBRLVTPN"); + return value; +} + +inline bool isSymbol(const QChar symbol) +{ + return symbols().contains(symbol); +} + +// Stable ordering for the card's explicitly recorded Essence identity. This +// is a list of distinct codes, NOT a derived resource cost or mana color. +inline QString normalizeIdentity(const QString &input) +{ + const QString uppercase = input.toUpper(); + QString normalized; + for (const QChar code : symbols()) { + if (uppercase.contains(code)) { + normalized.append(code); + } + } + return normalized; +} + +// Costs may contain bare or braced symbols, e.g. MSN or {M}{S}{N}. +// Decimal quantities are passed through as generic amounts for display. +// Hybrid/phrexian/other MTG-specific syntax is NOT defined as Essence. +inline QStringList parseCost(const QString &cost) +{ + static const QRegularExpression regex(QStringLiteral(R"(\{([^}]+)\}|(\d+)|([MSOGEAQCBRLVTPN]))")); + QStringList result; + auto matches = regex.globalMatch(cost); + while (matches.hasNext()) { + const auto match = matches.next(); + if (!match.captured(1).isEmpty()) { + const QString token = match.captured(1); + if (token.size() == 1 && isSymbol(token.at(0))) { + result.append(token); + } + } else if (!match.captured(2).isEmpty()) { + result.append(match.captured(2)); + } else if (!match.captured(3).isEmpty()) { + result.append(match.captured(3)); + } + } + return result; +} +} // namespace Essence + +#endif // CONTINUUMATRICE_ESSENCE_TYPES_H