#include #include "dbc.hpp" #include "goap.hpp" using namespace dbc; using namespace ailol; TEST_CASE("worldstate works", "[goap]") { enum StateNames { ENEMY_IN_RANGE, ENEMY_DEAD }; GOAPState goal; GOAPState start; std::vector actions; // start off enemy not dead and not in range start[ENEMY_DEAD] = false; start[ENEMY_IN_RANGE] = false; // end goal is enemy is dead goal[ENEMY_DEAD] = true; Action move_closer("move_closer", 10); move_closer.preconds[ENEMY_IN_RANGE] = false; move_closer.effects[ENEMY_IN_RANGE] = true; REQUIRE(move_closer.can_effect(start)); auto after_move_state = move_closer.apply_effect(start); REQUIRE(start[ENEMY_IN_RANGE] == false); REQUIRE(after_move_state[ENEMY_IN_RANGE] == true); REQUIRE(after_move_state[ENEMY_DEAD] == false); // start is clean but after move is dirty REQUIRE(move_closer.can_effect(start)); REQUIRE(!move_closer.can_effect(after_move_state)); REQUIRE(distance_to_goal(start, after_move_state) == 1); Action kill_it("kill_it", 10); kill_it.preconds[ENEMY_IN_RANGE] = true; kill_it.preconds[ENEMY_DEAD] = false; kill_it.effects[ENEMY_DEAD] = true; REQUIRE(!kill_it.can_effect(start)); REQUIRE(kill_it.can_effect(after_move_state)); auto after_kill_state = kill_it.apply_effect(after_move_state); REQUIRE(!kill_it.can_effect(after_kill_state)); REQUIRE(distance_to_goal(after_move_state, after_kill_state) == 1); actions.push_back(kill_it); actions.push_back(move_closer); REQUIRE(start != goal); } TEST_CASE("basic feature tests", "[goap]") { enum StateNames { ENEMY_IN_RANGE, ENEMY_DEAD }; GOAPState goal; GOAPState start; std::vector actions; // start off enemy not dead and not in range start[ENEMY_DEAD] = false; start[ENEMY_IN_RANGE] = false; // end goal is enemy is dead goal[ENEMY_DEAD] = true; Action move_closer("move_closer", 10); move_closer.preconds[ENEMY_IN_RANGE] = false; move_closer.effects[ENEMY_IN_RANGE] = true; Action kill_it("kill_it", 10); kill_it.preconds[ENEMY_IN_RANGE] = true; kill_it.preconds[ENEMY_DEAD] = false; kill_it.effects[ENEMY_DEAD] = true; // order seems to matter which is wrong actions.push_back(kill_it); actions.push_back(move_closer); auto result = plan_actions(actions, start, goal); REQUIRE(result != std::nullopt); auto state = start; for(auto& action : *result) { fmt::println("ACTION: {}", action.name); state = action.apply_effect(state); } REQUIRE(state[ENEMY_DEAD]); } TEST_CASE("wargame test from cppGOAP", "[goap]") { std::vector actions; // Constants for the various states are helpful to keep us from // accidentally mistyping a state name. enum WarGameStates { target_acquired, target_lost, target_in_warhead_range, target_dead }; // Now establish all the possible actions for the action pool // In this example we're providing the AI some different FPS actions Action spiral("searchSpiral", 5); spiral.preconds[target_acquired] = false; spiral.preconds[target_lost] = true; spiral.effects[target_acquired] = true; actions.push_back(spiral); Action serpentine("searchSerpentine", 5); serpentine.preconds[target_acquired] = false; serpentine.preconds[target_lost] = false; serpentine.effects[target_acquired] = true; actions.push_back(serpentine); Action intercept("interceptTarget", 5); intercept.preconds[target_acquired] = true; intercept.preconds[target_dead] = false; intercept.effects[target_in_warhead_range] = true; actions.push_back(intercept); Action detonateNearTarget("detonateNearTarget", 5); detonateNearTarget.preconds[target_in_warhead_range] = true; detonateNearTarget.preconds[target_acquired] = true; detonateNearTarget.preconds[target_dead] = false; detonateNearTarget.effects[target_dead] = true; actions.push_back(detonateNearTarget); // Here's the initial state... GOAPState initial_state; initial_state[target_acquired] = false; initial_state[target_lost] = true; initial_state[target_in_warhead_range] = false; initial_state[target_dead] = false; // ...and the goal state GOAPState goal_target_dead; goal_target_dead[target_dead] = true; auto result = 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[target_dead]); }