using System; namespace HomeRobotArticle { public enum Status { Success, Failure, Running } public abstract class Node { public abstract Status Tick(); } public interface IRobot { bool ChildNearby { get; } bool AtKitchen { get; } void MoveToKitchen(); void Stop(); } public sealed class GoToKitchen : Node { private readonly IRobot robot; public GoToKitchen(IRobot robot) => this.robot = robot; public override Status Tick() { if (robot.AtKitchen) { robot.Stop(); return Status.Success; } robot.MoveToKitchen(); return Status.Running; } } public sealed class Condition : Node { private readonly Func check; public Condition(Func check) => this.check = check; public override Status Tick() => check() ? Status.Success : Status.Failure; } public sealed class StopAndWait : Node { private readonly IRobot robot; public StopAndWait(IRobot robot) => this.robot = robot; public override Status Tick() { robot.Stop(); return Status.Running; } } public sealed class Sequence : Node { private readonly Node[] children; public Sequence(params Node[] children) => this.children = children; public override Status Tick() { foreach (Node child in children) { Status result = child.Tick(); if (result != Status.Success) return result; } return Status.Success; } } public sealed class Selector : Node { private readonly Node[] children; public Selector(params Node[] children) => this.children = children; public override Status Tick() { foreach (Node child in children) { Status result = child.Tick(); if (result != Status.Failure) return result; } return Status.Failure; } } public static class HomeRobotTree { public static Node Build(IRobot robot) => new Selector( new Sequence( new Condition(() => robot.ChildNearby), new StopAndWait(robot) ), new GoToKitchen(robot) ); } // A straight, obstacle-free hallway. Replace with a Unity mover in the scene. public sealed class SimulatedRobot : IRobot { public bool ChildNearby { get; set; } public float Position { get; private set; } public bool AtKitchen => Position >= 4f; public bool Moving { get; private set; } public void MoveToKitchen() => Moving = true; public void Stop() => Moving = false; public void Advance(float seconds) { if (Moving) Position = Math.Min(4f, Position + seconds); } } public static class Program { public static void Main() { var robot = new SimulatedRobot(); Node root = HomeRobotTree.Build(robot); for (int tick = 0; tick <= 7; tick++) { robot.ChildNearby = tick >= 2 && tick < 4; Status status = root.Tick(); Console.WriteLine($"Tick {tick}: child={robot.ChildNearby}, " + $"position={robot.Position}, moving={robot.Moving}, status={status}"); robot.Advance(1f); } } } }