mecha10-behavior-runtime 0.1.0

Behavior tree runtime for Mecha10 - unified AI and logic composition system
Documentation
//! Integration example demonstrating the complete workflow for loading and executing behavior trees.
//!
//! This example shows:
//! 1. Creating a NodeRegistry
//! 2. Registering custom behavior nodes
//! 3. Loading a behavior tree from JSON
//! 4. Executing the loaded tree with BehaviorExecutor
//!
//! Run this example with:
//! ```bash
//! cargo run --example load_and_execute
//! ```

use anyhow::Result;
use async_trait::async_trait;
use mecha10_behavior_runtime::prelude::*;
use std::time::Duration;

// ============================================================================
// Custom Behavior Implementations
// ============================================================================

/// A simple behavior that prints a message.
#[derive(Debug)]
struct PrintMessage {
    message: String,
}

impl PrintMessage {
    fn from_config(config: serde_json::Value) -> Result<Self> {
        let message = config
            .get("message")
            .and_then(|v| v.as_str())
            .unwrap_or("Hello, world!")
            .to_string();

        Ok(Self { message })
    }
}

#[async_trait]
impl BehaviorNode for PrintMessage {
    async fn tick(&mut self, _ctx: &Context) -> Result<NodeStatus> {
        println!(">>> {}", self.message);
        Ok(NodeStatus::Success)
    }

    fn name(&self) -> &str {
        "print_message"
    }
}

/// A behavior that waits for a specified duration.
#[derive(Debug)]
struct WaitBehavior {
    duration: Duration,
    started: Option<std::time::Instant>,
}

impl WaitBehavior {
    fn from_config(config: serde_json::Value) -> Result<Self> {
        let duration_seconds = config.get("duration_seconds").and_then(|v| v.as_f64()).unwrap_or(1.0);

        Ok(Self {
            duration: Duration::from_secs_f64(duration_seconds),
            started: None,
        })
    }
}

#[async_trait]
impl BehaviorNode for WaitBehavior {
    async fn tick(&mut self, _ctx: &Context) -> Result<NodeStatus> {
        if self.started.is_none() {
            println!(">>> Waiting for {:?}...", self.duration);
            self.started = Some(std::time::Instant::now());
        }

        if self.started.unwrap().elapsed() >= self.duration {
            println!(">>> Wait complete!");
            Ok(NodeStatus::Success)
        } else {
            Ok(NodeStatus::Running)
        }
    }

    async fn reset(&mut self) -> Result<()> {
        self.started = None;
        Ok(())
    }

    fn name(&self) -> &str {
        "wait"
    }
}

/// A behavior that counts to a target number.
#[derive(Debug)]
struct CounterBehavior {
    target: usize,
    current: usize,
}

impl CounterBehavior {
    fn from_config(config: serde_json::Value) -> Result<Self> {
        let target = config.get("target").and_then(|v| v.as_u64()).unwrap_or(5) as usize;

        Ok(Self { target, current: 0 })
    }
}

#[async_trait]
impl BehaviorNode for CounterBehavior {
    async fn tick(&mut self, _ctx: &Context) -> Result<NodeStatus> {
        self.current += 1;
        println!(">>> Count: {} / {}", self.current, self.target);

        if self.current >= self.target {
            Ok(NodeStatus::Success)
        } else {
            Ok(NodeStatus::Running)
        }
    }

    async fn reset(&mut self) -> Result<()> {
        self.current = 0;
        Ok(())
    }

    fn name(&self) -> &str {
        "counter"
    }
}

// ============================================================================
// Main Function
// ============================================================================

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize logging
    tracing_subscriber::fmt().with_max_level(tracing::Level::INFO).init();

    println!("\n=== Behavior Tree Loading and Execution Example ===\n");

    // Step 1: Create the node registry
    println!("Step 1: Creating NodeRegistry...");
    let mut registry = NodeRegistry::new();

    // Step 2: Register custom behavior nodes
    println!("Step 2: Registering custom nodes...");

    registry.register("print_message", |config| {
        Ok(Box::new(PrintMessage::from_config(config)?))
    });

    registry.register("wait", |config| Ok(Box::new(WaitBehavior::from_config(config)?)));

    registry.register("counter", |config| Ok(Box::new(CounterBehavior::from_config(config)?)));

    println!(
        "   Registered {} node types: {:?}",
        registry.len(),
        registry.registered_types()
    );

    // Step 3: Create the behavior loader
    println!("\nStep 3: Creating BehaviorLoader...");
    let loader = BehaviorLoader::new(registry);

    // Step 4: Load behavior tree from JSON
    println!("\nStep 4: Loading behavior tree from JSON...");

    // Option A: Load from a JSON string
    let json = r#"{
        "name": "demo_sequence",
        "description": "Demonstrates loading and executing a behavior tree",
        "root": {
            "type": "sequence",
            "children": [
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "Starting demo sequence..."
                    }
                },
                {
                    "type": "node",
                    "node": "counter",
                    "config": {
                        "target": 3
                    }
                },
                {
                    "type": "node",
                    "node": "wait",
                    "config": {
                        "duration_seconds": 0.5
                    }
                },
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "Demo complete!"
                    }
                }
            ]
        }
    }"#;

    let behavior = loader.load_from_json(json)?;
    println!("   Successfully loaded behavior tree!");

    // Option B: Load from a file (commented out, but shows how it works)
    // let behavior = loader.load_from_file("examples/simple_sequence.json")?;

    // Step 5: Create context for execution
    println!("\nStep 5: Creating execution context...");
    let ctx = Context::new("demo_robot").await?;

    // Step 6: Create the executor
    println!("\nStep 6: Creating BehaviorExecutor...");
    let mut executor = BehaviorExecutor::new(behavior, 30.0) // 30 Hz tick rate
        .with_max_ticks(1000); // Safety limit

    // Step 7: Initialize the behavior
    println!("\nStep 7: Initializing behavior...");
    executor.init(&ctx).await?;

    // Step 8: Execute the behavior tree
    println!("\nStep 8: Executing behavior tree...\n");
    println!("--- Execution Output ---");

    let (status, stats) = executor.run_until_complete(&ctx).await?;

    println!("--- Execution Complete ---\n");

    // Step 9: Display execution statistics
    println!("\nStep 9: Execution Statistics:");
    println!("   Final Status: {}", status);
    println!("   Total Ticks: {}", stats.tick_count);
    println!("   Total Duration: {:?}", stats.total_duration);
    println!("   Average Tick: {:?}", stats.avg_tick_duration);
    println!(
        "   Min/Max Tick: {:?} / {:?}",
        stats.min_tick_duration, stats.max_tick_duration
    );

    // Step 10: Cleanup
    println!("\nStep 10: Cleaning up...");
    executor.terminate(&ctx).await?;

    println!("\n=== Demo Complete! ===\n");

    // Additional example: Loading from different JSON configurations
    demonstrate_different_compositions(&loader, &ctx).await?;

    Ok(())
}

/// Demonstrates loading and executing different behavior tree compositions.
async fn demonstrate_different_compositions(loader: &BehaviorLoader, ctx: &Context) -> Result<()> {
    println!("\n=== Additional Examples ===\n");

    // Example 1: Selector (fallback)
    println!("Example 1: Selector Node (fallback behavior)");
    let selector_json = r#"{
        "name": "selector_demo",
        "root": {
            "type": "selector",
            "children": [
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "First option succeeded!"
                    }
                },
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "This won't execute"
                    }
                }
            ]
        }
    }"#;

    let behavior = loader.load_from_json(selector_json)?;
    let mut executor = BehaviorExecutor::new(behavior, 30.0);
    let (status, _) = executor.run_until_complete(ctx).await?;
    println!("   Result: {}\n", status);

    // Example 2: Parallel node
    println!("Example 2: Parallel Node (concurrent execution)");
    let parallel_json = r#"{
        "name": "parallel_demo",
        "root": {
            "type": "parallel",
            "policy": "require_all",
            "children": [
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "Parallel task 1"
                    }
                },
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "Parallel task 2"
                    }
                }
            ]
        }
    }"#;

    let behavior = loader.load_from_json(parallel_json)?;
    let mut executor = BehaviorExecutor::new(behavior, 30.0);
    let (status, _) = executor.run_until_complete(ctx).await?;
    println!("   Result: {}\n", status);

    // Example 3: Nested composition
    println!("Example 3: Nested Composition");
    let nested_json = r#"{
        "name": "nested_demo",
        "root": {
            "type": "sequence",
            "children": [
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "Outer sequence started"
                    }
                },
                {
                    "type": "selector",
                    "children": [
                        {
                            "type": "node",
                            "node": "print_message",
                            "config": {
                                "message": "Inner selector option"
                            }
                        }
                    ]
                },
                {
                    "type": "node",
                    "node": "print_message",
                    "config": {
                        "message": "Outer sequence complete"
                    }
                }
            ]
        }
    }"#;

    let behavior = loader.load_from_json(nested_json)?;
    let mut executor = BehaviorExecutor::new(behavior, 30.0);
    let (status, _) = executor.run_until_complete(ctx).await?;
    println!("   Result: {}\n", status);

    println!("=== All Examples Complete! ===\n");

    Ok(())
}