#include #include "testHarness.h" #include "vf/Brewing.h" using namespace vf; namespace { using Ingredient = BrewRecipe::Ingredient; BrewingStandState fueledStand(const PotionItem& bottle, int blazePowder = 5) { BrewingStandState stand; setBottle(stand, 0, bottle); addFuel(stand, blazePowder); return stand; } // Loads one ingredient and runs the stand until the brew lands. Returns the // bottle in the first slot afterwards. PotionItem brewOnce(BrewingStandState& stand, Ingredient ingredient) { setIngredient(stand, ingredient, 1); for (int tick = 0; tick < brewDurationTicks; ++tick) tickBrewing(stand); return stand.bottles[0].potion; } // Walks a whole chain of ingredients through a single bottle. PotionItem brewChain(const PotionItem& start, const std::vector& chain) { BrewingStandState stand = fueledStand(start, 10); PotionItem bottle = start; for (Ingredient ingredient : chain) bottle = brewOnce(stand, ingredient); return bottle; } PotionItem potionOf(PotionKind kind) { PotionItem potion; potion.kind = kind; return potion; } int durationOf(PotionKind kind, bool upgraded, bool extended) { PotionItem potion = potionOf(kind); potion.upgraded = upgraded; potion.extended = extended; return potionEffect(potion).durationTicks; } int levelOf(PotionKind kind, bool upgraded, bool extended) { PotionItem potion = potionOf(kind); potion.upgraded = upgraded; potion.extended = extended; return potionEffect(potion).level(); } } // namespace VF_TEST(theStandsPublishedConstantsAreTheOnesTheRestOfTheEngineWasBuiltAgainst) { CHECK_EQ(brewingBottleSlots, 3); CHECK_EQ(brewDurationTicks, 400); CHECK_EQ(brewDurationTicks, 20 * ticksPerSecond); CHECK_EQ(brewsPerBlazePowder, 20); CHECK_EQ(static_cast(PotionKind::Count), 17); CHECK_EQ(static_cast(BrewRecipe::Ingredient::None), 0); // A stand with one blaze powder in it runs a literal twenty second brew and // has nineteen brews of fuel left afterwards. BrewingStandState stand; setBottle(stand, 0, PotionItem{}); addFuel(stand, 1); setIngredient(stand, Ingredient::NetherWart, 1); CHECK_EQ(stand.fuel, 20); int completedOn = 0; for (int tick = 1; tick <= 500; ++tick) { if (tickBrewing(stand) && completedOn == 0) completedOn = tick; } CHECK_EQ(completedOn, 400); CHECK_EQ(stand.fuel, 19); } VF_TEST(recipeTableHasNoDuplicateInputAndIngredientPairs) { const std::vector& recipes = brewRecipes(); CHECK(recipes.size() >= 19); int collisions = 0; int selfLoops = 0; int emptyIngredients = 0; for (std::size_t i = 0; i < recipes.size(); ++i) { if (recipes[i].ingredient == Ingredient::None) ++emptyIngredients; if (recipes[i].input == recipes[i].output) ++selfLoops; for (std::size_t j = i + 1; j < recipes.size(); ++j) { if (recipes[i].input == recipes[j].input && recipes[i].ingredient == recipes[j].ingredient) { ++collisions; } } } CHECK_EQ(collisions, 0); CHECK_EQ(selfLoops, 0); CHECK_EQ(emptyIngredients, 0); } VF_TEST(netherWartTurnsWaterIntoAwkwardAndAwkwardOpensEveryBasePotion) { bool changed = false; CHECK_EQ(brewResult(PotionItem{}, Ingredient::NetherWart, changed).kind, PotionKind::Awkward); CHECK(changed); const struct { Ingredient ingredient; PotionKind expected; } branches[] = { {Ingredient::Sugar, PotionKind::Swiftness}, {Ingredient::GlisteringMelon, PotionKind::Healing}, {Ingredient::SpiderEye, PotionKind::Poison}, {Ingredient::BlazePowder, PotionKind::Strength}, {Ingredient::MagmaCream, PotionKind::FireResistance}, {Ingredient::PufferFish, PotionKind::WaterBreathing}, {Ingredient::GoldenCarrot, PotionKind::NightVision}, {Ingredient::GhastTear, PotionKind::Regeneration}, {Ingredient::RabbitFoot, PotionKind::Leaping}, }; for (const auto& branch : branches) { bool branched = false; PotionItem brewed = brewResult(potionOf(PotionKind::Awkward), branch.ingredient, branched); CHECK(branched); CHECK_EQ(brewed.kind, branch.expected); } } VF_TEST(aStandTurnsWaterIntoAwkwardOnTheFourHundredthTickAndNotOneTickEarlier) { BrewingStandState stand = fueledStand(PotionItem{}, 1); CHECK_EQ(stand.fuel, brewsPerBlazePowder); CHECK(setIngredient(stand, Ingredient::NetherWart, 1)); bool completedEarly = false; for (int tick = 1; tick < brewDurationTicks; ++tick) { if (tickBrewing(stand)) completedEarly = true; } CHECK(!completedEarly); CHECK(stand.brewing); CHECK_EQ(stand.progressTicks, brewDurationTicks - 1); CHECK_EQ(stand.bottles[0].potion.kind, PotionKind::Water); CHECK(tickBrewing(stand)); CHECK_EQ(stand.bottles[0].potion.kind, PotionKind::Awkward); CHECK_EQ(stand.progressTicks, 0); CHECK(!stand.brewing); CHECK_EQ(stand.fuel, brewsPerBlazePowder - 1); CHECK_EQ(stand.ingredientCount, 0); CHECK_EQ(stand.ingredient, Ingredient::None); } VF_TEST(theFullWaterToUpgradedSplashStrengthCycleRunsOnOneStand) { BrewingStandState stand = fueledStand(PotionItem{}, 1); CHECK_EQ(brewOnce(stand, Ingredient::NetherWart).kind, PotionKind::Awkward); CHECK_EQ(brewOnce(stand, Ingredient::BlazePowder).kind, PotionKind::Strength); PotionItem upgraded = brewOnce(stand, Ingredient::Glowstone); CHECK(upgraded.upgraded); CHECK(!upgraded.extended);