test: add more cases for testBehavior
This commit is contained in:
1 parent
e7781bb5ca
commit
332deeafe1
1 file changed
+169
@@ -475,3 +475,172 @@ VF_TEST(theAnimalTreeFollowsHeldFoodAndBreedsOnlyOncePerCooldown) {
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(behaviorHorizontalDistance(agent.position, ctx.playerPosition) <= ctx.tuning.breedRadius);
|
||||
CHECK(agent.breedCount >= 2);
|
||||
for (std::size_t i = 1; i < breedTicks.size(); ++i) {
|
||||
CHECK(breedTicks[i] - breedTicks[i - 1] > ctx.tuning.breedCooldownTicks);
|
||||
}
|
||||
}
|
||||
|
||||
VF_TEST(theAnimalTreeGrazesGrassForTheFullChewDuration) {
|
||||
World world = grassWorld();
|
||||
BehaviorAgent agent = makeAgent(Species::Cow, {0.5f, 1.0f, 0.5f});
|
||||
|
||||
BehaviorContext ctx = freshContext(&world);
|
||||
ctx.agent = &agent;
|
||||
ctx.playerPresent = false;
|
||||
ctx.matePresent = false;
|
||||
|
||||
BehaviorTree tree = makeGrazingAnimalTree();
|
||||
for (int step = 0; step < ctx.tuning.grazeDurationTicks - 1; ++step) tree.tick(ctx);
|
||||
CHECK_EQ(ctx.blackboard.count("meals"), 0);
|
||||
CHECK(agent.grazeTicks > 0);
|
||||
|
||||
tree.tick(ctx);
|
||||
CHECK_EQ(ctx.blackboard.count("meals"), 1);
|
||||
CHECK_EQ(agent.grazeTicks, 0);
|
||||
}
|
||||
|
||||
VF_TEST(theSunAvoiderWalksIntoShadeAndWaitsOutTheDay) {
|
||||
World world = flatWorld();
|
||||
for (int x = 3; x <= 6; ++x) {
|
||||
for (int z = -1; z <= 1; ++z) world.setBlock(x, 4, z, Block::Stone);
|
||||
}
|
||||
|
||||
BehaviorAgent agent = makeAgent(Species::Skeleton, {0.5f, 1.0f, 0.5f});
|
||||
BehaviorContext ctx = freshContext(&world);
|
||||
ctx.agent = &agent;
|
||||
ctx.daylight = true;
|
||||
ctx.skyLight = 15;
|
||||
|
||||
BehaviorTree tree = makeSunAvoiderTree();
|
||||
CHECK(!agent.sheltered);
|
||||
for (int step = 0; step < 80; ++step) tree.tick(ctx);
|
||||
|
||||
CHECK(agent.sheltered);
|
||||
CHECK(agent.position.x >= 3.0f);
|
||||
CHECK(agent.shelterTicks > 0);
|
||||
CHECK(ctx.blackboard.hasCell("shadeCell"));
|
||||
}
|
||||
|
||||
VF_TEST(theSunAvoiderPatrolsInsteadOfHidingAtNight) {
|
||||
World world = flatWorld();
|
||||
BehaviorAgent agent = makeAgent(Species::Skeleton, {0.5f, 1.0f, 0.5f});
|
||||
Vec3 start = agent.position;
|
||||
|
||||
BehaviorContext ctx = freshContext(&world);
|
||||
ctx.agent = &agent;
|
||||
ctx.daylight = false;
|
||||
ctx.skyLight = 4;
|
||||
|
||||
BehaviorTree tree = makeSunAvoiderTree();
|
||||
for (int step = 0; step < 30; ++step) tree.tick(ctx);
|
||||
|
||||
CHECK(behaviorHorizontalDistance(agent.position, start) > 1.0f);
|
||||
CHECK_EQ(agent.shelterTicks, 0);
|
||||
CHECK(!ctx.blackboard.hasCell("shadeCell"));
|
||||
}
|
||||
|
||||
VF_TEST(twoRunsOfTheSameTreeOnTheSameSeedProduceTheSameNodeSequence) {
|
||||
World world = grassWorld();
|
||||
|
||||
auto runOnce = [&world](std::vector<std::string>& trace, Vec3& endPosition) {
|
||||
BehaviorAgent agent = makeAgent(Species::Pig, {0.5f, 1.0f, 0.5f});
|
||||
agent.rng = 0x1234abcdu;
|
||||
BehaviorContext ctx;
|
||||
ctx.world = &world;
|
||||
ctx.agent = &agent;
|
||||
ctx.traceEnabled = true;
|
||||
ctx.playerPresent = true;
|
||||
ctx.playerPosition = {4.5f, 1.0f, 0.5f};
|
||||
ctx.playerHeldItem = ItemId::None;
|
||||
|
||||
BehaviorTree tree = makeGrazingAnimalTree();
|
||||
for (int step = 0; step < 60; ++step) tree.tick(ctx);
|
||||
trace = ctx.trace;
|
||||
endPosition = agent.position;
|
||||
};
|
||||
|
||||
std::vector<std::string> firstTrace;
|
||||
std::vector<std::string> secondTrace;
|
||||
Vec3 firstEnd;
|
||||
Vec3 secondEnd;
|
||||
runOnce(firstTrace, firstEnd);
|
||||
runOnce(secondTrace, secondEnd);
|
||||
|
||||
CHECK(firstTrace.size() > 100u);
|
||||
CHECK_EQ(firstTrace.size(), secondTrace.size());
|
||||
for (std::size_t i = 0; i < firstTrace.size() && i < secondTrace.size(); ++i) {
|
||||
CHECK_EQ(firstTrace[i], secondTrace[i]);
|
||||
}
|
||||
CHECK_NEAR(firstEnd.x, secondEnd.x, 1e-6f);
|
||||
CHECK_NEAR(firstEnd.z, secondEnd.z, 1e-6f);
|
||||
}
|
||||
|
||||
VF_TEST(eachSpeciesMapsToTheTreeThatFitsIt) {
|
||||
CHECK(roleForSpecies(Species::Zombie) == BehaviorRole::HostileHunter);
|
||||
CHECK(roleForSpecies(Species::Creeper) == BehaviorRole::HostileHunter);
|
||||
CHECK(roleForSpecies(Species::Skeleton) == BehaviorRole::SunAvoider);
|
||||
CHECK(roleForSpecies(Species::Sheep) == BehaviorRole::GrazingAnimal);
|
||||
|
||||
CHECK(breedingFoodFor(Species::Cow) == ItemId::Bread);
|
||||
CHECK(breedingFoodFor(Species::Pig) == ItemId::Apple);
|
||||
CHECK(breedingFoodFor(Species::Zombie) == ItemId::None);
|
||||
CHECK(isBreedingFood(Species::Cow, ItemId::Bread));
|
||||
CHECK(!isBreedingFood(Species::Cow, ItemId::Apple));
|
||||
CHECK(!isBreedingFood(Species::Zombie, ItemId::None));
|
||||
|
||||
for (int index = 0; index < static_cast<int>(BehaviorRole::Count); ++index) {
|
||||
BehaviorRole role = static_cast<BehaviorRole>(index);
|
||||
BehaviorTree tree = makeTreeForRole(role);
|
||||
CHECK(tree.structureValid());
|
||||
CHECK(tree.stats().nodeCount > 4);
|
||||
CHECK(std::string(behaviorRoleName(role)) != std::string("unknown"));
|
||||
}
|
||||
}
|
||||
|
||||
VF_TEST(theSharedRandomStreamIsReproducibleAndStaysInRange) {
|
||||
uint32_t first = 0x2545f491u;
|
||||
uint32_t second = 0x2545f491u;
|
||||
for (int draw = 0; draw < 64; ++draw) {
|
||||
CHECK_EQ(behaviorNextRandom(first), behaviorNextRandom(second));
|
||||
}
|
||||
|
||||
uint32_t stream = 7u;
|
||||
for (int draw = 0; draw < 200; ++draw) {
|
||||
float unit = behaviorUnitFloat(stream);
|
||||
CHECK(unit >= 0.0f);
|
||||
CHECK(unit < 1.0f);
|
||||
int ranged = behaviorRandomRange(stream, -3, 3);
|
||||
CHECK(ranged >= -3);
|
||||
CHECK(ranged <= 3);
|
||||
}
|
||||
CHECK_EQ(behaviorRandomRange(stream, 5, 5), 5);
|
||||
|
||||
CHECK(behaviorCellOf(Vec3{-0.5f, 1.9f, 3.2f}) == Vec3i(-1, 1, 3));
|
||||
CHECK_NEAR(behaviorCellCenter(Vec3i{2, 3, 4}).x, 2.5f, 1e-6f);
|
||||
CHECK_NEAR(behaviorCellCenter(Vec3i{2, 3, 4}).y, 3.0f, 1e-6f);
|
||||
CHECK(std::string(behaviorStatusName(BehaviorStatus::Running)) == std::string("running"));
|
||||
}
|
||||
|
||||
VF_TEST(routeFollowingWalksCellByCellAndReportsSuccessAtTheEnd) {
|
||||
World world = flatWorld();
|
||||
BehaviorAgent agent = makeAgent(Species::Cow, {0.5f, 1.0f, 0.5f});
|
||||
BehaviorContext ctx = freshContext(&world);
|
||||
ctx.agent = &agent;
|
||||
|
||||
CHECK(planRouteTo(ctx, Vec3i{4, 1, 0}, true));
|
||||
CHECK(agent.route.size() >= 5u);
|
||||
CHECK_EQ(agent.routeCursor, 1);
|
||||
|
||||
BehaviorStatus status = BehaviorStatus::Running;
|
||||
int guard = 0;
|
||||
while (status == BehaviorStatus::Running && guard < 200) {
|
||||
status = followRouteStep(ctx);
|
||||
++guard;
|
||||
}
|
||||
CHECK(status == BehaviorStatus::Success);
|
||||
CHECK(agent.routeFinished());
|
||||
CHECK_NEAR(agent.position.x, 4.5f, 1e-4f);
|
||||
CHECK(guard < 200);
|
||||
}
|
||||
Reference in new issue
Block a user