A bit more cleanup to avoid duplicate testing and to separate the GOAP algorithm code from the little AI Manager thing.

This commit is contained in:
Zed A. Shaw 2025-03-11 10:57:29 -04:00
parent b2c1b220ac
commit 3f83d3f0bb
8 changed files with 296 additions and 290 deletions

View file

@ -103,105 +103,9 @@ TEST_CASE("basic feature tests", "[ai]") {
REQUIRE(state[ENEMY_DEAD]);
}
TEST_CASE("wargame test from cppAI", "[ai]") {
std::vector<ai::Action> actions;
auto profile = R"({
"target_acquired": 0,
"target_lost": 1,
"target_in_warhead_range": 2,
"target_dead": 3
})"_json;
// Now establish all the possible actions for the action pool
// In this example we're providing the AI some different FPS actions
auto config = R"({
"name": "searchSpiral",
"cost": 5,
"needs": {
"target_acquired": false,
"target_lost": true
},
"effects": {
"target_acquired": true
}
})"_json;
auto spiral = ai::config_action(profile, config);
actions.push_back(spiral);
config = R"({
"name": "searchSerpentine",
"cost": 5,
"needs": {
"target_acquired": false,
"target_lost": false
},
"effects": {
"target_acquired": true
}
})"_json;
auto serpentine = ai::config_action(profile, config);
actions.push_back(serpentine);
config = R"({
"name": "interceptTarget",
"cost": 5,
"needs": {
"target_acquired": true,
"target_dead": false
},
"effects": {
"target_in_warhead_range": true
}
})"_json;
auto intercept = ai::config_action(profile, config);
actions.push_back(intercept);
config = R"({
"name": "detonateNearTarget",
"cost": 5,
"needs": {
"target_in_warhead_range": true,
"target_acquired": true,
"target_dead": false
},
"effects": {
"target_dead": true
}
})"_json;
auto detonateNearTarget = ai::config_action(profile, config);
actions.push_back(detonateNearTarget);
// Here's the initial state...
config = R"({
"target_acquired": false,
"target_lost": true,
"target_in_warhead_range": false,
"target_dead": false
})"_json;
auto initial_state = ai::config_state(profile, config);
// ...and the goal state
config = R"({
"target_dead": true
})"_json;
auto goal_target_dead = ai::config_state(profile, config);
auto result = ai::plan_actions(actions, initial_state, goal_target_dead);
REQUIRE(result != std::nullopt);
auto state = initial_state;
for(auto& action : *result) {
fmt::println("ACTION: {}", action.$name);
state = action.apply_effect(state);
}
REQUIRE(state[profile["target_dead"]]);
}
TEST_CASE("ai as a module like sound/sprites", "[ai]") {
ai::init();
ai::init("tests/ai_fixture.json");
auto start = ai::load_state("test_start");
auto goal = ai::load_state("test_goal");
@ -216,5 +120,4 @@ TEST_CASE("ai as a module like sound/sprites", "[ai]") {
}
REQUIRE(state[ai::state_id("target_dead")]);
}

85
tests/ai_fixture.json Normal file
View file

@ -0,0 +1,85 @@
{
"profile": {
"target_acquired": 0,
"target_lost": 1,
"target_in_warhead_range": 2,
"target_dead": 3
},
"actions": [
{
"name": "searchSpiral",
"cost": 10,
"needs": {
"target_acquired": false,
"target_lost": true
},
"effects": {
"target_acquired": true
}
},
{
"name": "searchSerpentine",
"cost": 5,
"needs": {
"target_acquired": false,
"target_lost": false
},
"effects": {
"target_acquired": true
}
},
{
"name": "searchSpiral",
"cost": 5,
"needs": {
"target_acquired": false,
"target_lost": true
},
"effects": {
"target_acquired": true
}
},
{
"name": "interceptTarget",
"cost": 5,
"needs": {
"target_acquired": true,
"target_dead": false
},
"effects": {
"target_in_warhead_range": true
}
},
{
"name": "detonateNearTarget",
"cost": 5,
"needs": {
"target_in_warhead_range": true,
"target_acquired": true,
"target_dead": false
},
"effects": {
"target_dead": true
}
}
],
"states": {
"test_start": {
"target_acquired": false,
"target_lost": true,
"target_in_warhead_range": false,
"target_dead": false
},
"test_goal": {
"target_dead": true
}
},
"scripts": {
"test1": [
"searchSpiral",
"searchSerpentine",
"searchSpiral",
"interceptTarget",
"detonateNearTarget"]
}
}