Merge pull request #33 from limbonaut/runlimit-policy

Runlimit policy
This commit is contained in:
Serhii Snitsaruk 2024-02-10 21:08:12 +01:00 committed by GitHub
commit 5621b433c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 64 additions and 7 deletions

View File

@ -16,23 +16,39 @@ void BTRunLimit::set_run_limit(int p_value) {
emit_changed();
}
void BTRunLimit::set_count_policy(CountPolicy p_policy) {
count_policy = p_policy;
emit_changed();
}
String BTRunLimit::_generate_name() {
return vformat("RunLimit x%d", run_limit);
}
BT::Status BTRunLimit::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(get_child_count() == 0, FAILURE, "BT decorator has no child.");
if (get_child(0)->get_status() != RUNNING) {
if (num_runs >= run_limit) {
return FAILURE;
}
if (num_runs >= run_limit) {
return FAILURE;
}
Status child_status = get_child(0)->execute(p_delta);
if ((count_policy == COUNT_SUCCESSFUL && child_status == SUCCESS) ||
(count_policy == COUNT_FAILED && child_status == FAILURE) ||
(count_policy == COUNT_ALL && child_status != RUNNING)) {
num_runs += 1;
}
return get_child(0)->execute(p_delta);
return child_status;
}
void BTRunLimit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_run_limit", "p_value"), &BTRunLimit::set_run_limit);
ClassDB::bind_method(D_METHOD("get_run_limit"), &BTRunLimit::get_run_limit);
ClassDB::bind_method(D_METHOD("set_count_policy", "p_policy"), &BTRunLimit::set_count_policy);
ClassDB::bind_method(D_METHOD("get_count_policy"), &BTRunLimit::get_count_policy);
ADD_PROPERTY(PropertyInfo(Variant::INT, "run_limit"), "set_run_limit", "get_run_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "count_policy", PROPERTY_HINT_ENUM, "COUNT_SUCCESSFUL,COUNT_FAILED,COUNT_ALL"), "set_count_policy", "get_count_policy");
BIND_ENUM_CONSTANT(COUNT_SUCCESSFUL);
BIND_ENUM_CONSTANT(COUNT_FAILED);
BIND_ENUM_CONSTANT(COUNT_ALL);
}

View File

@ -18,8 +18,16 @@ class BTRunLimit : public BTDecorator {
GDCLASS(BTRunLimit, BTDecorator);
TASK_CATEGORY(Decorators);
public:
enum CountPolicy {
COUNT_SUCCESSFUL,
COUNT_FAILED,
COUNT_ALL,
};
private:
int run_limit = 1;
CountPolicy count_policy = CountPolicy::COUNT_SUCCESSFUL;
int num_runs = 0;
protected:
@ -31,6 +39,11 @@ protected:
public:
void set_run_limit(int p_value);
int get_run_limit() const { return run_limit; }
void set_count_policy(CountPolicy p_policy);
CountPolicy get_count_policy() const { return count_policy; }
};
VARIANT_ENUM_CAST(BTRunLimit::CountPolicy);
#endif // BT_RUN_LIMIT_H

View File

@ -10,8 +10,22 @@
<tutorials>
</tutorials>
<members>
<member name="count_policy" type="int" setter="set_count_policy" getter="get_count_policy" enum="BTRunLimit.CountPolicy" default="0">
Which runs should be counted towards the limit: successful, failed, or all?
</member>
<member name="run_limit" type="int" setter="set_run_limit" getter="get_run_limit" default="1">
The maximum number of times the child is permitted to be executed.
</member>
</members>
<constants>
<constant name="COUNT_SUCCESSFUL" value="0" enum="CountPolicy">
Count only successful runs towards the limit.
</constant>
<constant name="COUNT_FAILED" value="1" enum="CountPolicy">
Count only failed runs towards the limit.
</constant>
<constant name="COUNT_ALL" value="2" enum="CountPolicy">
Count successful and failed runs towards the limit.
</constant>
</constants>
</class>

View File

@ -40,26 +40,40 @@ TEST_CASE("[Modules][LimboAI] BTRunLimit") {
SUBCASE("When the child task succeeds") {
task->ret_status = BTTask::SUCCESS;
lim->set_count_policy(BTRunLimit::COUNT_FAILED);
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 2, 2, 2); // * task executed
lim->set_count_policy(BTRunLimit::COUNT_SUCCESSFUL);
CHECK(lim->execute(0.01666) == BTTask::SUCCESS);
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::SUCCESS, 3, 3, 3); // * task executed
}
SUBCASE("When the child task fails") {
task->ret_status = BTTask::FAILURE;
lim->set_count_policy(BTRunLimit::COUNT_SUCCESSFUL);
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 2, 2, 2); // * task executed
lim->set_count_policy(BTRunLimit::COUNT_FAILED);
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
CHECK_STATUS_ENTRIES_TICKS_EXITS(task, BTTask::FAILURE, 3, 3, 3); // * task executed
}
task->ret_status = BTTask::SUCCESS;
lim->set_count_policy(BTRunLimit::COUNT_SUCCESSFUL);
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
CHECK_ENTRIES_TICKS_EXITS(task, 2, 2, 2); // * task not executed
CHECK_ENTRIES_TICKS_EXITS(task, 3, 3, 3); // * task not executed
CHECK(lim->execute(0.01666) == BTTask::FAILURE);
CHECK_ENTRIES_TICKS_EXITS(task, 2, 2, 2); // * task not executed
CHECK_ENTRIES_TICKS_EXITS(task, 3, 3, 3); // * task not executed
}
SUBCASE("When the child task takes more than one tick to finish") {
lim->set_run_limit(2);
lim->set_count_policy(BTRunLimit::COUNT_ALL);
task->ret_status = BTTask::RUNNING;
CHECK(lim->execute(0.01666) == BTTask::RUNNING);