simulate items on splitters and fix bugs where buildings could not pull from splitters
This commit is contained in:
@@ -296,8 +296,9 @@ TEST_CASE("BeltSystem: forEachVisualItem reports correct ItemType", "[belt]")
|
||||
|
||||
TEST_CASE("BeltSystem: splitter alternates between outputA and outputB", "[belt]")
|
||||
{
|
||||
// Layout: tileIn -> splitter -> tileA (West output)
|
||||
// -> tileB (East output)
|
||||
// Layout: tileIn -> splitter -> tileA (North output)
|
||||
// -> tileB (South output)
|
||||
// Pipeline per item: tileIn(1) -> back(2) -> front(3) -> output belt(4)
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
const QPoint tileIn(0, 0);
|
||||
@@ -311,12 +312,14 @@ TEST_CASE("BeltSystem: splitter alternates between outputA and outputB", "[belt]
|
||||
bs.placeBelt(tileB, Rotation::South);
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("item1"));
|
||||
bs.tick(); // item moves: tileIn -> splitter held
|
||||
bs.tick(); // item1: tileIn -> splitter back (progress 0)
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("item2"));
|
||||
bs.tick(); // item1 routes to outputA (North=tileA); item2 moves to splitter
|
||||
|
||||
bs.tick(); // item2 routes to outputB (South=tileB)
|
||||
bs.tick(); // item1 back -> 0.5 -> frontA; item2 advances but back is occupied
|
||||
bs.tick(); // item1 frontA -> 1.0 -> tileA; item2 enters splitter back
|
||||
bs.tick(); // item2 back -> 0.5 -> frontB; item1 at tileA output edge
|
||||
bs.tick(); // item2 frontB -> 1.0 -> tileB
|
||||
bs.tick(); // item2 at tileB output edge
|
||||
|
||||
const bool inA = bs.tryTakeItem(Port{tileA, Rotation::North}).has_value();
|
||||
const bool inB = bs.tryTakeItem(Port{tileB, Rotation::South}).has_value();
|
||||
@@ -345,12 +348,126 @@ TEST_CASE("BeltSystem: splitter routes filtered item to matching output", "[belt
|
||||
bs.placeBelt(tileB, Rotation::South);
|
||||
|
||||
// Filter: outputA = iron_ore only; outputB = accept all.
|
||||
// iron_ore matches both filters → alternation; preferred = outputA (nextOutputIsA=true).
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {});
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tick(); // tileIn -> splitter held
|
||||
bs.tick(); // routed to outputA (filter match)
|
||||
bs.tick(); // tileIn -> splitter back
|
||||
bs.tick(); // back -> frontA (filter + alternation → preferred outputA)
|
||||
bs.tick(); // frontA -> tileA
|
||||
bs.tick(); // item at tileA output edge
|
||||
|
||||
REQUIRE(bs.tryTakeItem(Port{tileA, Rotation::North}).has_value());
|
||||
REQUIRE_FALSE(bs.tryTakeItem(Port{tileB, Rotation::South}).has_value());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Splitter — direct building input (no output belts)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("BeltSystem: splitter back slot is capped at 0.5 and waits before routing", "[belt]")
|
||||
{
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
const QPoint tileIn(0, 0);
|
||||
const QPoint tileSpl(1, 0);
|
||||
|
||||
bs.placeBelt(tileIn, Rotation::East);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tick(); // item enters splitter back at progress 0; routing not yet triggered
|
||||
|
||||
// Back has not yet reached 0.5 — front slots empty, nothing available.
|
||||
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
|
||||
bs.tick(); // back advances to 0.5, routes to frontA at progress 0
|
||||
bs.tick(); // frontA advances to 1.0, available for building pickup
|
||||
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: splitter delivers item directly to building input via tryTakeItem", "[belt]")
|
||||
{
|
||||
// Bug 1: splitter could not insert into a building input with no belt in between.
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
const QPoint tileIn(0, 0);
|
||||
const QPoint tileSpl(1, 0);
|
||||
|
||||
bs.placeBelt(tileIn, Rotation::East);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
// No output belts — both outputs lead directly to building inputs.
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tick(); // tileIn -> splitter back
|
||||
bs.tick(); // back -> frontA at progress 0
|
||||
bs.tick(); // frontA reaches 1.0; no downstream belt, item waits for building pickup
|
||||
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
const std::optional<Item> taken = bs.tryTakeItem(Port{tileSpl, Rotation::North});
|
||||
REQUIRE(taken.has_value());
|
||||
REQUIRE(taken->type.id == "iron_ore");
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: splitter accepts new items after building pulls from front slot", "[belt]")
|
||||
{
|
||||
// Bug 2: when outputs had no belts, splitter never cleared its held state
|
||||
// so no new items could enter.
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
const QPoint tileIn(0, 0);
|
||||
const QPoint tileSpl(1, 0);
|
||||
|
||||
bs.placeBelt(tileIn, Rotation::East);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("item1"));
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick(); // item1 now in frontA at 1.0
|
||||
|
||||
// Building pulls item1 — clears frontA; nextOutputIsA toggled to false.
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
|
||||
// Feed item2; preferred is now South.
|
||||
bs.tryPutItem(tileIn, makeItem("item2"));
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick(); // item2 now in frontB at 1.0
|
||||
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: splitter alternates between two unregistered outputs (building inputs)", "[belt]")
|
||||
{
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
const QPoint tileIn(0, 0);
|
||||
const QPoint tileSpl(1, 0);
|
||||
|
||||
bs.placeBelt(tileIn, Rotation::East);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
// item1 → frontA (preferred, nextOutputIsA=true)
|
||||
bs.tryPutItem(tileIn, makeItem("item1"));
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
|
||||
// item2 → frontB (preferred, nextOutputIsA now false)
|
||||
bs.tryPutItem(tileIn, makeItem("item2"));
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
|
||||
// item3 → frontA again (nextOutputIsA toggled back to true)
|
||||
bs.tryPutItem(tileIn, makeItem("item3"));
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user