test: cover flatWorld and grassWorld
This commit is contained in:
1 parent
30b64ccfaa
commit
a53c700c17
1 file changed
+158
@@ -0,0 +1,158 @@
|
|||||||
|
#include "testHarness.h"
|
||||||
|
#include "vf/Behavior.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace vf;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
World flatWorld(int halfSpan = 24) {
|
||||||
|
World world;
|
||||||
|
for (int x = -halfSpan; x <= halfSpan; ++x) {
|
||||||
|
for (int z = -halfSpan; z <= halfSpan; ++z) world.setBlock(x, 0, z, Block::Stone);
|
||||||
|
}
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
World grassWorld(int halfSpan = 12) {
|
||||||
|
World world;
|
||||||
|
for (int x = -halfSpan; x <= halfSpan; ++x) {
|
||||||
|
for (int z = -halfSpan; z <= halfSpan; ++z) world.setBlock(x, 0, z, Block::Grass);
|
||||||
|
}
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<BehaviorNode> constantAction(const char* name, BehaviorStatus status, int* counter) {
|
||||||
|
return std::unique_ptr<BehaviorNode>(new ActionNode(name, [status, counter](BehaviorContext&) {
|
||||||
|
if (counter != nullptr) ++(*counter);
|
||||||
|
return status;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<BehaviorNode> flagCondition(const char* name, const bool* source) {
|
||||||
|
return std::unique_ptr<BehaviorNode>(
|
||||||
|
new ConditionNode(name, [source](BehaviorContext&) { return *source; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reports Running for `runningTicks` calls, then Success once, then restarts.
|
||||||
|
std::unique_ptr<BehaviorNode> slowAction(const char* name, int runningTicks, int* counter,
|
||||||
|
int* progress) {
|
||||||
|
return std::unique_ptr<BehaviorNode>(
|
||||||
|
new ActionNode(name, [runningTicks, counter, progress](BehaviorContext&) {
|
||||||
|
++(*counter);
|
||||||
|
++(*progress);
|
||||||
|
if (*progress <= runningTicks) return BehaviorStatus::Running;
|
||||||
|
*progress = 0;
|
||||||
|
return BehaviorStatus::Success;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorContext freshContext(const World* world) {
|
||||||
|
BehaviorContext ctx;
|
||||||
|
ctx.world = world;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
BehaviorAgent makeAgent(Species species, const Vec3& position) {
|
||||||
|
BehaviorAgent agent;
|
||||||
|
agent.species = species;
|
||||||
|
agent.position = position;
|
||||||
|
agent.homePosition = position;
|
||||||
|
const SpeciesStats& stats = speciesStats(species);
|
||||||
|
agent.health = stats.maxHealth;
|
||||||
|
agent.maxHealth = stats.maxHealth;
|
||||||
|
agent.moveStep = 0.25f;
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
VF_TEST(blackboardKeepsEachValueTypeInItsOwnDrawer) {
|
||||||
|
Blackboard board;
|
||||||
|
board.setNumber("threat", 4.5f);
|
||||||
|
board.setCount("meals", 3);
|
||||||
|
board.setFlag("engaged", true);
|
||||||
|
board.setPoint("lastSeen", Vec3{1.0f, 2.0f, 3.0f});
|
||||||
|
board.setCell("target", Vec3i{4, 5, 6});
|
||||||
|
board.setRoute("path", {Vec3i{0, 1, 0}, Vec3i{1, 1, 0}});
|
||||||
|
|
||||||
|
CHECK_NEAR(board.number("threat"), 4.5f, 1e-6f);
|
||||||
|
CHECK_EQ(board.count("meals"), 3);
|
||||||
|
CHECK(board.flag("engaged"));
|
||||||
|
CHECK_NEAR(board.point("lastSeen").y, 2.0f, 1e-6f);
|
||||||
|
CHECK(board.cell("target") == Vec3i(4, 5, 6));
|
||||||
|
CHECK(board.route("path") != nullptr);
|
||||||
|
CHECK_EQ(board.route("path")->size(), 2u);
|
||||||
|
CHECK_EQ(board.entryCount(), 6u);
|
||||||
|
|
||||||
|
// A key written as one type is invisible to every other type.
|
||||||
|
CHECK(!board.hasCount("threat"));
|
||||||
|
CHECK(!board.hasNumber("meals"));
|
||||||
|
CHECK(board.route("threat") == nullptr);
|
||||||
|
CHECK_NEAR(board.number("missing", -1.0f), -1.0f, 1e-6f);
|
||||||
|
|
||||||
|
board.eraseNumber("threat");
|
||||||
|
CHECK(!board.hasNumber("threat"));
|
||||||
|
CHECK_EQ(board.entryCount(), 5u);
|
||||||
|
board.eraseRoute("path");
|
||||||
|
CHECK(!board.hasRoute("path"));
|
||||||
|
|
||||||
|
board.clear();
|
||||||
|
CHECK_EQ(board.entryCount(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(sequenceStopsAtTheChildThatFailsAndSkipsTheRest) {
|
||||||
|
int firstRuns = 0;
|
||||||
|
int lastRuns = 0;
|
||||||
|
bool gateOpen = false;
|
||||||
|
|
||||||
|
auto root = std::unique_ptr<SequenceNode>(new SequenceNode("sequence"));
|
||||||
|
root->add(constantAction("first", BehaviorStatus::Success, &firstRuns));
|
||||||
|
root->add(flagCondition("gate", &gateOpen));
|
||||||
|
root->add(constantAction("last", BehaviorStatus::Success, &lastRuns));
|
||||||
|
|
||||||
|
BehaviorTree tree(std::move(root));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
CHECK_EQ(firstRuns, 1);
|
||||||
|
CHECK_EQ(lastRuns, 0);
|
||||||
|
|
||||||
|
gateOpen = true;
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(firstRuns, 2);
|
||||||
|
CHECK_EQ(lastRuns, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(selectorTakesTheFirstBranchThatSucceedsAndFailsWhenNoneDo) {
|
||||||
|
int firstRuns = 0;
|
||||||
|
int secondRuns = 0;
|
||||||
|
int thirdRuns = 0;
|
||||||
|
|
||||||
|
auto root = std::unique_ptr<SelectorNode>(new SelectorNode("selector"));
|
||||||
|
root->add(constantAction("first", BehaviorStatus::Failure, &firstRuns));
|
||||||
|
root->add(constantAction("second", BehaviorStatus::Success, &secondRuns));
|
||||||
|
root->add(constantAction("third", BehaviorStatus::Success, &thirdRuns));
|
||||||
|
|
||||||
|
BehaviorTree tree(std::move(root));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(firstRuns, 1);
|
||||||
|
CHECK_EQ(secondRuns, 1);
|
||||||
|
CHECK_EQ(thirdRuns, 0);
|
||||||
|
|
||||||
|
auto allFail = std::unique_ptr<SelectorNode>(new SelectorNode("selector"));
|
||||||
|
allFail->add(constantAction("a", BehaviorStatus::Failure, nullptr));
|
||||||
|
allFail->add(constantAction("b", BehaviorStatus::Failure, nullptr));
|
||||||
|
BehaviorTree hopeless(std::move(allFail));
|
||||||
|
CHECK(hopeless.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(aRunningNodeResumesNextTickInsteadOfRestartingTheSequence) {
|
||||||
|
int firstRuns = 0;
|
||||||
|
int slowRuns = 0;
|
||||||
|
int slowProgress = 0;
|
||||||
|
|
||||||
Reference in new issue
Block a user