From 01a341ab1f4c33e29c21b7b7a0c82986f8acccff Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Sun, 28 Jul 2024 20:48:36 +0200 Subject: [PATCH 01/15] Move to new schema structure --- build.gradle.kts | 2 + .../behaviourtree/BehaviourTreeDslMarker.kt | 4 - .../kotlin/com/tpcly/behaviourtree/Status.kt | 15 --- ...xecutionOrder.kt => TreeExecutionOrder.kt} | 2 +- .../com/tpcly/behaviourtree/TreeNodeResult.kt | 31 ----- .../com/tpcly/behaviourtree/node/Builder.kt | 96 --------------- .../tpcly/behaviourtree/node/Extensions.kt | 5 - .../com/tpcly/behaviourtree/node/Selector.kt | 36 ++++++ .../com/tpcly/behaviourtree/node/Sequencer.kt | 36 ++++++ .../com/tpcly/behaviourtree/node/Succeeder.kt | 21 ++++ .../com/tpcly/behaviourtree/node/TreeNode.kt | 21 ++-- .../behaviourtree/node/TreeNodeHandler.kt | 18 +++ .../node/TreeNodeHandlerCollection.kt | 22 ++++ .../behaviourtree/node/composite/Composite.kt | 23 ---- .../behaviourtree/node/composite/Selector.kt | 39 ------ .../behaviourtree/node/composite/Sequencer.kt | 38 ------ .../behaviourtree/node/decorator/Decorator.kt | 16 --- .../behaviourtree/node/decorator/Gate.kt | 23 ---- .../behaviourtree/node/decorator/Inverter.kt | 25 ---- .../node/decorator/RepeatUntil.kt | 36 ------ .../node/decorator/RepeatWhen.kt | 39 ------ .../behaviourtree/node/decorator/Succeeder.kt | 17 --- .../tpcly/behaviourtree/node/task/Action.kt | 19 --- .../behaviourtree/node/task/Conditional.kt | 15 --- .../tpcly/behaviourtree/node/task/Perform.kt | 17 --- .../com/tpcly/behaviourtree/node/task/Task.kt | 5 - .../com/tpcly/behaviourtree/GateTests.kt | 61 ---------- .../com/tpcly/behaviourtree/InverterTests.kt | 42 ------- .../com/tpcly/behaviourtree/JsonTests.kt | 38 ++++++ .../tpcly/behaviourtree/RepeatUntilTests.kt | 78 ------------ .../tpcly/behaviourtree/RepeatWhenTests.kt | 92 -------------- .../com/tpcly/behaviourtree/SelectorTests.kt | 115 ------------------ .../com/tpcly/behaviourtree/SequencerTests.kt | 115 ------------------ .../com/tpcly/behaviourtree/StateTests.kt | 42 ------- .../com/tpcly/behaviourtree/SucceederTests.kt | 42 ------- .../com/tpcly/behaviourtree/TestAction.kt | 22 ++++ .../tpcly/behaviourtree/TreeExecutorTests.kt | 78 ++++++++++++ 37 files changed, 282 insertions(+), 1064 deletions(-) delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/BehaviourTreeDslMarker.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/Status.kt rename src/main/kotlin/com/tpcly/behaviourtree/{ExecutionOrder.kt => TreeExecutionOrder.kt} (64%) delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeNodeResult.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Extensions.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Selector.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/composite/Composite.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/composite/Selector.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/composite/Sequencer.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Decorator.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Gate.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Inverter.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatUntil.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/decorator/RepeatWhen.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/decorator/Succeeder.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/task/Action.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/task/Conditional.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/task/Perform.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/task/Task.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/GateTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/InverterTests.kt create mode 100644 src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/RepeatUntilTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/RepeatWhenTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/SelectorTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/SequencerTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/StateTests.kt delete mode 100644 src/test/kotlin/com/tpcly/behaviourtree/SucceederTests.kt create mode 100644 src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt create mode 100644 src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt 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/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/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/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/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/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Selector.kt new file mode 100644 index 0000000..9bf8349 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/Selector.kt @@ -0,0 +1,36 @@ +package com.tpcly.behaviourtree.node + +import com.tpcly.behaviourtree.TreeExecutionOrder +import kotlinx.serialization.Serializable + +@Serializable +data class Selector( + val executionOrder: TreeExecutionOrder, + override val children: List, +) : TreeNode.Composite { + override val name: String = "selector" + override val description = "Executes its child nodes in order until one succeeds or all fail, similar to an `or` operator" + + class Handler : TreeNodeHandler { + override fun execute( + handlers: TreeNodeHandlerCollection, + descriptor: Selector, + ): TreeNodeHandler.Status { + val children = when (descriptor.executionOrder) { + TreeExecutionOrder.RANDOM -> descriptor.children.shuffled() + else -> descriptor.children + } + + for (child in children) { + val handler = handlers[child.name] + val result = handler.execute(handlers, child) + + if (result == TreeNodeHandler.Status.SUCCESS || result == TreeNodeHandler.Status.ABORT) { + return result + } + } + + return TreeNodeHandler.Status.FAILURE + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt new file mode 100644 index 0000000..754c7ee --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt @@ -0,0 +1,36 @@ +package com.tpcly.behaviourtree.node + +import com.tpcly.behaviourtree.TreeExecutionOrder +import kotlinx.serialization.Serializable + +@Serializable +data class Sequencer( + val executionOrder: TreeExecutionOrder, + override val children: List, +) : TreeNode.Composite { + override val name: String = "sequencer" + override val description = "Executes its child nodes in order until one fails or all succeed, similar to an `and` operator" + + class Handler : TreeNodeHandler { + override fun execute( + handlers: TreeNodeHandlerCollection, + descriptor: Sequencer, + ): TreeNodeHandler.Status { + val children = when (descriptor.executionOrder) { + TreeExecutionOrder.RANDOM -> descriptor.children.shuffled() + else -> descriptor.children + } + + for (child in children) { + val handler = handlers[child.name] + val result = handler.execute(handlers, child) + + if (result == TreeNodeHandler.Status.FAILURE || result == TreeNodeHandler.Status.ABORT) { + return result + } + } + + return TreeNodeHandler.Status.SUCCESS + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt new file mode 100644 index 0000000..8216b0f --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt @@ -0,0 +1,21 @@ +package com.tpcly.behaviourtree.node + +import kotlinx.serialization.Serializable + +@Serializable +data class Succeeder( + override val child: TreeNode, +) : TreeNode.Decorator { + override val name: String = "succeeder" + override val description: String = "Transforms the result of its child into success, regardless of what the child returns" + + class Handler : TreeNodeHandler { + override fun execute( + handlers: TreeNodeHandlerCollection, + descriptor: Succeeder, + ): TreeNodeHandler.Status { + val handler = handlers[descriptor.child.name] + return handler.execute(handlers, descriptor.child) + } + } +} \ 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 index 94b3c06..513c394 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt @@ -1,19 +1,14 @@ 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 + val description: String + + interface Composite : TreeNode { + val children: List + } - /** - * Executes a certain behaviour of the node - * @return the result of the execution, including relevant children results - */ - fun execute(): TreeNodeResult + interface Decorator : TreeNode { + val child: TreeNode + } } \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt new file mode 100644 index 0000000..99df4a1 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt @@ -0,0 +1,18 @@ +package com.tpcly.behaviourtree.node + +interface TreeNodeHandler { + fun execute( + handlers: TreeNodeHandlerCollection, + descriptor: T, + ): Status + + enum class Status { + SUCCESS, + FAILURE, + ABORT; + + companion object { + fun fromBoolean(value: Boolean): Status = if (value) SUCCESS else FAILURE + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt new file mode 100644 index 0000000..dd6ff5a --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt @@ -0,0 +1,22 @@ +package com.tpcly.behaviourtree.node + +class TreeNodeHandlerCollection( + handlers: Map>, +) { + private val allHandlers = handlers + defaultHandlers + + // Justification: TreeNodeHandler has a generic class constraint + @Suppress("UNCHECKED_CAST") + operator fun get(name: String): TreeNodeHandler { + val handler = allHandlers[name] ?: throw NullPointerException("Node $name not found") + return handler as TreeNodeHandler + } + + companion object { + private val defaultHandlers = mapOf( + "sequencer" to Sequencer.Handler(), + "selector" to Selector.Handler(), + "succeeder" to Succeeder.Handler() + ) + } +} \ 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..1ff0a4b --- /dev/null +++ b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt @@ -0,0 +1,38 @@ +package com.tpcly.behaviourtree + +import com.tpcly.behaviourtree.node.Sequencer +import com.tpcly.behaviourtree.node.Succeeder +import com.tpcly.behaviourtree.node.TreeNode +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( + TreeExecutionOrder.IN_ORDER, + listOf( + Succeeder(TestAction("test_1", 1337)), + TestAction("test_2", 69), + TestAction("test_3", 420) + ) + ) + + val json = Json { + prettyPrint = true + serializersModule = SerializersModule { + polymorphic(TreeNode::class) { + subclass(TestAction::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/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/TestAction.kt b/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt new file mode 100644 index 0000000..93438e9 --- /dev/null +++ b/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt @@ -0,0 +1,22 @@ +package com.tpcly.behaviourtree + +import com.tpcly.behaviourtree.node.TreeNode +import com.tpcly.behaviourtree.node.TreeNodeHandler +import com.tpcly.behaviourtree.node.TreeNodeHandlerCollection +import kotlinx.serialization.Serializable + +@Serializable +data class TestAction( + val inputOne: String, + val inputTwo: Int, +) : TreeNode { + override val name: String = "test" + override val description: String = "Test" + + class Handler : TreeNodeHandler { + override fun execute(handlers: TreeNodeHandlerCollection, descriptor: TestAction): TreeNodeHandler.Status { + println("${descriptor.inputOne} ${descriptor.inputTwo}") + return TreeNodeHandler.Status.SUCCESS + } + } +} \ 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..522a5c2 --- /dev/null +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -0,0 +1,78 @@ +package com.tpcly.behaviourtree + +import com.tpcly.behaviourtree.node.* +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 handlers = TreeNodeHandlerCollection( + mapOf( + "test" to TestAction.Handler() + ) + ) + + val json = Json { + prettyPrint = true + serializersModule = SerializersModule { + polymorphic(TreeNode::class) { + subclass(Sequencer::class) + subclass(Selector::class) + subclass(Succeeder::class) + subclass(TestAction::class) + } + } + } + + val tree= json.decodeFromString( + """ + { + "type": "com.tpcly.behaviourtree.node.Sequencer", + "executionOrder": "IN_ORDER", + "children": [ + { + "type": "com.tpcly.behaviourtree.node.Succeeder", + "child": { + "type": "com.tpcly.behaviourtree.TestAction", + "inputOne": "test_1", + "inputTwo": 1337 + } + }, + { + "type": "com.tpcly.behaviourtree.TestAction", + "inputOne": "test_2", + "inputTwo": 69 + }, + { + "type": "com.tpcly.behaviourtree.TestAction", + "inputOne": "test_3", + "inputTwo": 420 + } + ] + } + """ + ) + + println(tree) + +// val tree = Sequencer( +// TreeExecutionOrder.IN_ORDER, +// listOf( +// Succeeder( +// Action("test", "") +// ), +// Action("test", ""), +// Action("test", "") +// ) +// ) + + + val rootHandler = handlers.get(tree.name) + rootHandler.execute(handlers, tree) + } +} \ No newline at end of file From 2f324a1e1051976d48019198c581676b7a466095 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Wed, 7 Aug 2024 09:33:07 +0200 Subject: [PATCH 02/15] Add inverter and repeat until nodes --- .../com/tpcly/behaviourtree/node/Inverter.kt | 25 +++++++++++++ .../tpcly/behaviourtree/node/RepeatUntil.kt | 36 +++++++++++++++++++ .../node/TreeNodeHandlerCollection.kt | 6 +++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt new file mode 100644 index 0000000..40ab2c7 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt @@ -0,0 +1,25 @@ +package com.tpcly.behaviourtree.node + +import kotlinx.serialization.Serializable + +@Serializable +data class Inverter( + override val child: TreeNode, +) : TreeNode.Decorator { + override val name: String = "inverter" + override val description: String = "Inverts the result of its child, success becomes failure and vice-versa" + + class Handler : TreeNodeHandler { + override fun execute( + handlers: TreeNodeHandlerCollection, + descriptor: Inverter, + ): TreeNodeHandler.Status { + val handler = handlers[descriptor.child.name] + return when (val result = handler.execute(handlers, descriptor.child)) { + TreeNodeHandler.Status.SUCCESS -> TreeNodeHandler.Status.FAILURE + TreeNodeHandler.Status.FAILURE -> TreeNodeHandler.Status.SUCCESS + else -> result + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt new file mode 100644 index 0000000..052b931 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt @@ -0,0 +1,36 @@ +package com.tpcly.behaviourtree.node + +import kotlinx.serialization.Serializable + +@Serializable +data class RepeatUntil( + override val child: TreeNode, + val targetStatus: TreeNodeHandler.Status = TreeNodeHandler.Status.SUCCESS, + val limit: Int = 10, +) : TreeNode.Decorator { + override val name: String = "repeat_until" + override val description: String = "" + + class Handler : TreeNodeHandler { + override fun execute(handlers: TreeNodeHandlerCollection, descriptor: RepeatUntil): TreeNodeHandler.Status { + val handler = handlers[descriptor.child.name] + + var iteration = 0 + var currentStatus: TreeNodeHandler.Status + + do { + currentStatus = handler.execute(handlers, descriptor.child) + iteration++ + } while ( + currentStatus != descriptor.targetStatus && + currentStatus != TreeNodeHandler.Status.ABORT && + iteration < descriptor.limit + ) + + return when { + iteration >= descriptor.limit -> TreeNodeHandler.Status.FAILURE + else -> currentStatus + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt index dd6ff5a..a5f1f66 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt @@ -14,9 +14,13 @@ class TreeNodeHandlerCollection( companion object { private val defaultHandlers = mapOf( + // Composite "sequencer" to Sequencer.Handler(), "selector" to Selector.Handler(), - "succeeder" to Succeeder.Handler() + // Decorators + "repeat_until" to RepeatUntil.Handler(), + "succeeder" to Succeeder.Handler(), + "inverter" to Inverter.Handler() ) } } \ No newline at end of file From 20074b3f0aba2107602fe7ff5b47e72dbf411554 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Wed, 7 Aug 2024 10:16:31 +0200 Subject: [PATCH 03/15] Add initial DSL builders --- .../com/tpcly/behaviourtree/node/Builder.kt | 35 +++++++++++++++++++ .../node/TreeNodeCompositeBuilder.kt | 17 +++++++++ .../com/tpcly/behaviourtree/JsonTests.kt | 23 ++++++------ 3 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt new file mode 100644 index 0000000..bf10794 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt @@ -0,0 +1,35 @@ +package com.tpcly.behaviourtree.node + +import com.tpcly.behaviourtree.TreeExecutionOrder + +/** + * 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 succeeder(init: () -> TreeNode) = Succeeder(init()) + +fun repeatUntil( + status: TreeNodeHandler.Status = TreeNodeHandler.Status.SUCCESS, + limit: Int = 10, + init: () -> TreeNode, +) = RepeatUntil(init(), status, limit) \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt new file mode 100644 index 0000000..f2d0a78 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt @@ -0,0 +1,17 @@ +package com.tpcly.behaviourtree.node + +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/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt index 1ff0a4b..dc0e0d0 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt @@ -1,8 +1,6 @@ package com.tpcly.behaviourtree -import com.tpcly.behaviourtree.node.Sequencer -import com.tpcly.behaviourtree.node.Succeeder -import com.tpcly.behaviourtree.node.TreeNode +import com.tpcly.behaviourtree.node.* import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule @@ -13,20 +11,23 @@ import org.junit.jupiter.api.Test class JsonTests { @Test fun testJson() { - val tree = Sequencer( - TreeExecutionOrder.IN_ORDER, - listOf( - Succeeder(TestAction("test_1", 1337)), - TestAction("test_2", 69), - TestAction("test_3", 420) - ) - ) + val tree = sequencer { + +succeeder { + TestAction("test_1", 1337) + } + +repeatUntil { + TestAction("test_2", 69) + } + +TestAction("test_2", 69) + +TestAction("test_3", 420) + } val json = Json { prettyPrint = true serializersModule = SerializersModule { polymorphic(TreeNode::class) { subclass(TestAction::class) + subclass(RepeatUntil::class) subclass(Succeeder::class) } } From 9bee37c0aa5b8fd924ac8a07254fded130fa1e76 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 9 Aug 2024 20:21:11 +0200 Subject: [PATCH 04/15] Move files to root package --- .../kotlin/com/tpcly/behaviourtree/{node => }/Builder.kt | 4 +--- .../kotlin/com/tpcly/behaviourtree/{node => }/Inverter.kt | 2 +- .../com/tpcly/behaviourtree/{node => }/RepeatUntil.kt | 2 +- .../kotlin/com/tpcly/behaviourtree/{node => }/Selector.kt | 3 +-- .../kotlin/com/tpcly/behaviourtree/{node => }/Sequencer.kt | 3 +-- .../kotlin/com/tpcly/behaviourtree/{node => }/Succeeder.kt | 2 +- .../kotlin/com/tpcly/behaviourtree/{node => }/TreeNode.kt | 2 +- .../behaviourtree/{node => }/TreeNodeCompositeBuilder.kt | 2 +- .../com/tpcly/behaviourtree/{node => }/TreeNodeHandler.kt | 2 +- .../behaviourtree/{node => }/TreeNodeHandlerCollection.kt | 6 ++---- src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt | 1 - src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt | 3 --- .../kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt | 1 - 13 files changed, 11 insertions(+), 22 deletions(-) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/Builder.kt (90%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/Inverter.kt (95%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/RepeatUntil.kt (96%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/Selector.kt (92%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/Sequencer.kt (92%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/Succeeder.kt (94%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/TreeNode.kt (85%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/TreeNodeCompositeBuilder.kt (89%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/TreeNodeHandler.kt (90%) rename src/main/kotlin/com/tpcly/behaviourtree/{node => }/TreeNodeHandlerCollection.kt (86%) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt similarity index 90% rename from src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt rename to src/main/kotlin/com/tpcly/behaviourtree/Builder.kt index bf10794..fa63be4 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Builder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt @@ -1,6 +1,4 @@ -package com.tpcly.behaviourtree.node - -import com.tpcly.behaviourtree.TreeExecutionOrder +package com.tpcly.behaviourtree /** * Composite nodes diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt similarity index 95% rename from src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt rename to src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt index 40ab2c7..50dc08b 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Inverter.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -1,4 +1,4 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt similarity index 96% rename from src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt rename to src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt index 052b931..349c934 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/RepeatUntil.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -1,4 +1,4 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt similarity index 92% rename from src/main/kotlin/com/tpcly/behaviourtree/node/Selector.kt rename to src/main/kotlin/com/tpcly/behaviourtree/Selector.kt index 9bf8349..d6280cd 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Selector.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -1,6 +1,5 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree -import com.tpcly.behaviourtree.TreeExecutionOrder import kotlinx.serialization.Serializable @Serializable diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt similarity index 92% rename from src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt rename to src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt index 754c7ee..152c50f 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Sequencer.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -1,6 +1,5 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree -import com.tpcly.behaviourtree.TreeExecutionOrder import kotlinx.serialization.Serializable @Serializable diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt similarity index 94% rename from src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt rename to src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt index 8216b0f..f0b6ddb 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/Succeeder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -1,4 +1,4 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt similarity index 85% rename from src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt rename to src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt index 513c394..ee998c5 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNode.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt @@ -1,4 +1,4 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree interface TreeNode { val name: String diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeCompositeBuilder.kt similarity index 89% rename from src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt rename to src/main/kotlin/com/tpcly/behaviourtree/TreeNodeCompositeBuilder.kt index f2d0a78..5e5598b 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeCompositeBuilder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeCompositeBuilder.kt @@ -1,4 +1,4 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree class TreeNodeCompositeBuilder { private val children: MutableList = mutableListOf() diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt similarity index 90% rename from src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt rename to src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt index 99df4a1..b94fa02 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandler.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt @@ -1,4 +1,4 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree interface TreeNodeHandler { fun execute( diff --git a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerCollection.kt similarity index 86% rename from src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt rename to src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerCollection.kt index a5f1f66..f38527d 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/node/TreeNodeHandlerCollection.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerCollection.kt @@ -1,8 +1,6 @@ -package com.tpcly.behaviourtree.node +package com.tpcly.behaviourtree -class TreeNodeHandlerCollection( - handlers: Map>, -) { +class TreeNodeHandlerCollection(handlers: Map>) { private val allHandlers = handlers + defaultHandlers // Justification: TreeNodeHandler has a generic class constraint diff --git a/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt index dc0e0d0..e706971 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt @@ -1,6 +1,5 @@ package com.tpcly.behaviourtree -import com.tpcly.behaviourtree.node.* import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt b/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt index 93438e9..9fbf9f1 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt @@ -1,8 +1,5 @@ package com.tpcly.behaviourtree -import com.tpcly.behaviourtree.node.TreeNode -import com.tpcly.behaviourtree.node.TreeNodeHandler -import com.tpcly.behaviourtree.node.TreeNodeHandlerCollection import kotlinx.serialization.Serializable @Serializable diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt index 522a5c2..569db73 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -1,6 +1,5 @@ package com.tpcly.behaviourtree -import com.tpcly.behaviourtree.node.* import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic From 74c1de5c3b90a74cf95484c868692cb84bdfe695 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Thu, 15 Aug 2024 17:32:42 +0200 Subject: [PATCH 05/15] Rename to TreeNodeHandlerModule --- .../com/tpcly/behaviourtree/Inverter.kt | 2 +- .../com/tpcly/behaviourtree/RepeatUntil.kt | 2 +- .../com/tpcly/behaviourtree/Selector.kt | 2 +- .../com/tpcly/behaviourtree/Sequencer.kt | 2 +- .../com/tpcly/behaviourtree/Succeeder.kt | 2 +- .../tpcly/behaviourtree/TreeNodeHandler.kt | 2 +- ...Collection.kt => TreeNodeHandlerModule.kt} | 2 +- .../TreeNodeHandlerModuleBuilder.kt | 19 +++++++++++++++++++ .../com/tpcly/behaviourtree/TestAction.kt | 2 +- .../tpcly/behaviourtree/TreeExecutorTests.kt | 10 ++++------ 10 files changed, 31 insertions(+), 14 deletions(-) rename src/main/kotlin/com/tpcly/behaviourtree/{TreeNodeHandlerCollection.kt => TreeNodeHandlerModule.kt} (91%) create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt index 50dc08b..ca9603c 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -11,7 +11,7 @@ data class Inverter( class Handler : TreeNodeHandler { override fun execute( - handlers: TreeNodeHandlerCollection, + handlers: TreeNodeHandlerModule, descriptor: Inverter, ): TreeNodeHandler.Status { val handler = handlers[descriptor.child.name] diff --git a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt index 349c934..1582370 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -12,7 +12,7 @@ data class RepeatUntil( override val description: String = "" class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerCollection, descriptor: RepeatUntil): TreeNodeHandler.Status { + override fun execute(handlers: TreeNodeHandlerModule, descriptor: RepeatUntil): TreeNodeHandler.Status { val handler = handlers[descriptor.child.name] var iteration = 0 diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt index d6280cd..6941b5f 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -12,7 +12,7 @@ data class Selector( class Handler : TreeNodeHandler { override fun execute( - handlers: TreeNodeHandlerCollection, + handlers: TreeNodeHandlerModule, descriptor: Selector, ): TreeNodeHandler.Status { val children = when (descriptor.executionOrder) { diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt index 152c50f..d9b1fa2 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -12,7 +12,7 @@ data class Sequencer( class Handler : TreeNodeHandler { override fun execute( - handlers: TreeNodeHandlerCollection, + handlers: TreeNodeHandlerModule, descriptor: Sequencer, ): TreeNodeHandler.Status { val children = when (descriptor.executionOrder) { diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt index f0b6ddb..d099811 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -11,7 +11,7 @@ data class Succeeder( class Handler : TreeNodeHandler { override fun execute( - handlers: TreeNodeHandlerCollection, + handlers: TreeNodeHandlerModule, descriptor: Succeeder, ): TreeNodeHandler.Status { val handler = handlers[descriptor.child.name] diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt index b94fa02..9d1cd3b 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt @@ -2,7 +2,7 @@ package com.tpcly.behaviourtree interface TreeNodeHandler { fun execute( - handlers: TreeNodeHandlerCollection, + handlers: TreeNodeHandlerModule, descriptor: T, ): Status diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerCollection.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt similarity index 91% rename from src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerCollection.kt rename to src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt index f38527d..146ac35 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerCollection.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt @@ -1,6 +1,6 @@ package com.tpcly.behaviourtree -class TreeNodeHandlerCollection(handlers: Map>) { +class TreeNodeHandlerModule(handlers: Map>) { private val allHandlers = handlers + defaultHandlers // Justification: TreeNodeHandler has a generic class constraint diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt new file mode 100644 index 0000000..059f4d2 --- /dev/null +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt @@ -0,0 +1,19 @@ +package com.tpcly.behaviourtree + +class TreeNodeHandlerModuleBuilder { + val handlers: MutableMap> = mutableMapOf() + + fun handler(name: String, handler: TreeNodeHandler) { + handlers[name] = handler + } + + internal fun build(): TreeNodeHandlerModule { + return TreeNodeHandlerModule(handlers) + } +} + +fun TreeNodeHandlerModule(builderAction: TreeNodeHandlerModuleBuilder.() -> Unit): TreeNodeHandlerModule { + val builder = TreeNodeHandlerModuleBuilder() + builder.builderAction() + return builder.build() +} \ No newline at end of file diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt b/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt index 9fbf9f1..0113812 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt @@ -11,7 +11,7 @@ data class TestAction( override val description: String = "Test" class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerCollection, descriptor: TestAction): TreeNodeHandler.Status { + override fun execute(handlers: TreeNodeHandlerModule, descriptor: TestAction): TreeNodeHandler.Status { println("${descriptor.inputOne} ${descriptor.inputTwo}") return TreeNodeHandler.Status.SUCCESS } diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt index 569db73..ba76a33 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -10,11 +10,9 @@ class TreeExecutorTests { @Test fun testSequencer() { - val handlers = TreeNodeHandlerCollection( - mapOf( - "test" to TestAction.Handler() - ) - ) + val handlers = TreeNodeHandlerModule { + handler("test", TestAction.Handler()) + } val json = Json { prettyPrint = true @@ -71,7 +69,7 @@ class TreeExecutorTests { // ) - val rootHandler = handlers.get(tree.name) + val rootHandler = handlers[tree.name] rootHandler.execute(handlers, tree) } } \ No newline at end of file From 97cf3be380571f1610ccc67fda6b5dbbdae4735b Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Thu, 15 Aug 2024 17:40:26 +0200 Subject: [PATCH 06/15] Remove useless name and description properties --- .../com/tpcly/behaviourtree/Inverter.kt | 5 +--- .../com/tpcly/behaviourtree/RepeatUntil.kt | 5 +--- .../com/tpcly/behaviourtree/Selector.kt | 5 +--- .../com/tpcly/behaviourtree/Sequencer.kt | 5 +--- .../com/tpcly/behaviourtree/Succeeder.kt | 5 +--- .../com/tpcly/behaviourtree/TreeNode.kt | 3 --- .../behaviourtree/TreeNodeHandlerModule.kt | 15 +++++------ .../TreeNodeHandlerModuleBuilder.kt | 4 +-- .../com/tpcly/behaviourtree/JsonTests.kt | 10 ++++---- .../{TestAction.kt => MockAction.kt} | 9 +++---- .../tpcly/behaviourtree/TreeExecutorTests.kt | 25 +++++++++++++------ 11 files changed, 40 insertions(+), 51 deletions(-) rename src/test/kotlin/com/tpcly/behaviourtree/{TestAction.kt => MockAction.kt} (63%) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt index ca9603c..0cb502d 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -6,15 +6,12 @@ import kotlinx.serialization.Serializable data class Inverter( override val child: TreeNode, ) : TreeNode.Decorator { - override val name: String = "inverter" - override val description: String = "Inverts the result of its child, success becomes failure and vice-versa" - class Handler : TreeNodeHandler { override fun execute( handlers: TreeNodeHandlerModule, descriptor: Inverter, ): TreeNodeHandler.Status { - val handler = handlers[descriptor.child.name] + val handler = handlers[descriptor.child] return when (val result = handler.execute(handlers, descriptor.child)) { TreeNodeHandler.Status.SUCCESS -> TreeNodeHandler.Status.FAILURE TreeNodeHandler.Status.FAILURE -> TreeNodeHandler.Status.SUCCESS diff --git a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt index 1582370..1e09c98 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -8,12 +8,9 @@ data class RepeatUntil( val targetStatus: TreeNodeHandler.Status = TreeNodeHandler.Status.SUCCESS, val limit: Int = 10, ) : TreeNode.Decorator { - override val name: String = "repeat_until" - override val description: String = "" - class Handler : TreeNodeHandler { override fun execute(handlers: TreeNodeHandlerModule, descriptor: RepeatUntil): TreeNodeHandler.Status { - val handler = handlers[descriptor.child.name] + val handler = handlers[descriptor.child] var iteration = 0 var currentStatus: TreeNodeHandler.Status diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt index 6941b5f..92cc29e 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -7,9 +7,6 @@ data class Selector( val executionOrder: TreeExecutionOrder, override val children: List, ) : TreeNode.Composite { - override val name: String = "selector" - override val description = "Executes its child nodes in order until one succeeds or all fail, similar to an `or` operator" - class Handler : TreeNodeHandler { override fun execute( handlers: TreeNodeHandlerModule, @@ -21,7 +18,7 @@ data class Selector( } for (child in children) { - val handler = handlers[child.name] + val handler = handlers[child] val result = handler.execute(handlers, child) if (result == TreeNodeHandler.Status.SUCCESS || result == TreeNodeHandler.Status.ABORT) { diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt index d9b1fa2..48d8914 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -7,9 +7,6 @@ data class Sequencer( val executionOrder: TreeExecutionOrder, override val children: List, ) : TreeNode.Composite { - override val name: String = "sequencer" - override val description = "Executes its child nodes in order until one fails or all succeed, similar to an `and` operator" - class Handler : TreeNodeHandler { override fun execute( handlers: TreeNodeHandlerModule, @@ -21,7 +18,7 @@ data class Sequencer( } for (child in children) { - val handler = handlers[child.name] + val handler = handlers[child] val result = handler.execute(handlers, child) if (result == TreeNodeHandler.Status.FAILURE || result == TreeNodeHandler.Status.ABORT) { diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt index d099811..c7a349e 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -6,15 +6,12 @@ import kotlinx.serialization.Serializable data class Succeeder( override val child: TreeNode, ) : TreeNode.Decorator { - override val name: String = "succeeder" - override val description: String = "Transforms the result of its child into success, regardless of what the child returns" - class Handler : TreeNodeHandler { override fun execute( handlers: TreeNodeHandlerModule, descriptor: Succeeder, ): TreeNodeHandler.Status { - val handler = handlers[descriptor.child.name] + val handler = handlers[descriptor.child] return handler.execute(handlers, descriptor.child) } } diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt index ee998c5..887e38f 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt @@ -1,9 +1,6 @@ package com.tpcly.behaviourtree interface TreeNode { - val name: String - val description: String - interface Composite : TreeNode { val children: List } diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt index 146ac35..2ed1ba3 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt @@ -5,20 +5,21 @@ class TreeNodeHandlerModule(handlers: Map>) { // Justification: TreeNodeHandler has a generic class constraint @Suppress("UNCHECKED_CAST") - operator fun get(name: String): TreeNodeHandler { - val handler = allHandlers[name] ?: throw NullPointerException("Node $name not found") + operator fun get(child: TreeNode): TreeNodeHandler { + val className = child.javaClass.name + val handler = allHandlers[className] ?: throw NullPointerException("Node $className not found") return handler as TreeNodeHandler } companion object { private val defaultHandlers = mapOf( // Composite - "sequencer" to Sequencer.Handler(), - "selector" to Selector.Handler(), + Sequencer::class.java.name to Sequencer.Handler(), + Selector::class.java.name to Selector.Handler(), // Decorators - "repeat_until" to RepeatUntil.Handler(), - "succeeder" to Succeeder.Handler(), - "inverter" to Inverter.Handler() + RepeatUntil::class.java.name to RepeatUntil.Handler(), + Succeeder::class.java.name to Succeeder.Handler(), + Inverter::class.java.name to Inverter.Handler() ) } } \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt index 059f4d2..84cbcf8 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt @@ -3,8 +3,8 @@ package com.tpcly.behaviourtree class TreeNodeHandlerModuleBuilder { val handlers: MutableMap> = mutableMapOf() - fun handler(name: String, handler: TreeNodeHandler) { - handlers[name] = handler + inline fun handler(handler: TreeNodeHandler) { + handlers[T::class.java.name] = handler } internal fun build(): TreeNodeHandlerModule { diff --git a/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt index e706971..9c888ea 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/JsonTests.kt @@ -12,20 +12,20 @@ class JsonTests { fun testJson() { val tree = sequencer { +succeeder { - TestAction("test_1", 1337) + MockAction("test_1", 1337) } +repeatUntil { - TestAction("test_2", 69) + MockAction("test_2", 69) } - +TestAction("test_2", 69) - +TestAction("test_3", 420) + +MockAction("test_2", 69) + +MockAction("test_3", 420) } val json = Json { prettyPrint = true serializersModule = SerializersModule { polymorphic(TreeNode::class) { - subclass(TestAction::class) + subclass(MockAction::class) subclass(RepeatUntil::class) subclass(Succeeder::class) } diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt similarity index 63% rename from src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt rename to src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt index 0113812..cdbb6f3 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TestAction.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt @@ -3,15 +3,12 @@ package com.tpcly.behaviourtree import kotlinx.serialization.Serializable @Serializable -data class TestAction( +data class MockAction( val inputOne: String, val inputTwo: Int, ) : TreeNode { - override val name: String = "test" - override val description: String = "Test" - - class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerModule, descriptor: TestAction): TreeNodeHandler.Status { + class Handler : TreeNodeHandler { + override fun execute(handlers: TreeNodeHandlerModule, descriptor: MockAction): TreeNodeHandler.Status { println("${descriptor.inputOne} ${descriptor.inputTwo}") return TreeNodeHandler.Status.SUCCESS } diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt index ba76a33..aa51be8 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -11,7 +11,7 @@ class TreeExecutorTests { @Test fun testSequencer() { val handlers = TreeNodeHandlerModule { - handler("test", TestAction.Handler()) + handler(MockAction.Handler()) } val json = Json { @@ -19,9 +19,10 @@ class TreeExecutorTests { serializersModule = SerializersModule { polymorphic(TreeNode::class) { subclass(Sequencer::class) + subclass(RepeatUntil::class) subclass(Selector::class) subclass(Succeeder::class) - subclass(TestAction::class) + subclass(MockAction::class) } } } @@ -29,24 +30,32 @@ class TreeExecutorTests { val tree= json.decodeFromString( """ { - "type": "com.tpcly.behaviourtree.node.Sequencer", + "type": "com.tpcly.behaviourtree.Sequencer", "executionOrder": "IN_ORDER", "children": [ { - "type": "com.tpcly.behaviourtree.node.Succeeder", + "type": "com.tpcly.behaviourtree.Succeeder", "child": { - "type": "com.tpcly.behaviourtree.TestAction", + "type": "com.tpcly.behaviourtree.MockAction", "inputOne": "test_1", "inputTwo": 1337 } }, { - "type": "com.tpcly.behaviourtree.TestAction", + "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.TestAction", + "type": "com.tpcly.behaviourtree.MockAction", "inputOne": "test_3", "inputTwo": 420 } @@ -69,7 +78,7 @@ class TreeExecutorTests { // ) - val rootHandler = handlers[tree.name] + val rootHandler = handlers[tree] rootHandler.execute(handlers, tree) } } \ No newline at end of file From 6f65ce17a835a49c0f6ea27477ce90c44586e41d Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Thu, 15 Aug 2024 18:10:39 +0200 Subject: [PATCH 07/15] Replace get with execute --- src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt | 3 +-- src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt | 4 +--- src/main/kotlin/com/tpcly/behaviourtree/Selector.kt | 3 +-- src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt | 3 +-- src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt | 4 ++-- .../kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt | 6 ++++-- .../kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt | 4 +--- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt index 0cb502d..fb65f2e 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -11,8 +11,7 @@ data class Inverter( handlers: TreeNodeHandlerModule, descriptor: Inverter, ): TreeNodeHandler.Status { - val handler = handlers[descriptor.child] - return when (val result = handler.execute(handlers, descriptor.child)) { + return when (val result = handlers.execute(descriptor.child)) { TreeNodeHandler.Status.SUCCESS -> TreeNodeHandler.Status.FAILURE TreeNodeHandler.Status.FAILURE -> TreeNodeHandler.Status.SUCCESS else -> result diff --git a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt index 1e09c98..a55fbd4 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -10,13 +10,11 @@ data class RepeatUntil( ) : TreeNode.Decorator { class Handler : TreeNodeHandler { override fun execute(handlers: TreeNodeHandlerModule, descriptor: RepeatUntil): TreeNodeHandler.Status { - val handler = handlers[descriptor.child] - var iteration = 0 var currentStatus: TreeNodeHandler.Status do { - currentStatus = handler.execute(handlers, descriptor.child) + currentStatus = handlers.execute(descriptor.child) iteration++ } while ( currentStatus != descriptor.targetStatus && diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt index 92cc29e..c2523f0 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -18,8 +18,7 @@ data class Selector( } for (child in children) { - val handler = handlers[child] - val result = handler.execute(handlers, child) + val result = handlers.execute(child) if (result == TreeNodeHandler.Status.SUCCESS || result == TreeNodeHandler.Status.ABORT) { return result diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt index 48d8914..ce60825 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -18,8 +18,7 @@ data class Sequencer( } for (child in children) { - val handler = handlers[child] - val result = handler.execute(handlers, child) + val result = handlers.execute(child) if (result == TreeNodeHandler.Status.FAILURE || result == TreeNodeHandler.Status.ABORT) { return result diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt index c7a349e..d95003c 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -11,8 +11,8 @@ data class Succeeder( handlers: TreeNodeHandlerModule, descriptor: Succeeder, ): TreeNodeHandler.Status { - val handler = handlers[descriptor.child] - return handler.execute(handlers, descriptor.child) + handlers.execute(descriptor.child) + return TreeNodeHandler.Status.SUCCESS } } } \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt index 2ed1ba3..c4d8b44 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt @@ -5,10 +5,12 @@ class TreeNodeHandlerModule(handlers: Map>) { // Justification: TreeNodeHandler has a generic class constraint @Suppress("UNCHECKED_CAST") - operator fun get(child: TreeNode): TreeNodeHandler { + fun execute(child: TreeNode): TreeNodeHandler.Status { val className = child.javaClass.name val handler = allHandlers[className] ?: throw NullPointerException("Node $className not found") - return handler as TreeNodeHandler + val result = (handler as TreeNodeHandler).execute(this, child) + + return result } companion object { diff --git a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt index aa51be8..48f640e 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -77,8 +77,6 @@ class TreeExecutorTests { // ) // ) - - val rootHandler = handlers[tree] - rootHandler.execute(handlers, tree) + handlers.execute(tree) } } \ No newline at end of file From 866085b7a5d5ba4674e975ef93dd16852d1540b5 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Thu, 15 Aug 2024 18:12:16 +0200 Subject: [PATCH 08/15] Separate status into distinct enum class --- src/main/kotlin/com/tpcly/behaviourtree/Builder.kt | 2 +- src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt | 6 +++--- .../kotlin/com/tpcly/behaviourtree/RepeatUntil.kt | 10 +++++----- src/main/kotlin/com/tpcly/behaviourtree/Selector.kt | 6 +++--- src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt | 6 +++--- src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt | 4 ++-- .../com/tpcly/behaviourtree/TreeNodeHandler.kt | 12 +----------- .../com/tpcly/behaviourtree/TreeNodeHandlerModule.kt | 2 +- .../kotlin/com/tpcly/behaviourtree/TreeNodeStatus.kt | 11 +++++++++++ .../kotlin/com/tpcly/behaviourtree/MockAction.kt | 4 ++-- 10 files changed, 32 insertions(+), 31 deletions(-) create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeNodeStatus.kt diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt index fa63be4..25dad7a 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt @@ -27,7 +27,7 @@ fun inverter(init: () -> TreeNode) = Inverter(init()) fun succeeder(init: () -> TreeNode) = Succeeder(init()) fun repeatUntil( - status: TreeNodeHandler.Status = TreeNodeHandler.Status.SUCCESS, + status: TreeNodeStatus = TreeNodeStatus.SUCCESS, limit: Int = 10, init: () -> TreeNode, ) = RepeatUntil(init(), status, limit) \ 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 index fb65f2e..e578a26 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -10,10 +10,10 @@ data class Inverter( override fun execute( handlers: TreeNodeHandlerModule, descriptor: Inverter, - ): TreeNodeHandler.Status { + ): TreeNodeStatus { return when (val result = handlers.execute(descriptor.child)) { - TreeNodeHandler.Status.SUCCESS -> TreeNodeHandler.Status.FAILURE - TreeNodeHandler.Status.FAILURE -> TreeNodeHandler.Status.SUCCESS + TreeNodeStatus.SUCCESS -> TreeNodeStatus.FAILURE + TreeNodeStatus.FAILURE -> TreeNodeStatus.SUCCESS else -> result } } diff --git a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt index a55fbd4..5ed935a 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -5,25 +5,25 @@ import kotlinx.serialization.Serializable @Serializable data class RepeatUntil( override val child: TreeNode, - val targetStatus: TreeNodeHandler.Status = TreeNodeHandler.Status.SUCCESS, + val targetStatus: TreeNodeStatus = TreeNodeStatus.SUCCESS, val limit: Int = 10, ) : TreeNode.Decorator { class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerModule, descriptor: RepeatUntil): TreeNodeHandler.Status { + override fun execute(handlers: TreeNodeHandlerModule, descriptor: RepeatUntil): TreeNodeStatus { var iteration = 0 - var currentStatus: TreeNodeHandler.Status + var currentStatus: TreeNodeStatus do { currentStatus = handlers.execute(descriptor.child) iteration++ } while ( currentStatus != descriptor.targetStatus && - currentStatus != TreeNodeHandler.Status.ABORT && + currentStatus != TreeNodeStatus.ABORT && iteration < descriptor.limit ) return when { - iteration >= descriptor.limit -> TreeNodeHandler.Status.FAILURE + iteration >= descriptor.limit -> TreeNodeStatus.FAILURE else -> currentStatus } } diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt index c2523f0..e474d02 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -11,7 +11,7 @@ data class Selector( override fun execute( handlers: TreeNodeHandlerModule, descriptor: Selector, - ): TreeNodeHandler.Status { + ): TreeNodeStatus { val children = when (descriptor.executionOrder) { TreeExecutionOrder.RANDOM -> descriptor.children.shuffled() else -> descriptor.children @@ -20,12 +20,12 @@ data class Selector( for (child in children) { val result = handlers.execute(child) - if (result == TreeNodeHandler.Status.SUCCESS || result == TreeNodeHandler.Status.ABORT) { + if (result == TreeNodeStatus.SUCCESS || result == TreeNodeStatus.ABORT) { return result } } - return TreeNodeHandler.Status.FAILURE + 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 index ce60825..66df3fb 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -11,7 +11,7 @@ data class Sequencer( override fun execute( handlers: TreeNodeHandlerModule, descriptor: Sequencer, - ): TreeNodeHandler.Status { + ): TreeNodeStatus { val children = when (descriptor.executionOrder) { TreeExecutionOrder.RANDOM -> descriptor.children.shuffled() else -> descriptor.children @@ -20,12 +20,12 @@ data class Sequencer( for (child in children) { val result = handlers.execute(child) - if (result == TreeNodeHandler.Status.FAILURE || result == TreeNodeHandler.Status.ABORT) { + if (result == TreeNodeStatus.FAILURE || result == TreeNodeStatus.ABORT) { return result } } - return TreeNodeHandler.Status.SUCCESS + return TreeNodeStatus.SUCCESS } } } \ 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 index d95003c..378daba 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -10,9 +10,9 @@ data class Succeeder( override fun execute( handlers: TreeNodeHandlerModule, descriptor: Succeeder, - ): TreeNodeHandler.Status { + ): TreeNodeStatus { handlers.execute(descriptor.child) - return TreeNodeHandler.Status.SUCCESS + return TreeNodeStatus.SUCCESS } } } \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt index 9d1cd3b..bef1d99 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt @@ -4,15 +4,5 @@ interface TreeNodeHandler { fun execute( handlers: TreeNodeHandlerModule, descriptor: T, - ): Status - - enum class Status { - SUCCESS, - FAILURE, - ABORT; - - companion object { - fun fromBoolean(value: Boolean): Status = if (value) SUCCESS else FAILURE - } - } + ): TreeNodeStatus } \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt index c4d8b44..74158d0 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt @@ -5,7 +5,7 @@ class TreeNodeHandlerModule(handlers: Map>) { // Justification: TreeNodeHandler has a generic class constraint @Suppress("UNCHECKED_CAST") - fun execute(child: TreeNode): TreeNodeHandler.Status { + fun execute(child: TreeNode): TreeNodeStatus { val className = child.javaClass.name val handler = allHandlers[className] ?: throw NullPointerException("Node $className not found") val result = (handler as TreeNodeHandler).execute(this, child) 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/test/kotlin/com/tpcly/behaviourtree/MockAction.kt b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt index cdbb6f3..7e7a5f9 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt @@ -8,9 +8,9 @@ data class MockAction( val inputTwo: Int, ) : TreeNode { class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerModule, descriptor: MockAction): TreeNodeHandler.Status { + override fun execute(handlers: TreeNodeHandlerModule, descriptor: MockAction): TreeNodeStatus { println("${descriptor.inputOne} ${descriptor.inputTwo}") - return TreeNodeHandler.Status.SUCCESS + return TreeNodeStatus.SUCCESS } } } \ No newline at end of file From fdc2e0e86307acff0d90e4679e3e0b69daa3ed6d Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Tue, 20 Aug 2024 20:09:37 +0200 Subject: [PATCH 09/15] Remove separate handlers --- .../com/tpcly/behaviourtree/Inverter.kt | 15 ++++------ .../com/tpcly/behaviourtree/RepeatUntil.kt | 30 +++++++++---------- .../com/tpcly/behaviourtree/Selector.kt | 27 +++++++---------- .../com/tpcly/behaviourtree/Sequencer.kt | 27 +++++++---------- .../com/tpcly/behaviourtree/Succeeder.kt | 11 ++----- .../com/tpcly/behaviourtree/TreeNode.kt | 2 ++ .../tpcly/behaviourtree/TreeNodeHandler.kt | 8 ----- .../behaviourtree/TreeNodeHandlerModule.kt | 27 ----------------- .../TreeNodeHandlerModuleBuilder.kt | 19 ------------ .../com/tpcly/behaviourtree/MockAction.kt | 8 ++--- .../tpcly/behaviourtree/TreeExecutorTests.kt | 8 ++--- 11 files changed, 51 insertions(+), 131 deletions(-) delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt index e578a26..4f067a0 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Inverter.kt @@ -6,16 +6,11 @@ import kotlinx.serialization.Serializable data class Inverter( override val child: TreeNode, ) : TreeNode.Decorator { - class Handler : TreeNodeHandler { - override fun execute( - handlers: TreeNodeHandlerModule, - descriptor: Inverter, - ): TreeNodeStatus { - return when (val result = handlers.execute(descriptor.child)) { - TreeNodeStatus.SUCCESS -> TreeNodeStatus.FAILURE - TreeNodeStatus.FAILURE -> TreeNodeStatus.SUCCESS - else -> result - } + 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 index 5ed935a..1d1126d 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/RepeatUntil.kt @@ -8,24 +8,22 @@ data class RepeatUntil( val targetStatus: TreeNodeStatus = TreeNodeStatus.SUCCESS, val limit: Int = 10, ) : TreeNode.Decorator { - class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerModule, descriptor: RepeatUntil): TreeNodeStatus { - var iteration = 0 - var currentStatus: TreeNodeStatus + override fun execute(): TreeNodeStatus { + var iteration = 0 + var currentStatus: TreeNodeStatus - do { - currentStatus = handlers.execute(descriptor.child) - iteration++ - } while ( - currentStatus != descriptor.targetStatus && - currentStatus != TreeNodeStatus.ABORT && - iteration < descriptor.limit - ) + do { + currentStatus = child.execute() + iteration++ + } while ( + currentStatus != targetStatus && + currentStatus != TreeNodeStatus.ABORT && + iteration < limit + ) - return when { - iteration >= descriptor.limit -> TreeNodeStatus.FAILURE - else -> currentStatus - } + 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 index e474d02..851e597 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Selector.kt @@ -7,25 +7,20 @@ data class Selector( val executionOrder: TreeExecutionOrder, override val children: List, ) : TreeNode.Composite { - class Handler : TreeNodeHandler { - override fun execute( - handlers: TreeNodeHandlerModule, - descriptor: Selector, - ): TreeNodeStatus { - val children = when (descriptor.executionOrder) { - TreeExecutionOrder.RANDOM -> descriptor.children.shuffled() - else -> descriptor.children - } + override fun execute(): TreeNodeStatus { + val children = when (executionOrder) { + TreeExecutionOrder.RANDOM -> children.shuffled() + else -> children + } - for (child in children) { - val result = handlers.execute(child) + for (child in children) { + val result = child.execute() - if (result == TreeNodeStatus.SUCCESS || result == TreeNodeStatus.ABORT) { - return result - } + if (result == TreeNodeStatus.SUCCESS || result == TreeNodeStatus.ABORT) { + return result } - - return TreeNodeStatus.FAILURE } + + 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 index 66df3fb..cea29d6 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Sequencer.kt @@ -7,25 +7,20 @@ data class Sequencer( val executionOrder: TreeExecutionOrder, override val children: List, ) : TreeNode.Composite { - class Handler : TreeNodeHandler { - override fun execute( - handlers: TreeNodeHandlerModule, - descriptor: Sequencer, - ): TreeNodeStatus { - val children = when (descriptor.executionOrder) { - TreeExecutionOrder.RANDOM -> descriptor.children.shuffled() - else -> descriptor.children - } + override fun execute(): TreeNodeStatus { + val children = when (executionOrder) { + TreeExecutionOrder.RANDOM -> children.shuffled() + else -> children + } - for (child in children) { - val result = handlers.execute(child) + for (child in children) { + val result = child.execute() - if (result == TreeNodeStatus.FAILURE || result == TreeNodeStatus.ABORT) { - return result - } + if (result == TreeNodeStatus.FAILURE || result == TreeNodeStatus.ABORT) { + return result } - - return TreeNodeStatus.SUCCESS } + + return TreeNodeStatus.SUCCESS } } \ 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 index 378daba..2084f09 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Succeeder.kt @@ -6,13 +6,8 @@ import kotlinx.serialization.Serializable data class Succeeder( override val child: TreeNode, ) : TreeNode.Decorator { - class Handler : TreeNodeHandler { - override fun execute( - handlers: TreeNodeHandlerModule, - descriptor: Succeeder, - ): TreeNodeStatus { - handlers.execute(descriptor.child) - return TreeNodeStatus.SUCCESS - } + override fun execute(): TreeNodeStatus { + child.execute() + return TreeNodeStatus.SUCCESS } } \ 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 index 887e38f..5a091ed 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt @@ -1,6 +1,8 @@ package com.tpcly.behaviourtree interface TreeNode { + fun execute(): TreeNodeStatus + interface Composite : TreeNode { val children: List } diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt deleted file mode 100644 index bef1d99..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandler.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tpcly.behaviourtree - -interface TreeNodeHandler { - fun execute( - handlers: TreeNodeHandlerModule, - descriptor: T, - ): TreeNodeStatus -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt deleted file mode 100644 index 74158d0..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModule.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.tpcly.behaviourtree - -class TreeNodeHandlerModule(handlers: Map>) { - private val allHandlers = handlers + defaultHandlers - - // Justification: TreeNodeHandler has a generic class constraint - @Suppress("UNCHECKED_CAST") - fun execute(child: TreeNode): TreeNodeStatus { - val className = child.javaClass.name - val handler = allHandlers[className] ?: throw NullPointerException("Node $className not found") - val result = (handler as TreeNodeHandler).execute(this, child) - - return result - } - - companion object { - private val defaultHandlers = mapOf( - // Composite - Sequencer::class.java.name to Sequencer.Handler(), - Selector::class.java.name to Selector.Handler(), - // Decorators - RepeatUntil::class.java.name to RepeatUntil.Handler(), - Succeeder::class.java.name to Succeeder.Handler(), - Inverter::class.java.name to Inverter.Handler() - ) - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt deleted file mode 100644 index 84cbcf8..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNodeHandlerModuleBuilder.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tpcly.behaviourtree - -class TreeNodeHandlerModuleBuilder { - val handlers: MutableMap> = mutableMapOf() - - inline fun handler(handler: TreeNodeHandler) { - handlers[T::class.java.name] = handler - } - - internal fun build(): TreeNodeHandlerModule { - return TreeNodeHandlerModule(handlers) - } -} - -fun TreeNodeHandlerModule(builderAction: TreeNodeHandlerModuleBuilder.() -> Unit): TreeNodeHandlerModule { - val builder = TreeNodeHandlerModuleBuilder() - builder.builderAction() - return builder.build() -} \ 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 index 7e7a5f9..dafd840 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/MockAction.kt @@ -7,10 +7,8 @@ data class MockAction( val inputOne: String, val inputTwo: Int, ) : TreeNode { - class Handler : TreeNodeHandler { - override fun execute(handlers: TreeNodeHandlerModule, descriptor: MockAction): TreeNodeStatus { - println("${descriptor.inputOne} ${descriptor.inputTwo}") - return TreeNodeStatus.SUCCESS - } + 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/TreeExecutorTests.kt b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt index 48f640e..537d818 100644 --- a/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt +++ b/src/test/kotlin/com/tpcly/behaviourtree/TreeExecutorTests.kt @@ -10,10 +10,6 @@ class TreeExecutorTests { @Test fun testSequencer() { - val handlers = TreeNodeHandlerModule { - handler(MockAction.Handler()) - } - val json = Json { prettyPrint = true serializersModule = SerializersModule { @@ -27,7 +23,7 @@ class TreeExecutorTests { } } - val tree= json.decodeFromString( + val tree = json.decodeFromString( """ { "type": "com.tpcly.behaviourtree.Sequencer", @@ -77,6 +73,6 @@ class TreeExecutorTests { // ) // ) - handlers.execute(tree) + tree.execute() } } \ No newline at end of file From 221e9f6a58ec30ca035beacf662dd319beaed2b1 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Tue, 20 Aug 2024 20:52:10 +0200 Subject: [PATCH 10/15] Add sub trees --- src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt index 5a091ed..faab027 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt @@ -10,4 +10,12 @@ interface TreeNode { interface Decorator : TreeNode { val child: TreeNode } + + interface Sub : TreeNode { + val tree: TreeNode + + override fun execute(): TreeNodeStatus { + return tree.execute() + } + } } \ No newline at end of file From a430adfca49ea480d7209d44ebfcb06d5c6ef421 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Tue, 20 Aug 2024 20:52:45 +0200 Subject: [PATCH 11/15] Add pre-defined leaf nodes --- src/main/kotlin/com/tpcly/behaviourtree/Builder.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt index 25dad7a..6b03e66 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt @@ -1,5 +1,18 @@ package com.tpcly.behaviourtree +/** + * Pre-defined leaf nodes + * TODO: Somehow make these serializable + */ +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()) +} + + /** * Composite nodes */ From e9457c2507733cad37c467751e6265b7b528217f Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Tue, 20 Aug 2024 20:54:54 +0200 Subject: [PATCH 12/15] Add inverted extension --- src/main/kotlin/com/tpcly/behaviourtree/Builder.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt index 6b03e66..194c17f 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt @@ -37,6 +37,8 @@ fun sequencer( */ fun inverter(init: () -> TreeNode) = Inverter(init()) +fun TreeNode.inverted() = Inverter(this) + fun succeeder(init: () -> TreeNode) = Succeeder(init()) fun repeatUntil( From e085bcaa7b425f886b809ab6981f5ad0e6467085 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 6 Sep 2024 17:37:44 +0200 Subject: [PATCH 13/15] Replace builder with object and add additional functions --- .../kotlin/com/tpcly/behaviourtree/Builder.kt | 48 ---------------- .../com/tpcly/behaviourtree/TreeBuilder.kt | 57 +++++++++++++++++++ 2 files changed, 57 insertions(+), 48 deletions(-) delete mode 100644 src/main/kotlin/com/tpcly/behaviourtree/Builder.kt create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/TreeBuilder.kt diff --git a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt b/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt deleted file mode 100644 index 194c17f..0000000 --- a/src/main/kotlin/com/tpcly/behaviourtree/Builder.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.tpcly.behaviourtree - -/** - * Pre-defined leaf nodes - * TODO: Somehow make these serializable - */ -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()) -} - - -/** - * 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 repeatUntil( - status: TreeNodeStatus = TreeNodeStatus.SUCCESS, - limit: Int = 10, - init: () -> TreeNode, -) = RepeatUntil(init(), status, limit) \ 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) +} From 7e7e773519d8252ceaf0e65427a467890ca19db7 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 6 Sep 2024 17:38:00 +0200 Subject: [PATCH 14/15] Add conditional leaf node --- src/main/kotlin/com/tpcly/behaviourtree/Conditional.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/kotlin/com/tpcly/behaviourtree/Conditional.kt 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 From 43a26f6c3bac2a716b7e8e4c04349de690d71714 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 6 Sep 2024 17:38:10 +0200 Subject: [PATCH 15/15] Rename `tree` to `root` --- src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt index faab027..7f37434 100644 --- a/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt +++ b/src/main/kotlin/com/tpcly/behaviourtree/TreeNode.kt @@ -12,10 +12,10 @@ interface TreeNode { } interface Sub : TreeNode { - val tree: TreeNode + val root: TreeNode override fun execute(): TreeNodeStatus { - return tree.execute() + return root.execute() } } } \ No newline at end of file