From cce17cd304fbfc55dd0c856e03c254721f030a8a Mon Sep 17 00:00:00 2001 From: Luke Carr <me+oss@carr.sh> Date: Sun, 28 Apr 2024 21:31:07 +0100 Subject: [PATCH 1/3] refactor: introduced traits for rulesets --- crates/subtale-mimir/Cargo.toml | 8 +- crates/subtale-mimir/README.md | 14 +- ...tion.rs => weighted_ruleset_evaluation.rs} | 4 +- ...leset_init.rs => weighted_ruleset_init.rs} | 4 +- crates/subtale-mimir/src/ruleset.rs | 226 +++++++++++++----- 5 files changed, 186 insertions(+), 70 deletions(-) rename crates/subtale-mimir/benches/{ruleset_evaluation.rs => weighted_ruleset_evaluation.rs} (83%) rename crates/subtale-mimir/benches/{ruleset_init.rs => weighted_ruleset_init.rs} (87%) diff --git a/crates/subtale-mimir/Cargo.toml b/crates/subtale-mimir/Cargo.toml index 8fd9fbc..de46aad 100644 --- a/crates/subtale-mimir/Cargo.toml +++ b/crates/subtale-mimir/Cargo.toml @@ -4,7 +4,7 @@ version = "0.5.1" edition = "2021" authors = ["Luke Carr <luke@subtale.com>"] description = "Contextual query engine for dynamic video games" -homepage = "https://mimir.subtale.com" +homepage = "https://mimir.subtale.dev" repository = "https://github.com/subtalegames/mimir" license = "MIT OR Apache-2.0" readme = "README.md" @@ -12,22 +12,22 @@ readme = "README.md" [dependencies] float-cmp = { version = "0.9", optional = true } indexmap = "2.2" -rand = "0.8" serde = { version = "1.0", features = ["derive"], optional = true } [dev-dependencies] criterion = "0.5" +rand = "0.8" [[bench]] name = "float_evaluator" harness = false [[bench]] -name = "ruleset_evaluation" +name = "weighted_ruleset_evaluation" harness = false [[bench]] -name = "ruleset_init" +name = "weighted_ruleset_init" harness = false [features] diff --git a/crates/subtale-mimir/README.md b/crates/subtale-mimir/README.md index 9c8f017..113e924 100644 --- a/crates/subtale-mimir/README.md +++ b/crates/subtale-mimir/README.md @@ -20,9 +20,9 @@ Your game's world is defined as a collection of facts: the player killed x amoun In Mímir, facts are collected together into a map ([`Query<FactKey, FactType>`][query]), where the key is the unique identifier of the fact, and the value is the fact's value. -Also, your game will (most likey!) 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>`][rule]), where the key is the unique identifier of the fact, and the value is a predicate ([`Evaluator`][evaluator]) that is evaluated against 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>`][rule]), where the key is the unique identifier of the fact, and the value is a predicate ([`Evaluator`][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>`][ruleset]). Rulesets allow a query to be evaluated against many rules at once: 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.)* +Finally, rules can be stored together in collections known as rulesets ([`Ruleset<FactKey, FactType, FactEvaluator, Outcome>`][ruleset]). Rulesets allow a query to be evaluated against many rules at once: depending on the ruleset implementation, the query will return either the first rule that is satisfied (see `WeightedRuleset`), or all rules that are satisfied (see `SimpleRuleset`). ## Example @@ -40,7 +40,7 @@ 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]); +let ruleset = WeightedRuleset::new(vec![rule, more_specific_rule]); // run a query against the ruleset let mut query = Query::new(); @@ -48,7 +48,7 @@ let mut query = Query::new(); query.insert("enemies_killed", 2.5 + 1.5 + 1.); assert_eq!( - ruleset.evaluate(&query).unwrap().outcome, + ruleset.evaluate(&query).first().unwrap().outcome, "You killed 5 enemies!" ); @@ -58,14 +58,14 @@ 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, + ruleset.evaluate(&more_specific_query).first().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). +In the above example, we define a weighted ruleset with two rules. Both rules require that 5 enemies have been killed, but one rule has a higher weight because it' 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* satistfy 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). +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 lesser weighted rule is still satisfied, Mímir does not evaluate it as true because we're using a `WeightedRuleset`). [docs]: https://mimir.subtale.com [tutorial]: https://mimir.subtale.com/tutorial diff --git a/crates/subtale-mimir/benches/ruleset_evaluation.rs b/crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs similarity index 83% rename from crates/subtale-mimir/benches/ruleset_evaluation.rs rename to crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs index 10d9274..9e1fc9c 100644 --- a/crates/subtale-mimir/benches/ruleset_evaluation.rs +++ b/crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs @@ -17,9 +17,9 @@ fn benchmark(c: &mut Criterion) { rule_2.insert("fact_2", FloatEvaluator::lt(6.0)); rule_2.insert("fact_3", FloatEvaluator::range(9.0, 12.0)); - let ruleset = Ruleset::new(vec![rule_1, rule_2]); + let ruleset = WeightedRuleset::new(vec![rule_1, rule_2]); - c.bench_function("ruleset evaluate", |b| b.iter(|| ruleset.evaluate(&query))); + c.bench_function("weighted ruleset evaluate", |b| b.iter(|| ruleset.evaluate(&query))); } #[cfg(feature = "float")] diff --git a/crates/subtale-mimir/benches/ruleset_init.rs b/crates/subtale-mimir/benches/weighted_ruleset_init.rs similarity index 87% rename from crates/subtale-mimir/benches/ruleset_init.rs rename to crates/subtale-mimir/benches/weighted_ruleset_init.rs index ae53090..7426802 100644 --- a/crates/subtale-mimir/benches/ruleset_init.rs +++ b/crates/subtale-mimir/benches/weighted_ruleset_init.rs @@ -5,7 +5,7 @@ use subtale_mimir::prelude::*; fn benchmark(c: &mut Criterion) { let mut rng = rand::thread_rng(); - let mut group = c.benchmark_group("ruleset init"); + let mut group = c.benchmark_group("weighted ruleset init"); for &num_rules in &[10, 100, 1_000, 10_000] { group.bench_function(format!("{} rules", num_rules), |b| { @@ -20,7 +20,7 @@ fn benchmark(c: &mut Criterion) { }) .collect(); - let _ruleset = Ruleset::new(rules); + let _ruleset = WeightedRuleset::new(rules); }); }); } diff --git a/crates/subtale-mimir/src/ruleset.rs b/crates/subtale-mimir/src/ruleset.rs index ea701b2..f852eb1 100644 --- a/crates/subtale-mimir/src/ruleset.rs +++ b/crates/subtale-mimir/src/ruleset.rs @@ -1,30 +1,39 @@ -use rand::seq::SliceRandom; +use std::collections::BTreeMap; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{evaluator::Evaluator, query::Query, rule::Rule}; -/// A `Ruleset` is a collection of `Rule` instances, represented as a -/// `Vec<Rule<...>>`. -/// -/// Because Mímir evaluates rulesets by returning the most specific rule for a -/// given query, the rules are stored in descending order of requirement count. -/// This avoids scanning the entire ruleset for matching rules, as the first -/// rules in the underlying collection are the most specific. -/// -/// Where possible, you should look to divide your game's entire database of -/// rules into smaller rulesets that can be loaded in and out of memory -/// depending on the game's current state. -/// -/// For example, you might want to partition your rules into individual rulesets -/// for each level/map/region of your game. Otherwise, you'll be subjecting -/// yourself to an unnecessary performance cost by having Mímir evaluate rules -/// that have no relevance to the game's current state. +/// At a high level, a `Ruleset` is a collection of `Rule` instances that define +/// predicate behaviour for evaluating "facts" in a game world. +pub trait RulesetTrait<FactKey, FactType, FactEvaluator: Evaluator<FactType>, Outcome> +where + FactKey: std::hash::Hash + Eq, +{ + /// Creates a new ruleset from the provided collection of rules. + fn new(rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>) -> Self; + + /// Evaluates the ruleset against the provided query. + /// + /// Depending on the implementation, this function may return a single rule + /// (i.e. the most specific) or multiple rules that evaluate to true for the + /// provided query. + fn evaluate( + &self, + query: &Query<FactKey, FactType>, + ) -> Vec<&Rule<FactKey, FactType, FactEvaluator, Outcome>>; +} + +/// An implementation of a `Ruleset` that returns all rules that evaluate to +/// true for the provided query, regardless of their specificity. This means +/// that all rules in the ruleset are evaluated, and all rules that evaluate to +/// true are returned. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Ruleset<FactKey, FactType, FactEvaluator: Evaluator<FactType>, Outcome> +pub struct SimpleRuleset<FactKey, FactType, FactEvaluator: Evaluator<FactType>, Outcome> where FactKey: std::hash::Hash + Eq, { + /// The collection of rules that make up the ruleset. rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>, } @@ -33,61 +42,107 @@ impl< FactType: Copy, FactEvaluator: Evaluator<FactType> + Copy, Outcome, - > Ruleset<FactKey, FactType, FactEvaluator, Outcome> + > RulesetTrait<FactKey, FactType, FactEvaluator, Outcome> + for SimpleRuleset<FactKey, FactType, FactEvaluator, Outcome> { - fn sort(&mut self) { + /// Creates a new ruleset from the provided collection of rules. + fn new(rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>) -> Self { + Self { rules } + } + + /// Evaluates the ruleset against the provided query. + /// + /// Returns all rules in the ruleset that evaluate to true for the provided + /// query. + fn evaluate( + &self, + query: &Query<FactKey, FactType>, + ) -> Vec<&Rule<FactKey, FactType, FactEvaluator, Outcome>> { self.rules - .sort_unstable_by_key(|x| -(x.evaluators.len() as isize)); + .iter() + .filter(|rule| rule.evaluate(query)) + .collect() } +} + +/// An implementation of a `Ruleset` that returns the most specific rule that +/// evaluates to true for the provided query. +/// +/// By default, specificity (weight) is determined by the number of evaluators +/// in the rule. However, this can be overridden by setting the weight of the +/// rule explicitly. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct WeightedRuleset<FactKey, FactType, FactEvaluator: Evaluator<FactType>, Outcome> +where + FactKey: std::hash::Hash + Eq, +{ + /// The collection of rules that make up the ruleset, indexed by their weight + /// (which defaults to the number of evaluators in the rule, but can be + /// overridden). + rules: BTreeMap<isize, Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>>, + + /// The number of rules in the weight with the most rules. This is effectively + /// the maximum number of rules that will be evaluated for a given query. + /// + /// With this, we can pre-allocate the space for the rules that will be evaluated + /// for a given query. + largest_weight_cardinality: usize, +} +impl< + FactKey: std::hash::Hash + Eq, + FactType: Copy, + FactEvaluator: Evaluator<FactType> + Copy, + Outcome, + > RulesetTrait<FactKey, FactType, FactEvaluator, Outcome> + for WeightedRuleset<FactKey, FactType, FactEvaluator, Outcome> +{ /// Creates a new ruleset from the provided collection of rules. - pub fn new(rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>) -> Self { - let mut new = Self { rules }; - new.sort(); - new - } + fn new(rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>) -> Self { + let mut ruleset = Self { + rules: BTreeMap::new(), + largest_weight_cardinality: 0, + }; + + for rule in rules { + let weight = rule.evaluators.len() as isize; + let rules = ruleset.rules.entry(weight).or_default(); + rules.push(rule); + + if rules.len() > ruleset.largest_weight_cardinality { + ruleset.largest_weight_cardinality = rules.len(); + } + } - /// Appends all rules from another ruleset into the ruleset. - pub fn append(&mut self, ruleset: &mut Ruleset<FactKey, FactType, FactEvaluator, Outcome>) { - self.rules.append(&mut ruleset.rules); - self.sort(); + ruleset } /// Evaluates the ruleset against the provided query. /// /// Returns the most specific (most evaluators) rule in the ruleset that /// evaluates to true for the provided query. If multiple rules evaluate - /// to true with the same specificness, they are all returned. - pub fn evaluate_all( + /// to true with the same weight/specificity, they are all returned. + fn evaluate( &self, query: &Query<FactKey, FactType>, ) -> Vec<&Rule<FactKey, FactType, FactEvaluator, Outcome>> { - let mut matched = Vec::<&Rule<FactKey, FactType, FactEvaluator, Outcome>>::new(); + let mut rules = Vec::with_capacity(self.largest_weight_cardinality); - for rule in self.rules.iter() { - if matched.first().map_or(0, |x| x.evaluators.len()) <= rule.evaluators.len() { - if rule.evaluate(query) { - matched.push(rule); + for (_, rule) in self.rules.iter().rev() { + for r in rule { + if r.evaluate(query) { + rules.push(r); } - } else { + } + + if !rules.is_empty() { break; } } - matched - } + rules.shrink_to_fit(); - /// Evaluates the ruleset against the provided query. - /// - /// Returns the most specific (most evaluators) rule in the ruleset that - /// evaluates to true for the provided query. If multiple rules evaluate - /// to true with the same specificness, one is picked at random. - pub fn evaluate( - &self, - query: &Query<FactKey, FactType>, - ) -> Option<&Rule<FactKey, FactType, FactEvaluator, Outcome>> { - let matched = self.evaluate_all(query); - matched.choose(&mut rand::thread_rng()).copied() + rules } } @@ -97,7 +152,68 @@ mod tests { use crate::prelude::*; #[test] - fn ruleset_evaluation() { + fn simple_ruleset_init() { + let mut rule = Rule::new("You killed 5 enemies!"); + rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); + + 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::gt(2.)); + + let ruleset = SimpleRuleset::new(vec![rule, more_specific_rule]); + + assert_eq!(ruleset.rules.len(), 2); + } + + #[test] + fn simple_ruleset_evaluation() { + let mut rule = Rule::new("You killed 5 enemies!"); + rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); + + 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::gt(2.)); + + let ruleset = SimpleRuleset::new(vec![rule, more_specific_rule]); + + let mut query = Query::new(); + query.insert("enemies_killed", 5.); + + assert_eq!( + ruleset.evaluate(&query).first().unwrap().outcome, + "You killed 5 enemies!" + ); + + let mut more_specific_query = Query::new(); + more_specific_query.insert("enemies_killed", 5.); + more_specific_query.insert("doors_opened", 10.); + + assert_eq!( + ruleset.evaluate(&more_specific_query).len(), 2, + ); + } + + #[test] + fn weighted_ruleset_init() { + let mut rule = Rule::new("You killed 5 enemies!"); + rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); + + 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::gt(2.)); + + let mut another_specific_rule = Rule::new("You killed 5 enemies and collected 10 coins!"); + another_specific_rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); + another_specific_rule.insert("coins_collected", FloatEvaluator::EqualTo(10.)); + + let ruleset = WeightedRuleset::new(vec![rule, more_specific_rule, another_specific_rule]); + + assert_eq!(ruleset.rules.len(), 2); + assert_eq!(ruleset.largest_weight_cardinality, 2); + } + + #[test] + fn weighted_ruleset_evaluation() { let mut rule = Rule::new("You killed 5 enemies!"); rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); @@ -105,13 +221,13 @@ mod tests { more_specific_rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); more_specific_rule.insert("doors_opened", FloatEvaluator::gt(2.)); - let ruleset = Ruleset::new(vec![rule, more_specific_rule]); + let ruleset = WeightedRuleset::new(vec![rule, more_specific_rule]); let mut query = Query::new(); query.insert("enemies_killed", 2.5 + 1.5 + 1.); assert_eq!( - ruleset.evaluate(&query).unwrap().outcome, + ruleset.evaluate(&query).first().unwrap().outcome, "You killed 5 enemies!" ); @@ -120,7 +236,7 @@ mod tests { more_specific_query.insert("doors_opened", 10.); assert_eq!( - ruleset.evaluate(&more_specific_query).unwrap().outcome, + ruleset.evaluate(&more_specific_query).first().unwrap().outcome, "You killed 5 enemies and opened 2 doors!" ); } From 770be5791e1c1075c434c56497e6e48938ea75a0 Mon Sep 17 00:00:00 2001 From: Luke Carr <me+oss@carr.sh> Date: Sun, 28 Apr 2024 21:31:26 +0100 Subject: [PATCH 2/3] chore: applied `cargo fmt` --- .../benches/weighted_ruleset_evaluation.rs | 4 ++- crates/subtale-mimir/src/query.rs | 4 +-- crates/subtale-mimir/src/ruleset.rs | 30 ++++++++++--------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs b/crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs index 9e1fc9c..de4cb9a 100644 --- a/crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs +++ b/crates/subtale-mimir/benches/weighted_ruleset_evaluation.rs @@ -19,7 +19,9 @@ fn benchmark(c: &mut Criterion) { let ruleset = WeightedRuleset::new(vec![rule_1, rule_2]); - c.bench_function("weighted ruleset evaluate", |b| b.iter(|| ruleset.evaluate(&query))); + c.bench_function("weighted ruleset evaluate", |b| { + b.iter(|| ruleset.evaluate(&query)) + }); } #[cfg(feature = "float")] diff --git a/crates/subtale-mimir/src/query.rs b/crates/subtale-mimir/src/query.rs index 3b9a54f..80e3548 100644 --- a/crates/subtale-mimir/src/query.rs +++ b/crates/subtale-mimir/src/query.rs @@ -47,9 +47,7 @@ where pub facts: IndexMap<FactKey, FactType>, } -impl<FactKey: std::hash::Hash + Eq, FactType: Copy> - Query<FactKey, FactType> -{ +impl<FactKey: std::hash::Hash + Eq, FactType: Copy> Query<FactKey, FactType> { /// Instantiates a new instance of `Query` without allocating an underlying /// `IndexMap`. /// diff --git a/crates/subtale-mimir/src/ruleset.rs b/crates/subtale-mimir/src/ruleset.rs index f852eb1..2f96711 100644 --- a/crates/subtale-mimir/src/ruleset.rs +++ b/crates/subtale-mimir/src/ruleset.rs @@ -1,4 +1,5 @@ use std::collections::BTreeMap; + #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -46,9 +47,7 @@ impl< for SimpleRuleset<FactKey, FactType, FactEvaluator, Outcome> { /// Creates a new ruleset from the provided collection of rules. - fn new(rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>) -> Self { - Self { rules } - } + fn new(rules: Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>) -> Self { Self { rules } } /// Evaluates the ruleset against the provided query. /// @@ -76,16 +75,17 @@ pub struct WeightedRuleset<FactKey, FactType, FactEvaluator: Evaluator<FactType> where FactKey: std::hash::Hash + Eq, { - /// The collection of rules that make up the ruleset, indexed by their weight - /// (which defaults to the number of evaluators in the rule, but can be - /// overridden). + /// The collection of rules that make up the ruleset, indexed by their + /// weight (which defaults to the number of evaluators in the rule, but + /// can be overridden). rules: BTreeMap<isize, Vec<Rule<FactKey, FactType, FactEvaluator, Outcome>>>, - /// The number of rules in the weight with the most rules. This is effectively - /// the maximum number of rules that will be evaluated for a given query. + /// The number of rules in the weight with the most rules. This is + /// effectively the maximum number of rules that will be evaluated for a + /// given query. /// - /// With this, we can pre-allocate the space for the rules that will be evaluated - /// for a given query. + /// With this, we can pre-allocate the space for the rules that will be + /// evaluated for a given query. largest_weight_cardinality: usize, } @@ -188,9 +188,7 @@ mod tests { more_specific_query.insert("enemies_killed", 5.); more_specific_query.insert("doors_opened", 10.); - assert_eq!( - ruleset.evaluate(&more_specific_query).len(), 2, - ); + assert_eq!(ruleset.evaluate(&more_specific_query).len(), 2,); } #[test] @@ -236,7 +234,11 @@ mod tests { more_specific_query.insert("doors_opened", 10.); assert_eq!( - ruleset.evaluate(&more_specific_query).first().unwrap().outcome, + ruleset + .evaluate(&more_specific_query) + .first() + .unwrap() + .outcome, "You killed 5 enemies and opened 2 doors!" ); } From 3f029d8af442c6ba618b70cb7ec1d3355a607848 Mon Sep 17 00:00:00 2001 From: Luke Carr <me+oss@carr.sh> Date: Tue, 30 Apr 2024 00:28:30 +0100 Subject: [PATCH 3/3] docs: migrated to new docs site --- docs/.gitignore | 178 +++++++++++++++++- docs/.vitepress/config.mts | 70 +++++++ docs/.vitepress/theme/index.ts | 17 ++ docs/.vitepress/theme/style.css | 92 +++++++++ docs/README.md | 15 ++ docs/book.toml | 6 - docs/bun.lockb | Bin 0 -> 49853 bytes docs/package.json | 15 ++ docs/src/SUMMARY.md | 24 --- docs/src/concepts/evaluator.md | 26 ++- docs/src/concepts/rule.md | 4 +- docs/src/concepts/ruleset.md | 8 +- docs/src/index.md | 68 +++++++ docs/src/inspiration.md | 4 +- docs/src/introduction.md | 40 ---- docs/src/overview.md | 17 +- docs/src/performance.md | 10 +- docs/src/public/hero.svg | 10 + docs/src/public/mimir-dark.svg | 26 +++ docs/src/public/mimir-light.svg | 26 +++ docs/src/public/powerful.svg | 1 + docs/src/quick-start.md | 20 ++ .../repeated-evaluations.md | 0 docs/src/{use-cases => recipes}/tips.md | 24 ++- docs/src/{changelog.md => release-notes.md} | 2 +- docs/src/serialisation.md | 16 ++ docs/src/serialization.md | 14 -- docs/src/tutorial.md | 22 --- docs/tsconfig.json | 27 +++ 29 files changed, 646 insertions(+), 136 deletions(-) create mode 100644 docs/.vitepress/config.mts create mode 100644 docs/.vitepress/theme/index.ts create mode 100644 docs/.vitepress/theme/style.css create mode 100644 docs/README.md delete mode 100644 docs/book.toml create mode 100755 docs/bun.lockb create mode 100644 docs/package.json delete mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/index.md delete mode 100644 docs/src/introduction.md create mode 100644 docs/src/public/hero.svg create mode 100644 docs/src/public/mimir-dark.svg create mode 100644 docs/src/public/mimir-light.svg create mode 100644 docs/src/public/powerful.svg create mode 100644 docs/src/quick-start.md rename docs/src/{use-cases => recipes}/repeated-evaluations.md (100%) rename docs/src/{use-cases => recipes}/tips.md (77%) rename docs/src/{changelog.md => release-notes.md} (99%) create mode 100644 docs/src/serialisation.md delete mode 100644 docs/src/serialization.md delete mode 100644 docs/src/tutorial.md create mode 100644 docs/tsconfig.json diff --git a/docs/.gitignore b/docs/.gitignore index 7585238..2aa00cf 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,177 @@ -book +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +.vitepress/cache \ No newline at end of file diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 0000000..19d5d7d --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,70 @@ +import { defineConfig } from 'vitepress' + +export default defineConfig({ + title: "Mímir", + description: "Contextual query engine for dynamic video games.", + srcDir: "src", + + themeConfig: { + logo: { + light: "/mimir-light.svg", + dark: "/mimir-dark.svg", + }, + + siteTitle: false, + + footer: { + message: "Made with ❤️ in Exeter", + copyright: "Copyright © 2024 Subtale" + }, + + nav: [ + { text: 'Home', link: '/' }, + { text: 'Guide', link: '/overview' }, + { text: 'Release notes', link: '/release-notes' }, + { text: 'API Reference', link: 'https://docs.rs/subtale-mimir' }, + ], + + sidebar: [ + { + text: 'Introduction', + items: [ + { text: 'High-level overview', link: '/overview' }, + { text: 'Quick start', link: '/quick-start' }, + { text: 'Inspiration', link: '/inspiration' }, + ] + }, + { + text: 'Concepts', + items: [ + { text: 'Evaluator', link: '/concepts/evaluator' }, + { text: 'Query', link: '/concepts/query' }, + { text: 'Rule', link: '/concepts/rule' }, + { text: 'Ruleset', link: '/concepts/ruleset' }, + ] + }, + { + text: 'Recipes', + items: [ + { text: 'Loading screen tips', link: '/recipes/tips' }, + { text: 'Repeated evaluations', link: '/recipes/repeated-evaluations' }, + ] + }, + { + text: 'Miscellaneous', + items: [ + { text: 'Performance', link: '/performance' }, + { text: 'Serialisation', link: '/serialisation' }, + ] + } + ], + + socialLinks: [ + { icon: 'github', link: 'https://github.com/subtalegames/mimir' }, + { icon: 'x', link: 'https://x.com/subtalegames' }, + { icon: 'instagram', link: 'https://instagram.com/subtalegames' }, + { icon: 'youtube', link: 'https://youtube.com/@subtalegames' }, + { icon: 'discord', link: 'https://discord.subtale.com' }, + ] + } +}) diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 0000000..def4cfc --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,17 @@ +// https://vitepress.dev/guide/custom-theme +import { h } from 'vue' +import type { Theme } from 'vitepress' +import DefaultTheme from 'vitepress/theme' +import './style.css' + +export default { + extends: DefaultTheme, + Layout: () => { + return h(DefaultTheme.Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + }) + }, + enhanceApp({ app, router, siteData }) { + // ... + } +} satisfies Theme diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 0000000..03618e5 --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,92 @@ +:root { + --vp-c-default-1: var(--vp-c-gray-1); + --vp-c-default-2: var(--vp-c-gray-2); + --vp-c-default-3: var(--vp-c-gray-3); + --vp-c-default-soft: var(--vp-c-gray-soft); + + --vp-c-brand-1: #5686f3; + --vp-c-brand-2: #5686f3; + --vp-c-brand-3: #2060d3; + --vp-c-brand-soft: var(--vp-c-indigo-soft); + + --vp-c-tip-1: var(--vp-c-brand-1); + --vp-c-tip-2: var(--vp-c-brand-2); + --vp-c-tip-3: var(--vp-c-brand-3); + --vp-c-tip-soft: var(--vp-c-brand-soft); + + --vp-c-warning-1: var(--vp-c-yellow-1); + --vp-c-warning-2: var(--vp-c-yellow-2); + --vp-c-warning-3: var(--vp-c-yellow-3); + --vp-c-warning-soft: var(--vp-c-yellow-soft); + + --vp-c-danger-1: var(--vp-c-red-1); + --vp-c-danger-2: var(--vp-c-red-2); + --vp-c-danger-3: var(--vp-c-red-3); + --vp-c-danger-soft: var(--vp-c-red-soft); + + --vp-button-brand-border: transparent; + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand-3); + --vp-button-brand-hover-border: transparent; + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-2); + --vp-button-brand-active-border: transparent; + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: #cadff4; + + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #2060d3 30%, + #5686f3 + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #5686f3 50%, + #cadff4 50% + ); + --vp-home-hero-image-filter: blur(44px); + + --vp-custom-block-tip-border: transparent; + --vp-custom-block-tip-text: var(--vp-c-text-1); + --vp-custom-block-tip-bg: var(--vp-c-brand-soft); + --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); + --vp-code-color: #090242; +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(68px); + } +} + +:root.dark { + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #0b3c93, + #051f4f + ); + + --vp-code-color: #f0f0f1; +} + +.DocSearch { + --docsearch-primary-color: var(--vp-c-brand-1) !important; +} + +a.title > span { + font-size: 23px; + margin-top: 7px; + color: #090242; +} + +.dark a.title > span { + color: #ffffff; +} \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..66130bb --- /dev/null +++ b/docs/README.md @@ -0,0 +1,15 @@ +# docs + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.0.33. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index 2448a70..0000000 --- a/docs/book.toml +++ /dev/null @@ -1,6 +0,0 @@ -[book] -authors = ["Luke Carr"] -language = "en" -multilingual = false -src = "src" -title = "Mímir" diff --git a/docs/bun.lockb b/docs/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..9d48b6e8f0863d1ba0592d79ac7373db459cb074 GIT binary patch literal 49853 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 literal 0 HcmV?d00001 diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..4c68b79 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "devDependencies": { + "@types/bun": "latest", + "vitepress": "^1.1.4" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "scripts": { + "dev": "vitepress dev", + "build": "vitepress build", + "preview": "vitepress preview" + } +} \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index 4ff927c..0000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -1,24 +0,0 @@ -# Introduction - -[Introduction](./introduction.md) -[High-level overview](./overview.md) -[Inspiration](./inspiration.md) -[Tutorial](./tutorial.md) -[Changelog](./changelog.md) - -# Concepts - -- [Evaluator](./concepts/evaluator.md) -- [Query](./concepts/query.md) -- [Rule](./concepts/rule.md) -- [Ruleset](./concepts/ruleset.md) - -# Use cases - -- [Loading screen tips](./use-cases/tips.md) -- [Repeated evaluations](./use-cases/repeated-evaluations.md) - -# Miscellaneous - -- [Performance](./performance.md) -- [Serialization](./serialization.md) diff --git a/docs/src/concepts/evaluator.md b/docs/src/concepts/evaluator.md index 6f9be76..d165dd4 100644 --- a/docs/src/concepts/evaluator.md +++ b/docs/src/concepts/evaluator.md @@ -16,16 +16,20 @@ You can choose to create your own implementation of the trait, or use the provid In the real world, an evaluator represents a condition that must be true for a contextual event to take place. However, events will typically have many evaluators that need to evaluate to true, not just one! -> ℹ️ An NPC might query Mímir to ensure that they're only commenting on another NPC's behaviour if they've not exhibited the same behaviour previously (to avoid being hypocritical). +::: tip +An NPC might query Mímir to ensure that they're only commenting on another NPC's behaviour if they've not exhibited the same behaviour previously (to avoid being hypocritical). +::: ## FloatEvaluator -> ⚠️ To use the pre-made `FloatEvaluator` implementation, you must enable the `float` feature in your project's `Cargo.toml`: -> -> ```toml -> [dependencies] -> subtale-mimir = { version = "0.5.0", features = ["float"] } -> ``` +::: warning +To use the pre-made `FloatEvaluator` implementation, you must enable the `float` feature in your project's `Cargo.toml`: + +```toml +[dependencies] +subtale-mimir = { version = "0.5.0", features = ["float"] } +``` +::: The `FloatEvaluator` is a built-in implementation of the `Evaluator<T>` trait, allowing you to define evaluators that match against floating-point numbers. @@ -41,7 +45,9 @@ enum FloatEvaluator { If you're interested in how we've implemented the `Evaluator<f64>` trait for `FloatEvaluator`, check out the [source code on GitHub][float-src]! -> ℹ️ `FloatRangeBound` is an enum that holds a boundary value that can be inclusive (`FloatRangeBound::Inclusive(f64)`) or exclusive (`FloatRangeBound::Exclusive(f64)`). +::: info +`FloatRangeBound` is an enum that holds a boundary value that can be inclusive (`FloatRangeBound::Inclusive(f64)`) or exclusive (`FloatRangeBound::Exclusive(f64)`). +::: ### Helper functions @@ -55,7 +61,9 @@ Several helper functions are exposed to easily instantiate `FloatEvaluator` with | `FloatEvaluator::gte(5.)` | `FloatEvaluator::GreaterThan(FloatRangeBound::Inclusive(5.))` | `x ≥ 5` | | `FloatEvaluator::range(5., 10.)` | `FloatEvaluator::InRange(FloatRangeBound::Inclusive(5.), RangeFloatRangeBoundBound::Exclusive(10.))` | `5 ≤ x < 10` | -> ℹ️ `FloatEvaluator::range` is designed to mimic the functionality of [Python's built-in range function][py-range]. +::: info +`FloatEvaluator::range` is designed to mimic the functionality of [Python's built-in range function][py-range]. +::: ### Floating-point equality diff --git a/docs/src/concepts/rule.md b/docs/src/concepts/rule.md index 82502c0..7eb6ef1 100644 --- a/docs/src/concepts/rule.md +++ b/docs/src/concepts/rule.md @@ -29,7 +29,9 @@ assert!(rule.evaluate(&query)); In the above example, the rule evaluates to true for the supplied query because it's expecting 5 enemies to be killed (`enemies_killed`), and the query confirms the fact that 5 (`2.5 + 1.5 + 1`) have been killed. -> ℹ️ Our generic outcome type (`Outcome`) for the example is just a standard boolean value (`true`). In the real-world, you'd probably use a more complex enum to denote different types of outcome (e.g. dialog, animation). +::: tip +Our generic outcome type (`Outcome`) for the example is just a standard boolean value (`true`). In the real-world, you'd probably use a more complex enum to denote different types of outcome (e.g. dialog, animation). +::: ## Insertion order diff --git a/docs/src/concepts/ruleset.md b/docs/src/concepts/ruleset.md index 20cd9aa..d0b0def 100644 --- a/docs/src/concepts/ruleset.md +++ b/docs/src/concepts/ruleset.md @@ -11,7 +11,9 @@ where } ``` -> ℹ️ Check out the [ruleset storage section](/performance.html#ruleset-storage) on the performance page for further details on how Mímir represents rulesets in Rust to improve performance when evaluating queries against them. +::: tip +Check out the [ruleset storage section](/performance.html#ruleset-storage) on the performance page for further details on how Mímir represents rulesets in Rust to improve performance when evaluating queries against them. +::: ## Evaluation @@ -51,4 +53,6 @@ In the above example, we define a ruleset with two rules. Both rules require tha 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* satistfy the doors opened requirement. -> ℹ️ In the second query, although the simpler rule is satisfied, Mímir does not evaluate it as true because it's less specific (i.e. contains fewer evaluators). +::: info +In the second query, although the simpler rule is satisfied, Mímir does not evaluate it as true because it's less specific (i.e. contains fewer evaluators). +::: diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..1f1e8e9 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,68 @@ +--- +layout: home + +hero: + name: "Mímir" + text: "Contextual query engine for dynamic video games." + tagline: "Game logic expressed as queryable rules." + image: + src: /hero.svg + alt: Brain illustration + actions: + - theme: brand + text: Get Started + link: /markdown-examples + - theme: brand + text: API Reference + link: https://docs.rs/subtale-mimir + - theme: alt + text: More Subtale Open Source + link: https://github.com/subtalegames + +features: + - icon: 📋 + title: Rule-based events + details: Compose rules from predicates that trigger outcomes in your game's world. + - icon: ⚡️ + title: Optimised ruleset storage + details: Process groups of rules efficiently, avoiding unnecessary evaluations. + - icon: 🛟 + title: Compile-time safety + details: Catch errors in your game's ruleset at compile-time, not runtime. +--- + +## Quickstart + +Add `subtale-mimir = "0.6"` to your `Cargo.toml` file's `[dependencies]` section, then declare your game's first ruleset: + +```rust +use subtale_mimir::prelude::*; + +fn main() { + let mut rule = Rule::new("You killed 5 enemies!"); + rule.insert("enemies_killed", FloatEvaluator::EqualTo(5.)); + + 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.)); + + let ruleset = Ruleset::new(vec![rule, more_specific_rule]); + + let mut query = Query::new(); + query.insert("enemies_killed", 2.5 + 1.5 + 1.); + + assert_eq!( + ruleset.evaluate(&query).first().unwrap().outcome, + "You killed 5 enemies!" + ); + + 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).first().unwrap().outcome, + "You killed 5 enemies and opened 2 doors!" + ); +} +``` diff --git a/docs/src/inspiration.md b/docs/src/inspiration.md index a25719f..ccb29c9 100644 --- a/docs/src/inspiration.md +++ b/docs/src/inspiration.md @@ -9,6 +9,8 @@ Mímir is heavily inspired (both in concept and general architecture) by [Elan R </div> </div> -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. +Fundamentally speaking, Mímir is simply a Rust implementation of Elan's proposed system for dynamic dialogue. + +However, Mímir does offer some differences and/or extensions that cater specifically to games developed internally at Subtale, as well as expanding in scope to include general purpose rule evaluation (not limited to dialogue). [gdc]: https://www.youtube.com/watch?v=tAbBID3N64A \ No newline at end of file diff --git a/docs/src/introduction.md b/docs/src/introduction.md deleted file mode 100644 index 5fa8a58..0000000 --- a/docs/src/introduction.md +++ /dev/null @@ -1,40 +0,0 @@ -# Introduction - -Mímir is a contextual query engine for video games with dynamic events (e.g. dialog, animations) driven by their current world's state. - -```rs - 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!" - ); - ``` diff --git a/docs/src/overview.md b/docs/src/overview.md index af96cb6..f9dbba3 100644 --- a/docs/src/overview.md +++ b/docs/src/overview.md @@ -2,16 +2,25 @@ ## Queries -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. +Your game's world is defined as a collection of facts, such as: + +* the player killed x amount of enemies +* an NPC has opened y amount of doors +* the player is currently near z NPC In Mímir, facts are collected together into a map ([`Query`](/concepts/query.html)), where the key is the unique identifier of the fact, and the value is the fact's value. ## Rules and evaluators -Also, your game will (most likey!) have predefined rules that define behaviour that should occur when one or more facts are true. We represent rules as a map ([`Rule`](/concepts/rule.html)), where the key is the unique identifier of the fact, and the value is a predicate ([`Evaluator`](/concepts/evaluator.html)) that is evaluated against 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`](/concepts/rule.html)), where the key is the unique identifier of the fact, and the value is a predicate ([`Evaluator`](/concepts/evaluator.html)) that is evaluated against the fact's value. ## Rulesets -Finally, rules can be stored together in collections known as rulesets ([`Ruleset`](/concepts/ruleset.html)). Rulesets allow a query to be evaluated against many rules at once: Mímir will always look to match a query against the rule in the ruleset with the most requirements (i.e. more specific). +Finally, rules can be stored together in collections known as rulesets ([`Ruleset`](/concepts/ruleset.html)). + +Rulesets allow a query to be evaluated against many rules at once: 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 with the same specificity are matched within a ruleset, one is chosen at random. +::: tip +If multiple rules with the same specificity are matched within a ruleset, one is chosen at random. diff --git a/docs/src/performance.md b/docs/src/performance.md index 351f7fc..36c5a61 100644 --- a/docs/src/performance.md +++ b/docs/src/performance.md @@ -1,4 +1,4 @@ -# Performance (WIP) +# Performance <Badge type="warning" text="WIP" /> ## Ruleset storage @@ -6,7 +6,9 @@ Because Mímir evaluates rulesets by returning the most specific rule for a give However, this does mean that care should be taken when invoking `ruleset.append(...)` to introduce more rules into a ruleset, as this function also triggers the underlying collection to be sorted again after the new rules are appended. -> ℹ️ In production, we recommend that rulesets are only manipulated during your game's loading state, and then only evaluated during your game's main loop. +::: tip +In production, we recommend that rulesets are only manipulated during your game's loading state, and then only evaluated during your game's main loop. +::: ## Multiple rulesets @@ -14,4 +16,6 @@ Where possible, you should look to divide your game's entire database of rules i For example, you might want to partition your rules into individual rulesets for each level/map/region of your game. Otherwise, you'll be subjecting yourself to an unnecessary performance cost by having Mímir evaluate rules that have no relevance to the game's current state. -> ℹ️ The specific implementation of a system as described above is outside the scope of Mímir. +::: info +The specific implementation of a system as described above is outside the scope of Mímir. +::: diff --git a/docs/src/public/hero.svg b/docs/src/public/hero.svg new file mode 100644 index 0000000..547b004 --- /dev/null +++ b/docs/src/public/hero.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 459.62 471.05"> + <defs> + <linearGradient id="linear-gradient" x1="368.6" y1="65.07" x2="105.71" y2="520.41" gradientTransform="translate(0 473) scale(1 -1)" gradientUnits="userSpaceOnUse"> + <stop offset="0" stop-color="#090242"/> + <stop offset=".5" stop-color="#2060d3"/> + </linearGradient> + </defs> + <path id="Layer_1-2" data-name="Layer 1-2" d="M225.52,377.97c-.68,0-1.35-.09-2-.26-4-1.07-6.37-5.19-5.3-9.19h0l8.6-32.09c.93-4.04,4.96-6.55,8.99-5.62,4.04.93,6.55,4.96,5.62,8.99-.04.17-.08.34-.14.51l-8.6,32.1c-.86,3.26-3.8,5.53-7.17,5.56ZM208.81,377.96c-1.99,0-3.9-.79-5.31-2.19l-16-16c-2.93-2.93-2.93-7.68,0-10.61h0l16-16c2.98-2.88,7.73-2.8,10.61.18,2.81,2.91,2.81,7.51,0,10.42l-10.74,10.75,10.74,10.74c2.93,2.93,2.92,7.68,0,10.61-1.4,1.4-3.31,2.19-5.29,2.19v-.09h0ZM250.81,377.96c-4.14,0-7.5-3.36-7.49-7.51,0-1.99.79-3.89,2.19-5.29l10.74-10.74-10.74-10.75c-2.98-2.88-3.06-7.63-.18-10.61,2.88-2.98,7.63-3.06,10.61-.18l.18.18,16,16c2.93,2.93,2.93,7.68,0,10.61h0l-16,16c-1.4,1.45-3.31,2.27-5.31,2.29ZM229.81,420.32c-36.4,0-65.91-29.51-65.91-65.91s29.51-65.91,65.91-65.91,65.91,29.51,65.91,65.91h0c-.06,36.39-29.53,65.86-65.91,65.91ZM229.81,303.5c-28.12,0-50.91,22.79-50.91,50.91s22.79,50.91,50.91,50.91,50.91-22.79,50.91-50.91c-.05-28.1-22.81-50.86-50.91-50.91ZM233.93,471c-.44,0-.88-.04-1.32-.12l-45.62-8.12c-4.07-.73-6.78-4.62-6.07-8.69l2.94-16.52c-3.54-1.96-6.96-4.14-10.22-6.54l-13.76,9.6c-3.4,2.37-8.07,1.54-10.44-1.86l-26.52-38c-2.36-3.41-1.51-8.08,1.89-10.44l13.76-9.6c-1.12-3.89-1.99-7.85-2.61-11.85l-16.52-2.94c-4.08-.73-6.79-4.62-6.07-8.7l8.12-45.62c.72-4.07,4.6-6.79,8.68-6.07h.02l16.48,2.97c1.96-3.54,4.15-6.96,6.55-10.22l-9.6-13.78c-2.38-3.39-1.55-8.07,1.84-10.45h0l38-26.51c3.39-2.37,8.06-1.55,10.43,1.83,0,.01,0,.02.02.03l9.6,13.76c3.89-1.14,7.85-2.03,11.85-2.66l2.94-16.52c.73-4.08,4.62-6.79,8.7-6.07l45.62,8.12c4.08.73,6.79,4.62,6.07,8.7l-2.89,16.56c3.54,1.96,6.96,4.14,10.22,6.54l13.76-9.61c3.4-2.37,8.07-1.54,10.44,1.86l26.52,38c2.34,3.41,1.47,8.08-1.94,10.42l-13.76,9.61c1.12,3.89,1.99,7.85,2.61,11.85l16.52,2.94c4.08.73,6.79,4.62,6.07,8.7l-8.12,45.61c-.73,4.08-4.62,6.8-8.7,6.07h0l-16.51-2.94c-1.96,3.55-4.15,6.96-6.55,10.23l9.6,13.76c2.37,3.39,1.54,8.07-1.85,10.44l-38,26.52c-3.38,2.36-8.02,1.56-10.42-1.79l-9.6-13.77c-3.89,1.13-7.85,2-11.85,2.61l-2.94,16.52c-.63,3.58-3.74,6.19-7.38,6.19v-.05h0ZM197.01,449.31l30.85,5.49,2.59-14.59c.6-3.32,3.34-5.84,6.7-6.15,6.62-.6,13.15-2.04,19.41-4.28,3.18-1.13,6.73,0,8.67,2.77l8.48,12.16,25.7-17.93-8.48-12.15c-1.93-2.77-1.77-6.49.39-9.09,4.26-5.11,7.85-10.74,10.71-16.75,1.45-3.05,4.76-4.76,8.09-4.16l14.58,2.59,5.5-30.85-14.59-2.6c-3.32-.59-5.84-3.33-6.15-6.69-.61-6.63-2.05-13.15-4.28-19.42-1.14-3.18,0-6.73,2.77-8.66l12.16-8.5-17.94-25.7-12.15,8.49c-2.77,1.94-6.5,1.78-9.09-.39-5.11-4.26-10.74-7.86-16.75-10.71-3.05-1.45-4.75-4.76-4.16-8.09l2.59-14.6-30.8-5.47-2.59,14.59c-.6,3.32-3.34,5.84-6.7,6.15-6.62.61-13.14,2.05-19.41,4.28-3.18,1.14-6.74,0-8.67-2.77l-8.48-12.16-25.7,17.93,8.48,12.16c1.93,2.77,1.77,6.49-.39,9.09-4.26,5.11-7.86,10.73-10.71,16.74-1.45,3.05-4.76,4.76-8.09,4.17l-14.58-2.6-5.55,30.89,14.59,2.6c3.33.59,5.85,3.33,6.16,6.7.61,6.62,2.04,13.15,4.28,19.41,1.13,3.19,0,6.73-2.78,8.67l-12.16,8.48,17.94,25.7,12.15-8.56c2.77-1.94,6.5-1.78,9.09.39,5.11,4.25,10.74,7.85,16.75,10.71,3.05,1.45,4.75,4.76,4.16,8.09l-2.59,14.62ZM427.4,335.08h-114.33c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h114.33c9.51-.01,17.21-7.71,17.22-17.22V32.22c-.01-9.51-7.71-17.21-17.22-17.22H32.22c-9.51,0-17.21,7.71-17.22,17.22v270.64c0,9.51,7.71,17.21,17.22,17.22h94c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5H32.22C14.43,335.06.02,320.65,0,302.86V32.22C.02,14.43,14.43.02,32.22,0h395.18c17.79.02,32.2,14.43,32.22,32.22v270.64c-.02,17.79-14.43,32.2-32.22,32.22ZM452.12,72.03H7.5c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h444.62c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5ZM313.55,43.5h-50.05c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h50.05c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5ZM400.26,43.5h-50.05c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h50.05c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5h0ZM374.3,307.74c-14.74,0-30.62-13.62-48.42-41.47-7.67-12.16-14.6-24.78-20.75-37.77h-150.64c-6.15,13.01-13.08,25.63-20.75,37.81-19.27,30.15-36.29,43.63-52.06,41.19-20-3.1-32.2-30.32-38.34-85.68-4.75-42.85,14-78.85,54.24-104.11,33.71-21.21,81.91-33.32,132.23-33.32s98.52,12.11,132.23,33.28c40.24,25.26,59,61.26,54.24,104.11-6.14,55.36-18.32,82.58-38.34,85.68-1.2.18-2.42.28-3.64.28ZM229.81,99.23c-45.58,0-91.16,10.38-124.25,31.15-24.43,15.33-52.46,43.34-47.31,89.75,7,62.93,20.31,71.67,25.72,72.5,16.41,2.51,44.84-43.08,58.87-74.71,1.2-2.71,3.89-4.46,6.86-4.46h160.22c2.97,0,5.65,1.74,6.86,4.45,6.37,13.94,13.67,27.44,21.83,40.41,14.6,22.82,28.44,35.63,37,34.31,5.41-.83,18.74-9.58,25.72-72.5,5.15-46.41-22.88-74.42-47.31-89.75-33.05-20.77-78.63-31.15-124.21-31.15ZM162.69,195.17h-22.27c-4.14,0-7.5-3.36-7.5-7.5v-3.63h-3.64c-4.14-.02-7.49-3.39-7.47-7.53h0v-22.24c0-4.14,3.36-7.5,7.5-7.5h3.64v-3.64c0-4.14,3.36-7.5,7.5-7.5h22.27c4.14,0,7.5,3.36,7.5,7.5h0v3.64h3.59c4.14,0,7.5,3.36,7.5,7.5v22.23c0,4.14-3.36,7.5-7.5,7.5h-3.63v3.63c.02,4.14-3.32,7.52-7.46,7.54h-.03ZM147.92,180.17h7.27v-3.67c0-4.14,3.34-7.49,7.48-7.5h3.65v-7.27h-3.63c-4.14,0-7.5-3.36-7.5-7.5v-3.64h-7.27v3.64c0,4.14-3.36,7.5-7.5,7.5h-3.61v7.27h3.64c4.14,0,7.5,3.36,7.5,7.5l-.03,3.67ZM310.29,150.97h-3.76c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h3.76c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5ZM310.29,194.83h-3.76c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h3.76c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5ZM330.34,174.78c-4.14,0-7.5-3.36-7.5-7.5v-3.78c0-4.14,3.36-7.5,7.5-7.5s7.5,3.36,7.5,7.5v3.76c.01,4.14-3.34,7.51-7.48,7.52h-.02ZM286.48,174.78c-4.14,0-7.5-3.36-7.5-7.5v-3.78c0-4.14,3.36-7.5,7.5-7.5s7.5,3.36,7.5,7.5v3.76c.01,4.14-3.34,7.51-7.48,7.52h-.02ZM247.9,142.64h-36.18c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h36.18c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5ZM247.9,171.33h-36.18c-4.14,0-7.5-3.36-7.5-7.5s3.36-7.5,7.5-7.5h36.18c4.14,0,7.5,3.36,7.5,7.5s-3.36,7.5-7.5,7.5Z" style="fill: url(#linear-gradient); stroke-width: 0px;"/> +</svg> \ No newline at end of file diff --git a/docs/src/public/mimir-dark.svg b/docs/src/public/mimir-dark.svg new file mode 100644 index 0000000..90da4e8 --- /dev/null +++ b/docs/src/public/mimir-dark.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1922.5 260"> + <g> + <path d="M1520.42,239.44h-26.91l-8.4-77.92v-.02c-2.95-27.41-.82-44.57-.82-44.57,0,0-5.93,20.08-17.28,43.39l-38.18,78.39-38.18-78.39c-11.36-23.31-17.29-43.39-17.29-43.39,0,0,2.13,17.17-.81,44.56l-8.4,77.94h-26.91l17.51-162.38h24.64l49.44,101.51,49.44-101.51h24.64l17.53,162.39Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M1534.9,92.61c0-9.42,7.63-17.05,17.05-17.05s17.05,7.63,17.05,17.05-7.63,17.05-17.05,17.05-17.05-7.63-17.05-17.05ZM1538.56,237.94v-115.97h26.78v115.97h-26.78Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M1766.5,237.95h-26.82l.06-61.86v-7.69c-.05-2.13-.18-4.19-.54-6.16-.36-1.96-.84-3.83-1.51-5.54-.68-1.77-1.5-3.42-2.44-4.82-1.88-2.83-4.04-4.88-6.44-6.29-1.22-.76-2.52-1.38-3.95-1.81-1.41-.5-3.02-.85-4.82-1.02-3.67-.34-6.84-.37-10.29.63-3.39.86-6.92,2.84-10.02,6.01-3.11,3.16-5.57,7.33-7.04,11.86-.77,2.28-1.27,4.68-1.58,7.11-.21,1.95-.37,3.93-.52,5.91v1.81l.06,61.86h-26.78l.06-61.86v-7.69c-.05-2.13-.18-4.19-.54-6.16-.36-1.96-.84-3.83-1.51-5.54-.68-1.77-1.5-3.42-2.44-4.82-1.88-2.83-4.04-4.88-6.44-6.29-1.22-.76-2.52-1.38-3.95-1.81-1.41-.5-3.02-.85-4.82-1.02-3.67-.34-6.84-.37-10.29.63-3.39.86-6.92,2.84-10.02,6.01-3.11,3.16-5.57,7.33-7.04,11.86-.77,2.28-1.27,4.68-1.58,7.11-.18,1.72-.33,3.46-.47,5.2v64.38h-26.78v-115.97h26.78v10.92c1.99-2.55,4.39-4.96,7.27-7.01,3.12-2.2,6.72-3.99,10.52-5.26,3.81-1.23,7.77-1.87,11.61-2.09,3.86-.17,7.94.09,12.01.91,4.06.89,8.02,2.34,11.62,4.29,7.29,3.99,12.82,10.01,16.22,16.27.46.83.88,1.67,1.28,2.5,0-.02,0-.03,0-.04,2.16-5.95,6.26-12.09,12.6-16.6,3.12-2.2,6.71-3.99,10.52-5.26,3.81-1.23,7.77-1.87,11.61-2.09,3.86-.17,7.94.09,12,.91,4.06.89,8.02,2.34,11.62,4.29,7.29,3.99,12.82,10.01,16.22,16.27,1.74,3.14,3.01,6.31,3.98,9.47.97,3.23,1.64,6.42,2.02,9.57.38,3.15.59,6.23.56,9.26v7.78s.05,61.86.05,61.86h0Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M1782.44,92.61c0-9.42,7.63-17.05,17.05-17.05s17.05,7.63,17.05,17.05-7.63,17.05-17.05,17.05-17.05-7.63-17.05-17.05ZM1786.1,237.94v-115.97h26.78v115.97h-26.78Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M1915.1,121.09l-6.16,25.89c-.77-.52-1.56-1.01-2.4-1.44-2.91-1.5-6.15-2.48-10.16-2.81-4.03-.32-7.68-.25-11.38.74-3.71.93-7.34,2.7-10.7,5.63-3.33,2.86-6.21,6.9-8.16,11.27-1.96,4.46-3.03,9.26-3.42,14.5-.13,1.44-.24,2.87-.37,4.31v58.76h-26.78v-115.97h26.78v13.99c2.99-4.24,7.05-8.15,11.98-11.07,6.54-3.93,14.54-6.08,22.05-6.38,3.79-.16,7.74.06,11.7.74,2.38.42,4.74,1.06,7.03,1.84Z" style="fill: #fff; stroke-width: 0px;"/> + </g> + <g> + <g> + <path d="M187.94,191.51h-110.53c-4.94,0-9.68-1.96-13.17-5.45l-53.67-53.67h118.24l59.12,59.12Z" style="fill: #2060d3; stroke-width: 0px;"/> + <path d="M365.31,132.39l-105.08,105.08c-7.27,7.27-19.06,7.27-26.33,0l-45.96-45.96,59.12-59.12h118.24Z" style="fill: #2060d3; stroke-width: 0px;"/> + <path d="M247.08,69.07v8.39c-14.04,0-28.12,5.37-38.86,16.11-10.74,10.71-16.08,24.75-16.08,38.83h-8.39c0-14.04-5.37-28.12-16.11-38.86-10.71-10.74-24.75-16.08-38.83-16.08v-8.39c14.04,0,28.12-5.37,38.86-16.11,10.74-10.71,16.08-24.75,16.08-38.83h8.39c0,14.04,5.37,28.12,16.11,38.86,10.71,10.74,24.75,16.08,38.83,16.08Z" style="fill: #5686f3; stroke-width: 0px;"/> + </g> + <g> + <path d="M488.95,141.87c-2.38-.76-4.54-1.46-6.36-2.13-1.24-.48-2.67-.95-4.19-1.49-18.53-6.39-29.11-12.11-28.25-22.47,1.46-18.43,27.55-18.65,28.66-18.65,17.13,0,28.12,6.01,33.56,18.4l18.88-18.88c-10.9-15.28-29.11-23.55-52.43-23.55-35.56,0-53.42,21.23-55,40.93-2.45,30.25,30.95,41.75,45.22,46.68,1.37.48,2.61.89,3.69,1.3,2.13.76,4.61,1.59,7.37,2.48,19.51,6.32,35.69,12.84,36.13,28.63.19,5.56-1.91,10.3-6.16,14.08-6.51,5.81-17.51,8.99-29.52,8.45-15.41-.67-33.87-8.2-39.47-27.3l-19.64,19.64c11.12,18.53,32.44,30.57,57.83,31.65,1.3.06,2.61.1,3.91.1,17.92,0,34.29-5.5,45.38-15.35,9.6-8.55,14.46-19.57,14.08-31.94-.99-33.52-35.27-44.65-53.67-50.59Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M642.84,178.62c-.12,1.41-.24,2.82-.36,4.23-.38,5.17-1.44,9.9-3.37,14.31-1.92,4.32-4.77,8.29-8.05,11.12-3.32,2.89-6.9,4.63-10.56,5.55-3.66.98-7.26,1.04-11.23.73-3.95-.33-7.15-1.29-10.02-2.78-2.85-1.46-5.31-3.45-7.46-6.17-2.13-2.63-3.93-6.24-5-9.97-.52-1.91-.94-3.92-1.14-6-.11-1.04-.17-2.11-.21-3.19l-.05-3.61.07-62.22h-26.42l.07,62.22v4.17c.03,1.51.1,3.05.23,4.59.27,3.07.66,6.22,1.39,9.37,1.46,6.37,3.88,12.59,8.07,18.61,2.12,2.96,4.63,5.77,7.58,8.27,2.99,2.47,6.35,4.66,9.97,6.31,3.61,1.63,7.49,2.89,11.39,3.57,3.92.67,7.81.88,11.56.73,7.41-.3,15.3-2.41,21.76-6.29,4.86-2.88,8.87-6.75,11.81-10.93v13.81h26.42v-114.42h-26.44v57.99Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M788,122.48c-7.95-3.06-16.63-4.52-25.19-4.38-8.55.23-17.06,2.09-24.64,5.53-7,3.18-13.1,7.62-18.15,12.76v-61.57h-26.42v160.22h26.42v-15.73c5.05,5.13,11.14,9.59,18.13,12.75,7.57,3.44,16.1,5.3,24.65,5.53,8.55.15,17.25-1.31,25.2-4.38,7.96-3.06,15.11-7.68,21.03-13.28,11.8-11.28,18.37-26.75,18.25-42.11.11-15.35-6.47-30.81-18.27-42.09-5.92-5.6-13.07-10.21-21.02-13.26ZM789.54,202.82c-7.06,6.6-16.7,10.33-26.73,10.72-5.04.22-10.09-.44-15.01-1.88-4.93-1.41-9.71-3.73-13.88-6.94-8.45-6.29-13.92-16.42-13.86-26.88-.07-10.47,5.38-20.6,13.84-26.91,4.16-3.21,8.94-5.55,13.88-6.96,4.92-1.44,9.99-2.1,15.03-1.89,10.04.38,19.7,4.14,26.75,10.74,7.18,6.48,11.33,15.63,11.38,25.01-.06,9.37-4.21,18.52-11.39,24.98Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M1036.85,136.41c-5.05-5.14-11.14-9.59-18.14-12.76-7.57-3.44-16.09-5.3-24.64-5.53-8.55-.15-17.24,1.31-25.18,4.38-7.95,3.06-15.1,7.66-21.02,13.26-11.8,11.26-18.37,26.73-18.27,42.09-.11,15.35,6.45,30.82,18.25,42.11,5.92,5.6,13.07,10.22,21.03,13.28,7.95,3.06,16.64,4.53,25.2,4.38,8.55-.23,17.08-2.1,24.65-5.54,6.99-3.18,13.09-7.62,18.13-12.75v15.73h26.42v-114.43h-26.42v15.78ZM1022.96,204.72c-4.16,3.21-8.94,5.54-13.87,6.94-4.92,1.44-9.98,2.09-15.02,1.88-10.03-.37-19.67-4.12-26.72-10.72-7.18-6.47-11.35-15.61-11.4-24.98.06-9.38,4.2-18.53,11.39-25.01,7.06-6.61,16.71-10.36,26.74-10.74,5.05-.21,10.1.44,15.03,1.89,4.93,1.42,9.72,3.75,13.87,6.96,8.46,6.31,13.91,16.44,13.84,26.91.07,10.46-5.4,20.58-13.86,26.88Z" style="fill: #fff; stroke-width: 0px;"/> + <rect x="1087.3" y="74.89" width="26.42" height="160.22" style="fill: #fff; stroke-width: 0px;"/> + <path d="M1260.96,177.84c0-32.95-28.95-59.74-64.54-59.74s-64.51,26.79-64.51,59.74,30.92,59.74,68.89,59.74c19.67,0,37.94-7.21,50.75-19.38l-17.83-17.79c-7.91,8.2-19.89,13.16-32.92,13.16-19.35,0-35.65-10.93-40.77-25.83h99.97c.6-3.24.95-6.55.95-9.88ZM1159.88,167.96c4.58-14.9,19.23-25.83,36.54-25.83s32,10.93,36.57,25.83h-73.12Z" style="fill: #fff; stroke-width: 0px;"/> + <path d="M886.35,74.82l-26.41,26.41v20.18h-25.8v24.05h25.8v46.84c0,14.46,1.94,29.11,15.92,38.16,7.69,4.96,17.48,6.58,28.06,6.58,4.86,0,9.88-.35,14.93-.86v-24.18c-9.63,1.11-22.82,2-27.68-1.11-2.45-1.59-4.83-4.32-4.83-18.59v-46.84h32.51v-24.02h-32.51v-46.61Z" style="fill: #fff; stroke-width: 0px;"/> + </g> + </g> +</svg> \ No newline at end of file diff --git a/docs/src/public/mimir-light.svg b/docs/src/public/mimir-light.svg new file mode 100644 index 0000000..8965d6e --- /dev/null +++ b/docs/src/public/mimir-light.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1922.5 260"> + <g> + <g> + <path d="M187.49,191.42h-110.53c-4.94,0-9.68-1.96-13.17-5.45l-53.67-53.67h118.24l59.12,59.12Z" style="fill: #2060d3; stroke-width: 0px;"/> + <path d="M364.85,132.3l-105.08,105.08c-7.27,7.27-19.06,7.27-26.33,0l-45.96-45.96,59.12-59.12h118.24Z" style="fill: #2060d3; stroke-width: 0px;"/> + <path d="M246.62,68.98v8.39c-14.04,0-28.12,5.37-38.86,16.11-10.74,10.71-16.08,24.75-16.08,38.83h-8.39c0-14.04-5.37-28.12-16.11-38.86-10.71-10.74-24.75-16.08-38.83-16.08v-8.39c14.04,0,28.12-5.37,38.86-16.11,10.74-10.71,16.08-24.75,16.08-38.83h8.39c0,14.04,5.37,28.12,16.11,38.86,10.71,10.74,24.75,16.08,38.83,16.08Z" style="fill: #5686f3; stroke-width: 0px;"/> + </g> + <g> + <path d="M488.49,141.78c-2.38-.76-4.54-1.46-6.36-2.13-1.24-.48-2.67-.95-4.19-1.49-18.53-6.39-29.11-12.11-28.25-22.47,1.46-18.43,27.55-18.65,28.66-18.65,17.13,0,28.12,6.01,33.56,18.4l18.88-18.88c-10.9-15.28-29.11-23.55-52.43-23.55-35.56,0-53.42,21.23-55,40.93-2.45,30.25,30.95,41.75,45.22,46.68,1.37.48,2.61.89,3.69,1.3,2.13.76,4.61,1.59,7.37,2.48,19.51,6.32,35.69,12.84,36.13,28.63.19,5.56-1.91,10.3-6.16,14.08-6.51,5.81-17.51,8.99-29.52,8.45-15.41-.67-33.87-8.2-39.47-27.3l-19.64,19.64c11.12,18.53,32.44,30.57,57.83,31.65,1.3.06,2.61.1,3.91.1,17.92,0,34.29-5.5,45.38-15.35,9.6-8.55,14.46-19.57,14.08-31.94-.99-33.52-35.27-44.65-53.67-50.59Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M642.38,178.53c-.12,1.41-.24,2.82-.36,4.23-.38,5.17-1.44,9.9-3.37,14.31-1.92,4.32-4.77,8.29-8.05,11.12-3.32,2.89-6.9,4.63-10.56,5.55-3.66.98-7.26,1.04-11.23.73-3.95-.33-7.15-1.29-10.02-2.78-2.85-1.46-5.31-3.45-7.46-6.17-2.13-2.63-3.93-6.24-5-9.97-.52-1.91-.94-3.92-1.14-6-.11-1.04-.17-2.11-.21-3.19l-.05-3.61.07-62.22h-26.42l.07,62.22v4.17c.03,1.51.1,3.05.23,4.59.27,3.07.66,6.22,1.39,9.37,1.46,6.37,3.88,12.59,8.07,18.61,2.12,2.96,4.63,5.77,7.58,8.27,2.99,2.47,6.35,4.66,9.97,6.31,3.61,1.63,7.49,2.89,11.39,3.57,3.92.67,7.81.88,11.56.73,7.41-.3,15.3-2.41,21.76-6.29,4.86-2.88,8.87-6.75,11.81-10.93v13.81h26.42v-114.42h-26.44v57.99Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M787.54,122.39c-7.95-3.06-16.63-4.52-25.19-4.38-8.55.23-17.06,2.09-24.64,5.53-7,3.18-13.1,7.62-18.15,12.76v-61.57h-26.42v160.22h26.42v-15.73c5.05,5.13,11.14,9.59,18.13,12.75,7.57,3.44,16.1,5.3,24.65,5.53,8.55.15,17.25-1.31,25.2-4.38,7.96-3.06,15.11-7.68,21.03-13.28,11.8-11.28,18.37-26.75,18.25-42.11.11-15.35-6.47-30.81-18.27-42.09-5.92-5.6-13.07-10.21-21.02-13.26ZM789.08,202.73c-7.06,6.6-16.7,10.33-26.73,10.72-5.04.22-10.09-.44-15.01-1.88-4.93-1.41-9.71-3.73-13.88-6.94-8.45-6.29-13.92-16.42-13.86-26.88-.07-10.47,5.38-20.6,13.84-26.91,4.16-3.21,8.94-5.55,13.88-6.96,4.92-1.44,9.99-2.1,15.03-1.89,10.04.38,19.7,4.14,26.75,10.74,7.18,6.48,11.33,15.63,11.38,25.01-.06,9.37-4.21,18.52-11.39,24.98Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M1036.39,136.32c-5.05-5.14-11.14-9.59-18.14-12.76-7.57-3.44-16.09-5.3-24.64-5.53-8.55-.15-17.24,1.31-25.18,4.38-7.95,3.06-15.1,7.66-21.02,13.26-11.8,11.26-18.37,26.73-18.27,42.09-.11,15.35,6.45,30.82,18.25,42.11,5.92,5.6,13.07,10.22,21.03,13.28,7.95,3.06,16.64,4.53,25.2,4.38,8.55-.23,17.08-2.1,24.65-5.54,6.99-3.18,13.09-7.62,18.13-12.75v15.73h26.42v-114.43h-26.42v15.78ZM1022.5,204.63c-4.16,3.21-8.94,5.54-13.87,6.94-4.92,1.44-9.98,2.09-15.02,1.88-10.03-.37-19.67-4.12-26.72-10.72-7.18-6.47-11.35-15.61-11.4-24.98.06-9.38,4.2-18.53,11.39-25.01,7.06-6.61,16.71-10.36,26.74-10.74,5.05-.21,10.1.44,15.03,1.89,4.93,1.42,9.72,3.75,13.87,6.96,8.46,6.31,13.91,16.44,13.84,26.91.07,10.46-5.4,20.58-13.86,26.88Z" style="fill: #090242; stroke-width: 0px;"/> + <rect x="1086.84" y="74.8" width="26.42" height="160.22" style="fill: #090242; stroke-width: 0px;"/> + <path d="M1260.51,177.75c0-32.95-28.95-59.74-64.54-59.74s-64.51,26.79-64.51,59.74,30.92,59.74,68.89,59.74c19.67,0,37.94-7.21,50.75-19.38l-17.83-17.79c-7.91,8.2-19.89,13.16-32.92,13.16-19.35,0-35.65-10.93-40.77-25.83h99.97c.6-3.24.95-6.55.95-9.88ZM1159.42,167.87c4.58-14.9,19.23-25.83,36.54-25.83s32,10.93,36.57,25.83h-73.12Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M885.89,74.73l-26.41,26.41v20.18h-25.8v24.05h25.8v46.84c0,14.46,1.94,29.11,15.92,38.16,7.69,4.96,17.48,6.58,28.06,6.58,4.86,0,9.88-.35,14.93-.86v-24.18c-9.63,1.11-22.82,2-27.68-1.11-2.45-1.59-4.83-4.32-4.83-18.59v-46.84h32.51v-24.02h-32.51v-46.61Z" style="fill: #090242; stroke-width: 0px;"/> + </g> + </g> + <g> + <path d="M1520.42,239.44h-26.91l-8.4-77.92v-.02c-2.95-27.41-.82-44.57-.82-44.57,0,0-5.93,20.08-17.28,43.39l-38.18,78.39-38.18-78.39c-11.36-23.31-17.29-43.39-17.29-43.39,0,0,2.13,17.17-.81,44.56l-8.4,77.94h-26.91l17.51-162.38h24.64l49.44,101.51,49.44-101.51h24.64l17.53,162.39Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M1534.9,92.61c0-9.42,7.63-17.05,17.05-17.05s17.05,7.63,17.05,17.05-7.63,17.05-17.05,17.05-17.05-7.63-17.05-17.05ZM1538.56,237.94v-115.97h26.78v115.97h-26.78Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M1766.5,237.95h-26.82l.06-61.86v-7.69c-.05-2.13-.18-4.19-.54-6.16-.36-1.96-.84-3.83-1.51-5.54-.68-1.77-1.5-3.42-2.44-4.82-1.88-2.83-4.04-4.88-6.44-6.29-1.22-.76-2.52-1.38-3.95-1.81-1.41-.5-3.02-.85-4.82-1.02-3.67-.34-6.84-.37-10.29.63-3.39.86-6.92,2.84-10.02,6.01-3.11,3.16-5.57,7.33-7.04,11.86-.77,2.28-1.27,4.68-1.58,7.11-.21,1.95-.37,3.93-.52,5.91v1.81l.06,61.86h-26.78l.06-61.86v-7.69c-.05-2.13-.18-4.19-.54-6.16-.36-1.96-.84-3.83-1.51-5.54-.68-1.77-1.5-3.42-2.44-4.82-1.88-2.83-4.04-4.88-6.44-6.29-1.22-.76-2.52-1.38-3.95-1.81-1.41-.5-3.02-.85-4.82-1.02-3.67-.34-6.84-.37-10.29.63-3.39.86-6.92,2.84-10.02,6.01-3.11,3.16-5.57,7.33-7.04,11.86-.77,2.28-1.27,4.68-1.58,7.11-.18,1.72-.33,3.46-.47,5.2v64.38h-26.78v-115.97h26.78v10.92c1.99-2.55,4.39-4.96,7.27-7.01,3.12-2.2,6.72-3.99,10.52-5.26,3.81-1.23,7.77-1.87,11.61-2.09,3.86-.17,7.94.09,12.01.91,4.06.89,8.02,2.34,11.62,4.29,7.29,3.99,12.82,10.01,16.22,16.27.46.83.88,1.67,1.28,2.5,0-.02,0-.03,0-.04,2.16-5.95,6.26-12.09,12.6-16.6,3.12-2.2,6.71-3.99,10.52-5.26,3.81-1.23,7.77-1.87,11.61-2.09,3.86-.17,7.94.09,12,.91,4.06.89,8.02,2.34,11.62,4.29,7.29,3.99,12.82,10.01,16.22,16.27,1.74,3.14,3.01,6.31,3.98,9.47.97,3.23,1.64,6.42,2.02,9.57.38,3.15.59,6.23.56,9.26v7.78s.05,61.86.05,61.86h0Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M1782.44,92.61c0-9.42,7.63-17.05,17.05-17.05s17.05,7.63,17.05,17.05-7.63,17.05-17.05,17.05-17.05-7.63-17.05-17.05ZM1786.1,237.94v-115.97h26.78v115.97h-26.78Z" style="fill: #090242; stroke-width: 0px;"/> + <path d="M1915.1,121.09l-6.16,25.89c-.77-.52-1.56-1.01-2.4-1.44-2.91-1.5-6.15-2.48-10.16-2.81-4.03-.32-7.68-.25-11.38.74-3.71.93-7.34,2.7-10.7,5.63-3.33,2.86-6.21,6.9-8.16,11.27-1.96,4.46-3.03,9.26-3.42,14.5-.13,1.44-.24,2.87-.37,4.31v58.76h-26.78v-115.97h26.78v13.99c2.99-4.24,7.05-8.15,11.98-11.07,6.54-3.93,14.54-6.08,22.05-6.38,3.79-.16,7.74.06,11.7.74,2.38.42,4.74,1.06,7.03,1.84Z" style="fill: #090242; stroke-width: 0px;"/> + </g> +</svg> \ No newline at end of file diff --git a/docs/src/public/powerful.svg b/docs/src/public/powerful.svg new file mode 100644 index 0000000..f45d122 --- /dev/null +++ b/docs/src/public/powerful.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" width="928.52587" height="635.2126" viewBox="0 0 928.52587 635.2126" xmlns:xlink="http://www.w3.org/1999/xlink"><circle cx="546.40861" cy="233" r="233" fill="#f2f2f2"/><path d="M267.14836,695.73927a39.063,39.063,0,0,0,37.72112-6.64475c13.212-11.08918,17.35435-29.35853,20.72485-46.275l9.96924-50.03559-20.87165,14.37143c-15.00965,10.33508-30.35744,21.00127-40.75007,35.97113s-14.929,35.40506-6.57885,51.60315" transform="translate(-135.73706 -132.3937)" fill="#e6e6e6"/><path d="M268.93246,756.31777c-2.11249-15.38745-4.28468-30.97255-2.80288-46.52494,1.316-13.81219,5.52988-27.30282,14.10882-38.36422a63.8196,63.8196,0,0,1,16.37384-14.83745c1.63739-1.03352,3.14442,1.5614,1.51416,2.59041a60.67183,60.67183,0,0,0-23.99889,28.9564c-5.22489,13.28948-6.06393,27.77612-5.16355,41.89338.54449,8.53722,1.69847,17.01851,2.86134,25.48891a1.55428,1.55428,0,0,1-1.04766,1.84517,1.50915,1.50915,0,0,1-1.84518-1.04766Z" transform="translate(-135.73706 -132.3937)" fill="#f2f2f2"/><path d="M408.73336,735.12443a22.26366,22.26366,0,0,0,20.4619,7.60621c9.69252-1.64386,16.99541-9.4324,23.52884-16.77825l19.32458-21.72757L457.644,705.27456c-10.359.75491-20.97969,1.57515-30.4046,5.93973s-17.54587,13.10443-18.10946,23.4756" transform="translate(-135.73706 -132.3937)" fill="#e6e6e6"/><path d="M392.14858,765.42363c3.39683-8.17455,6.82128-16.46349,12.03288-23.68308,4.62846-6.41176,10.589-11.8301,17.99559-14.7958a36.37365,36.37365,0,0,1,12.32755-2.57538c1.103-.03617,1.096,1.67409-.00222,1.7101a34.57944,34.57944,0,0,0-20.14644,7.31937c-6.39969,5.028-10.988,11.90886-14.61472,19.1095-2.19317,4.35449-4.07055,8.85716-5.94042,13.35705a.88586.88586,0,0,1-1.047.60523.86012.86012,0,0,1-.60523-1.047Z" transform="translate(-135.73706 -132.3937)" fill="#f2f2f2"/><path d="M289.27958,729.08926a28.75529,28.75529,0,0,0,25.05474,12.93133c12.68315-.60208,23.25684-9.45412,32.77516-17.85807l28.15329-24.85722L356.63,698.41361c-13.39961-.64126-27.14492-1.23941-39.90759,2.89278s-24.53307,14.0769-26.8668,27.28729" transform="translate(-135.73706 -132.3937)" fill="#e6e6e6"/><path d="M262.96242,765.16329c10.16773-17.99115,21.9614-37.98626,43.03473-44.377a48.022,48.022,0,0,1,18.10109-1.869c1.9218.16571,1.44191,3.1281-.4764,2.9627a44.61381,44.61381,0,0,0-28.88649,7.64231c-8.1449,5.544-14.48659,13.25165-19.85414,21.42525-3.28779,5.00658-6.23278,10.22556-9.17833,15.43754-.94134,1.66563-3.69279.46328-2.74046-1.22182Z" transform="translate(-135.73706 -132.3937)" fill="#f2f2f2"/><path d="M702.07,458.07511c20.21713-11.11154,45.37875-8.88155,67.16141-1.28454s41.52211,20.04317,62.67523,29.24883c64.64,28.1308,142.54824,23.5948,203.48691-11.84751,5.76335,13.406-.41945,28.74022-7.21108,41.65571s-14.53209,26.914-11.7363,41.236c3.57763,18.32718,22.793,29.31536,30.57874,46.28791,7.39086,16.11167,3.42387,35.0325-2.24615,51.82719s-13.028,33.7152-12.06453,51.415,13.82268,36.416,31.54867,36.43c-26.26936,4.08171-53.0555,8.16626-79.3089,3.9832s-52.36859-18.06777-64.55825-41.693c-6.09277-11.80863-8.44148-25.38593-15.43872-36.68213-11.91826-19.2406-34.79115-28.28862-56.37224-35.1079s-44.6762-13.38826-59.871-30.16213c-14.47787-15.98239-19.06353-38.24611-26.73413-58.4007a186.53661,186.53661,0,0,0-64.58839-84.22984" transform="translate(-135.73706 -132.3937)" fill="#6c63ff"/><path d="M725.49486,374.3382c-7.097-3.30155-15.81044-4.38389-23.03182-1.316s-12.14452,10.93154-9.80947,17.89083c1.05869,3.15524,3.46989,6.25835,2.53051,9.445-.72307,2.45285-3.24546,4.07226-5.72259,5.22372s-5.18694,2.11952-6.97842,4.06155-2.16509,5.28533.07286,6.79139c.73731.49618,1.65885.7315,2.37375,1.25437a3.772,3.772,0,0,1,1.16432,4.22219,8.89329,8.89329,0,0,1-2.85084,3.75066c-2.54053,2.19094-5.89807,4.69849-5.10925,7.80873a5.47831,5.47831,0,0,0,3.697,3.45788,18.36721,18.36721,0,0,0,5.42688.71626l74.96621,2.36156a28.422,28.422,0,0,0,7.40167-.41344,8.76185,8.76185,0,0,0,5.81294-3.905c1.43559-2.65728.4931-5.93057-1.2798-8.41186s-4.282-4.43862-6.35525-6.71709-3.76948-5.1227-3.404-8.06747c.29256-2.35733,1.84718-4.39471,2.96321-6.53661s1.76427-4.81847.31886-6.78893c-2.03678-2.77665-6.92687-2.5255-9.24284-5.11283-1.74777-1.95256-1.41027-4.76346-1.58405-7.28135-.418-6.05656-4.61117-11.77645-10.58027-14.43257a20.83058,20.83058,0,0,0-18.95323,1.29081Z" transform="translate(-135.73706 -132.3937)" fill="#2f2e41"/><polygon points="542.385 622.099 530.644 618.571 538.663 571.604 555.992 576.811 542.385 622.099" fill="#a0616a"/><path d="M677.69743,766.77357l-37.85868-11.37465.14386-.47886a15.386,15.386,0,0,1,19.16265-10.30886l.00093.00028,23.12268,6.9473Z" transform="translate(-135.73706 -132.3937)" fill="#2f2e41"/><polygon points="653.182 622.496 640.922 622.495 635.09 575.207 653.184 575.208 653.182 622.496" fill="#a0616a"/><path d="M792.04534,766.77357l-39.53051-.00147v-.5a15.38605,15.38605,0,0,1,15.38647-15.38623h.001l24.1438.001Z" transform="translate(-135.73706 -132.3937)" fill="#2f2e41"/><path d="M771.90046,719.12347a4.50048,4.50048,0,0,1-4.4414-3.78808L749.25569,601.7807a3.50022,3.50022,0,0,0-6.68824-.78906L695.40608,714.52777a4.51622,4.51622,0,0,1-5.57862,2.54394L672.79328,711.393a4.48511,4.48511,0,0,1-2.9663-5.26074L708.4759,535.11078a4.52538,4.52538,0,0,1,3.37183-3.3916L763.68025,519.687a4.5623,4.5623,0,0,1,3.70752.76953c34.13037,24.79883,31.24707,105.82129,24.88623,193.79785a4.50192,4.50192,0,0,1-4.29444,4.17383l-15.88183.69141C772.03181,719.12152,771.96589,719.12347,771.90046,719.12347Z" transform="translate(-135.73706 -132.3937)" fill="#2f2e41"/><circle cx="592.53158" cy="282.47071" r="24.56103" fill="#a0616a"/><path d="M729.32893,542.16156a39.89858,39.89858,0,0,1-17.02368-3.93457l-.18262-.085-.07251-.18848-22.02954-57.07617-.41895-9.917A23.7725,23.7725,0,0,1,714.692,446.22015l23.38769,1.31739a23.787,23.787,0,0,1,22.43677,23.61035c.60742,1.167,4.92139,10.292-3.533,18.86523-.31592,1.95215-3.27319,22.24707,7.88672,33.40723l.31372.31347-.27417.34864C764.732,524.309,750.39949,542.15863,729.32893,542.16156Z" transform="translate(-135.73706 -132.3937)" fill="#3f3d56"/><path d="M762.17091,546.46725a10.05576,10.05576,0,0,0,5.277-14.48823l23.35887-27.04334-18.41436-2.39689-19.35886,26.04828a10.11028,10.11028,0,0,0,9.13733,17.88018Z" transform="translate(-135.73706 -132.3937)" fill="#a0616a"/><path d="M776.77546,525.59418a4.48916,4.48916,0,0,1-2.46875-.74024l-11.553-7.57715a4.49134,4.49134,0,0,1-1.39892-6.06445l8.36377-14.05176a6.27236,6.27236,0,0,0-2.824-8.93164l-24.02783-10.77246a14.32392,14.32392,0,0,1-8.11206-15.915h0a14.24089,14.24089,0,0,1,20.43237-9.7539l36.45215,18.67871a19.17711,19.17711,0,0,1,6.80493,28.28906L780.4239,523.725A4.48892,4.48892,0,0,1,776.77546,525.59418Z" transform="translate(-135.73706 -132.3937)" fill="#3f3d56"/><path d="M703.142,411.37254c7.75642-.62285,14.19623-8.3714,13.38973-16.11089a13.00909,13.00909,0,0,0,11.14121,13.24574c3.55787.392,7.4584-.68444,10.55524,1.11047,3.43,1.988,4.52758,6.81578,8.10091,8.53283,3.45255,1.659,7.83771-.60362,9.54346-4.03331s1.28713-7.5502.15669-11.21005a31.65248,31.65248,0,0,0-52.68951-12.97513c-3.26143,3.28049-5.851,7.46146-6.271,12.06821s1.717,9.60535,5.85416,11.67486Z" transform="translate(-135.73706 -132.3937)" fill="#2f2e41"/><path d="M646.00131,343.3379a10.05578,10.05578,0,0,1,3.10451,15.10357l19.15887,30.16483-18.567-.31813-15.34707-28.59626a10.11027,10.11027,0,0,1,11.65066-16.354Z" transform="translate(-135.73706 -132.3937)" fill="#a0616a"/><path d="M661.44385,364.81768l24.67358,58.30169,25.86871,43.86419a14.18889,14.18889,0,0,1-8.12022,20.7901h0a14.26532,14.26532,0,0,1-16.95083-7.56917L637.0578,373.67221a4.49993,4.49993,0,0,1,2.69943-6.19151l16.16594-5.193a4.60035,4.60035,0,0,1,.94246-.19733A4.47425,4.47425,0,0,1,661.44385,364.81768Z" transform="translate(-135.73706 -132.3937)" fill="#3f3d56"/><path d="M892.73706,767.6063h-756a1,1,0,0,1,0-2h756a1,1,0,0,1,0,2Z" transform="translate(-135.73706 -132.3937)" fill="#ccc"/><path d="M733.59533,240.6063v82a1,1,0,1,1-2,0v-82a1,1,0,0,1,2,0Z" transform="translate(-135.73706 -132.3937)" fill="#ccc"/><path d="M820.42095,310.17127l-28.28427,28.28427a1,1,0,0,1-1.41421-1.41421l28.28427-28.28427a1,1,0,1,1,1.41421,1.41421Z" transform="translate(-135.73706 -132.3937)" fill="#ccc"/><path d="M644.76971,310.17127,673.054,338.45554a1,1,0,0,0,1.41422-1.41421l-28.28428-28.28427a1,1,0,1,0-1.41421,1.41421Z" transform="translate(-135.73706 -132.3937)" fill="#ccc"/></svg> \ No newline at end of file diff --git a/docs/src/quick-start.md b/docs/src/quick-start.md new file mode 100644 index 0000000..9c43dbb --- /dev/null +++ b/docs/src/quick-start.md @@ -0,0 +1,20 @@ +# Quick start <Badge type="warning" text="WIP" /> + +This guide serves as a tutorial for newcomers to Mímir; a hypothetical scenario is established for a game and then an exemplar, step-by-step implementation is provided. + +## Scenario + +You're working on an open-world game: more specifically, there's a non-linear quest tree (i.e. the player is free to explore and start/complete quests as they please). + +You've decided that you'd like your game's dialogue to be more *aware* of what's happened. Specifically, you'd like NPCs to make different remarks depending on what the player has (or hasn't) already done. + +This scenario can be easily achieved with Mímir and, more importantly, is exactly the kind of scenario Mímir was designed for. With this in mind, let's get started! + +## Installation + +Start off by adding Mímir to your project's `Cargo.toml`, including the optional `float` feature (whose relevance will be explained later): + +```toml +[dependencies] +subtale-mimir = { version = "0.6", features = ["float"] } +``` diff --git a/docs/src/use-cases/repeated-evaluations.md b/docs/src/recipes/repeated-evaluations.md similarity index 100% rename from docs/src/use-cases/repeated-evaluations.md rename to docs/src/recipes/repeated-evaluations.md diff --git a/docs/src/use-cases/tips.md b/docs/src/recipes/tips.md similarity index 77% rename from docs/src/use-cases/tips.md rename to docs/src/recipes/tips.md index 6b9d39f..f31b9d5 100644 --- a/docs/src/use-cases/tips.md +++ b/docs/src/recipes/tips.md @@ -32,13 +32,17 @@ let mut finished_level_three = Rule::new(Outcome::Tip { }); ``` -> ℹ️ In a production environment (i.e. distributing your game), it makes more sense to serialize your tips during development and include them in your distributed assets, ready to be [deserialized at runtime](/serialization.html)! +::: tip +In a production environment (i.e. distributing your game), it makes more sense to serialize your tips during development and include them in your distributed assets, ready to be [deserialized at runtime](/serialization.html)! +::: ### Adding requirements Without [evaluators](/concepts/evaluator.html) (requirements), these tips are pretty useless. Let's add some! -> ⚠️ The `FloatEvaluator` implementation used in this example requires enabling the `float` feature in your project's `Cargo.toml`! +::: warning +The `FloatEvaluator` implementation used in this example requires enabling the `float` feature in your project's `Cargo.toml`! +::: ```rs just_died.insert( @@ -55,9 +59,11 @@ finished_level_three.insert( // `last_level_completed`: this logic is outside of Mímir's scope! ``` -> ℹ️ In the above example, we mimick a `bool` by checking if the float's value is equal to `1.0` (`FloatEvaluator::EqualTo(1.)`). -> -> Alternatively, you could write your own implementation of `Evaluator` that can evaluate boolean values. +::: tip +In the above example, we mimic a `bool` by checking if the float's value is equal to `1.0` (`FloatEvaluator::EqualTo(1.)`). + +Alternatively, you could write your own implementation of `Evaluator` that can evaluate boolean values. +::: ## Bundling the tips @@ -67,9 +73,11 @@ Now let's bundle the tips into a [ruleset](/concepts/ruleset.html) so we can eva let tips = Ruleset::new(vec![just_died, finished_level_three]); ``` -> ⚠️ As outlined on the [performance page](/performance.html#ruleset-storage), invoking `Ruleset::new` is expensive! -> -> Instead of creating the ruleset each time your game enters a loading screen state, you should setup your ruleset once during your game's initial load. +::: warning +As outlined on the [performance page](/performance.html#ruleset-storage), invoking `Ruleset::new` is expensive! + +Instead of creating the ruleset each time your game enters a loading screen state, you should setup your ruleset once during your game's initial load. +::: ## Retrieving a valid tip diff --git a/docs/src/changelog.md b/docs/src/release-notes.md similarity index 99% rename from docs/src/changelog.md rename to docs/src/release-notes.md index 85794d2..7affcab 100644 --- a/docs/src/changelog.md +++ b/docs/src/release-notes.md @@ -1,4 +1,4 @@ -# Changelog +# Release notes Visit the [releases page on GitHub][releases] for a list of all historical releases. diff --git a/docs/src/serialisation.md b/docs/src/serialisation.md new file mode 100644 index 0000000..ae2ed17 --- /dev/null +++ b/docs/src/serialisation.md @@ -0,0 +1,16 @@ +# Serialisation + +Evaluators (including the `FloatEvaluator` implementation), rules, and rulesets are all (de)serialisable using [serde][serde] if you enable the respective feature in your project's `Cargo.toml`: + +```toml +[dependencies] +subtale-mimir = { version = "0.5.1", features = ["serde"] } +``` + +This makes it easy for you to serialise rulesets into a persistent medium (i.e. files) during your game's development process, bundle them with your game, and deserialise them at runtime. + +::: tip +This also means that Mímir can effortlessly support modding by allowing you to deserialize and load user-defined rulesets at runtime. +::: + +[serde]: https://serde.rs/ \ No newline at end of file diff --git a/docs/src/serialization.md b/docs/src/serialization.md deleted file mode 100644 index 33581f4..0000000 --- a/docs/src/serialization.md +++ /dev/null @@ -1,14 +0,0 @@ -# Serialization - -Evaluators (including the `FloatEvaluator` implementation), rules, and rulesets are all (de)serializable using [serde][serde] if you enable the respective feature in your project's `Cargo.toml`: - -```toml -[dependencies] -subtale-mimir = { version = "0.5.1", features = ["serde"] } -``` - -This makes it easy for you to serialize rulesets into a persistent medium (i.e. files) during your game's development process, bundle them with your game, and deserialize them at runtime. - -> ℹ️ This also means that Mímir can effortlessly support modding by allowing you to deserialize and load user-defined rulesets at runtime. - -[serde]: https://serde.rs/ \ No newline at end of file diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md deleted file mode 100644 index 336dfcc..0000000 --- a/docs/src/tutorial.md +++ /dev/null @@ -1,22 +0,0 @@ -# Tutorial (WIP) - -This guide serves as a tutorial for newcomers to Mímir; a hypothetical scenario is established for a game and then an exemplar, step-by-step implementation is provided. - -## Scenario - -You're working on a game that is an "open world": more specifically, there is a non-linear quest tree (i.e. the player is free to explore and start/complete quests as they please). - -As a game designer, you've decided that you'd like your game's dialogue to be more "aware" of what has happened. You'd like NPCs to make different remarks depending on what the player has/hasn't already done. - -This scenario can be easily achieved with Mímir; let's get started! - -## Steps - -### Installation - -Start off by adding Mímir to your project's `Cargo.toml`, including the optional `float` feature (whose relevance will be explained later): - -```toml -[dependencies] -subtale-mimir = { version = "0.5.1", features = ["float"] } -``` diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..0fef23a --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}