MatchEngine: Difference between revisions

From Intrigues Wiki
No edit summary
No edit summary
Β 
(One intermediate revision by the same user not shown)
Line 2: Line 2:


== Overview ==
== Overview ==
The <code>MatchEngine</code> is part of the Intrigues core system and provides a lightweight, asynchronous matchmaking mechanism between actors. It is designed to help characters find suitable partners based on rule-based logic and customizable conditions.
The <code>MatchEngine</code> is part of the Intrigues core system and provides a lightweight, callback-based matchmaking mechanism between actors.
Β 
It helps characters find suitable partners using rule-based compatibility checks and user-defined filters.
----
----


== πŸ”§ <code>CandidateFilter</code> (Optional) ==
== πŸ”§ CandidateFilter (Optional) ==
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
IM.Instance.MatchEngine.CandidateFilter = (self, other) => ...;
IM.Instance.MatchEngine.CandidateFilter = (self, other) => ...;
Line 11: Line 13:


=== Description ===
=== Description ===
<code>CandidateFilter</code> is a user-defined delegate that determines which actors are eligible to be evaluated as potential matches. It is executed '''before''' expensive rule-based compatibility checks (<code>IsCompatibleAsync</code>) are triggered.
<code>CandidateFilter</code> is a user-defined function that determines who can be evaluated as a match.
Β 
It runs '''before''' expensive rule-based logic (<code>IsCompatibleAsync</code>) to filter out invalid candidates.
----


=== Signature ===
=== Signature ===
Line 17: Line 22:
public Func<Actor, Actor, bool> CandidateFilter;
public Func<Actor, Actor, bool> CandidateFilter;
</syntaxhighlight>
</syntaxhighlight>
----


=== Purpose ===
=== Purpose ===
To '''filter out clearly invalid candidates early''', improving performance and allowing customization of match logic (e.g., support for same-gender marriages, polygamy, or role-based restrictions).
To filter out obviously incompatible actors early β€” improving performance and allowing logic customization
Β 
(e.g., same-gender marriage, family bans, rank restrictions).
----


=== Example ===
=== Example ===
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
// Allow only opposite-gender, unmarried actors
// Only opposite-gender, unmarried actors
IM.Instance.MatchEngine.CandidateFilter = (self, other) =>
IM.Instance.MatchEngine.CandidateFilter = (self, other) =>
other.HasSpouse == false && other.Gender != self.Gender;
Β  Β  !other.HasSpouse && other.Gender != self.Gender;


// Allow same-gender matches
// Same-gender allowed
IM.Instance.MatchEngine.CandidateFilter = (self, other) =>
IM.Instance.MatchEngine.CandidateFilter = (self, other) =>
other.HasSpouse == false; // no gender check
Β  Β  !other.HasSpouse; // No gender check
</syntaxhighlight>πŸ“ '''Note:''' This filter is typically set once during initialization (e.g., in <code>DemoManager.cs > Start()</code>), and applies globally across all match operations.
</syntaxhighlight><blockquote>πŸ“ '''Note:''' CandidateFilter is typically set in <code>DemoManager.cs > Start()</code> and affects all matchmaking.</blockquote>
----


Β 
== πŸ” FindMatch (Callback-Based) ==
πŸ” <code>FindMatch(Actor actor, string rule)</code><syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
Actor matched = await IM.Instance.MatchEngine.FindMatch(actor, "Love Rule");
IM.Instance.MatchEngine.FindMatch(actor, "Love Rule", match => {
Β  Β  if (match != null)
Β  Β  Β  Β  actor.StartScheme("Love", match);
});
</syntaxhighlight>
</syntaxhighlight>


=== Description ===
=== Description ===
Scans all eligible actors to find a suitable match for the given <code>actor</code>, based on a specified rule (e.g., <code>"Love Rule"</code>). Returns the first actor that passes both the <code>CandidateFilter</code> (if any) and the <code>IsCompatibleAsync(...)</code> rule evaluation.
<code>FindMatch</code> scans all eligible candidates for the given actor, using a specified rule (like <code>"Love Rule"</code>).
Β 
It executes asynchronously in the background and returns the first valid match through a callback.
----


=== Signature ===
=== Signature ===
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
public async Task<Actor> FindMatch(Actor actor, string rule)
public void FindMatch(Actor actor, string rule, Action<Actor> onMatchFound)
</syntaxhighlight>
</syntaxhighlight>
----


=== Parameters ===
=== Parameters ===
Line 53: Line 70:
|<code>actor</code>
|<code>actor</code>
|<code>Actor</code>
|<code>Actor</code>
|The actor who is seeking a match
|The actor seeking a match
|-
|-
|<code>rule</code>
|<code>rule</code>
|<code>string</code>
|<code>string</code>
|The name of the rule used to check compatibility
|The rule used to determine compatibility
|-
|<code>onMatchFound</code>
|<code>Action<Actor></code>
|Called with the matched actor or <code>null</code> if none found
|}
|}
Β 
----
=== Returns ===
Β 
* <code>Actor</code> – A compatible partner, or <code>null</code> if no match is found


=== Example ===
=== Example ===
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
private async void TryFindSpouse(Actor actor)
IM.Instance.MatchEngine.FindMatch(actor, "Love Rule", match => {
{
Β  Β  var match = await IM.Instance.MatchEngine.FindMatch(actor, "Love Rule");
Β 
Β Β  Β  if (match != null)
Β Β  Β  if (match != null)
Β  Β  {
Β Β  Β  Β  Β  actor.StartScheme("Love", match);
Β Β  Β  Β  Β  actor.StartScheme("Love", match);
Β  Β  }
Β Β  Β  else
Β Β  Β  else
Β  Β  {
Β Β  Β  Β  Β  Debug.Log("No match found.");
Β Β  Β  Β  Β  Debug.Log("No compatible match found.");
});
Β  Β  }
}
</syntaxhighlight>
</syntaxhighlight>
----
=== Notes ===


=== How It Works ===
* The callback '''always fires''', even if no match is found (with <code>null</code>).
* Ideal for use in <code>Update()</code>, timers, <code>MarriageTimer()</code>, or any non-async context.
* Evaluation is spread over frames to avoid freezing the main thread.


# Filters all actors using <code>CandidateFilter</code> (if set)
----
# Shuffles the list to avoid bias
# Iterates through each candidate and checks rule-based compatibility
# Returns the first matching actor


== ⚠️ Performance Tip ==
== ⚠️ Performance Tip ==
In large simulations (100+ actors), skipping <code>CandidateFilter</code> can cause significant slowdowns. Always define a lightweight <code>CandidateFilter</code> to minimize expensive async rule checks.
Always define a <code>CandidateFilter</code> β€” especially in simulations with 100+ actors β€” to avoid evaluating thousands of unnecessary compatibility rules.
----
----


== πŸ§ͺ Debugging Tip ==
== πŸ§ͺ Debugging Tip ==
To check how many candidates are being evaluated, you can log:<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
var count = IM.Actors.Count(p => MatchEngine.CandidateFilter(actor, p));
var count = IM.Actors.Count(p => MatchEngine.CandidateFilter(actor, p));
Debug.Log($"Evaluating {count} candidates for {actor.Name}");
Debug.Log($"Evaluating {count} candidates for {actor.Name}");
</syntaxhighlight>
</syntaxhighlight>You can log how many candidates passed the filter:
----


== βœ… Summary ==
== βœ… Summary ==
Line 103: Line 117:
!Purpose
!Purpose
|-
|-
|<code>CandidateFilter</code>
|CandidateFilter
|Filters actors before compatibility check
|Pre-filters actors before heavy compatibility check
|-
|-
|<code>FindMatch</code>
|FindMatch
|Asynchronously finds the first compatible match
|Finds the first compatible partner via callback
|-
|-
|Fully async
|Fully async
|Doesn’t freeze the main thread
|Internally asynchronous without requiring async/await
|-
|Lightweight
|Designed to scale to hundreds of actors
|-
|-
|Customizable
|Customizable
|Users define matching conditions
|Users define their own filtering logic
|-
|Performant
|Filtering avoids unnecessary rule execution
|}
|}
----<blockquote>For more advanced matching (e.g., scoring, top-N matches, or group dynamics), consider extending <code>MatchEngine</code> or integrating it with custom rule graphs.</blockquote>
----

Latest revision as of 22:23, 16 May 2025

🧠 Intrigues MatchEngine API Reference

Overview

The MatchEngine is part of the Intrigues core system and provides a lightweight, callback-based matchmaking mechanism between actors.

It helps characters find suitable partners using rule-based compatibility checks and user-defined filters.


πŸ”§ CandidateFilter (Optional)

IM.Instance.MatchEngine.CandidateFilter = (self, other) => ...;

Description

CandidateFilter is a user-defined function that determines who can be evaluated as a match.

It runs before expensive rule-based logic (IsCompatibleAsync) to filter out invalid candidates.


Signature

public Func<Actor, Actor, bool> CandidateFilter;

Purpose

To filter out obviously incompatible actors early β€” improving performance and allowing logic customization

(e.g., same-gender marriage, family bans, rank restrictions).


Example

// Only opposite-gender, unmarried actors
IM.Instance.MatchEngine.CandidateFilter = (self, other) =>
    !other.HasSpouse && other.Gender != self.Gender;

// Same-gender allowed
IM.Instance.MatchEngine.CandidateFilter = (self, other) =>
    !other.HasSpouse; // No gender check

πŸ“ Note: CandidateFilter is typically set in DemoManager.cs > Start() and affects all matchmaking.


πŸ” FindMatch (Callback-Based)

IM.Instance.MatchEngine.FindMatch(actor, "Love Rule", match => {
    if (match != null)
        actor.StartScheme("Love", match);
});

Description

FindMatch scans all eligible candidates for the given actor, using a specified rule (like "Love Rule").

It executes asynchronously in the background and returns the first valid match through a callback.


Signature

public void FindMatch(Actor actor, string rule, Action<Actor> onMatchFound)

Parameters

Name Type Description
actor Actor The actor seeking a match
rule string The rule used to determine compatibility
onMatchFound Action<Actor> Called with the matched actor or null if none found

Example

IM.Instance.MatchEngine.FindMatch(actor, "Love Rule", match => {
    if (match != null)
        actor.StartScheme("Love", match);
    else
        Debug.Log("No match found.");
});

Notes

  • The callback always fires, even if no match is found (with null).
  • Ideal for use in Update(), timers, MarriageTimer(), or any non-async context.
  • Evaluation is spread over frames to avoid freezing the main thread.

⚠️ Performance Tip

Always define a CandidateFilter β€” especially in simulations with 100+ actors β€” to avoid evaluating thousands of unnecessary compatibility rules.


πŸ§ͺ Debugging Tip

var count = IM.Actors.Count(p => MatchEngine.CandidateFilter(actor, p));
Debug.Log($"Evaluating {count} candidates for {actor.Name}");

You can log how many candidates passed the filter:


βœ… Summary

Feature Purpose
CandidateFilter Pre-filters actors before heavy compatibility check
FindMatch Finds the first compatible partner via callback
Fully async Internally asynchronous without requiring async/await
Lightweight Designed to scale to hundreds of actors
Customizable Users define their own filtering logic