Skip to content

Added card matcher functions and related.#1299

Open
AllUniversal wants to merge 35 commits into
Steamodded:mainfrom
AllUniversal:card-matchers
Open

Added card matcher functions and related.#1299
AllUniversal wants to merge 35 commits into
Steamodded:mainfrom
AllUniversal:card-matchers

Conversation

@AllUniversal
Copy link
Copy Markdown
Contributor

@AllUniversal AllUniversal commented Apr 2, 2026

+Title, I moved this over from #838, the latest two comments on that PR explain card matchers.

Additional Info:

  • I didn't modify api's or I've made a PR to the wiki repo.
  • I didn't modify api's or I've updated lsp definitions.
  • I didn't make new lovely files or all new lovely files have appropriate priority.

*/-Title, also removed some minor features, will re-add once Steamodded#838 is merged. :)
…d stuff.

+/*Title, the new subcondition `count` allows for matching according to the total number of cards that share a property, for example; A matcher that matches any card, as long as its seal appears at least twice.
+Also added `Card:get_suits()`, as I had a need for it.
@AllUniversal
Copy link
Copy Markdown
Contributor Author

AllUniversal commented Apr 3, 2026

I now added a new subcondition count. It should make it possible to match for cards based on how many cards share a property, like matching a card only if its seal appears exactly once, or at_least twice, or at_most (=> subflags of count) three times, or if a custom func (subflag of count) returns true when called with the total number of the card's property.

EDIT: Oh, and just to mention it here, I also added Card:get_suits().

@AllUniversal AllUniversal marked this pull request as draft April 3, 2026 02:17
@AllUniversal
Copy link
Copy Markdown
Contributor Author

Realized stuff ain't working right, will look at it tomorrow.

*Title, the `count` subcondition is now evaluated at the same time as the others.
-> This was necessary for it to not mess up if `condition.invert` was true. (Or in general)
@AllUniversal AllUniversal marked this pull request as ready for review April 3, 2026 09:58
@AllUniversal
Copy link
Copy Markdown
Contributor Author

Fixed (and tested) it, .count should now be working!

*Title, modified an early return condition.
*Title, realized I didn't like an early initilization of a map.
+Title
*Title, nuff said.
*/-Title, removed relic.
*Title, forgor to put `check_function` into the `SMODS.insert_card_matcher_condition()` whitelist
Comment thread src/utils.lua Outdated
return matchers_met_cards, cards_met_matchers
end

function SMODS.get_hand_from_matching(matchers_to_cards, cards_to_matchers, deduplicate_matches, all_matched_cards_score)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: this has too many arguments. table args would be preferred

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently you can call it like this;
image
The first to params of SMODS.get_hand_from_matching() match the return values from SMODS.match_cards(),
should I turn the two boolean params into one args param?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, I phrased that sloppily. That's exactly what I mean.

@Aurelius7309
Copy link
Copy Markdown
Member

Ran into some hard crashes while testing it out, so I just went ahead and fixed those ^^
I'm still not close to having fully tested it, but I did notice this not working as expected:
SMODS.create_card_matcher { enhancement = { any = { m_gold = true, m_steel = true }, count = { at_least = 3 } } } - in fact I couldn't get it to fire at all. Is this user error on my part or a bug?

@AllUniversal
Copy link
Copy Markdown
Contributor Author

@Aurelius7309 The following code works for me;
image
Specifically, this matcher would match a card if;

  1. It is either a Gold or Steel card
  2. Its enhancement (no matter what) must appear at least three times.
    -> Thus, in a hand of three or more Gold cards and less than three Steel cards, it matches every Gold card, but no Steel card.

If you want it to match three cards that are either Gold or Steel each, I don't think this is currently supported actually, in which case I'll look into how I'd add it.

@Aurelius7309
Copy link
Copy Markdown
Member

Huh, I placed this matcher into the flush Poker Hand Part and it did nothing even with 3 gold + 3 steel cards. Might have done it wrong, I guess I'll experiment some more tomorrow. Also without looking at code I'd have expected the count to relate to the property and not the actual matched card - though either can be useful.

I suppose this means there's also currently no support for linking properties, e.g., two cards of the same rank and suit?

@AllUniversal
Copy link
Copy Markdown
Contributor Author

Indeed, hmm. I'll see about that too.

+Title, this new boolean subflag makes it so that a card's property being counted is propagated to all other properties in the `any` flag of a matcher. (As long as the card matches the `any` flag).
-> This allows counting e.g. a Steel card matching an `any = {m_steel = true, m_gold = true}` as a Gold card too.
@AllUniversal
Copy link
Copy Markdown
Contributor Author

AllUniversal commented Apr 3, 2026

EDIT 2026.5.28: This is outdated now, the any_related subflag has been removed. The below functionality should be achievable by checking the number of cards matched instead.

If you want it to match three cards that are either Gold or Steel each, I don't think this is currently supported actually, in which case I'll look into how I'd add it.

This is now supported through the use of the new count subflag any_related; It makes it so that if a card matches a matcher.condition's any flag, and that condition (e.g. enhancement) is counted, the matcher also adds 1 to all the other values of the any flag, thus essentially merging their counts into one.

So the above example of matching at_least three cards that are either Steel or Gold is now possible with this matcher;

local matcher = SMODS.create_card_matcher({enhancement = {any = {m_gold = true, m_steel = true}, count = {at_least = 3, any_related = true}}})

…ax_cards `

+Title, this allows easy `matcher` duplication for hand detection, without having to calculate the same `matcher` multiple times.
…toring

+/*Title, see LSP defs or PR for details on `overlap`.
@AllUniversal
Copy link
Copy Markdown
Contributor Author

AllUniversal commented Apr 4, 2026

@Aurelius7309 Aight so I added a new count subflag overlap; It too has the all, any or none (new) subflags, but for conditions, not keys. This is a matcher for matching two or more cards, as long as they have a Red seal and share enhancements;

SMODS.create_card_matcher({seal = {any = {Red = true}, count = {at_least = 2, overlap = {any = {enhancement = true}}}}})

One for matching exactly two cards with a Red seal and shared enhancement;

SMODS.create_card_matcher({seal = {any = {Red = true}, count = {exact = 2, overlap = {any = {enhancement = true}}}}})

(This one would not match two cards with Red seals if there's a third one, unless that one has a different enhancement)

@AllUniversal
Copy link
Copy Markdown
Contributor Author

Was just inspired to try implementing Holy Fool's Faust Joker's Devil's Hand pokerhand using card matchers, this is the code (replacing Flush for testing):

SMODS.PokerHandPart {
    key = '_flush',
    func = function(hand) 
        local matcher = SMODS.create_card_matcher({suit = {count = {exact = 1}}, rank = {count = {exact = 1}}})
        local m_t_c, c_t_m = SMODS.match_cards(hand, {matcher})
        return SMODS.get_hand_from_matching(m_t_c, c_t_m, {matcher_max_cards = {[matcher] = 4}})
    end -- get_flush(hand) end,
}

Making these four cards count as a Devil's Hand / Flush:
image

…s + added some warn messages

-/+Title;
-`count.any_related` subflag has been removed
-> Instead check for the number of cards matched after the fact.
+ `count.overlap` has gained new subflags, allowing even more fine-grained matching. Example in the PR.
+Added some warn messages when a matcher is created with invalid flags.
@AllUniversal
Copy link
Copy Markdown
Contributor Author

AllUniversal commented May 28, 2026

I have now added subflags to count.overlap[condition];

"any" = boolean             -> any [condition]s overlap
"all" = boolean             -> all [condition]s of the primary card checked overlap with the card checked against
"all_either" = boolean      -> all [condition]s of the card with fewer [condition]s overlap with the other card 
"none" = boolean            -> none of the [condition]s overlap
"exact" = integer           -> [exact] number of cards overlap
"at_least" = integer        -> [at_least] number of cards overlap
"at_most" = integer         -> [at_most] number of cards overlap
"func" = functions          -> see `count.func`

These allow checking overlap more precisely, e.g.;

{suit = {count = {at_least = 3, overlap = {any = {enhancement = {none = true}, seal = {none = true}}}}}}

^ This is a matcher to match at least 3 cards which share a suit, as long as they either share no enhancements or no seals.

{suit = {count = {at_least = 3, overlap = {any = {enhancement = {none = true}, seal = {any = true, at_least = 3}}}}}}

^ This is a matcher to match at least 3 cards which share a suit, as long as they either share no enhancements or a seal with 2 other cards (with which they share a suit).

…cherCondition` + fixed/refactored some stuff

*/+Title, `SMODS.MatcherCondition` allows creating new conditions easily, for e.g. Paperback's paperclips.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants