Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runlimit policy #33

Merged
merged 3 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions bt/tasks/decorators/bt_run_limit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
13 changes: 13 additions & 0 deletions bt/tasks/decorators/bt_run_limit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -31,6 +39,11 @@ class BTRunLimit : public BTDecorator {
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
14 changes: 14 additions & 0 deletions doc_classes/BTRunLimit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
18 changes: 16 additions & 2 deletions tests/test_run_limit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading