behavior_tree 0.1.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8efa0e81c4f8f0b000ad45d14b7b75a244083f4d0013e8e42cf6b3732aef2ab
4
- data.tar.gz: 3a6c2e710f8ec044f0069f98d1e201840f8ca4d267f881d5a0d1f7f26907cae6
3
+ metadata.gz: 97c05c7c3c6b0316fe40d4bd20acd66189b3dc34265b45061ac87e7daabd1f15
4
+ data.tar.gz: 6a5ef7dfca3096d744a005aa9e6a60c9c932b69c77c05e63d18be882d010ee44
5
5
  SHA512:
6
- metadata.gz: ffd66c343d131acac25d3f6b8d1a797bf888968eaf6ae6ddb18902c3c6b6a20e146d30ac760695fd6be2b44b196cba2ef23fd453638f9759ca7c50d3106ee7d2
7
- data.tar.gz: 9a259429cc64aaef5e71456fd5864d708f574790d1ce6b364ff97849b53964e3ec7f9fab42b8b99a8dd3bb37af9e10ab67474d833dc9ea820b189d275e73bfa5
6
+ metadata.gz: d1367b71529a3257493aa12ed119bb05901c5094b24042e9ee86a9ff8a1d422cd97649a7c226e6b88ad32efce17fd1d348864bd328907f0f7ecdb44279da30c8
7
+ data.tar.gz: 5cdc8673bb4927e399ac6a79938114cc26bd178e7fcef71d2b04bd4986728be735c5d3f08f5e5b97557ffd85ae02e24270612e754d61e652ef573cac7e917b82
data/.rubocop.yml CHANGED
@@ -1,3 +1,4 @@
1
+ # require: rubocop-rspec
1
2
  AllCops:
2
3
  TargetRubyVersion: 2.6
3
4
  NewCops: enable
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- behavior_tree (0.1.7)
4
+ behavior_tree (1.0.0)
5
5
  colorize (~> 0.8.1)
6
6
 
7
7
  GEM
@@ -69,4 +69,4 @@ DEPENDENCIES
69
69
  simplecov
70
70
 
71
71
  BUNDLED WITH
72
- 2.1.2
72
+ 2.2.23
data/README.md CHANGED
@@ -1,50 +1,842 @@
1
- # Ruby Behavior Tree
1
+ # Behavior Tree
2
2
 
3
- ![](https://api.travis-ci.com/FeloVilches/Ruby-Behavior-Tree.svg?branch=main)
3
+ [![Travis CI](https://api.travis-ci.com/FeloVilches/Ruby-Behavior-Tree.svg?branch=main)](https://travis-ci.org/github/FeloVilches/Ruby-Behavior-Tree) [![Gem Version](https://badge.fury.io/rb/behavior_tree.svg)](https://rubygems.org/gems/behavior_tree)
4
4
 
5
- ============
5
+ A robust and customizable Ruby gem for creating Behavior Trees, used in games, AI, robotics, and more.
6
6
 
7
- (Copy from the auto-generated readme file. Some names changed, so some things are incorrect.)
7
+ <p align="center">
8
+ <img src="https://github.com/FeloVilches/ruby-behavior-tree/blob/main/assets/logo.png?raw=true" />
9
+ </p>
8
10
 
9
- WIP
11
+ ## Quick start
10
12
 
11
- ============
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'behavior_tree'
17
+ ```
18
+
19
+ And then execute:
12
20
 
13
- # Ruby::Behavior::Tree
21
+ $ bundle install
14
22
 
15
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ruby/behavior/tree`. To experiment with that code, run `bin/console` for an interactive prompt.
23
+ Or install it yourself as:
16
24
 
17
- TODO: Delete this and the text above, and describe your gem
25
+ $ gem install behavior_tree
18
26
 
19
- ## Installation
27
+ ### Build your first tree
20
28
 
21
- Add this line to your application's Gemfile:
29
+ Require the gem if necessary:
22
30
 
23
31
  ```ruby
24
- gem 'ruby-behavior-tree'
32
+ require 'behavior_tree'
25
33
  ```
26
34
 
27
- And then execute:
35
+ Create a tree using the DSL:
28
36
 
29
- $ bundle install
37
+ ```ruby
38
+ my_tree = BehaviorTree::Builder.build do
39
+ sequence do
40
+ task {
41
+ puts "I'm a task!"
42
+ status.success!
43
+ }
44
+ condition ->(context) { context[:some_value] < 200 } do
45
+ task {
46
+ context[:some_value] += 1
47
+ status.success!
48
+ }
49
+ end
50
+ task { context[:some_value] > 100 ? status.failure! : status.success! }
51
+ end
52
+ end
30
53
 
31
- Or install it yourself as:
54
+ my_tree.print
55
+ # Output:
56
+ # ∅
57
+ # └─sequence success (0 ticks)
58
+ # ├─task success (0 ticks)
59
+ # ├─condition success (0 ticks)
60
+ # │ └─task success (0 ticks)
61
+ # └─task success (0 ticks)
62
+ ```
63
+
64
+ Later in the guide you'll learn how to add your own custom classes so they are available inside the DSL as well.
65
+
66
+ If the tree or part of it cannot be built using the DSL, you can build it using plain old Ruby objects like so:
67
+
68
+ ```ruby
69
+ # Initialize an empty sequence.
70
+ sequence = BehaviorTree::Sequence.new
71
+
72
+ # Creating some tasks. You can also create tasks by extending BehaviorTree::Task,
73
+ # keep reading to learn how.
74
+ task1 = BehaviorTree::Task.new {
75
+ puts 'Hello world'
76
+ status.success!
77
+ }
78
+
79
+ task2 = BehaviorTree::Task.new {
80
+ puts 'Another simple task'
81
+ status.success!
82
+ }
83
+
84
+ # Add as children.
85
+ sequence << task1
86
+ sequence << task2
87
+
88
+ # Finally build the tree.
89
+ another_tree = BehaviorTree::Tree.new sequence
90
+
91
+ another_tree.print
92
+ # Output:
93
+ # ∅
94
+ # └─sequence success (0 ticks)
95
+ # ├─task success (0 ticks)
96
+ # └─task success (0 ticks)
97
+ ```
98
+
99
+ You can join trees created with any of the above methods (DSL or plain old Ruby objects). Let's join both of the trees we just created:
100
+
101
+ ```ruby
102
+ sequence << my_tree
103
+
104
+ another_tree.print
105
+ # Output:
106
+ # ∅
107
+ # └─sequence success (0 ticks)
108
+ # ├─task success (0 ticks)
109
+ # ├─task success (0 ticks)
110
+ # └─sequence success (0 ticks)
111
+ # ├─task success (0 ticks)
112
+ # ├─condition success (0 ticks)
113
+ # │ └─task success (0 ticks)
114
+ # └─task success (0 ticks)
115
+ ```
116
+
117
+ Finally, let's tick the tree to put it into motion.
118
+
119
+ ```ruby
120
+ # We need to assign the initial context data first.
121
+ another_tree.context = { some_value: 5 }
122
+
123
+ 200.times { another_tree.tick! }
124
+
125
+ another_tree.print
126
+ # Output:
127
+ # ∅
128
+ # └─sequence failure (200 ticks)
129
+ # ├─task success (200 ticks)
130
+ # ├─task success (200 ticks)
131
+ # └─sequence success (200 ticks)
132
+ # ├─task success (200 ticks)
133
+ # ├─condition failure (200 ticks)
134
+ # │ └─task success (195 ticks)
135
+ # └─task success (195 ticks)
136
+ ```
137
+
138
+ ## Learn how to use
139
+
140
+ - [Quick start](#quick-start)
141
+ * [Build your first tree](#build-your-first-tree)
142
+ - [Learn how to use](#learn-how-to-use)
143
+ - [Basics](#basics)
144
+ * [Ticking the tree](#ticking-the-tree)
145
+ * [Node status](#node-status)
146
+ * [Storage](#storage)
147
+ + [Global context](#global-context)
148
+ + [Per-node storage](#per-node-storage)
149
+ * [Types of nodes](#types-of-nodes)
150
+ + [Task nodes](#task-nodes)
151
+ + [Control nodes](#control-nodes)
152
+ + [Decorators and condition nodes](#decorators-and-condition-nodes)
153
+ - [Create custom nodes](#create-custom-nodes)
154
+ * [Custom task](#custom-task)
155
+ * [Custom control node](#custom-control-node)
156
+ * [Custom decorator](#custom-decorator)
157
+ * [Custom condition](#custom-condition)
158
+ - [Node API](#node-api)
159
+ * [Status](#status)
160
+ * [tick!](#tick-)
161
+ * [halt!](#halt-)
162
+ * [Status related callbacks and hooks](#status-related-callbacks-and-hooks)
163
+ - [Add custom nodes to the DSL](#add-custom-nodes-to-the-dsl)
164
+ - [Troubleshoot and debug your trees](#troubleshoot-and-debug-your-trees)
165
+ * [Detecting cycles](#detecting-cycles)
166
+ * [Checking nodes are all unique objects](#checking-nodes-are-all-unique-objects)
167
+ * [Visualize a tree](#visualize-a-tree)
168
+ - [Miscellaneous](#miscellaneous)
169
+ * [Generate random trees](#generate-random-trees)
170
+ - [Contributing](#contributing)
171
+ - [License](#license)
172
+ - [Code of Conduct](#code-of-conduct)
173
+
174
+ ## Basics
175
+
176
+ What is a behavior tree? According to the [Wikipedia](https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)):
177
+
178
+ > A behavior tree is a mathematical model of plan execution used in computer science, robotics, control systems and video games. They describe switchings between a finite set of tasks in a modular fashion. Their strength comes from their ability to create very complex tasks composed of simple tasks, without worrying how the simple tasks are implemented.
179
+
180
+ In simple words, it's a modular way to describe your program's control flow, in a very flexible and scalable way. It avoids the common pitfalls of usual control flow (i.e. `if-else`), such as spaghetti code, by structuring the logic as a tree, with branches (conditionals, sequence of tasks, etc) and leaf nodes (tasks to be executed).
181
+
182
+ ### Ticking the tree
183
+
184
+ The tick is the most important part of using a behavior tree. When you tick the root node of a tree, it will propagate it by ticking its children, which in turn will tick their children. Tasks (leaf nodes) are executed when the tick reaches them. Non-leaf nodes would execute their own logic when they are ticked (e.g. decoration logic, executing a sequence, etc).
185
+
186
+ ```ruby
187
+ my_tree.tick!
188
+
189
+ # Effects are propagated down the tree.
190
+ ```
191
+
192
+ ### Node status
193
+
194
+ Each node has a status, which can have three possible values:
195
+
196
+ 1. `success` which is returned in certain situations depending on the node type, but usually indicating that the operation they are in charge of was completed. Tasks usually return `success` when they execute/complete successfully, sequences return `success` when the entire sequence is executed successfully, and so on. Since this is the default status of all nodes, a node is in `success` status if it has never been ticked, or if it has been halted (using `halt!`).
197
+ 2. `running` which is returned by nodes that are currently executing.
198
+ 3. `failure` which is returned to signal that execution failed.
199
+
200
+ Each type of node has different logic for returning these three values.
201
+
202
+ ### Storage
203
+
204
+ #### Global context
205
+
206
+ Just like you would have access to local variables inside an `if-else` block, a behavior tree has a data structure called `context` which it can operate on. If this didn't exist, there would be no data to work with, and/or use to take decisions. In other implementations, it's called *blackboard*, a concept which refers to a global memory.
207
+
208
+ You can use any Ruby object as context, but the easiest way to get started is to pass a `Hash` object.
209
+
210
+ This example shows how to initialize a tree's context with an empty `Hash`, and when assigning it, the tree will propagate the object to all nodes of the tree.
211
+
212
+ ```ruby
213
+ my_tree.context = {}
214
+ ```
215
+
216
+ The method `context` is available on all nodes, which provides a reference to this object.
217
+
218
+ #### Per-node storage
219
+
220
+ Arbitrary data can also be stored on a per node basis.
221
+
222
+ ```ruby
223
+ node_instance[:arbitrary_variable] = :hello_world
224
+ ```
225
+
226
+ The preferred way to store node-scoped data is to use vanilla Ruby `@instance_variables`, but this is only possible if you are creating a custom class, and if the node manipulates its own data. Instead, a parent node may use this mechanism to manipulate its children data when necessary.
227
+
228
+ **Note:** `node_instance` is **not** a `Hash` object, but instead a Node object. This is a `[]` and `[]=` operator overload.
229
+
230
+ ### Types of nodes
231
+
232
+ #### Task nodes
233
+
234
+ A task node is the only type of leaf node, and it usually executes an arbitrary procedure.
235
+
236
+ Tasks would usually return `success` or `failure` when they complete, and return `running` if they haven't completed yet.
237
+
238
+ **Example #1: Custom task class**
239
+
240
+ ```ruby
241
+ class DecreaserTask < BehaviorTree::Task
242
+ def on_tick
243
+ context[:my_number] -= 1
244
+ status.success!
245
+ end
246
+ end
247
+ ```
248
+
249
+ Learn [how to register your custom nodes](#add-custom-nodes-to-the-dsl) so they become available in the DSL.
250
+
251
+ **Example #2: Insert inline logic in the DSL**
252
+
253
+ ```ruby
254
+ BehaviorTree::Builder.build do
255
+ task do
256
+ context[:my_number] -= 1
257
+ status.success!
258
+ end
259
+ end
260
+ ```
261
+
262
+ #### Control nodes
263
+
264
+ A control node decides the flow of the execution. In simpler words, it uses a certain logic to decide which branch to execute. It could be one branch, multiple branches, or all of them.
265
+
266
+ A control node cannot be a leaf (i.e. it must have children).
267
+
268
+ By default, there are two types of control nodes, and custom ones can be easily created. (See: [Examples of custom control nodes](#custom-control-node)).
269
+
270
+ 1. **Sequence:**
271
+ a. Begins executing the first child node.
272
+ b. If the child returns `running`, the sequence also returns `running`.
273
+ c. If child returns `failure`, all children are halted, and the sequence returns `failure`.
274
+ d. If the child returns `success`, it continues with the next child node executing the same logic.
275
+ 2. **Selector:**
276
+ a. Begins executing the first child node.
277
+ b. If the child returns `running`, the sequence also returns `running`.
278
+ c. If child returns `success`, halt all children and return `success`.
279
+ d. If child returns `failure`, then continue with the next child.
280
+ e. If no node ever returned `success`, then return `failure`.
281
+
282
+ [Learn about "halting nodes" and what it means.](#halt-)
283
+
284
+ When a control node is ticked, by default it traverses children and ticks them using this logic:
285
+
286
+ 1. If at least one node is `running`, then begin from that one, in order.
287
+ 2. If no node is `running`, then traverse all nodes starting from the first one, in order.
288
+
289
+ **Example #1: Creating a sequence and a selector**
290
+
291
+ ```ruby
292
+ sequence = BehaviorTree::Sequence.new
293
+ selector = BehaviorTree::Selector.new
294
+
295
+ 3.times { sequence << BehaviorTree::Task.new }
296
+ 2.times { selector << BehaviorTree::Task.new }
297
+
298
+ # Make the selector a child of the sequence
299
+ sequence << selector
300
+
301
+ my_tree = BehaviorTree::Builder.build do
302
+ chain sequence
303
+ end
304
+
305
+ my_tree.print
306
+ # Output:
307
+ # ∅
308
+ # └─sequence success (0 ticks)
309
+ # ├─task success (0 ticks)
310
+ # ├─task success (0 ticks)
311
+ # ├─task success (0 ticks)
312
+ # └─selector success (0 ticks)
313
+ # ├─task success (0 ticks)
314
+ # └─task success (0 ticks)
315
+ ```
316
+
317
+ #### Decorators and condition nodes
318
+
319
+ A decorator can have only one child, and acts as a modifier for its child.
320
+
321
+ By default the decorator nodes present in this library are:
322
+
323
+ | Name | Class | DSL | Description |
324
+ | --- | --- | --- | --- |
325
+ | Condition | `BehaviorTree::Decorators::Condition` | `condition` or `cond` | Ticks its child only if the condition succeeds. If not, returns `failure` (and has no effect whatsoever on the child). If the condition succeeds, it ticks the child and returns its status. |
326
+ | Force Failure | `BehaviorTree::Decorators::ForceFailure` | `force_failure` | Ticks the child, and always returns `failure` regardless of what the child returned. |
327
+ | Force Success | `BehaviorTree::Decorators::ForceSuccess` | `force_success`| Ticks the child, and always returns `success` regardless of what the child returned. |
328
+ | Inverter | `BehaviorTree::Decorators::Inverter` | `inverter` or `inv` | Returns `running` if the child returns `running`, and returns the opposite (inverted status) when the child returns `failure` or `success`. |
329
+ | Repeater | `BehaviorTree::Decorators::Repeater` | `repeater` or `rep` | Ticks the child again N times while it's returning `success`. |
330
+ | Retry | `BehaviorTree::Decorators::Retry` | `re_try` | Ticks the child again N times while it's returning `failure`. |
331
+
332
+ **Example #1: Creating a tree with some decorators**
333
+
334
+ ```ruby
335
+ my_tree = BehaviorTree::Builder.build do
336
+ inv {
337
+ sel {
338
+ task -> { puts 'Task 1' }
339
+ force_failure { task -> { puts 'Task 2' } }
340
+ task -> { puts 'Task 3' }
341
+ }
342
+ }
343
+ task
344
+ end
345
+ ```
346
+
347
+ ## Create custom nodes
348
+
349
+ ### Custom task
350
+
351
+ There are two main ways to create tasks.
352
+
353
+ 1. By instantiating `BehaviorTree::TaskBase` (or its alias `BehaviorTree::Task`) and passing a lambda or block.
354
+ 2. By subclassing `BehaviorTree::TaskBase` and overriding the `on_tick` method with the desired procedure to execute.
355
+
356
+ Let's see examples of both.
357
+
358
+ **Example #1 Empty task (i.e. does nothing)**
359
+
360
+ ```ruby
361
+ task = BehaviorTree::TaskBase.new
362
+ ```
363
+
364
+ **Example #2 Task that returns status based on the context**
365
+
366
+ ```ruby
367
+ task = BehaviorTree::TaskBase.new do
368
+ if context[:a] > 1
369
+ status.success!
370
+ elsif context[:a] < -1
371
+ status.failure!
372
+ else
373
+ status.running!
374
+ end
375
+
376
+ context[:a] += 1
377
+ end
378
+
379
+ # Initialize context.
380
+ task.context = { a: -2 }
381
+
382
+ task.tick!; task.status #=> failure
383
+ task.tick!; task.status #=> running
384
+ task.tick!; task.status #=> running
385
+ task.tick!; task.status #=> running
386
+ task.tick!; task.status #=> success
387
+ task.tick!; task.status #=> success
388
+ ```
389
+
390
+ **Example #3: Same as #2, but using lambdas instead**
391
+
392
+ When using lambdas instead of normal `Proc` (or blocks), you must pass the `context` and `node` arguments if you want to access their data. Both parameters are optional.
393
+
394
+ ```ruby
395
+ task = BehaviorTree::TaskBase.new -> (context, node) {
396
+ if context[:a] > 1
397
+ # 'node' is the 'self' node (i.e. the task node).
398
+ node.status.success!
399
+ elsif context[:a] < -1
400
+ node.status.failure!
401
+ else
402
+ node.status.running!
403
+ end
404
+
405
+ context[:a] += 1
406
+ }
407
+ ```
408
+
409
+ **Example #4: Create a custom task class**
410
+
411
+ In this task not only we override the `on_tick` method, but also the constructor, and now this task needs a parameter to be instantiated.
412
+
413
+ ```ruby
414
+ class CustomTaskWithConstructorArgument < BehaviorTree::Task
415
+ def initialize(inc)
416
+ super()
417
+ @inc = inc
418
+ end
419
+
420
+ def on_tick
421
+ context[:a] += @inc
422
+ context[:a].even? ? status.success! : status.running!
423
+ end
424
+ end
425
+
426
+ task = CustomTaskWithConstructorArgument.new(3)
427
+
428
+ initial_context = { a: 3 }
429
+
430
+ task.context = initial_context
431
+
432
+ task.tick!
433
+
434
+ task.status.to_sym # => :success
435
+
436
+ initial_context # => {:a=>6}
437
+ ```
438
+
439
+ ### Custom control node
440
+
441
+ Control nodes use a concept called `traversal strategy`, which refers to the way the nodes are iterated.
442
+
443
+ The default strategy (named `prioritize_running`) is to:
444
+ 1. If there's at least one child running, then begin (or actually, resume) from there, and in order.
445
+ 2. If no child is running, then traverse all nodes starting from the first one, in order.
446
+
447
+ In order to change the strategy used by a class, you must execute (inside the class, not instance) the method `children_traversal_strategy`, and specify which strategy to use. The strategy is simply the name of a method that returns an `Enumerable` (an array, etc). This `Enumerable` must have the children to traverse.
448
+
449
+ Not executing `children_traversal_strategy` will make your class use the default strategy (i.e. `prioritize_running`).
450
+
451
+ **Example #1: Shuffle (random) traversal**
452
+
453
+ In this example, the `Shuffle` class changes only the traversal strategy, but doesn't change the way `Sequence` works. In other words, this is a sequence with random order.
454
+
455
+ ```ruby
456
+ class Shuffle < BehaviorTree::Sequence
457
+ children_traversal_strategy :shuffle
458
+
459
+ private
460
+
461
+ # Memoize shuffled order. Keep the same order while the sequence is running.
462
+ def shuffle
463
+ @shuffled_order ||= @children.shuffle
464
+ running_idx = @shuffled_order.find_index { |node| node.status.running? }.to_i
465
+
466
+ @shuffled_order[running_idx..]
467
+ end
468
+
469
+ # Un-memoize the shuffled order so that it's
470
+ # shuffled again (everytime the status goes from not-running to running).
471
+ def on_started_running
472
+ @shuffled_order = nil
473
+ end
474
+ end
475
+ ```
476
+
477
+ **Example #2: Overriding the control flow logic**
478
+
479
+ The example above defines a new traversal strategy, but keeps the same logic as a vanilla `Sequence`. In the following example, we continue to use the strategy defined above, but create a different control flow logic.
480
+
481
+ Control nodes execute their control flow logic in the `on_tick` method, so this is the method that must be overriden.
482
+
483
+ ```ruby
484
+ # Return success if all either succeeded or failed. Otherwise return failure.
485
+ # (Similar to other control nodes, return running when a child is running.)
486
+ class AllOrNothing < ControlNodeBase
487
+ children_traversal_strategy :shuffle
488
+
489
+ def on_tick
490
+ success_count = 0
491
+ fail_count = 0
492
+
493
+ # This loop iterates children using the shuffle strategy, AND ticks each child.
494
+ tick_each_children do |child|
495
+ return status.running! if child.status.running?
496
+
497
+ # Regardless of whether the child succeeded or failed,
498
+ # continue with the next child. Just store whether it succeeded or not.
499
+ success_count += 1 if child.status.success?
500
+ fail_count += 1 if child.status.failure?
501
+
502
+ # Can be optimized to return failure as soon as it encounters at least one
503
+ # that failed and at least one that succeeded.
504
+ end
505
+
506
+ # Set self node and all children to success.
507
+ halt!
508
+
509
+ # Status is already success from the halt! above. Do nothing.
510
+ return if success_count == children.count || fail_count == children.count
511
+
512
+ # Else return fail. Results are not "all success" or "all failure".
513
+ status.failure!
514
+ end
515
+
516
+ # From here it's omitted. Copy code from example above.
517
+
518
+ def shuffle
519
+ # ...
520
+ end
521
+
522
+ def on_started_running
523
+ # ...
524
+ end
525
+ end
526
+ ```
527
+
528
+ Note that under the hood, `tick_each_children` uses the strategy defined (i.e. `shuffle` method), and traverses its children while also ticking them. You don't need to send `tick!` manually to the child. The code in the block given to `tick_each_children` is executed *right after* the child is ticked.
529
+
530
+ ### Custom decorator
531
+
532
+ **Note:** Condition nodes are also a type of decorator, but they are covered separately here: [Custom condition nodes](#custom-condition).
533
+
534
+ Here's an example of how to create a custom decorator. Simply inherit from `BehaviorTree::Decorators::DecoratorBase` and override any or both of these two methods, `decorate` and `status_map`.
535
+
536
+ ```ruby
537
+ class CustomDecorator < BehaviorTree::Decorators::DecoratorBase
538
+ protected
539
+
540
+ def decorate
541
+ # Additional logic to be executed when the node is ticked.
542
+ end
543
+
544
+ # This method must change the self node status in function
545
+ # of the child status. The default behavior is to copy its status.
546
+ # The status is mapped at the end of the tick lifecycle.
547
+ def status_map
548
+ self.status = child.status
549
+ end
550
+ end
551
+ ```
552
+
553
+ ### Custom condition
554
+
555
+ Creating a condition as a class. The `should_tick?` method must be overriden, and it must return a `boolean` value.
556
+
557
+ ```ruby
558
+ class CustomCondition < BehaviorTree::Decorators::Condition
559
+ def should_tick?
560
+ context[:a] > -1
561
+ end
562
+ end
563
+ ```
564
+
565
+ Or create condition nodes using inline lambdas in the DSL. When using the DSL, before starting the block (i.e. where the child is defined), you must pass a `lambda` which receives two parameters (both optional), `context` and `node` (which is the `self` of the condition node).
566
+
567
+ ```ruby
568
+ my_tree = BehaviorTree::Builder.build do
569
+ condition ->(context, node) { context[:a].positive? } do
570
+ task # The decorated task (condition node's child)
571
+ end
572
+ end
573
+
574
+ # Define tree's context data.
575
+ my_tree.context = { a: -1 }
576
+
577
+ # Tick the tree once.
578
+ my_tree.tick!
579
+
580
+ # Inspect the tree. Condition failed, and task node hasn't been touched.
581
+ my_tree.print
582
+ # Output:
583
+ # ∅
584
+ # └─condition failure (1 ticks)
585
+ # └─task success (0 ticks)
586
+ ```
587
+
588
+ **Note:** Other behavior tree implementations prefer the use of `sequence` control nodes, and placing conditional nodes as leaves, but with the role of simply returning `failure` or `success`. Since sequences execute the next node only if the previous one succeeded, it would also behave like a conditional node, preventing the next nodes in the sequence from executing if the condition failed. In this implementation, however, both patterns are available and you are free to choose which one to use.
589
+
590
+ ## Node API
591
+
592
+ ### Status
593
+
594
+ Every instance of node classes have a status object, where you can execute the following methods.
595
+
596
+ **Setters**
597
+
598
+ ```ruby
599
+ node.status.running!
600
+ node.status.success!
601
+ node.status.failure!
602
+
603
+ node.status = other_node.status # Copy status from other node
604
+ ```
605
+
606
+ **Querying status**
607
+
608
+ ```ruby
609
+ node.status.running? # => boolean
610
+ node.status.success? # => boolean
611
+ node.status.failure? # => boolean
612
+ ```
613
+
614
+ **Accessing previous status**
615
+
616
+ The previous status is also stored inside a node. It can be used in `on_status_change` to trigger a certain action in function of the current and previous state (See: [Status related callbacks and hooks](#status-related-callbacks-and-hooks)).
617
+
618
+ ```ruby
619
+ node.status.success!
620
+
621
+ node.status.running!
622
+
623
+ node.status.to_sym # => :running
624
+
625
+ node.prev_status.to_sym # => :success
626
+ ```
627
+
628
+ **Warning:** Don't modify the `prev_status` manually. It's updated automatically.
629
+
630
+ ### tick!
631
+
632
+ As you have seen in other examples, all nodes have a `tick!` method, which as the name says, ticks the node, and propagates it down to its children (if any).
633
+
634
+ The first thing it does is always setting the node to `running`. After this, what happens depends on the type of node. You can learn more about each node type and how to override their behavior in: [Create custom nodes](#create-custom-nodes).
635
+
636
+ ### halt!
637
+
638
+ This simply sets the node to `success`, and when a node has children, it executes `halt!` on all children as well, which propagates the `halt!` action down the tree.
639
+
640
+ This method is usually used when you want to reset the node and its children's status. In control nodes, since they follow the strategy of *"resume from the running nodes, if there is any"* is used by default, it's imperative to execute `halt!` once the sequence/selector has finished, so it can start again from the first node (unless you override this logic in a custom control node). Other than that, you may decide not to halt them if it's not necessary.
641
+
642
+ ### Status related callbacks and hooks
643
+
644
+ **on_status_change**
32
645
 
33
- $ gem install ruby-behavior-tree
646
+ This method is executed everytime the node status changes. It's only triggered when there's a change (i.e. previous value and next value are different).
34
647
 
35
- ## Usage
648
+ Therefore, the following code only triggers the callback once:
36
649
 
37
- TODO: Write usage instructions here
650
+ ```ruby
651
+ # Current status is success.
652
+ node.status.success? # => true
653
+
654
+ node.status.failure! # Triggers the callback.
655
+ node.status.failure! # Does not trigger.
656
+ node.status.failure!
657
+ ```
658
+
659
+ ```ruby
660
+ class RandomStatusTask < BehaviorTree::Task
661
+ def on_tick
662
+ puts 'Being ticked...'
663
+ possible_status = [:running, :success, :failure]
664
+ status.send("#{possible_status.sample}!")
665
+ end
666
+
667
+ def on_status_change
668
+ prev = prev_status
669
+ curr = status
670
+
671
+ puts "My status went from #{prev.to_sym} to #{curr.to_sym} (tick_count = #{tick_count})"
672
+ end
673
+ end
674
+
675
+ task = RandomStatusTask.new
676
+
677
+ 5.times { task.tick! }
678
+
679
+ # Output:
680
+ # My status went from success to running (tick_count = 1)
681
+ # Being ticked...
682
+ # My status went from running to failure (tick_count = 1)
683
+ # My status went from failure to running (tick_count = 2)
684
+ # Being ticked...
685
+ # My status went from running to failure (tick_count = 2)
686
+ # My status went from failure to running (tick_count = 3)
687
+ # Being ticked...
688
+ # My status went from running to success (tick_count = 3)
689
+ # My status went from success to running (tick_count = 4)
690
+ # Being ticked...
691
+ # My status went from running to success (tick_count = 4)
692
+ # My status went from success to running (tick_count = 5)
693
+ # Being ticked...
694
+ # My status went from running to failure (tick_count = 5)
695
+ ```
696
+
697
+ In the output of the example above, one thing to note is that the first line (change from `success` to `running`) happens because `tick!` **immediately and always** sets the node to `running`. This happens even before the task logic (`on_tick` method) has been executed.
698
+
699
+ The second line of the output is the `puts` of the actual task logic. The third line happens as a result of the task logic changing the status, therefore triggering a `on_status_change` call.
700
+
701
+ **on_started_running**
702
+
703
+ Similar to `on_status_change`, but only triggers when the node has been set to `running` (changed from a status other than `running`.
704
+
705
+ In some implementations, this is called `initialization`.
706
+
707
+ **on_finished_running**
708
+
709
+ Similar to `on_status_change`, but only triggers when the node has been set to a status other than `running`.
710
+
711
+ In some implementations, this is called `shutdown`.
712
+
713
+ ## Add custom nodes to the DSL
714
+
715
+ You can register new nodes to be used in the DSL, take for example the following code:
716
+
717
+ ```ruby
718
+ BehaviorTree::Builder.register(
719
+ :my_control_node,
720
+ 'CustomNodes::MyControlNode',
721
+ children: :multiple
722
+ )
723
+ ```
724
+
725
+ When using `BehaviorTree::Builder#register`, you must supply three arguments, the keyword to be used in the DSL, the class name, and an optional parameter indicating how many children your node must have.
726
+
727
+ The possible values for the `children:` argument are:
728
+
729
+ 1. `none` when your node is a leaf node (i.e. task). Default value if not specified.
730
+ 2. `multiple` when your node is a control node.
731
+ 3. `single` when your node is a decorator, conditional, etc.
732
+
733
+ Next, you can use the registered node in the DSL.
734
+
735
+ ```ruby
736
+ BehaviorTree::Builder.build do
737
+ my_control_node do
738
+ # The rest of the tree here.
739
+ end
740
+ end
741
+ ```
742
+
743
+ You can also define an alias for your node:
744
+
745
+ ```ruby
746
+ # First argument is original key (existing one).
747
+ # Second argument is the new alias.
748
+ BehaviorTree::Builder.register_alias(:my_control_node, :my_ctrl_node)
749
+ ```
750
+
751
+ This way, both `my_control_node` and `my_ctrl_node` can be used in the DSL.
752
+
753
+ ## Troubleshoot and debug your trees
38
754
 
39
- ## Development
755
+ Sometimes you may run into issues with your tree, and it's generally difficult to debug a recursive structure, but here are a few ways to make it a bit easier to debug and troubleshoot.
40
756
 
41
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
757
+ ### Detecting cycles
758
+
759
+ You can check if your tree has cycles by executing the following:
760
+
761
+ ```ruby
762
+ my_tree.cycle?
763
+ # => false
764
+ ```
765
+
766
+ ### Checking nodes are all unique objects
767
+
768
+ Sometimes you might accidentally chain the same node to a parent node. When this happens, the node will have multiple parents. Since this might be a desired situation in some cases, nodes are not cloned automatically by default.
769
+
770
+ You can check for repeated nodes using the following methods:
771
+
772
+ ```ruby
773
+ my_tree.uniq_nodes?
774
+ # => true
775
+ ```
776
+
777
+ Or obtain the actual repeated nodes:
778
+
779
+ ```ruby
780
+ my_tree.repeated_nodes
781
+ # => <Set: { ... repeated nodes ... }>
782
+ ```
783
+
784
+ **Note:** Object equality is tested using `Set#include?`.
785
+
786
+ ### Visualize a tree
787
+
788
+ Printing the tree is not only useful for verifying it has the desired structure, but also for detecting various issues.
789
+
790
+ ```ruby
791
+ 200.times { my_tree.tick! }
792
+
793
+ my_tree.print
794
+ ```
795
+
796
+ The above code generates the following output:
797
+
798
+ <p align="center">
799
+ <img src="https://github.com/FeloVilches/ruby-behavior-tree/blob/main/assets/printed_tree.jpg?raw=true" width="400"/>
800
+ </p>
801
+
802
+ In the example above, you can see that the bottom nodes haven't been ticked at all. Node starvation might occur for various reasons, such as having a `force_failure` node as one of the children of a `sequence` (the nodes after the `force_failure` would all be prevented from executing).
803
+
804
+ Printing can also be useful in detecting bugs in your custom nodes.
805
+
806
+ ## Miscellaneous
807
+
808
+ ### Generate random trees
809
+
810
+ Mostly created for debugging and testing various trees in development mode, you can generate random trees by executing the following code:
811
+
812
+ ```ruby
813
+ random_tree = BehaviorTree::Builder.build_random_tree
814
+
815
+ 100.times { random_tree.tick! }
816
+ random_tree.print
817
+ # Output:
818
+ # ∅
819
+ # └─selector running (100 ticks)
820
+ # ├─forcefailure failure (76 ticks)
821
+ # │ └─repeater success (76 ticks)
822
+ # │ └─retry success (95 ticks)
823
+ # │ └─task success (114 ticks)
824
+ # ├─inverter running (47 ticks)
825
+ #
826
+ # (the rest is omitted)
827
+ ```
828
+
829
+ Or make it smaller/larger by tweaking the optional argument `recursion_amount`:
830
+
831
+ ```ruby
832
+ random_tree = BehaviorTree::Builder.build_random_tree(recursion_amount: 9)
833
+ ```
42
834
 
43
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
835
+ Keep in mind this is only for development purposes, and the generated trees don't make sense at all. Also, only vanilla default nodes are used. Conditional nodes fail or succeed randomly, and task nodes generate random return values as well.
44
836
 
45
837
  ## Contributing
46
838
 
47
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ruby-behavior-tree. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/ruby-behavior-tree/blob/master/CODE_OF_CONDUCT.md).
839
+ Bug reports and pull requests are welcome on GitHub at https://github.com/FeloVilches/ruby-behavior-tree. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/FeloVilches/ruby-behavior-tree/blob/main/CODE_OF_CONDUCT.md).
48
840
 
49
841
 
50
842
  ## License
@@ -53,4 +845,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
53
845
 
54
846
  ## Code of Conduct
55
847
 
56
- Everyone interacting in the Ruby::Behavior::Tree project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/ruby-behavior-tree/blob/master/CODE_OF_CONDUCT.md).
848
+ Everyone interacting in the Behavior Tree project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/FeloVilches/ruby-behavior-tree/blob/main/CODE_OF_CONDUCT.md).