@@ -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 . findSi te( id ) ! = nullptr ) ;
REQUIRE ( findSite ( f . sta te, 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 , BeltSys tem & belts , BuildingId id ,
Tick & tick )
void buildToCompletion ( BuildingSystem & bs , const FactorySta te& 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 . findSi te( id ) ;
const ConstructionSite * site = findSite ( f . sta te, id ) ;
REQUIRE ( site ! = nullptr ) ;
std : : set < std : : pair < int , int > > bodySet ;