test: cover surroundWithShelves and sameOffer
This commit is contained in:
1 parent
ebff0be679
commit
ad3cf51ca6
1 file changed
+166
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user