migrate every factory query off BuildingSystem onto the free functions
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "catch.hpp"
|
||||
#include "FactoryQueries.h"
|
||||
|
||||
#include <map>
|
||||
#include <random>
|
||||
@@ -125,11 +126,11 @@ TEST_CASE("BuildingSystem: place miner occupies expected body tiles", "[building
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
|
||||
// Miner mask ["AA","A>"] with East rotation → body at (0,0),(1,0),(0,1).
|
||||
REQUIRE(bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE(bs.isTileOccupied(QPoint(1, 0)));
|
||||
REQUIRE(bs.isTileOccupied(QPoint(0, 1)));
|
||||
REQUIRE(isTileOccupied(state_bs, QPoint(0, 0)));
|
||||
REQUIRE(isTileOccupied(state_bs, QPoint(1, 0)));
|
||||
REQUIRE(isTileOccupied(state_bs, QPoint(0, 1)));
|
||||
// (1,1) is the output-port tile, NOT a body cell.
|
||||
REQUIRE_FALSE(bs.isTileOccupied(QPoint(1, 1)));
|
||||
REQUIRE_FALSE(isTileOccupied(state_bs, QPoint(1, 1)));
|
||||
}
|
||||
|
||||
// -- World-bounds rejection (REQ-BLD-PLACE-VALID) ---------------------------
|
||||
@@ -142,8 +143,8 @@ TEST_CASE("BuildingSystem: place rejects a building above the world (y < 0)", "[
|
||||
// row sits above the world.
|
||||
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner, QPoint(0, -1), Rotation::East, 0);
|
||||
REQUIRE_FALSE(id.has_value());
|
||||
REQUIRE(f.bs.getAllSites().empty());
|
||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE(getAllSites(f.state).empty());
|
||||
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: place rejects a building below the world (y >= height)", "[building]")
|
||||
@@ -156,7 +157,7 @@ TEST_CASE("BuildingSystem: place rejects a building below the world (y >= height
|
||||
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner,
|
||||
QPoint(0, heightTiles - 1), Rotation::East, 0);
|
||||
REQUIRE_FALSE(id.has_value());
|
||||
REQUIRE(f.bs.getAllSites().empty());
|
||||
REQUIRE(getAllSites(f.state).empty());
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: place rejects a building left of the asteroid edge", "[building]")
|
||||
@@ -167,7 +168,7 @@ TEST_CASE("BuildingSystem: place rejects a building left of the asteroid edge",
|
||||
const std::optional<BuildingId> id = f.bs.place(BuildingType::Miner,
|
||||
QPoint(leftEdgeX - 1, 0), Rotation::East, 0);
|
||||
REQUIRE_FALSE(id.has_value());
|
||||
REQUIRE(f.bs.getAllSites().empty());
|
||||
REQUIRE(getAllSites(f.state).empty());
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: place accepts a building flush against the world's left edge",
|
||||
@@ -180,7 +181,7 @@ TEST_CASE("BuildingSystem: place accepts a building flush against the world's le
|
||||
const BuildingId id = f.bs.place(BuildingType::Miner,
|
||||
QPoint(leftEdgeX, 0), Rotation::East, 0).value();
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
REQUIRE(f.bs.isTileOccupied(QPoint(leftEdgeX, 0)));
|
||||
REQUIRE(isTileOccupied(f.state, QPoint(leftEdgeX, 0)));
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: place imposes no right-side bound (space extends rightward)",
|
||||
@@ -240,9 +241,9 @@ TEST_CASE("BuildingSystem: placing a belt registers it with BeltSystem after con
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||
|
||||
REQUIRE(belts.tryPutItem(QPoint(5, 5), makeItem("iron_ore"), Rotation::East));
|
||||
REQUIRE(bs.getAllBuildings().size() == 1);
|
||||
REQUIRE(bs.getAllBuildings()[0].type == BuildingType::Belt);
|
||||
REQUIRE(bs.getAllBuildings()[0].anchor == QPoint(5, 5));
|
||||
REQUIRE(getAllBuildings(state_bs).size() == 1);
|
||||
REQUIRE(getAllBuildings(state_bs)[0].type == BuildingType::Belt);
|
||||
REQUIRE(getAllBuildings(state_bs)[0].anchor == QPoint(5, 5));
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: placed building enters construction queue", "[building]")
|
||||
@@ -262,9 +263,9 @@ TEST_CASE("BuildingSystem: placed building enters construction queue", "[buildin
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
||||
|
||||
REQUIRE(bs.getAllSites().size() == 1);
|
||||
REQUIRE(bs.getAllBuildings().empty());
|
||||
REQUIRE(bs.findSite(id) != nullptr);
|
||||
REQUIRE(getAllSites(state_bs).size() == 1);
|
||||
REQUIRE(getAllBuildings(state_bs).empty());
|
||||
REQUIRE(findSite(state_bs, id) != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: deconstructing a construction site removes it instantly with full refund",
|
||||
@@ -279,8 +280,8 @@ TEST_CASE("BuildingSystem: deconstructing a construction site removes it instant
|
||||
const int refund = f.bs.deconstruct(id, 0);
|
||||
|
||||
REQUIRE(refund == 15); // Miner cost = 15
|
||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE(f.bs.getAllSites().empty());
|
||||
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||
REQUIRE(getAllSites(f.state).empty());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -304,7 +305,7 @@ TEST_CASE("BuildingSystem: first queued building starts construction immediately
|
||||
rng);
|
||||
|
||||
bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
||||
REQUIRE(bs.getAllSites().front().completesAt > 0);
|
||||
REQUIRE(getAllSites(state_bs).front().completesAt > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[building]")
|
||||
@@ -325,9 +326,9 @@ TEST_CASE("BuildingSystem: second queued building waits (completesAt == 0)", "[b
|
||||
bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0);
|
||||
bs.place(BuildingType::Miner, QPoint(5, 5), Rotation::East, 0);
|
||||
|
||||
REQUIRE(bs.getAllSites().size() == 2);
|
||||
REQUIRE(bs.getAllSites()[0].completesAt > 0);
|
||||
REQUIRE(bs.getAllSites()[1].completesAt == 0);
|
||||
REQUIRE(getAllSites(state_bs).size() == 2);
|
||||
REQUIRE(getAllSites(state_bs)[0].completesAt > 0);
|
||||
REQUIRE(getAllSites(state_bs)[1].completesAt == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: construction completes after configured duration", "[building]")
|
||||
@@ -352,8 +353,8 @@ TEST_CASE("BuildingSystem: construction completes after configured duration", "[
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||
|
||||
REQUIRE(bs.getAllSites().empty());
|
||||
REQUIRE(bs.findBuilding(id) != nullptr);
|
||||
REQUIRE(getAllSites(state_bs).empty());
|
||||
REQUIRE(findBuilding(state_bs, id) != nullptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -363,11 +364,11 @@ TEST_CASE("BuildingSystem: construction completes after configured duration", "[
|
||||
// Runs ticks until the building with the given id is operational, or fails.
|
||||
static void runUntilBuilt(PlacementFixture& f, BuildingId id, Tick& tick)
|
||||
{
|
||||
for (int i = 0; i < 100000 && f.bs.findBuilding(id) == nullptr; ++i)
|
||||
for (int i = 0; i < 100000 && findBuilding(f.state, id) == nullptr; ++i)
|
||||
{
|
||||
runTicks(f.bs, f.belts, 1, tick);
|
||||
}
|
||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
||||
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: deconstructing a built building queues it; refund credited on completion",
|
||||
@@ -384,15 +385,15 @@ TEST_CASE("BuildingSystem: deconstructing a built building queues it; refund cre
|
||||
// stopping it operating while its tiles stay occupied (REQ-BLD-DECON-QUEUE).
|
||||
const int refund = f.bs.deconstruct(id, tick);
|
||||
REQUIRE(refund == 0);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(id));
|
||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE(isQueuedForDeconstruction(f.state, id));
|
||||
REQUIRE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||
REQUIRE(f.stock == 0);
|
||||
|
||||
// After the deconstruction time (0.1s = 3 ticks) it is removed and the partial
|
||||
// refund (15 * 75 / 100 = 11) is credited exactly once.
|
||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 1, tick);
|
||||
REQUIRE(f.bs.findBuilding(id) == nullptr);
|
||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE(findBuilding(f.state, id) == nullptr);
|
||||
REQUIRE_FALSE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||
REQUIRE(f.stock == 15 * f.cfg.world.refundPercentage / 100);
|
||||
}
|
||||
|
||||
@@ -409,20 +410,20 @@ TEST_CASE("BuildingSystem: deconstruction queue removes one building at a time",
|
||||
// Queue both in one tick; 'a' is at the front of the deconstruction queue.
|
||||
f.bs.deconstruct(a, tick);
|
||||
f.bs.deconstruct(b, tick);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(a));
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(b));
|
||||
REQUIRE(isQueuedForDeconstruction(f.state, a));
|
||||
REQUIRE(isQueuedForDeconstruction(f.state, b));
|
||||
|
||||
// After one deconstruction interval only the front building is gone; the
|
||||
// second is still queued and its refund not yet credited.
|
||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 1, tick);
|
||||
REQUIRE(f.bs.findBuilding(a) == nullptr);
|
||||
REQUIRE(f.bs.findBuilding(b) != nullptr);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(b));
|
||||
REQUIRE(findBuilding(f.state, a) == nullptr);
|
||||
REQUIRE(findBuilding(f.state, b) != nullptr);
|
||||
REQUIRE(isQueuedForDeconstruction(f.state, b));
|
||||
REQUIRE(f.stock == 15 * f.cfg.world.refundPercentage / 100);
|
||||
|
||||
// The second drains next.
|
||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 2, tick);
|
||||
REQUIRE(f.bs.findBuilding(b) == nullptr);
|
||||
REQUIRE(findBuilding(f.state, b) == nullptr);
|
||||
REQUIRE(f.stock == 2 * (15 * f.cfg.world.refundPercentage / 100));
|
||||
}
|
||||
|
||||
@@ -437,18 +438,18 @@ TEST_CASE("BuildingSystem: cancelling deconstruction resumes the building with n
|
||||
runUntilBuilt(f, id, tick);
|
||||
|
||||
f.bs.deconstruct(id, tick);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(id));
|
||||
REQUIRE(isQueuedForDeconstruction(f.state, id));
|
||||
|
||||
// Un-queue before it drains: it operates again, no refund, tiles still occupied.
|
||||
f.bs.cancelDeconstruction(id);
|
||||
REQUIRE_FALSE(f.bs.isQueuedForDeconstruction(id));
|
||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE_FALSE(isQueuedForDeconstruction(f.state, id));
|
||||
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||
REQUIRE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||
REQUIRE(f.stock == 0);
|
||||
|
||||
// It is never removed even after more than a deconstruction interval passes.
|
||||
runTicks(f.bs, f.belts, static_cast<int>(secondsToTicks(0.1)) + 5, tick);
|
||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
||||
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||
REQUIRE(f.stock == 0);
|
||||
}
|
||||
|
||||
@@ -466,7 +467,7 @@ TEST_CASE("BuildingSystem: queued belt stops transporting; cancel restores it",
|
||||
// accepts or transports items, though the tile stays occupied (REQ-BLD-DECON-QUEUE).
|
||||
f.bs.deconstruct(id, tick);
|
||||
REQUIRE_FALSE(f.belts.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East));
|
||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
REQUIRE(isTileOccupied(f.state, QPoint(0, 0)));
|
||||
|
||||
// Un-queuing re-registers the belt tile so it transports again.
|
||||
f.bs.cancelDeconstruction(id);
|
||||
@@ -519,9 +520,9 @@ TEST_CASE("BuildingSystem: second building starts after first completes", "[buil
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||
|
||||
REQUIRE(bs.getAllSites().size() == 1);
|
||||
REQUIRE(bs.getAllSites().front().id == id2);
|
||||
REQUIRE(bs.getAllSites().front().completesAt > 0);
|
||||
REQUIRE(getAllSites(state_bs).size() == 1);
|
||||
REQUIRE(getAllSites(state_bs).front().id == id2);
|
||||
REQUIRE(getAllSites(state_bs).front().completesAt > 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -553,7 +554,7 @@ TEST_CASE("BuildingSystem: miner produces iron_ore after recipe duration", "[bui
|
||||
static_cast<int>(secondsToTicks(10.0)) + static_cast<int>(secondsToTicks(1.0)) + 1,
|
||||
tick);
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
// No belt at the output port, so the produced item emerges and stays on the
|
||||
// building's virtual output belt (REQ-MAT-OUTPUT-EMERGE).
|
||||
@@ -591,7 +592,7 @@ TEST_CASE("BuildingSystem: miner output buffer stalls when full", "[building]")
|
||||
+ 2 * static_cast<int>(secondsToTicks(1.0)) + 2,
|
||||
tick);
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
// Both produced items are held on the output side (buffer + emerging lane),
|
||||
// which is what the capacity rule counts (REQ-MAT-OUTPUT-EMERGE).
|
||||
@@ -624,23 +625,23 @@ TEST_CASE("BuildingSystem: productionBuildingCount excludes construction sites",
|
||||
|
||||
Tick tick = 0;
|
||||
// Both still under construction.
|
||||
REQUIRE(bs.getProductionBuildingCount() == 0);
|
||||
REQUIRE(getProductionBuildingCount(state_bs) == 0);
|
||||
|
||||
// The queue builds one at a time: miner (10s) completes at tick 300, then
|
||||
// the smelter (15s) starts and completes at tick 300 + 450 = 750.
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||
REQUIRE(bs.getProductionBuildingCount() == 1);
|
||||
REQUIRE(getProductionBuildingCount(state_bs) == 1);
|
||||
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(15.0)), tick);
|
||||
REQUIRE(bs.getProductionBuildingCount() == 2);
|
||||
REQUIRE(getProductionBuildingCount(state_bs) == 2);
|
||||
|
||||
// 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.getActiveProductionBuildingCount() == 0);
|
||||
REQUIRE(getActiveProductionBuildingCount(state_bs) == 0);
|
||||
|
||||
bs.setRecipe(minerId, "mine_iron_ore");
|
||||
runTicks(bs, belts, 1, tick);
|
||||
REQUIRE(bs.getActiveProductionBuildingCount() == 1);
|
||||
REQUIRE(getActiveProductionBuildingCount(state_bs) == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle state",
|
||||
@@ -664,21 +665,21 @@ TEST_CASE("BuildingSystem: activeProductionBuildingCount tracks production cycle
|
||||
|
||||
Tick tick = 0;
|
||||
// Not yet operational while under construction.
|
||||
REQUIRE(bs.getActiveProductionBuildingCount() == 0);
|
||||
REQUIRE(getActiveProductionBuildingCount(state_bs) == 0);
|
||||
|
||||
// Construction completes at tick 300; cycle 1 starts the same tick (completesAt=330).
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(10.0)) + 1, tick);
|
||||
REQUIRE(bs.getActiveProductionBuildingCount() == 1);
|
||||
REQUIRE(getActiveProductionBuildingCount(state_bs) == 1);
|
||||
|
||||
// Run cycles 1 and 2 to completion (1s each); cycle 3 stalls once the
|
||||
// output buffer (capacity 2) is full (REQ-MAT-OUTPUT-BUFFER).
|
||||
runTicks(bs, belts, 2 * static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
REQUIRE(b->getOutputItemCount() == 2);
|
||||
REQUIRE_FALSE(b->production.has_value());
|
||||
REQUIRE(bs.getActiveProductionBuildingCount() == 0);
|
||||
REQUIRE(getActiveProductionBuildingCount(state_bs) == 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -719,7 +720,7 @@ TEST_CASE("BuildingSystem: smelter input buffer fills from adjacent west-flowing
|
||||
|
||||
bs.tickBeltPull();
|
||||
|
||||
const Building* b = bs.findBuilding(sid);
|
||||
const Building* b = findBuilding(state_bs, sid);
|
||||
REQUIRE(b != nullptr);
|
||||
// The item was accepted; it may still be travelling inward on the input belt,
|
||||
// so count buffered + in-transit (REQ-MAT-INPUT-INTAKE).
|
||||
@@ -754,7 +755,7 @@ TEST_CASE("BuildingSystem: accepted input travels inward before entering the buf
|
||||
belts.tick();
|
||||
bs.tickBeltPull(); // accepts the item onto the input belt at progress 0.0
|
||||
|
||||
const Building* b = bs.findBuilding(sid);
|
||||
const Building* b = findBuilding(state_bs, sid);
|
||||
REQUIRE(b != nullptr);
|
||||
// Reserved but not yet consumable: nothing in the buffer, but it counts against
|
||||
// the cap via pendingInputCount.
|
||||
@@ -802,7 +803,7 @@ TEST_CASE("BuildingSystem: input reservation caps buffered plus in-transit at th
|
||||
bs.tickBeltPull();
|
||||
}
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
const int cap = b->inputBuffer.caps.at(ItemType{"scrap"});
|
||||
REQUIRE(cap > 0);
|
||||
@@ -848,7 +849,7 @@ TEST_CASE("BuildingSystem: smelter auto-smelts ore without a recipe selection",
|
||||
// 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);
|
||||
const Building* b = findBuilding(state_bs, sid);
|
||||
REQUIRE(b != nullptr);
|
||||
bool hasIronIngot = false;
|
||||
for (const Item& item : outputSideItems(*b))
|
||||
@@ -896,7 +897,7 @@ TEST_CASE("BuildingSystem: smelter runs a satisfiable recipe while an incomplete
|
||||
// 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);
|
||||
const Building* b = findBuilding(state_bs, sid);
|
||||
REQUIRE(b != nullptr);
|
||||
|
||||
// Copper was smelted; the lone iron_ore still waits for a second unit.
|
||||
@@ -984,7 +985,7 @@ TEST_CASE("BuildingSystem: output port couples directly into an adjacent input p
|
||||
// Smelter build (15s) + margin for coupling and a smelt cycle.
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(30.0)), tick);
|
||||
|
||||
const Building* smelter = bs.findBuilding(smelterId);
|
||||
const Building* smelter = findBuilding(state_bs, smelterId);
|
||||
REQUIRE(smelter != nullptr);
|
||||
// iron_ore reached the smelter over the direct coupling and was smelted.
|
||||
bool hasIronIngot = false;
|
||||
@@ -1024,8 +1025,8 @@ TEST_CASE("BuildingSystem: direct coupling to a non-consumer leaves the item stu
|
||||
// Both miners build sequentially (10s each), then the producer runs and jams.
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)), tick);
|
||||
|
||||
const Building* miner = bs.findBuilding(minerId);
|
||||
const Building* sink = bs.findBuilding(sinkId);
|
||||
const Building* miner = findBuilding(state_bs, minerId);
|
||||
const Building* sink = findBuilding(state_bs, sinkId);
|
||||
REQUIRE(miner != nullptr);
|
||||
REQUIRE(sink != nullptr);
|
||||
// Nothing was delivered, and the producer's output side has backed up to its cap.
|
||||
@@ -1063,14 +1064,14 @@ TEST_CASE("BuildingSystem: setRecipe clears output buffer and active production"
|
||||
tick);
|
||||
|
||||
{
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
REQUIRE(b->getOutputItemCount() > 0);
|
||||
}
|
||||
|
||||
bs.setRecipe(id, "mine_copper_ore");
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
// Clearing the output buffer on a recipe change also discards emerging items
|
||||
// (REQ-MAT-OUTPUT-EMERGE).
|
||||
REQUIRE(b->getOutputItemCount() == 0);
|
||||
@@ -1106,7 +1107,7 @@ TEST_CASE("BuildingSystem: reprocessing plant output buffer capacity equals max
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(25.0)) + 1, tick);
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
// reprocessing_cycle outputs: 2 iron_ingot (60%), 1 circuit_board (30%),
|
||||
// 1 advanced_alloy (10%). Max per roll = 2. Capacity = 2 (1× max).
|
||||
@@ -1153,7 +1154,7 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
|
||||
// Verify all five scrap were accepted; some may still be travelling inward on
|
||||
// the input belt (REQ-MAT-INPUT-INTAKE), so count buffered + in-transit.
|
||||
{
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
REQUIRE(b->pendingInputCount(ItemType{"scrap"}) == 5);
|
||||
}
|
||||
@@ -1161,7 +1162,7 @@ TEST_CASE("BuildingSystem: reprocessing plant produces one cycle output then sta
|
||||
// Run production cycle (3s = 90 ticks + 1 for the completion tick).
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(3.0)) + 1, tick);
|
||||
|
||||
const Building* b = bs.findBuilding(id);
|
||||
const Building* b = findBuilding(state_bs, id);
|
||||
REQUIRE(b != nullptr);
|
||||
// Cycle should have completed and output deposited.
|
||||
REQUIRE_FALSE(b->outputBuffer.items.empty());
|
||||
@@ -1237,7 +1238,7 @@ TEST_CASE("BuildingSystem: findRotateInPlaceTarget returns the building id for a
|
||||
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||
REQUIRE(bs.getAllSites().empty());
|
||||
REQUIRE(getAllSites(state_bs).empty());
|
||||
|
||||
const std::optional<BuildingId> result =
|
||||
bs.findRotateInPlaceTarget(BuildingType::Belt, QPoint(0, 0), Rotation::South);
|
||||
@@ -1367,11 +1368,11 @@ TEST_CASE("BuildingSystem: rotateInPlace updates the rotation field of a constru
|
||||
rng);
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::Belt, QPoint(0, 0), Rotation::East, 0).value();
|
||||
REQUIRE(bs.findSite(id)->rotation == Rotation::East);
|
||||
REQUIRE(findSite(state_bs, id)->rotation == Rotation::East);
|
||||
|
||||
bs.rotateInPlace(id, Rotation::North);
|
||||
|
||||
REQUIRE(bs.findSite(id)->rotation == Rotation::North);
|
||||
REQUIRE(findSite(state_bs, id)->rotation == Rotation::North);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of a queued site",
|
||||
@@ -1391,12 +1392,12 @@ TEST_CASE("BuildingSystem: rotateInPlace preserves the construction progress of
|
||||
rng);
|
||||
|
||||
const BuildingId id = bs.place(BuildingType::Belt, QPoint(0, 0), Rotation::East, 0).value();
|
||||
const Tick completesAt = bs.findSite(id)->completesAt;
|
||||
const Tick completesAt = findSite(state_bs, id)->completesAt;
|
||||
REQUIRE(completesAt > 0);
|
||||
|
||||
bs.rotateInPlace(id, Rotation::South);
|
||||
|
||||
REQUIRE(bs.findSite(id)->completesAt == completesAt);
|
||||
REQUIRE(findSite(state_bs, id)->completesAt == completesAt);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direction on an operational building",
|
||||
@@ -1419,14 +1420,14 @@ TEST_CASE("BuildingSystem: rotateInPlace updates rotation and output port direct
|
||||
|
||||
Tick tick = 0;
|
||||
runTicks(bs, belts, static_cast<int>(secondsToTicks(1.0)) + 1, tick);
|
||||
REQUIRE(bs.findBuilding(id) != nullptr);
|
||||
REQUIRE(findBuilding(state_bs, id) != nullptr);
|
||||
|
||||
const Building& before = *bs.findBuilding(id);
|
||||
const Building& before = *findBuilding(state_bs, id);
|
||||
REQUIRE(before.outputPorts[0].direction == Rotation::East);
|
||||
|
||||
bs.rotateInPlace(id, Rotation::North);
|
||||
|
||||
const Building& after = *bs.findBuilding(id);
|
||||
const Building& after = *findBuilding(state_bs, id);
|
||||
REQUIRE(after.rotation == Rotation::North);
|
||||
REQUIRE(after.outputPorts[0].direction == Rotation::North);
|
||||
}
|
||||
@@ -1468,11 +1469,11 @@ TEST_CASE("BuildingSystem: rotateInPlace preserves the output filters of a split
|
||||
|
||||
// Run until construction completes, so the splitter is registered with BeltSystem.
|
||||
Tick tick = 0;
|
||||
while (f.bs.getAllBuildings().empty() && tick < 100000)
|
||||
while (getAllBuildings(f.state).empty() && tick < 100000)
|
||||
{
|
||||
runTicks(f.bs, f.belts, 1, tick);
|
||||
}
|
||||
REQUIRE(f.bs.getAllBuildings().size() == 1);
|
||||
REQUIRE(getAllBuildings(f.state).size() == 1);
|
||||
|
||||
const std::vector<ItemType> filterA{ ItemType{"iron_ore"} };
|
||||
const std::vector<ItemType> filterB{ ItemType{"copper_ore"} };
|
||||
@@ -1496,7 +1497,7 @@ TEST_CASE("BuildingSystem: splitter filters configured on a construction site ca
|
||||
const QPoint tile(5, 5);
|
||||
const BuildingId id = f.bs.place(BuildingType::Splitter, tile, Rotation::East, 0).value();
|
||||
REQUIRE(id != kInvalidBuildingId);
|
||||
REQUIRE(f.bs.findSite(id) != nullptr);
|
||||
REQUIRE(findSite(f.state, id) != nullptr);
|
||||
|
||||
// Configure an output filter on the still-queued splitter site.
|
||||
const std::vector<ItemType> filterA{ ItemType{"iron_ore"} };
|
||||
@@ -1513,12 +1514,12 @@ TEST_CASE("BuildingSystem: splitter filters configured on a construction site ca
|
||||
|
||||
// Run until construction completes.
|
||||
Tick tick = 0;
|
||||
while (f.bs.getAllBuildings().empty() && tick < 100000)
|
||||
while (getAllBuildings(f.state).empty() && tick < 100000)
|
||||
{
|
||||
runTicks(f.bs, f.belts, 1, tick);
|
||||
}
|
||||
REQUIRE(f.bs.getAllBuildings().size() == 1);
|
||||
REQUIRE(f.bs.getAllBuildings()[0].type == BuildingType::Splitter);
|
||||
REQUIRE(getAllBuildings(f.state).size() == 1);
|
||||
REQUIRE(getAllBuildings(f.state)[0].type == BuildingType::Splitter);
|
||||
|
||||
// The built splitter is registered with BeltSystem carrying the filters.
|
||||
const std::optional<BeltSystem::SplitterInfo> builtInfo = f.belts.getSplitterInfo(tile);
|
||||
@@ -1669,10 +1670,10 @@ namespace
|
||||
|
||||
// Advances the sim until the given site becomes an operational building, or a
|
||||
// safety cap is reached.
|
||||
void buildToCompletion(BuildingSystem& bs, BeltSystem& belts, BuildingId id,
|
||||
Tick& tick)
|
||||
void buildToCompletion(BuildingSystem& bs, const FactoryState& state,
|
||||
BeltSystem& belts, BuildingId id, Tick& tick)
|
||||
{
|
||||
for (int i = 0; i < 20000 && bs.findBuilding(id) == nullptr; ++i)
|
||||
for (int i = 0; i < 20000 && findBuilding(state, id) == nullptr; ++i)
|
||||
{
|
||||
runTicks(bs, belts, 1, tick);
|
||||
}
|
||||
@@ -1709,8 +1710,8 @@ TEST_CASE("BuildingSystem: getInputPorts matches between a site and the built bu
|
||||
const BuildingId id = f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
||||
|
||||
const std::vector<Port> sitePorts = f.bs.getInputPorts(id);
|
||||
buildToCompletion(f.bs, f.belts, id, tick);
|
||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
||||
buildToCompletion(f.bs, f.state, f.belts, id, tick);
|
||||
REQUIRE(findBuilding(f.state, id) != nullptr);
|
||||
const std::vector<Port> builtPorts = f.bs.getInputPorts(id);
|
||||
|
||||
// The operational path (stored inputPorts) agrees with the site path (mask-derived).
|
||||
@@ -1725,7 +1726,7 @@ TEST_CASE("BuildingSystem: getInputPorts invariants hold for a rotated site", "[
|
||||
{
|
||||
PlacementFixture f;
|
||||
const BuildingId id = f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::South, 0).value();
|
||||
const ConstructionSite* site = f.bs.findSite(id);
|
||||
const ConstructionSite* site = findSite(f.state, id);
|
||||
REQUIRE(site != nullptr);
|
||||
|
||||
std::set<std::pair<int, int>> bodySet;
|
||||
|
||||
Reference in New Issue
Block a user