diff --git a/build.gradle.kts b/build.gradle.kts index c95bec7..85e91b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,6 @@ plugins { kotlin("jvm") version "1.8.10" + kotlin("plugin.serialization") version "1.8.10" } group = "com.tpcly" @@ -10,6 +11,7 @@ repositories { } dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") testImplementation(kotlin("test")) testImplementation("io.mockk:mockk:1.13.7") } diff --git a/src/main/kotlin/com/tpcly/behaviourtree/BehaviourTreeDslMarker.kt b/src/main/kotlin/com/tpcly/behaviourtree/BehaviourTreeDslMarker.kt deleted file mode 100644 index d8a0d33..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/BehaviourTreeDslMarker.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.tpcly.behaviourtree - -@DslMarker -annotation class BehaviourTreeDslMarker \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Conditional.kt b/src/main/kotlin/com/tpcly/behaviourtree/Conditional.kt new file mode 100644 index 0000000..90f9e3f --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/Conditional.kt @@ -0,0 +1,9 @@ +package com.tpcly.behaviourtree + +abstract class Conditional : TreeNode { + abstract fun validate(): Boolean + + override fun execute(): TreeNodeStatus { + return TreeNodeStatus.fromBoolean(validate()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt new file mode 100644 index 0000000..4f067a0 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -0,0 +1,16 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.Serializable + +@Serializable +data class Inverter( + override val child: TreeNode, +) : TreeNode.Decorator { + override fun execute(): TreeNodeStatus { + return when (val result = child.execute()) { + TreeNodeStatus.SUCCESS -> TreeNodeStatus.FAILURE + TreeNodeStatus.FAILURE -> TreeNodeStatus.SUCCESS + else -> result + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt new file mode 100644 index 0000000..1d1126d --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -0,0 +1,29 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.Serializable + +@Serializable +data class RepeatUntil( + override val child: TreeNode, + val targetStatus: TreeNodeStatus = TreeNodeStatus.SUCCESS, + val limit: Int = 10, +) : TreeNode.Decorator { + override fun execute(): TreeNodeStatus { + var iteration = 0 + var currentStatus: TreeNodeStatus + + do { + currentStatus = child.execute() + iteration++ + } while ( + currentStatus != targetStatus && + currentStatus != TreeNodeStatus.ABORT && + iteration < limit + ) + + return when { + iteration >= limit -> TreeNodeStatus.FAILURE + else -> currentStatus + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt new file mode 100644 index 0000000..851e597 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -0,0 +1,26 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.Serializable + +@Serializable +data class Selector( + val executionOrder: TreeExecutionOrder, + override val children: List, +) : TreeNode.Composite { + override fun execute(): TreeNodeStatus { + val children = when (executionOrder) { + TreeExecutionOrder.RANDOM -> children.shuffled() + else -> children + } + + for (child in children) { + val result = child.execute() + + if (result == TreeNodeStatus.SUCCESS || result == TreeNodeStatus.ABORT) { + return result + } + } + + return TreeNodeStatus.FAILURE + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt new file mode 100644 index 0000000..cea29d6 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -0,0 +1,26 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.Serializable + +@Serializable +data class Sequencer( + val executionOrder: TreeExecutionOrder, + override val children: List, +) : TreeNode.Composite { + override fun execute(): TreeNodeStatus { + val children = when (executionOrder) { + TreeExecutionOrder.RANDOM -> children.shuffled() + else -> children + } + + for (child in children) { + val result = child.execute() + + if (result == TreeNodeStatus.FAILURE || result == TreeNodeStatus.ABORT) { + return result + } + } + + return TreeNodeStatus.SUCCESS + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Status.kt b/src/main/kotlin/com/tpcly/behaviourtree/Status.kt deleted file mode 100644 index 57819a1..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/Status.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tpcly.behaviourtree - -/** - * Enum of all possible tree result statuses - */ -enum class Status { - SUCCESS, - FAILURE, - RUNNING, - ABORT; - - companion object { - fun fromCondition(condition: Boolean): Status = if (condition) SUCCESS else FAILURE - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt new file mode 100644 index 0000000..2084f09 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -0,0 +1,13 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.Serializable + +@Serializable +data class Succeeder( + override val child: TreeNode, +) : TreeNode.Decorator { + override fun execute(): TreeNodeStatus { + child.execute() + return TreeNodeStatus.SUCCESS + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeBuilder.kt new file mode 100644 index 0000000..f840f65 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeBuilder.kt @@ -0,0 +1,57 @@ +package com.tpcly.behaviourtree + +object TreeBuilder { + /** + * Pre-defined leaf nodes + */ + fun action(execute: () -> TreeNodeStatus): TreeNode = object : TreeNode { + override fun execute(): TreeNodeStatus = execute() + } + + fun conditional(validate: () -> Boolean): TreeNode = object : TreeNode { + override fun execute(): TreeNodeStatus = TreeNodeStatus.fromBoolean(validate()) + } + + fun perform(execute: () -> Unit): TreeNode = object : TreeNode { + override fun execute(): TreeNodeStatus { + execute() + return TreeNodeStatus.SUCCESS + } + } + + /** + * Composite nodes + */ + fun selector( + executionOrder: TreeExecutionOrder = TreeExecutionOrder.IN_ORDER, + init: TreeNodeCompositeBuilder.() -> Unit, + ): Selector { + val builder = TreeNodeCompositeBuilder().apply(init) + return Selector(executionOrder, builder.build()) + } + + fun sequencer( + executionOrder: TreeExecutionOrder = TreeExecutionOrder.IN_ORDER, + init: TreeNodeCompositeBuilder.() -> Unit, + ): Sequencer { + val builder = TreeNodeCompositeBuilder().apply(init) + return Sequencer(executionOrder, builder.build()) + } + + /** + * Decorator nodes + */ + fun inverter(init: () -> TreeNode) = Inverter(init()) + + fun TreeNode.inverted() = Inverter(this) + + fun succeeder(init: () -> TreeNode) = Succeeder(init()) + + fun failer(init: () -> TreeNode) = succeeder(init).inverted() + + fun repeatUntil( + status: TreeNodeStatus = TreeNodeStatus.SUCCESS, + limit: Int = 10, + init: () -> TreeNode, + ) = RepeatUntil(init(), status, limit) +} diff --git a/src/main/kotlin/com/tpcly/behaviourtree/ExecutionOrder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeExecutionOrder.kt similarity index 64% rename from src/main/kotlin/com/tpcly/behaviourtree/ExecutionOrder.kt rename to src/main/kotlin/com/tpcly/behaviourtree/TreeExecutionOrder.kt index e2b4a62..315a0cf 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/ExecutionOrder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeExecutionOrder.kt @@ -1,6 +1,6 @@ package com.tpcly.behaviourtree -enum class ExecutionOrder { +enum class TreeExecutionOrder { IN_ORDER, RANDOM } \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt new file mode 100644 index 0000000..7f37434 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt @@ -0,0 +1,21 @@ +package com.tpcly.behaviourtree + +interface TreeNode { + fun execute(): TreeNodeStatus + + interface Composite : TreeNode { + val children: List + } + + interface Decorator : TreeNode { + val child: TreeNode + } + + interface Sub : TreeNode { + val root: TreeNode + + override fun execute(): TreeNodeStatus { + return root.execute() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeCompositeBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeCompositeBuilder.kt new file mode 100644 index 0000000..5e5598b --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeCompositeBuilder.kt @@ -0,0 +1,17 @@ +package com.tpcly.behaviourtree + +class TreeNodeCompositeBuilder { + private val children: MutableList = mutableListOf() + + operator fun TreeNode.unaryPlus() { + children += this + } + + operator fun TreeNode.unaryMinus() { + children -= this + } + + fun build(): List { + return children.toList() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeResult.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeResult.kt deleted file mode 100644 index 7074962..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeResult.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode - -/** - * This class holds the result for each executed node and its children - * - * @property origin the origin node of the result - * @property status the status of the result - * @property children the children of the result - */ -data class TreeNodeResult( - val origin: TreeNode, - val status: Status, - val children: List? = null -) { - fun stackTrace(indent: Int = 0): Sequence = sequence { - yield("${"\t".repeat(indent)}> ${origin.javaClass.simpleName} :${origin.name} ${status.name}") - - // Append children if they exist - children?.forEach { - yieldAll(it.stackTrace(indent + 1)) - } - } - - companion object { - fun success(parent: TreeNode, children: List? = null) = TreeNodeResult(parent, Status.SUCCESS, children) - - fun failure(parent: TreeNode, children: List? = null) = TreeNodeResult(parent, Status.FAILURE, children) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeStatus.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeStatus.kt new file mode 100644 index 0000000..94b9d26 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeStatus.kt @@ -0,0 +1,11 @@ +package com.tpcly.behaviourtree + +enum class TreeNodeStatus { + SUCCESS, + FAILURE, + ABORT; + + companion object { + fun fromBoolean(value: Boolean): TreeNodeStatus = if (value) SUCCESS else FAILURE + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt deleted file mode 100644 index 9bf2957..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.tpcly.behaviourtree.node - -import com.tpcly.behaviourtree.ExecutionOrder -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.composite.Selector -import com.tpcly.behaviourtree.node.composite.Sequencer -import com.tpcly.behaviourtree.node.decorator.* -import com.tpcly.behaviourtree.node.task.Action -import com.tpcly.behaviourtree.node.task.Conditional -import com.tpcly.behaviourtree.node.task.Perform - -/** - * Task nodes - */ -fun run( - name: String = "", - action: () -> Status, -) = object : Action(name) { - override fun action(): Status = action() -} - -fun conditional( - name: String = "", - validate: () -> Boolean, -) = object : Conditional(name) { - override fun validate(): Boolean = validate() -} - -fun perform( - name: String = "", - action: () -> Unit, -) = object : Perform(name) { - override fun action() = action() -} - -/** - * Decorator nodes - */ -fun gate( - name: String = "", - validate: () -> Boolean, - init: () -> TreeNode, -) = Gate(name, init(), validate) - -fun inverter( - name: String = "", - init: () -> TreeNode, -) = Inverter(name, init()) - -fun succeeder( - name: String = "", - init: () -> TreeNode, -) = Succeeder(name, init()) - -fun repeatWhen( - name: String = "", - validate: () -> Boolean, - limit: Int = 10, - init: () -> TreeNode, -) = RepeatWhen(name, limit, init(), validate) - -fun repeatUntil( - validate: (TreeNodeResult) -> Boolean, - limit: Int = 10, - name: String = "", - init: () -> TreeNode, -) = RepeatUntil(name, limit, init(), validate) - -fun repeatUntil( - status: Status, - limit: Int = 10, - name: String = "", - init: () -> TreeNode, -) = RepeatUntil(name, limit, init()) { it.status == status } - -/** - * Composite nodes - */ - -fun selector( - name: String = "", - executionOrder: ExecutionOrder = ExecutionOrder.IN_ORDER, - init: Selector.() -> Unit, -) = initNode(Selector(name, executionOrder), init) - -fun sequencer( - name: String = "", - executionOrder: ExecutionOrder = ExecutionOrder.IN_ORDER, - init: Sequencer.() -> Unit, -) = initNode(Sequencer(name, executionOrder), init) - -internal fun initNode(node: T, init: T.() -> Unit): T { - node.init() - return node -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Extensions.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Extensions.kt deleted file mode 100644 index 60b8d96..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Extensions.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.tpcly.behaviourtree.node - -import com.tpcly.behaviourtree.node.decorator.Inverter - -fun TreeNode.inverted() = Inverter(name, this) \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt deleted file mode 100644 index 94b3c06..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tpcly.behaviourtree.node - -import com.tpcly.behaviourtree.TreeNodeResult - -/** - * Interface for a node of an executable tree - */ -interface TreeNode { - /** - * A descriptive name for the tree node - */ - val name: String - - /** - * Executes a certain behaviour of the node - * @return the result of the execution, including relevant children results - */ - fun execute(): TreeNodeResult -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Composite.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Composite.kt deleted file mode 100644 index 587dc87..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Composite.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.tpcly.behaviourtree.node.composite - -import com.tpcly.behaviourtree.BehaviourTreeDslMarker -import com.tpcly.behaviourtree.node.TreeNode - -/** - * This is an abstract class for a type of tree node called the composite node - * Composite nodes are used to process one or more children in a sequence depending on the implementation - * - * @property name a descriptive name of the composites' usage - */ -@BehaviourTreeDslMarker -sealed interface Composite : TreeNode { - val children: MutableList - - operator fun TreeNode.unaryPlus() { - children += this - } - - operator fun TreeNode.unaryMinus() { - children -= this - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Selector.kt deleted file mode 100644 index 80cf85b..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Selector.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tpcly.behaviourtree.node.composite - -import com.tpcly.behaviourtree.ExecutionOrder -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A composite node which executes its child nodes in order until one succeeds or all fail, similar to an `or` operator - * - * @property order the order in which the children should be executed - */ -open class Selector( - override val name: String, - private val order: ExecutionOrder = ExecutionOrder.IN_ORDER, - override val children: MutableList = mutableListOf(), -) : Composite { - override fun execute(): TreeNodeResult { - val children = if (order == ExecutionOrder.RANDOM) { - children.shuffled() - } else { - children - } - - val results = mutableListOf() - - for (child in children) { - val result = child.execute() - results.add(result) - - if (result.status == Status.SUCCESS || result.status == Status.ABORT) { - return TreeNodeResult(this, result.status, results) - } - } - - return TreeNodeResult.failure(this, results) - } -} - diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Sequencer.kt deleted file mode 100644 index 7f536ce..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/composite/Sequencer.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.tpcly.behaviourtree.node.composite - -import com.tpcly.behaviourtree.ExecutionOrder -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A composite node which executes its child nodes in order until one fails or all succeed, similar to an `and` operator - * - * @property order the order in which the children should be executed - */ -open class Sequencer( - override val name: String, - private val order: ExecutionOrder = ExecutionOrder.IN_ORDER, - override val children: MutableList = mutableListOf() -) : Composite { - override fun execute(): TreeNodeResult { - val children = if (order == ExecutionOrder.RANDOM) { - children.shuffled() - } else { - children - } - - val results = mutableListOf() - - for (child in children) { - val result = child.execute() - results.add(result) - - if (result.status == Status.FAILURE || result.status == Status.ABORT) { - return TreeNodeResult(this, result.status, results) - } - } - - return TreeNodeResult.success(this, results) - } -} diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Decorator.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Decorator.kt deleted file mode 100644 index c9959ab..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Decorator.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tpcly.behaviourtree.node.decorator - -import com.tpcly.behaviourtree.BehaviourTreeDslMarker -import com.tpcly.behaviourtree.node.TreeNode - -/** - * This is an abstract class for a type of tree node called the decorator node - * Decorator nodes are used to modify the behaviour of their child node - * - * @property name a descriptive name of the decorators' usage - * @property child the child node which behaviour is modified by the decorator - */ -@BehaviourTreeDslMarker -sealed interface Decorator: TreeNode { - val child: TreeNode -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Gate.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Gate.kt deleted file mode 100644 index 0b0bd7b..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Gate.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.tpcly.behaviourtree.node.decorator - -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A decorator node which executes its child only when a condition is met - * Returns status of the child if executed, otherwise failure - */ -open class Gate( - override val name: String, - override val child: TreeNode, - val validate: () -> Boolean -) : Decorator { - override fun execute(): TreeNodeResult { - if (validate()) { - val result = child.execute() - return TreeNodeResult(this, result.status, listOf(result)) - } - - return TreeNodeResult.failure(this) - } -} diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Inverter.kt deleted file mode 100644 index b5b5ccb..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Inverter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tpcly.behaviourtree.node.decorator - -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A decorator node that inverts the result from its child, success becomes failure and vice-versa - */ -open class Inverter( - override val name: String, - override val child: TreeNode -) : Decorator { - override fun execute(): TreeNodeResult { - val result = child.execute() - val status = when (result.status) { - Status.SUCCESS -> Status.FAILURE - Status.FAILURE -> Status.SUCCESS - else -> result.status - } - - return TreeNodeResult(this, status, listOf(result)) - } -} - diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatUntil.kt deleted file mode 100644 index ad08be9..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatUntil.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tpcly.behaviourtree.node.decorator - -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A decorator node that repeatedly executes its child until the specified condition is met - */ -open class RepeatUntil( - override val name: String, - private val limit: Int, - override val child: TreeNode, - val validate: (result: TreeNodeResult) -> Boolean -) : Decorator { - override fun execute(): TreeNodeResult { - val results = mutableListOf() - var iteration = 0 - - var result: TreeNodeResult - do { - result = child.execute() - results.add(result) - - iteration++ - } while (!validate(result) && result.status != Status.ABORT && iteration < limit) - - val status = if (iteration >= limit) { - Status.FAILURE - } else { - result.status - } - - return TreeNodeResult(this, status, results) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatWhen.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatWhen.kt deleted file mode 100644 index dfd8252..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatWhen.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tpcly.behaviourtree.node.decorator - -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A decorator node that repeatedly executes its child if the specified condition is met - */ -open class RepeatWhen( - override val name: String, - private val limit: Int, - override val child: TreeNode, - val validate: () -> Boolean -) : Decorator { - override fun execute(): TreeNodeResult { - val results = mutableListOf() - var iteration = 0 - - while (validate() && iteration < limit) { - val result = child.execute() - results.add(result) - - if (result.status == Status.ABORT) { - break - } - - iteration++ - } - - val status = if (iteration >= limit) { - Status.FAILURE - } else { - results.lastOrNull()?.status ?: Status.SUCCESS - } - - return TreeNodeResult(this, status, results) - } -} diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Succeeder.kt deleted file mode 100644 index fe0d0e7..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Succeeder.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tpcly.behaviourtree.node.decorator - -import com.tpcly.behaviourtree.TreeNodeResult -import com.tpcly.behaviourtree.node.TreeNode - -/** - * A decorator node that turns the result of its child into success, regardless of what the child returns - */ -class Succeeder( - override val name: String, - override val child: TreeNode -) : Decorator { - override fun execute(): TreeNodeResult { - val result = child.execute() - return TreeNodeResult.success(this, listOf(result)) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Action.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/task/Action.kt deleted file mode 100644 index 925661a..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Action.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tpcly.behaviourtree.node.task - -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult - -/** - * This is an abstract class for a type of tree node called the action node - * Action nodes represent actual actions that the agent can perform - * These could be things like moving, attacking, or interacting with objects - * - * @property name a descriptive name of the actions' usage - */ -abstract class Action( - override val name: String, -) : Task { - abstract fun action(): Status - - override fun execute(): TreeNodeResult = TreeNodeResult(this, action()) -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Conditional.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/task/Conditional.kt deleted file mode 100644 index e657faf..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Conditional.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tpcly.behaviourtree.node.task - -import com.tpcly.behaviourtree.Status -import com.tpcly.behaviourtree.TreeNodeResult - -/** - * An action node which turns a boolean result into a status, with true being successful and false being failure - */ -abstract class Conditional( - override val name: String, -) : Task { - abstract fun validate(): Boolean - - override fun execute(): TreeNodeResult = TreeNodeResult(this, Status.fromCondition(validate())) -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Perform.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/task/Perform.kt deleted file mode 100644 index 3029e64..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Perform.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tpcly.behaviourtree.node.task - -import com.tpcly.behaviourtree.TreeNodeResult - -/** - * An action node which performs an action and always returns a successful resul - */ -abstract class Perform( - override val name: String, -) : Task { - abstract fun action() - - override fun execute(): TreeNodeResult { - action() - return TreeNodeResult.success(this) - } -} diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Task.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/task/Task.kt deleted file mode 100644 index c3e1f9a..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/task/Task.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.tpcly.behaviourtree.node.task - -import com.tpcly.behaviourtree.node.TreeNode - -sealed interface Task : TreeNode \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/GateTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/GateTests.kt deleted file mode 100644 index 8dd6e91..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/GateTests.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.gate -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class GateTests { - - @Test - fun testOpenGateSuccess() = testOpenGate(Status.SUCCESS) - - @Test - fun testOpenGateFailure() = testOpenGate(Status.FAILURE) - - @Test - fun testOpenGateRunning() = testOpenGate(Status.RUNNING) - - @Test - fun testOpenGateExit() = testOpenGate(Status.ABORT) - - private fun testOpenGate(inputStatus: Status) { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult(this, inputStatus) - } - - val node = gate(validate = { true }) { - mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(inputStatus, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testClosedGate() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult(this, Status.SUCCESS) - } - - val node = gate(validate = { false }) { - mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.FAILURE, result.status) - verify(exactly = 0) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/InverterTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/InverterTests.kt deleted file mode 100644 index 4ed1516..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/InverterTests.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.inverter -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class InverterTests { - - @Test - fun testExecutionSuccess() = testExecution(Status.SUCCESS, Status.FAILURE) - - @Test - fun testExecutionFailure() = testExecution(Status.FAILURE, Status.SUCCESS) - - @Test - fun testExecutionRunning() = testExecution(Status.RUNNING, Status.RUNNING) - - @Test - fun testExecutionExit() = testExecution(Status.ABORT, Status.ABORT) - - private fun testExecution(inputStatus: Status, expectedOutputStatus: Status) { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult(this, inputStatus) - } - - val node = inverter { - mockNode - } - - // Act - val result = node.execute() - - // Asserts - assertEquals(expectedOutputStatus, result.status) - verify(exactly = 1) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt new file mode 100644 index 0000000..9c888ea --- /dev/null +++ b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt @@ -0,0 +1,38 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass +import org.junit.jupiter.api.Test + +class JsonTests { + @Test + fun testJson() { + val tree = sequencer { + +succeeder { + MockAction("test_1", 1337) + } + +repeatUntil { + MockAction("test_2", 69) + } + +MockAction("test_2", 69) + +MockAction("test_3", 420) + } + + val json = Json { + prettyPrint = true + serializersModule = SerializersModule { + polymorphic(TreeNode::class) { + subclass(MockAction::class) + subclass(RepeatUntil::class) + subclass(Succeeder::class) + } + } + } + + val serialized = json.encodeToString(tree) + println(serialized) + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt new file mode 100644 index 0000000..dafd840 --- /dev/null +++ b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt @@ -0,0 +1,14 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.Serializable + +@Serializable +data class MockAction( + val inputOne: String, + val inputTwo: Int, +) : TreeNode { + override fun execute(): TreeNodeStatus { + println("$inputOne $inputTwo") + return TreeNodeStatus.SUCCESS + } +} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/RepeatUntilTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/RepeatUntilTests.kt deleted file mode 100644 index 14b3a5a..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/RepeatUntilTests.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.repeatUntil -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class RepeatUntilTests { - @Test - fun testStatus() { - // Arrange - var count = 0 - val node = repeatUntil(Status.SUCCESS) { - com.tpcly.behaviourtree.node.run { - when (count) { - 3 -> Status.SUCCESS - else -> { - count++ - Status.FAILURE - } - } - } - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - assertEquals(3, count) - } - - @Test - fun testAbort() { - // Arrange - var count = 0 - val node = repeatUntil(Status.SUCCESS) { - com.tpcly.behaviourtree.node.run { - when (count) { - 3 -> Status.ABORT - else -> { - count++ - Status.FAILURE - } - } - } - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.ABORT, result.status) - assertEquals(3, count) - } - - @Test - fun testLimit() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = repeatUntil(Status.FAILURE, 10) { - mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.FAILURE, result.status) - verify(exactly = 10) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/RepeatWhenTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/RepeatWhenTests.kt deleted file mode 100644 index 8610a75..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/RepeatWhenTests.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.perform -import com.tpcly.behaviourtree.node.repeatWhen -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class RepeatWhenTests { - @Test - fun testStatus() { - // Arrange - var count = 0 - val node = repeatWhen(validate = { count != 3 }) { - perform { - count++ - } - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - assertEquals(3, count) - } - - @Test - fun testAlreadySatisfied() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.failure(this) - } - - val node = repeatWhen(validate = { false }) { - mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 0) { mockNode.execute() } - } - - @Test - fun testAbort() { - // Arrange - var count = 0 - val node = repeatWhen(validate = { true }) { - com.tpcly.behaviourtree.node.run { - when (count) { - 3 -> Status.ABORT - else -> { - count++ - Status.SUCCESS - } - } - } - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.ABORT, result.status) - assertEquals(3, count) - } - - @Test - fun testLimit() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = repeatWhen(validate = { true }, limit = 10) { - mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.FAILURE, result.status) - verify(exactly = 10) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/SelectorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/SelectorTests.kt deleted file mode 100644 index 1a67a7f..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/SelectorTests.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.selector -import com.tpcly.behaviourtree.node.sequencer -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class SelectorTests { - @Test - fun testExecutionSuccess() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = selector { - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testExecutionFailure() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.failure(this) - } - - val node = selector { - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.FAILURE, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testOrder() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = selector { - +mockNode - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testEarlyExit() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = selector { - +mockNode - +selector { +com.tpcly.behaviourtree.node.run { Status.FAILURE } } - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testAbort() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.failure(this) - } - - val node = selector { - +mockNode - +sequencer { - +com.tpcly.behaviourtree.node.run { Status.ABORT } - +mockNode - } - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.ABORT, result.status) - verify(exactly = 1) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/SequencerTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/SequencerTests.kt deleted file mode 100644 index b9b06c6..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/SequencerTests.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.sequencer -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class SequencerTests { - @Test - fun testExecutionSuccess() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = sequencer { - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testExecutionFailure() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.failure(this) - } - - val node = sequencer { - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.FAILURE, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testOrder() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = sequencer { - +mockNode - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 2) { mockNode.execute() } - } - - @Test - fun testEarlyExit() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = sequencer { - +mockNode - +sequencer { +com.tpcly.behaviourtree.node.run { Status.FAILURE } } - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.FAILURE, result.status) - verify(exactly = 1) { mockNode.execute() } - } - - @Test - fun testAbort() { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult.success(this) - } - - val node = sequencer { - +mockNode - +sequencer { - +mockNode - +com.tpcly.behaviourtree.node.run { Status.ABORT } - +mockNode - } - +mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.ABORT, result.status) - verify(exactly = 2) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/StateTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/StateTests.kt deleted file mode 100644 index 7af39ad..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/StateTests.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.conditional -import com.tpcly.behaviourtree.node.perform -import com.tpcly.behaviourtree.node.sequencer -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class StateTests { - data class MockDataClass(override val rootValue: String, override val childValue: String) : MockRootData - - interface MockRootData : MockChildDate { - val rootValue: String - } - - interface MockChildDate { - val childValue: String - } - - @Test - fun testStateHierarchy() { - // Arrange - val state = MockDataClass("test_root", "test_child") - val tree = sequencer { - +conditional { - state.rootValue == "test_root" - } - +conditional { - state.childValue == "test_child" - } - +perform { - println("test") - } - } - - // Act - val result = tree.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/SucceederTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/SucceederTests.kt deleted file mode 100644 index 7513e8a..0000000 --- a/src/test/kotlin/com/tpcly/behaviourtree/SucceederTests.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tpcly.behaviourtree - -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.succeeder -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import kotlin.test.Test -import kotlin.test.assertEquals - -internal class SucceederTests { - - @Test - fun testExecutionSuccess() = testExecution(Status.SUCCESS) - - @Test - fun testExecutionFailure() = testExecution(Status.FAILURE) - - @Test - fun testExecutionRunning() = testExecution(Status.RUNNING) - - @Test - fun testExecutionExit() = testExecution(Status.ABORT) - - private fun testExecution(inputStatus: Status) { - // Arrange - val mockNode = mockk { - every { execute() } returns TreeNodeResult(this, inputStatus) - } - - val node = succeeder { - mockNode - } - - // Act - val result = node.execute() - - // Assert - assertEquals(Status.SUCCESS, result.status) - verify(exactly = 1) { mockNode.execute() } - } -} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt new file mode 100644 index 0000000..537d818 --- /dev/null +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -0,0 +1,78 @@ +package com.tpcly.behaviourtree + +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass +import org.junit.jupiter.api.Test + +class TreeExecutorTests { + + @Test + fun testSequencer() { + val json = Json { + prettyPrint = true + serializersModule = SerializersModule { + polymorphic(TreeNode::class) { + subclass(Sequencer::class) + subclass(RepeatUntil::class) + subclass(Selector::class) + subclass(Succeeder::class) + subclass(MockAction::class) + } + } + } + + val tree = json.decodeFromString( + """ + { + "type": "com.tpcly.behaviourtree.Sequencer", + "executionOrder": "IN_ORDER", + "children": [ + { + "type": "com.tpcly.behaviourtree.Succeeder", + "child": { + "type": "com.tpcly.behaviourtree.MockAction", + "inputOne": "test_1", + "inputTwo": 1337 + } + }, + { + "type": "com.tpcly.behaviourtree.RepeatUntil", + "child": { + "type": "com.tpcly.behaviourtree.MockAction", + "inputOne": "test_2", + "inputTwo": 69 + } + }, + { + "type": "com.tpcly.behaviourtree.MockAction", + "inputOne": "test_2", + "inputTwo": 69 + }, + { + "type": "com.tpcly.behaviourtree.MockAction", + "inputOne": "test_3", + "inputTwo": 420 + } + ] + } + """ + ) + + println(tree) + +// val tree = Sequencer( +// TreeExecutionOrder.IN_ORDER, +// listOf( +// Succeeder( +// Action("test", "") +// ), +// Action("test", ""), +// Action("test", "") +// ) +// ) + + tree.execute() + } +} \ No newline at end of file