encounter-rs 0.1.0

Resolution engine for multi-character encounters with pluggable scoring backends
Documentation

encounter

crates.io docs.rs license

encounter resolves what happens when several characters interact in a scene. Give it the what could happen (a catalog of possible actions) and the who is present (the characters); it picks one of three protocols (one-shot exchange, turn-by-turn scene, or long-running scheme) and returns a structured what happened — beats and typed effects, replayable and testable. It does not generate prose, run a drama manager, or decide policy — those live in the layer above.

[dependencies]
encounter-rs = "0.1"

The package is published as encounter-rs (the unsuffixed name was squatted by an unrelated placeholder); the library name is encounter, so user code is unchanged: use encounter::resolution::SingleExchange;.

Quickstart

use encounter::resolution::SingleExchange;
use encounter::scoring::{AlwaysAccept, ScoredAffordance};
use encounter::affordance::{AffordanceSpec, CatalogEntry};
use encounter::types::Effect;

let entry = CatalogEntry {
    spec: AffordanceSpec {
        name: "greet".into(),
        domain: "social".into(),
        bindings: vec!["self".into(), "target".into()],
        considerations: Vec::new(),
        effects_on_accept: vec![Effect::RelationshipDelta {
            axis: "friendship".into(),
            from: "target".into(),
            to: "self".into(),
            delta: 0.05,
        }],
        effects_on_reject: Vec::new(),
        drive_alignment: Vec::new(),
    },
    precondition: String::new(),
};
let scored = ScoredAffordance {
    entry,
    score: 0.8,
    bindings: [("self".into(), "alice".into()), ("target".into(), "bob".into())]
        .into_iter()
        .collect(),
};

let result = SingleExchange.resolve("alice", "bob", &[scored], &AlwaysAccept);
assert!(result.beats[0].accepted);

Runnable versions of all three protocols live in examples/.

The three protocols

  • SingleExchange — one initiator picks an action, the responder accepts or rejects, the scene ends in a single beat. For one-shot dramatic moments.
  • MultiBeat — turn-based scene; participants cycle and scoring is recomputed each beat, so mid-scene world changes affect later choices.
  • BackgroundScheme — long-running plot that accumulates progress over many ticks, then resolves to one consequential beat.

Where you plug in

encounter depends on serde, toml, and thiserror — and nothing else. The two pieces of consumer policy it pushes out as traits:

  • ActionScorer<P> — given an actor and the actions available to them, returns each action with a utility score. This is where your scoring policy lives — a utility/salience model, a GOAP planner, or an LLM call.
  • AcceptanceEval<P> — given a responder and a scored action, returns true if they accept. This is where your fabula evaluator, reaction model, or argumentation backend lives. Fabula here is the precondition language for action availability — typically a small DSL the bridge crate parses.

On each beat, the protocol asks ActionScorer for ranked actions and then asks AcceptanceEval whether the chosen action lands.

The <P> parameter is the precondition type. The default is String (raw fabula source); bridges typically substitute a typed pattern.

Use with argumentation

The canonical reasoning backend is the argumentation crate, via the encounter-argumentation bridge. It implements ActionScorer and AcceptanceEval using a Dung-framework-style argument graph with weighted-bipolar attacks and a β-budget acceptance dial.

If you need encounter to do more than what the built-in AlwaysAccept / AlwaysReject test helpers offer, start with that bridge.

Inspirations

encounter borrows shape from several published systems. The implementations are small, opinionated reductions — not faithful reproductions:

  • SingleExchange reduces the intent/reaction step from McCoy et al., Comme il Faut (Game AI Pro 3, ch. 43). Full CiF social-games are out of scope.
  • MultiBeat takes the speaker-rotation loop from Evans & Short, Versu (IEEE TCIAIG 2014). Full social-practice goal stacks, role tableaux, and obligations are out of scope.
  • BackgroundScheme takes the progress-bar shape from the scheme system in Crusader Kings III (CK3). Agents, discovery rolls, and counter-actions are out of scope.
  • TurnPolicy::AdjacencyPair is the adjacency-pair model from Sacks, Schegloff & Jefferson, Lectures on Conversation (1992).

License

Dual-licensed under either of:

at your option.