test: cover surroundWithShelves and sameOffer

This commit is contained in:
dualfroz committed 2026-09-11 17:37:24 +02:00
1 parent ebff0be679
commit ad3cf51ca6
1 file changed
+166
+166
View File
@@ -0,0 +1,166 @@
#include <algorithm>
#include "testHarness.h"
#include "vf/Enchant.h"
using namespace vf;
namespace {
// Places `count` shelves on the ring the table reads, filling the lower layer
// before the upper one.
void surroundWithShelves(World& world, const Vec3i& table, int count) {
int placed = 0;
for (int dy = 0; dy <= 1 && placed < count; ++dy) {
for (int dx = -2; dx <= 2 && placed < count; ++dx) {
for (int dz = -2; dz <= 2 && placed < count; ++dz) {
if (std::max(dx < 0 ? -dx : dx, dz < 0 ? -dz : dz) != 2) continue;
world.setBlock(table.x + dx, table.y + dy, table.z + dz, Block::Bookshelf);
++placed;
}
}
}
}
bool sameOffer(const EnchantOffer& a, const EnchantOffer& b) {
if (a.requiredLevels != b.requiredLevels || a.lapisCost != b.lapisCost) return false;
if (a.enchants.entries.size() != b.enchants.entries.size()) return false;
for (std::size_t i = 0; i < a.enchants.entries.size(); ++i) {
if (a.enchants.entries[i].kind != b.enchants.entries[i].kind) return false;
if (a.enchants.entries[i].level != b.enchants.entries[i].level) return false;
}
return true;
}
EnchantSet setOf(EnchantKind kind, int level) {
EnchantSet set;
addEnchant(set, kind, level);
return set;
}
} // namespace
VF_TEST(bookshelvesOnTheRingRaiseTheOfferedLevelsUpToTheCap) {
World bare;
CHECK_EQ(bookshelfCount(bare, {0, 64, 0}), 0);
World lightlyStocked;
surroundWithShelves(lightlyStocked, {0, 64, 0}, 6);
CHECK_EQ(bookshelfCount(lightlyStocked, {0, 64, 0}), 6);
World library;
surroundWithShelves(library, {0, 64, 0}, 30);
CHECK_EQ(bookshelfCount(library, {0, 64, 0}), maxBookshelfPower);
}
VF_TEST(shelvesWithSomethingSolidInBetweenDoNotCount) {
World world;
world.setBlock(2, 64, 0, Block::Bookshelf);
CHECK_EQ(bookshelfCount(world, {0, 64, 0}), 1);
world.setBlock(1, 64, 0, Block::Stone);
CHECK_EQ(bookshelfCount(world, {0, 64, 0}), 0);
// A torch in the gap is see-through, so the shelf counts again.
world.setBlock(1, 64, 0, Block::Torch);
CHECK_EQ(bookshelfCount(world, {0, 64, 0}), 1);
}
VF_TEST(offersAreDeterministicForTheSameSeedAndMoveOnAfterOneIsTaken) {
EnchantTableState first;
first.seed = 20250904u;
first.bookshelves = 15;
first.target = Enchantment::Target::Sword;
first.enchantability = 10;
refreshOffers(first);
EnchantTableState second;
second.seed = 20250904u;
second.bookshelves = 15;
second.target = Enchantment::Target::Sword;
second.enchantability = 10;
refreshOffers(second);
for (int slot = 0; slot < 3; ++slot) {
CHECK(sameOffer(first.offers[static_cast<std::size_t>(slot)],
second.offers[static_cast<std::size_t>(slot)]));
}
uint32_t seedBefore = first.seed;
EnchantOffer granted = takeOffer(first, 2, 40, 3);
CHECK(granted.valid());
CHECK(first.seed != seedBefore);
CHECK_EQ(granted.lapisCost, 3);
}
VF_TEST(topRowNeedsThirtyLevelsInAFullyShelvedLibrary) {
for (uint32_t seed = 1; seed <= 40; ++seed) {
EnchantTableState table;
table.seed = seed * 2654435761u + 1u;
table.bookshelves = 15;
table.target = Enchantment::Target::Sword;
table.enchantability = 14;
refreshOffers(table);
CHECK_EQ(table.offers[2].requiredLevels, 30);
CHECK_EQ(table.offers[2].lapisCost, 3);
CHECK(table.offers[0].requiredLevels <= table.offers[1].requiredLevels);
CHECK(table.offers[1].requiredLevels <= table.offers[2].requiredLevels);
}
}
VF_TEST(anEmptyTableOffersFarLessThanAFullyShelvedOne) {
int bareTotal = 0;
int shelvedTotal = 0;
for (uint32_t seed = 1; seed <= 40; ++seed) {
EnchantTableState bare;
bare.seed = seed * 40503u + 7u;
bare.bookshelves = 0;
bare.target = Enchantment::Target::Pickaxe;
bare.enchantability = 10;
refreshOffers(bare);
EnchantTableState shelved = bare;
shelved.bookshelves = 15;
refreshOffers(shelved);
for (int slot = 0; slot < 3; ++slot) {
bareTotal += bare.offers[static_cast<std::size_t>(slot)].requiredLevels;
shelvedTotal += shelved.offers[static_cast<std::size_t>(slot)].requiredLevels;
}
}
CHECK(shelvedTotal > bareTotal * 2);
}
VF_TEST(takingAnOfferFailsWithoutEnoughLevelsOrLapis) {
EnchantTableState table;
table.seed = 987654321u;
table.bookshelves = 15;
table.target = Enchantment::Target::Chestplate;
table.enchantability = 9;
refreshOffers(table);
int cost = table.offers[2].requiredLevels;
CHECK(cost > 0);
CHECK(!takeOffer(table, 2, cost - 1, 3).valid());
CHECK(!takeOffer(table, 2, cost, 2).valid());
CHECK(!takeOffer(table, 7, 100, 64).valid());
CHECK(takeOffer(table, 2, cost, 3).valid());
}
VF_TEST(rolledEnchantmentsRespectTargetsCapsAndConflicts) {
for (uint32_t seed = 1; seed <= 300; ++seed) {
uint32_t rng = seed * 2246822519u + 3u;
EnchantSet rolled = rollEnchantments(Enchantment::Target::Sword, 14, 30, rng);
for (std::size_t i = 0; i < rolled.entries.size(); ++i) {
const Enchantment& entry = rolled.entries[i];
CHECK(entry.level >= 1);
CHECK(entry.level <= maxEnchantLevel(entry.kind));
CHECK(enchantApplies(entry.kind, Enchantment::Target::Sword));
for (std::size_t j = i + 1; j < rolled.entries.size(); ++j) {
CHECK(entry.kind != rolled.entries[j].kind);
CHECK(!enchantsConflict(entry.kind, rolled.entries[j].kind));
}
}
}
}