Giter Club home page Giter Club logo

mimir's Introduction

Mimir GitHub banner

OSS by Subtale Chat on Discord Rust Docs MIT License Apache-2.0 License

Mimir is a contextual query engine (implemented in Rust) for video games with dynamic events (e.g. dialogue, animations) driven by their current world's state.

Documentation

Our official documentation at mimir.subtale.com offers significantly more detail than this README, including:

Inspiration

Mímir is heavily inspired (both in concept and general architecture) by Elan Ruskin's amazing session from GDC 2012 on AI-driven Dynamic Dialog.

Fundamentally speaking, Mímir is simply a Rust implementation of Elan's proposed system for dynamic dialog. However, Mímir does offer some differences and/or extensions that cater specifically to games developed internally at Subtale (documented below).

High-level overview

Your game's world is defined as a collection of facts: the player killed x amount of enemies, an NPC has opened y amount of doors, the player is currently near z NPC, etc.

In Mímir, facts are collected together into a map (Query<FactKey, FactType>), where the key is the unique identifier of the fact, and the value is the fact's value.

Also, your game will (most likely!) have predefined rules that define behaviour that should occur when one or more facts are true. We represent rules as a map (Rule<FactKey, FactType, FactEvaluator, Outcome>), where the key is the unique identifier of the fact, and the value is a predicate (Evaluator) that is evaluated against the fact's value.

Finally, rules can be stored together in collections known as rulesets (Ruleset<FactKey, FactType, FactEvaluator, Outcome>). Rulesets allow a query to be evaluated against many rules simultaneously: Mímir will always look to match a query against the rule in the ruleset with the most requirements (i.e. more specific). (If multiple rules are matched with the same specificity, one is chosen at random.)

Example

use subtale_mimir::prelude::*;

// create a rule requiring that five enemies have been killed
let mut rule = Rule::new("You killed 5 enemies!");
// Rule<&str, f64, FloatEvaluator, &str>
rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.));

// create a more specific rule that also requires at least 2 doors to have been opened
let mut more_specific_rule = Rule::new("You killed 5 enemies and opened 2 doors!");
more_specific_rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.));
more_specific_rule.insert("doors_opened", FloatEvaluator::gte(2.));

// bundle the rules into a ruleset
let ruleset = Ruleset::new(vec![rule, more_specific_rule]);

// run a query against the ruleset
let mut query = Query::new();
// Query<&str, f64>
query.insert("enemies_killed", 2.5 + 1.5 + 1.);

assert_eq!(
    ruleset.evaluate(&query).unwrap().outcome,
    "You killed 5 enemies!"
);

// run a more specific query against the ruleset
let mut more_specific_query = Query::new();
more_specific_query.insert("enemies_killed", 2.5 + 1.5 + 1.);
more_specific_query.insert("doors_opened", 10.);

assert_eq!(
    ruleset.evaluate(&more_specific_query).unwrap().outcome,
    "You killed 5 enemies and opened 2 doors!"
);

In the above example, we define a ruleset with two rules. Both rules require that 5 enemies have been killed, but one rule is more specific (also requiring that more than 2 doors have been opened).

The first query evaluates to the simpler rule because the query does not satisfy the doors opened requirement. However, the second query evaluates to the more complex rule because the query does satisfy the doors opened requirement (note that even though the simpler rule is still satisfied, Mímir does not evaluate it as true because it's less specific/contains fewer requirements).

Libraries used

Without the following libraries, Mímir would not be where it is now:

  • float-cmp: used to approximate floating-point number comparisons
  • indexmap: used as the implementation of underlying map structures
  • rand: used to randomly select evaluated rules when multiple are evaluated as true
  • serde: used to offer methods of (de)serialization
  • criterion: used to write benchmarking test suites

License

Mímir is free and open source. Unless explicitly noted otherwise, all code in this repository is dual-licensed under the MIT License and Apache License, Version 2.0.

This licensing approach is the de facto standard within the Rust ecosystem.

Contributions

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

mimir's People

Contributors

lukecarr avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Forkers

fossabot

mimir's Issues

API reference

The documentation site is currently missing an API reference for the mimir crate.

Although most of the API can be pieced together from the contents of the current documentation, we need to have a central API reference that people can refer to.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

cargo
crates/subtale-mimir/Cargo.toml
  • float-cmp 0.9
  • indexmap 2.2
  • rand 0.8
  • serde 1.0
  • criterion 0.5
devcontainer
.devcontainer/devcontainer.json
  • mcr.microsoft.com/devcontainers/rust 1-1-buster
  • ghcr.io/devcontainers/features/node 1
  • ghcr.io/guiyomh/features/just 0
github-actions
.github/workflows/rust.yml
  • actions/checkout v4
  • actions/cache v4

  • Check this box to trigger a request for Renovate to run again on this repository

Trait-based rulesets

Currently, we only have one ruleset implemented within Mimir. This ruleset treats rules with more requirements as more "specific" and only evaluates the most specific rule.

This behaviour is desirable for performance because we can sort the ruleset in descending order of specificity and avoid iterating over the entire ruleset (breaking out early if we find a matching rule).

However, there are scenarios in which we might want to willingly evaluate all rules in a ruleset, irrespective of specificity. For example, suppose we use a ruleset to store the behaviour for unlocking achievements. In that case, we want to evaluate and find all achievements that should be unlocked, not just the achievement with the most requirements (this is the behaviour that would currently happen in Mimir).

With this in mind, we should refactor the codebase to replace the concrete implementation of the current ruleset with a trait-based approach to define rulesets.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.