Auto-process smelter and reprocessing plant (no recipe selection)
This commit is contained in:
@@ -474,7 +474,8 @@ TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites",
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)), tick);
|
||||
REQUIRE(bs.productionBuildingCount() == 2);
|
||||
|
||||
// Neither has a recipe selected, so neither has an active cycle.
|
||||
// Neither is producing yet: the miner has no recipe selected, and the
|
||||
// smelter (auto-recipe, REQ-BLD-SMELTER) has no input feeding it.
|
||||
REQUIRE(bs.activeProductionBuildingCount() == 0);
|
||||
|
||||
bs.setRecipe(minerId, "mine_iron_ore");
|
||||
@@ -542,7 +543,8 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing
|
||||
// Smelter mask ["AA ","AA>"] → body (0,0),(1,0),(0,1),(1,1).
|
||||
// Output port (2,1) East. Input port example: (2,0) West.
|
||||
const BuildingId sid = bs.place(BuildingType::Smelter, QPoint(0, 0), Rotation::East, 0);
|
||||
bs.setRecipe(sid, "iron_ingot");
|
||||
// Smelters have no recipe selection (REQ-BLD-SMELTER); they auto-accept any
|
||||
// ore/scrap that is an input to a smelter recipe.
|
||||
|
||||
// Complete construction (15s → tick 450+1 = 451 ticks).
|
||||
Tick tick = 0;
|
||||
@@ -563,6 +565,105 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing
|
||||
REQUIRE(it->second >= 1);
|
||||
}
|
||||
|
||||
// A smelter auto-selects the matching recipe for whatever it is fed, with no
|
||||
// player recipe selection (REQ-BLD-SMELTER).
|
||||
TEST_CASE("BuildingSystem: smelter auto-smelts ore without a recipe selection",
|
||||
"[building]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
BeltSystem belts(static_cast<double>(kTickRateHz));
|
||||
int stock = 0;
|
||||
std::mt19937 rng(0);
|
||||
BuildingId nextBuildingId = 1;
|
||||
BuildingSystem bs(cfg, belts,
|
||||
[&nextBuildingId]() { return nextBuildingId++; },
|
||||
[&stock](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
[](const std::string&) -> bool { return true; },
|
||||
rng);
|
||||
|
||||
const BuildingId sid = bs.place(BuildingType::Smelter, QPoint(0, 0), Rotation::East, 0);
|
||||
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)) + 1, tick);
|
||||
|
||||
// Feed 2 iron_ore (the test-config iron_ingot recipe needs 2) via a
|
||||
// west-flowing belt at input port (2,0).
|
||||
belts.placeBelt(QPoint(2, 0), Rotation::West);
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
belts.tryPutItem(QPoint(2, 0), makeItem("iron_ore"));
|
||||
belts.tick();
|
||||
bs.tickBeltPull();
|
||||
}
|
||||
|
||||
// iron_ingot recipe cycle is 2s; run to completion.
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(2.0)) + 2, tick);
|
||||
|
||||
const Building* b = bs.findBuilding(sid);
|
||||
REQUIRE(b != nullptr);
|
||||
bool hasIronIngot = false;
|
||||
for (const Item& item : b->outputBuffer.items)
|
||||
{
|
||||
if (item.type.id == "iron_ingot") { hasIronIngot = true; }
|
||||
}
|
||||
REQUIRE(hasIronIngot);
|
||||
}
|
||||
|
||||
// With mixed inputs, the smelter runs whichever recipe is currently satisfiable
|
||||
// and leaves an incomplete batch of another input waiting (see the union-of-
|
||||
// inputs caps in initAutoBuffers).
|
||||
TEST_CASE("BuildingSystem: smelter runs a satisfiable recipe while an incomplete batch waits",
|
||||
"[building]")
|
||||
{
|
||||
const GameConfig cfg = loadConfig();
|
||||
BeltSystem belts(static_cast<double>(kTickRateHz));
|
||||
int stock = 0;
|
||||
std::mt19937 rng(0);
|
||||
BuildingId nextBuildingId = 1;
|
||||
BuildingSystem bs(cfg, belts,
|
||||
[&nextBuildingId]() { return nextBuildingId++; },
|
||||
[&stock](int n) { stock += n; },
|
||||
[](const std::string&, QVector2D, const std::optional<ShipLayoutConfig>&) {},
|
||||
[](const std::string&) -> bool { return true; },
|
||||
rng);
|
||||
|
||||
const BuildingId sid = bs.place(BuildingType::Smelter, QPoint(0, 0), Rotation::East, 0);
|
||||
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)) + 1, tick);
|
||||
|
||||
// Feed 1 iron_ore (iron_ingot needs 2 — incomplete) then 2 copper_ore
|
||||
// (copper_ingot needs 2 — satisfiable) via the west-flowing input belt.
|
||||
belts.placeBelt(QPoint(2, 0), Rotation::West);
|
||||
const char* fed[] = { "iron_ore", "copper_ore", "copper_ore" };
|
||||
for (const char* id : fed)
|
||||
{
|
||||
belts.tryPutItem(QPoint(2, 0), makeItem(id));
|
||||
belts.tick();
|
||||
bs.tickBeltPull();
|
||||
}
|
||||
|
||||
// copper_ingot cycle is 2.5s; run to completion.
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(2.5)) + 2, tick);
|
||||
|
||||
const Building* b = bs.findBuilding(sid);
|
||||
REQUIRE(b != nullptr);
|
||||
|
||||
// Copper was smelted; the lone iron_ore still waits for a second unit.
|
||||
bool hasCopperIngot = false;
|
||||
for (const Item& item : b->outputBuffer.items)
|
||||
{
|
||||
if (item.type.id == "copper_ingot") { hasCopperIngot = true; }
|
||||
}
|
||||
REQUIRE(hasCopperIngot);
|
||||
|
||||
const std::map<ItemType, int>::const_iterator ironIt =
|
||||
b->inputBuffer.counts.find(ItemType{"iron_ore"});
|
||||
REQUIRE(ironIt != b->inputBuffer.counts.end());
|
||||
REQUIRE(ironIt->second == 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Belt push → belt tile
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -664,7 +765,8 @@ TEST_CASE("BuildingSystem: reprocessing plant output buffer capacity equals max
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::ReprocessingPlant,
|
||||
QPoint(0, 0), Rotation::East, 0);
|
||||
bs.setRecipe(id, "reprocessing_cycle");
|
||||
// Reprocessing plants have no recipe selection (REQ-BLD-REPROCESSING); the
|
||||
// single reprocessing recipe is applied automatically on completion.
|
||||
|
||||
// Complete construction (25s).
|
||||
Tick tick = 0;
|
||||
@@ -695,7 +797,8 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::ReprocessingPlant,
|
||||
QPoint(0, 0), Rotation::East, 0);
|
||||
bs.setRecipe(id, "reprocessing_cycle");
|
||||
// Reprocessing plants have no recipe selection (REQ-BLD-REPROCESSING); the
|
||||
// single reprocessing recipe is applied automatically on completion.
|
||||
|
||||
// Complete construction (25s).
|
||||
Tick tick = 0;
|
||||
|
||||
Reference in New Issue
Block a user