From 7bbc7d5fbea204825fee3bb2d49f42d2a6b0258a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= <kzm@zkat.tech> Date: Fri, 14 Jan 2022 22:22:36 -0800 Subject: [PATCH] doc: update docs for new API stuff --- README.md | 66 ++++++++++++++---------------------- src/actions.rs | 34 ++++++++++++++----- src/lib.rs | 92 ++++++++++++++++++++------------------------------ src/scorers.rs | 34 +++++++++---------- 4 files changed, 103 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index 676a5d5..9f838bc 100644 --- a/README.md +++ b/README.md @@ -27,40 +27,22 @@ See [the documentation](https://docs.rs/big-brain) for more details. ### Example -First, you define actions and considerations, which are just plain old Bevy -Components and Systems. As a developer, you write application-dependent code -to define [`Scorers`](#scorers) and [`Actions`](#actions), and then put it -all together like building blocks, using [`Thinkers`](#thinkers) that will -define the actual behavior. +As a developer, you write application-dependent code to define +[`Scorers`](#scorers) and [`Actions`](#actions), and then put it all together +like building blocks, using [`Thinkers`](#thinkers) that will define the +actual behavior. #### Scorers `Scorer`s are entities that look at the world and evaluate into [`Score`](scorers::Score) values. You can think of them as the "eyes" of the AI system. They're a highly-parallel way of being able to look at the `World` and use it to make some decisions later. -They are created by types that implement [`ScorerBuilder`](scorers::ScorerBuilder). - ```rust use bevy::prelude::*; use big_brain::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Component)] pub struct Thirsty; -impl Thirsty { - fn build() -> ThirstyBuilder { - ThirstyBuilder - } -} - -#[derive(Debug, Clone)] -pub struct ThirstyBuilder; - -impl ScorerBuilder for ThirstyBuilder { - fn build(&self, cmd: &mut Commands, scorer: Entity, _actor: Entity) { - cmd.entity(scorer).insert(Thirsty); - } -} - pub fn thirsty_scorer_system( thirsts: Query<&Thirst>, mut query: Query<(&Actor, &mut Score), With<Thirsty>>, @@ -75,30 +57,17 @@ pub fn thirsty_scorer_system( #### Actions -`Action`s are the actual things your entities will _do_. They are connected to [`ActionState`](actions::ActionState)s, and are created by types implementing [`ActionBuilder`](actions::ActionBuilder). +`Action`s are the actual things your entities will _do_. They are connected to +[`ActionState`](actions::ActionState)s that represent the current execution +state of the state machine. ```rust use bevy::prelude::*; use big_brain::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Component)] pub struct Drink; -impl Drink { - pub fn build() -> DrinkBuilder { - DrinkBuilder - } -} - -#[derive(Debug, Clone)] -pub struct DrinkBuilder; - -impl ActionBuilder for DrinkBuilder { - fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { - cmd.entity(action).insert(Drink); - } -} - fn drink_action_system( mut thirsts: Query<&mut Thirst>, mut query: Query<(&Actor, &mut ActionState), With<Drink>>, @@ -129,10 +98,25 @@ regular Component: cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert( Thinker::build() .picker(FirstToScore { threshold: 0.8 }) - .when(Thirsty::build(), Drink::build()), + .when(Thirsty, Drink), ); ``` +#### App + +Once all that's done, we just add our systems and off we go! + +```rust +App::new() + .add_plugins(DefaultPlugins) + .add_plugin(BigBrainPlugin) + .add_startup_system(init_entities) + .add_system(thirst_system) + .add_system_to_stage(BigBrainStage::Actions, drink_action_system) + .add_system_to_stage(BigBrainStage::Scorers, thirsty_scorer_system) + .run(); +``` + ### Contributing 1. Install the latest Rust toolchain (stable supported). diff --git a/src/actions.rs b/src/actions.rs index 609bdf7..0d6d5db 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -64,18 +64,34 @@ impl ActionBuilderWrapper { } /** -Trait that must be defined by types in order to be `ActionBuilder`s. `ActionBuilder`s' job is to spawn new `Action` entities. In general, most of this is already done for you, and the only method you really have to implement is `.build()`. +Trait that must be defined by types in order to be `ActionBuilder`s. `ActionBuilder`s' job is to spawn new `Action` entities on demand. In general, most of this is already done for you, and the only method you really have to implement is `.build()`. The `build()` method MUST be implemented for any `ActionBuilder`s you want to define. */ pub trait ActionBuilder: std::fmt::Debug + Send + Sync { /** - MUST insert your concrete Action component into the `action` [`Entity`], using `cmd`. You _may_ use `actor`, but it's perfectly normal to just ignore it. + + MUST insert your concrete Action component into the Scorer [`Entity`], using + `cmd`. You _may_ use `actor`, but it's perfectly normal to just ignore it. + + Note that this method is automatically implemented for any Components that + implement Clone, so you don't need to define it yourself unless you want + more complex parameterization of your Actions. ### Example + Using `Clone` (the easy way): + + ```no_run + #[derive(Debug, Clone, Component)] + struct MyAction; + ``` + + Implementing it manually: + ```no_run struct MyBuilder; + #[derive(Debug, Component)] struct MyAction; impl ActionBuilder for MyBuilder { @@ -155,8 +171,8 @@ Thinker::build() .when( MyScorer, Steps::build() - .step(MyAction::build()) - .step(MyNextAction::build()) + .step(MyAction) + .step(MyNextAction) ) ``` */ @@ -247,7 +263,7 @@ pub fn steps_system( } /** -[`ActionBuilder`] for the [`Concurrent`] component. Constructed through `Concurrent::build()`. +[`ActionBuilder`] for the [`Concurrently`] component. Constructed through `Concurrently::build()`. */ #[derive(Debug)] pub struct ConcurrentlyBuilder { @@ -292,8 +308,8 @@ Thinker::build() .when( MyScorer, Concurrent::build() - .push(MyAction::build()) - .push(MyOtherAction::build()) + .push(MyAction) + .push(MyOtherAction) ) ``` */ @@ -304,7 +320,7 @@ pub struct Concurrently { impl Concurrently { /** - Construct a new [`ConcurrentBuilder`] to define the actions to take. + Construct a new [`ConcurrentlyBuilder`] to define the actions to take. */ pub fn build() -> ConcurrentlyBuilder { ConcurrentlyBuilder { @@ -314,7 +330,7 @@ impl Concurrently { } /** -System that takes care of executing any existing [`Concurrent`] Actions. +System that takes care of executing any existing [`Concurrently`] Actions. */ pub fn concurrent_system( concurrent_q: Query<(Entity, &Concurrently)>, diff --git a/src/lib.rs b/src/lib.rs index 9959c6f..3b11456 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ behavior. See [the documentation](https://docs.rs/big-brain) for more details. -## Features +### Features * Highly concurrent/parallelizable evaluation. * Integrates smoothly with Bevy. @@ -24,42 +24,24 @@ See [the documentation](https://docs.rs/big-brain) for more details. * State machine-style continuous actions/behaviors. * Action cancellation. -## Example +### Example -First, you define actions and considerations, which are just plain old Bevy -Components and Systems. As a developer, you write application-dependent code -to define [`Scorers`](#scorers) and [`Actions`](#actions), and then put it -all together like building blocks, using [`Thinkers`](#thinkers) that will -define the actual behavior. +As a developer, you write application-dependent code to define +[`Scorers`](#scorers) and [`Actions`](#actions), and then put it all together +like building blocks, using [`Thinkers`](#thinkers) that will define the +actual behavior. -### Scorers +#### Scorers `Scorer`s are entities that look at the world and evaluate into [`Score`](scorers::Score) values. You can think of them as the "eyes" of the AI system. They're a highly-parallel way of being able to look at the `World` and use it to make some decisions later. -They are created by types that implement [`ScorerBuilder`](scorers::ScorerBuilder). - -``` +```rust use bevy::prelude::*; use big_brain::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Component)] pub struct Thirsty; -impl Thirsty { - fn build() -> ThirstyBuilder { - ThirstyBuilder - } -} - -#[derive(Debug, Clone)] -pub struct ThirstyBuilder; - -impl ScorerBuilder for ThirstyBuilder { - fn build(&self, cmd: &mut Commands, scorer: Entity, _actor: Entity) { - cmd.entity(scorer).insert(Thirsty); - } -} - pub fn thirsty_scorer_system( thirsts: Query<&Thirst>, mut query: Query<(&Actor, &mut Score), With<Thirsty>>, @@ -72,32 +54,19 @@ pub fn thirsty_scorer_system( } ``` -### Actions +#### Actions -`Action`s are the actual things your entities will _do_. They are connected to [`ActionState`](actions::ActionState)s, and are created by types implementing [`ActionBuilder`](actions::ActionBuilder). +`Action`s are the actual things your entities will _do_. They are connected to +[`ActionState`](actions::ActionState)s that represent the current execution +state of the state machine. -``` +```rust use bevy::prelude::*; use big_brain::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Component)] pub struct Drink; -impl Drink { - pub fn build() -> DrinkBuilder { - DrinkBuilder - } -} - -#[derive(Debug, Clone)] -pub struct DrinkBuilder; - -impl ActionBuilder for DrinkBuilder { - fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { - cmd.entity(action).insert(Drink); - } -} - fn drink_action_system( mut thirsts: Query<&mut Thirst>, mut query: Query<(&Actor, &mut ActionState), With<Drink>>, @@ -119,32 +88,43 @@ fn drink_action_system( } ``` -### Thinkers +#### Thinkers Finally, you can use it when define the [`Thinker`](thinker::Thinker), which you can attach as a regular Component: -```no_run +```rust cmd.spawn().insert(Thirst::new(70.0, 2.0)).insert( Thinker::build() .picker(FirstToScore { threshold: 0.8 }) - .when(Thirsty::build(), Drink::build()), + .when(Thirsty, Drink), ); ``` -## Contributing +#### App + +Once all that's done, we just add our systems and off we go! + +```rust +App::new() + .add_plugins(DefaultPlugins) + .add_plugin(BigBrainPlugin) + .add_startup_system(init_entities) + .add_system(thirst_system) + .add_system_to_stage(BigBrainStage::Actions, drink_action_system) + .add_system_to_stage(BigBrainStage::Scorers, thirsty_scorer_system) + .run(); +``` + +### Contributing 1. Install the latest Rust toolchain (stable supported). 2. `cargo run --example thirst` 3. Happy hacking! -## License - -This project is licensed under [the Parity License](LICENSE.md). Third-party contributions are licensed under Apache-2.0 and belong to their respective authors. - -The Parity License is a copyleft license that, unlike the GPL family, allows you to license derivative and connected works under permissive licenses like MIT or Apache-2.0. It's free to use provided the work you do is freely available! +### License -For proprietary use, please [contact me](mailto:kzm@zkat.tech?subject=big-brain%20license), or just [sponsor me on GitHub](https://github.com/users/zkat/sponsorship) under the appropriate tier to [acquire a proprietary-use license](LICENSE-PATRON.md)! This funding model helps me make my work sustainable and compensates me for the work it took to write this crate! +This project is licensed under [the Apache-2.0 License](LICENSE.md). */ diff --git a/src/scorers.rs b/src/scorers.rs index 12fd5af..a6ec194 100644 --- a/src/scorers.rs +++ b/src/scorers.rs @@ -46,12 +46,27 @@ The `build()` method MUST be implemented for any `ScorerBuilder`s you want to de */ pub trait ScorerBuilder: std::fmt::Debug + Sync + Send { /** - MUST insert your concrete Scorer component into the Scorer [`Entity`], using `cmd`. You _may_ use `actor`, but it's perfectly normal to just ignore it. + MUST insert your concrete Scorer component into the Scorer [`Entity`], using + `cmd`. You _may_ use `actor`, but it's perfectly normal to just ignore it. + + Note that this method is automatically implemented for any Components that + implement Clone, so you don't need to define it yourself unless you want + more complex parameterization of your Actions. ### Example + Using `Clone` (the easy way): + + ```no_run + #[derive(Debug, Clone, Component)] + struct MyScorer; + ``` + + Implementing it manually: + ```no_run struct MyBuilder; + #[derive(Debug, Component)] struct MyScorer; impl ScorerBuilder for MyBuilder { @@ -91,27 +106,12 @@ Scorer that always returns the same, fixed score. Good for combining with things #[derive(Clone, Component, Debug)] pub struct FixedScore(f32); -impl FixedScore { - pub fn build(score: f32) -> FixedScoreBuilder { - FixedScoreBuilder(score) - } -} - pub fn fixed_score_system(mut query: Query<(&FixedScore, &mut Score)>) { for (FixedScore(fixed), mut score) in query.iter_mut() { score.set(*fixed); } } -#[derive(Clone, Component, Debug)] -pub struct FixedScoreBuilder(f32); - -impl ScorerBuilder for FixedScoreBuilder { - fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) { - cmd.entity(action).insert(FixedScore(self.0)); - } -} - /** Composite Scorer that takes any number of other Scorers and returns the sum of their [`Score`] values if each _individual_ [`Score`] is at or above the configured `threshold`. @@ -385,7 +385,7 @@ unlike other composite scorers, `EvaluatingScorer` only takes one scorer upon bu Thinker::build() .when( EvaluatingScorer::build(MyScorer, MyEvaluator), - MyAction::build()); + MyAction); ``` */ #[derive(Clone, Component, Debug)] -- GitLab