fix issue where items were accepted by a belt from opposite travel direction
This commit is contained in:
@@ -44,14 +44,14 @@ TEST_CASE("BeltSystem: tryPutItem succeeds on registered belt", "[belt]")
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("iron_ore")));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East));
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: tryPutItem fails on unregistered tile", "[belt]")
|
||||
{
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
REQUIRE_FALSE(bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore")));
|
||||
REQUIRE_FALSE(bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East));
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: tryPutItem fails after removeTile", "[belt]")
|
||||
@@ -61,7 +61,7 @@ TEST_CASE("BeltSystem: tryPutItem fails after removeTile", "[belt]")
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.removeTile(tile);
|
||||
|
||||
REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("iron_ore")));
|
||||
REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -74,10 +74,10 @@ TEST_CASE("BeltSystem: four items fit in one tile", "[belt]")
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("a")));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("b")));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("c")));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("d")));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("a"), Rotation::East));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("b"), Rotation::East));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("c"), Rotation::East));
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("d"), Rotation::East));
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: fifth tryPutItem on full tile returns false", "[belt]")
|
||||
@@ -86,12 +86,12 @@ TEST_CASE("BeltSystem: fifth tryPutItem on full tile returns false", "[belt]")
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
|
||||
bs.tryPutItem(tile, makeItem("a"));
|
||||
bs.tryPutItem(tile, makeItem("b"));
|
||||
bs.tryPutItem(tile, makeItem("c"));
|
||||
bs.tryPutItem(tile, makeItem("d"));
|
||||
bs.tryPutItem(tile, makeItem("a"), Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("b"), Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("c"), Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("d"), Rotation::East);
|
||||
|
||||
REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("e")));
|
||||
REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("e"), Rotation::East));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -103,7 +103,7 @@ TEST_CASE("BeltSystem: tryTakeItem returns placed item after reaching output edg
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // advance to output edge
|
||||
|
||||
const std::optional<Item> taken = bs.tryTakeItem(eastPort(tile));
|
||||
@@ -117,7 +117,7 @@ TEST_CASE("BeltSystem: tryTakeItem requires item to reach output edge before yie
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
// Item placed but not yet at output edge — must not be available.
|
||||
REQUIRE_FALSE(bs.tryTakeItem(eastPort(tile)).has_value());
|
||||
@@ -133,8 +133,8 @@ TEST_CASE("BeltSystem: tryTakeItem with two items returns both after each reache
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("first"));
|
||||
bs.tryPutItem(tile, makeItem("second"));
|
||||
bs.tryPutItem(tile, makeItem("first"), Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("second"), Rotation::East);
|
||||
|
||||
// Front item reaches output edge after one tick.
|
||||
bs.tick();
|
||||
@@ -169,7 +169,7 @@ TEST_CASE("BeltSystem: item transfers from tile A to tile B and becomes availabl
|
||||
bs.placeBelt(tileA, Rotation::East);
|
||||
bs.placeBelt(tileB, Rotation::East);
|
||||
|
||||
bs.tryPutItem(tileA, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileA, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // item reaches output edge of A, moves to B at progress 0
|
||||
bs.tick(); // item reaches output edge of B
|
||||
|
||||
@@ -185,7 +185,7 @@ TEST_CASE("BeltSystem: item stays at progress 1.0 when next tile is absent", "[b
|
||||
const QPoint tileA(0, 0);
|
||||
bs.placeBelt(tileA, Rotation::East);
|
||||
|
||||
bs.tryPutItem(tileA, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileA, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick();
|
||||
|
||||
// Item should still be on tileA (no registered tile to the east).
|
||||
@@ -202,7 +202,7 @@ TEST_CASE("BeltSystem: item traverses 3-tile chain in 3 ticks (one per tile)", "
|
||||
bs.placeBelt(tileB, Rotation::East);
|
||||
bs.placeBelt(tileC, Rotation::East);
|
||||
|
||||
bs.tryPutItem(tileA, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileA, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // A output edge → moves to B at progress 0
|
||||
bs.tick(); // B output edge → moves to C at progress 0
|
||||
bs.tick(); // C output edge → available for pickup
|
||||
@@ -221,13 +221,13 @@ TEST_CASE("BeltSystem: item stays blocked when next tile is full", "[belt]")
|
||||
bs.placeBelt(tileB, Rotation::East);
|
||||
|
||||
// Fill tileB to capacity.
|
||||
bs.tryPutItem(tileB, makeItem("b1"));
|
||||
bs.tryPutItem(tileB, makeItem("b2"));
|
||||
bs.tryPutItem(tileB, makeItem("b3"));
|
||||
bs.tryPutItem(tileB, makeItem("b4"));
|
||||
bs.tryPutItem(tileB, makeItem("b1"), Rotation::East);
|
||||
bs.tryPutItem(tileB, makeItem("b2"), Rotation::East);
|
||||
bs.tryPutItem(tileB, makeItem("b3"), Rotation::East);
|
||||
bs.tryPutItem(tileB, makeItem("b4"), Rotation::East);
|
||||
|
||||
// Place item in tileA — should be blocked.
|
||||
bs.tryPutItem(tileA, makeItem("a1"));
|
||||
bs.tryPutItem(tileA, makeItem("a1"), Rotation::East);
|
||||
bs.tick();
|
||||
|
||||
// Item in tileA must still be there.
|
||||
@@ -246,13 +246,13 @@ TEST_CASE("BeltSystem: belt second slot is capped at progress 0.75", "[belt]")
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
|
||||
// Advance front item to the output edge; it stays there (no next tile).
|
||||
bs.tryPutItem(tile, makeItem("front_item"));
|
||||
bs.tryPutItem(tile, makeItem("front_item"), Rotation::East);
|
||||
bs.tick(); // slot[0]: 0.4
|
||||
bs.tick(); // slot[0]: 0.8
|
||||
bs.tick(); // slot[0]: 1.0 (capped, stuck)
|
||||
|
||||
// Place second item; slot[0] is at 1.0.
|
||||
bs.tryPutItem(tile, makeItem("back_item"));
|
||||
bs.tryPutItem(tile, makeItem("back_item"), Rotation::East);
|
||||
bs.tick(); // slot[1]: 0.4
|
||||
bs.tick(); // slot[1] would reach 0.8 — capped at 0.75
|
||||
|
||||
@@ -272,8 +272,8 @@ TEST_CASE("BeltSystem: clearTiles removes all items from specified tiles", "[bel
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tile, makeItem("copper_ore"));
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("copper_ore"), Rotation::East);
|
||||
|
||||
bs.clearTiles({tile});
|
||||
|
||||
@@ -289,7 +289,7 @@ TEST_CASE("BeltSystem: forEachVisualItem visits items inside viewport", "[belt]"
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(5, 5);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
int count = 0;
|
||||
bs.forEachVisualItem(QRect(0, 0, 20, 20), [&count](VisualItem) { ++count; });
|
||||
@@ -302,7 +302,7 @@ TEST_CASE("BeltSystem: forEachVisualItem skips items outside viewport", "[belt]"
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(50, 50);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
int count = 0;
|
||||
bs.forEachVisualItem(QRect(0, 0, 20, 20), [&count](VisualItem) { ++count; });
|
||||
@@ -315,7 +315,7 @@ TEST_CASE("BeltSystem: forEachVisualItem reports correct ItemType", "[belt]")
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::East);
|
||||
bs.tryPutItem(tile, makeItem("copper_ingot"));
|
||||
bs.tryPutItem(tile, makeItem("copper_ingot"), Rotation::East);
|
||||
|
||||
std::vector<ItemType> seen;
|
||||
bs.forEachVisualItem(QRect(-1, -1, 10, 10), [&seen](VisualItem vi)
|
||||
@@ -348,10 +348,10 @@ TEST_CASE("BeltSystem: splitter alternates between outputA and outputB", "[belt]
|
||||
bs.placeBelt(tileA, Rotation::North);
|
||||
bs.placeBelt(tileB, Rotation::South);
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("item1"));
|
||||
bs.tryPutItem(tileIn, makeItem("item1"), Rotation::East);
|
||||
bs.tick(); // item1: tileIn -> splitter back (progress 0)
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("item2"));
|
||||
bs.tryPutItem(tileIn, makeItem("item2"), Rotation::East);
|
||||
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
|
||||
@@ -388,7 +388,7 @@ TEST_CASE("BeltSystem: splitter routes to preferred output when item matches bot
|
||||
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {});
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // tileIn -> splitter back
|
||||
bs.tick(); // back -> frontA (both match, alternation, preferred A)
|
||||
bs.tick(); // frontA -> tileA
|
||||
@@ -411,7 +411,7 @@ TEST_CASE("BeltSystem: splitter routes item to output A when only filter A match
|
||||
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {ItemType{"copper_ore"}});
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // tileIn -> splitter back
|
||||
bs.tick(); // back -> frontA (exclusive match to A)
|
||||
bs.tick(); // frontA reaches 1.0; no downstream belt, waits for building pickup
|
||||
@@ -433,7 +433,7 @@ TEST_CASE("BeltSystem: splitter routes item to output B when only filter B match
|
||||
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"copper_ore"}}, {ItemType{"iron_ore"}});
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
@@ -456,17 +456,17 @@ TEST_CASE("BeltSystem: splitter alternates A then B when item matches both expli
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {ItemType{"iron_ore"}});
|
||||
|
||||
// Item 1 → preferred A (nextOutputIsA=true initially).
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick();
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
|
||||
// Item 2 → preferred B (nextOutputIsA toggled to false).
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick();
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
|
||||
// Item 3 → preferred A again (nextOutputIsA toggled back to true).
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick();
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
@@ -486,7 +486,7 @@ TEST_CASE("BeltSystem: splitter routes unmatched item to the unfiltered output",
|
||||
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"copper_ore"}}, {});
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick();
|
||||
|
||||
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
@@ -506,7 +506,7 @@ TEST_CASE("BeltSystem: splitter stalls when item matches neither filter", "[belt
|
||||
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"copper_ore"}}, {ItemType{"iron_ingot"}});
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // tileIn -> splitter back
|
||||
bs.tick(); // back reaches 0.5; routing fires but stalls (no filter match)
|
||||
bs.tick(); // back stays at 0.5; stall persists
|
||||
@@ -530,17 +530,17 @@ TEST_CASE("BeltSystem: splitter falls back to other output when preferred is blo
|
||||
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {ItemType{"iron_ore"}});
|
||||
|
||||
// Item 1 → preferred A (nextOutputIsA=true → false after routing).
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick(); // frontA = item1 at 1.0
|
||||
|
||||
// Item 2 → preferred B (nextOutputIsA=false → true after routing). Take item2 to free frontB.
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick(); // frontB = item2 at 1.0
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
|
||||
// frontA still holds item1; nextOutputIsA=true (prefer A).
|
||||
// Item 3: both match, preferred A is occupied → fallback to B without toggling nextOutputIsA.
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick(); // frontB = item3 at 1.0
|
||||
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value()); // item1 still in A
|
||||
@@ -549,7 +549,7 @@ TEST_CASE("BeltSystem: splitter falls back to other output when preferred is blo
|
||||
// nextOutputIsA was not toggled by the fallback: next item should still prefer A.
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value()); // free frontA
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value()); // free frontB
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); bs.tick(); bs.tick();
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value()); // item4 → A (preferA still true)
|
||||
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
|
||||
@@ -591,14 +591,14 @@ TEST_CASE("BeltSystem: splitter fallback enters the open output at progress 0.75
|
||||
|
||||
// Permanently block output A: route one item to frontA where it sticks at 1.0
|
||||
// (North has no downstream tile, so it can never move out).
|
||||
bs.tryPutItem(tileSpl, makeItem("blockA"));
|
||||
bs.tryPutItem(tileSpl, makeItem("blockA"), Rotation::East);
|
||||
bs.tick(); // back: 0.25
|
||||
bs.tick(); // back: 0.5 -> frontA at 0.75 (preferred A), nextOutputIsA = false
|
||||
bs.tick(); bs.tick(); // frontA: 0.75 -> 1.0 (stuck, no North downstream)
|
||||
|
||||
// Cycle one item through B as the *preferred* output (also enters at 0.75) to
|
||||
// flip nextOutputIsA back to true and free frontB for the fallback case below.
|
||||
bs.tryPutItem(tileSpl, makeItem("toB_pref"));
|
||||
bs.tryPutItem(tileSpl, makeItem("toB_pref"), Rotation::East);
|
||||
bs.tick(); // back: 0.25
|
||||
bs.tick(); // back: 0.5 -> frontB at 0.75 (preferred B), nextOutputIsA = true
|
||||
REQUIRE(southProgressOf("toB_pref") == Approx(0.75));
|
||||
@@ -608,7 +608,7 @@ TEST_CASE("BeltSystem: splitter fallback enters the open output at progress 0.75
|
||||
|
||||
// Next item prefers A again (nextOutputIsA == true), but A is still blocked,
|
||||
// so it falls back to B — and must enter near the edge at progress 0.75.
|
||||
bs.tryPutItem(tileSpl, makeItem("toB_fallback"));
|
||||
bs.tryPutItem(tileSpl, makeItem("toB_fallback"), Rotation::East);
|
||||
bs.tick(); // back: 0.25
|
||||
bs.tick(); // back: 0.5 -> fallback routes to frontB at 0.75
|
||||
REQUIRE(southProgressOf("toB_fallback") == Approx(0.75));
|
||||
@@ -648,13 +648,13 @@ TEST_CASE("BeltSystem: splitter with an exclusive filter enters its only output
|
||||
};
|
||||
|
||||
// iron_ore matches filterA only -> sole eligible output A.
|
||||
bs.tryPutItem(tileSpl, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileSpl, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick(); // back: 0.25
|
||||
bs.tick(); // back: 0.5 -> routes to frontA at 0.75
|
||||
REQUIRE(progressOf("iron_ore", Rotation::North) == Approx(0.75));
|
||||
|
||||
// copper_ore matches filterB only -> sole eligible output B.
|
||||
bs.tryPutItem(tileSpl, makeItem("copper_ore"));
|
||||
bs.tryPutItem(tileSpl, makeItem("copper_ore"), Rotation::East);
|
||||
bs.tick(); // back: 0.25
|
||||
bs.tick(); // back: 0.5 -> routes to frontB at 0.75
|
||||
REQUIRE(progressOf("copper_ore", Rotation::South) == Approx(0.75));
|
||||
@@ -692,13 +692,13 @@ TEST_CASE("BeltSystem: splitter alternation enters the preferred output at progr
|
||||
};
|
||||
|
||||
// First item: preferred A (nextOutputIsA starts true) -> frontA at 0.75.
|
||||
bs.tryPutItem(tileSpl, makeItem("first"));
|
||||
bs.tryPutItem(tileSpl, makeItem("first"), Rotation::East);
|
||||
bs.tick(); // back: 0.25
|
||||
bs.tick(); // back: 0.5 -> routes to preferred frontA at 0.75, nextOutputIsA = false
|
||||
REQUIRE(progressOf("first", Rotation::North) == Approx(0.75));
|
||||
|
||||
// Second item: preference flipped, B is free -> frontB at 0.75.
|
||||
bs.tryPutItem(tileSpl, makeItem("second"));
|
||||
bs.tryPutItem(tileSpl, makeItem("second"), Rotation::East);
|
||||
bs.tick(); // back: 0.25 (first sticks at North 1.0, no downstream)
|
||||
bs.tick(); // back: 0.5 -> routes to preferred frontB at 0.75
|
||||
REQUIRE(progressOf("second", Rotation::South) == Approx(0.75));
|
||||
@@ -718,7 +718,7 @@ TEST_CASE("BeltSystem: splitter back slot is capped at 0.5 and waits before rout
|
||||
bs.placeBelt(tileIn, Rotation::East);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
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.
|
||||
@@ -743,7 +743,7 @@ TEST_CASE("BeltSystem: splitter delivers item directly to building input via try
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
// No output belts — both outputs lead directly to building inputs.
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(tileIn, makeItem("iron_ore"), Rotation::East);
|
||||
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
|
||||
@@ -766,7 +766,7 @@ TEST_CASE("BeltSystem: splitter accepts new items after building pulls from fron
|
||||
bs.placeBelt(tileIn, Rotation::East);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
bs.tryPutItem(tileIn, makeItem("item1"));
|
||||
bs.tryPutItem(tileIn, makeItem("item1"), Rotation::East);
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick(); // item1 now in frontA at 1.0
|
||||
@@ -775,7 +775,7 @@ TEST_CASE("BeltSystem: splitter accepts new items after building pulls from fron
|
||||
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value());
|
||||
|
||||
// Feed item2; preferred is now South.
|
||||
bs.tryPutItem(tileIn, makeItem("item2"));
|
||||
bs.tryPutItem(tileIn, makeItem("item2"), Rotation::East);
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick(); // item2 now in frontB at 1.0
|
||||
@@ -812,21 +812,21 @@ TEST_CASE("BeltSystem: splitter alternates between two unregistered outputs (bui
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
// item1 → frontA (preferred, nextOutputIsA=true)
|
||||
bs.tryPutItem(tileIn, makeItem("item1"));
|
||||
bs.tryPutItem(tileIn, makeItem("item1"), Rotation::East);
|
||||
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.tryPutItem(tileIn, makeItem("item2"), Rotation::East);
|
||||
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.tryPutItem(tileIn, makeItem("item3"), Rotation::East);
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
@@ -847,7 +847,7 @@ TEST_CASE("BeltSystem: tunnel pairing — basic pair within max distance", "[bel
|
||||
bs.placeTunnelEntry(entry, Rotation::East, 10);
|
||||
bs.placeTunnelExit(exit, Rotation::East);
|
||||
|
||||
bs.tryPutItem(entry, makeItem("iron_ore"));
|
||||
bs.tryPutItem(entry, makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
// With kFastBeltSpeed, items cross one tile per tick.
|
||||
// entry tile: 1 tick to reach front progress 1.0
|
||||
@@ -873,7 +873,7 @@ TEST_CASE("BeltSystem: tunnel pairing — wrong direction prevents pair", "[belt
|
||||
bs.placeTunnelEntry(QPoint(0, 0), Rotation::East, 10);
|
||||
bs.placeTunnelExit(QPoint(3, 0), Rotation::North);
|
||||
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"));
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
@@ -891,7 +891,7 @@ TEST_CASE("BeltSystem: tunnel pairing — beyond max distance prevents pair", "[
|
||||
bs.placeTunnelEntry(QPoint(0, 0), Rotation::East, 2);
|
||||
bs.placeTunnelExit(QPoint(3, 0), Rotation::East);
|
||||
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"));
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
@@ -912,7 +912,7 @@ TEST_CASE("BeltSystem: tunnel pairing — same-dir entry between blocks pairing"
|
||||
bs.placeTunnelExit(QPoint(4, 0), Rotation::East);
|
||||
|
||||
// Put item on Entry2 — should reach exit.
|
||||
bs.tryPutItem(QPoint(2, 0), makeItem("copper_ore"));
|
||||
bs.tryPutItem(QPoint(2, 0), makeItem("copper_ore"), Rotation::East);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
bs.tick();
|
||||
@@ -921,7 +921,7 @@ TEST_CASE("BeltSystem: tunnel pairing — same-dir entry between blocks pairing"
|
||||
bs.tryTakeItem(Port{QPoint(4, 0), Rotation::East});
|
||||
|
||||
// Put item on Entry1 — should NOT reach exit (Entry1 is unpaired).
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"));
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
bs.tick();
|
||||
@@ -939,7 +939,7 @@ TEST_CASE("BeltSystem: tunnel pairing — cross-dir entry between is ignored", "
|
||||
bs.placeTunnelEntry(QPoint(2, 0), Rotation::North, 10);
|
||||
bs.placeTunnelExit(QPoint(4, 0), Rotation::East);
|
||||
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"));
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East);
|
||||
for (int i = 0; i < 20; ++i)
|
||||
{
|
||||
bs.tick();
|
||||
@@ -958,7 +958,7 @@ TEST_CASE("BeltSystem: unpaired entry blocks items at front", "[belt]")
|
||||
bs.placeTunnelEntry(QPoint(0, 0), Rotation::East, 10);
|
||||
// No exit placed — entry is unpaired.
|
||||
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"));
|
||||
bs.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
bs.tick();
|
||||
@@ -984,7 +984,7 @@ TEST_CASE("BeltSystem: demolish entry discards transit items", "[belt]")
|
||||
bs.placeTunnelEntry(entry, Rotation::East, 10);
|
||||
bs.placeTunnelExit(exit, Rotation::East);
|
||||
|
||||
bs.tryPutItem(entry, makeItem("iron_ore"));
|
||||
bs.tryPutItem(entry, makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
// Advance just enough for item to enter transit but not reach exit.
|
||||
bs.tick(); // item enters entry front
|
||||
@@ -1010,7 +1010,7 @@ TEST_CASE("BeltSystem: clearTiles discards tunnel transit items", "[belt]")
|
||||
bs.placeTunnelEntry(entry, Rotation::East, 10);
|
||||
bs.placeTunnelExit(exit, Rotation::East);
|
||||
|
||||
bs.tryPutItem(entry, makeItem("iron_ore"));
|
||||
bs.tryPutItem(entry, makeItem("iron_ore"), Rotation::East);
|
||||
bs.tick();
|
||||
bs.tick();
|
||||
|
||||
@@ -1037,7 +1037,7 @@ TEST_CASE("BeltSystem: belt to entry to transit to exit to belt full chain", "[b
|
||||
bs.placeTunnelExit(exit, Rotation::East);
|
||||
bs.placeBelt(beltOut, Rotation::East);
|
||||
|
||||
bs.tryPutItem(beltIn, makeItem("iron_ore"));
|
||||
bs.tryPutItem(beltIn, makeItem("iron_ore"), Rotation::East);
|
||||
|
||||
for (int i = 0; i < 30; ++i)
|
||||
{
|
||||
@@ -1061,11 +1061,11 @@ TEST_CASE("BeltSystem: multiple items transit tunnel in order", "[belt]")
|
||||
bs.placeTunnelEntry(entry, Rotation::East, 10);
|
||||
bs.placeTunnelExit(exit, Rotation::East);
|
||||
|
||||
bs.tryPutItem(entry, makeItem("item1"));
|
||||
bs.tryPutItem(entry, makeItem("item1"), Rotation::East);
|
||||
bs.tick();
|
||||
bs.tick(); // item1 enters transit
|
||||
|
||||
bs.tryPutItem(entry, makeItem("item2"));
|
||||
bs.tryPutItem(entry, makeItem("item2"), Rotation::East);
|
||||
|
||||
for (int i = 0; i < 30; ++i)
|
||||
{
|
||||
@@ -1086,3 +1086,94 @@ TEST_CASE("BeltSystem: multiple items transit tunnel in order", "[belt]")
|
||||
REQUIRE(taken2.has_value());
|
||||
REQUIRE(taken2->type.id == "item2");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Output-edge rejection (REQ-MAT-ACCEPT-DIR)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("BeltSystem: belt refuses an item arriving through its output edge", "[belt]")
|
||||
{
|
||||
// Belt A flows East into belt B, but B flows West — so the hand-off would
|
||||
// enter B through its own (West) output edge and must be refused. Without the
|
||||
// guard the item would ping-pong between the two belts forever.
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tileA(0, 0);
|
||||
const QPoint tileB(1, 0);
|
||||
bs.placeBelt(tileA, Rotation::East);
|
||||
bs.placeBelt(tileB, Rotation::West);
|
||||
|
||||
REQUIRE(bs.tryPutItem(tileA, makeItem("iron_ore"), Rotation::East));
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
bs.tick();
|
||||
}
|
||||
|
||||
// B never accepts the item through its output edge...
|
||||
REQUIRE_FALSE(bs.tryTakeItem(Port{tileB, Rotation::West}).has_value());
|
||||
// ...and it stays blocked at A's output edge.
|
||||
REQUIRE(bs.tryTakeItem(eastPort(tileA)).has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: tryPutItem refuses a deposit onto a belt facing the source", "[belt]")
|
||||
{
|
||||
// A belt whose output edge faces back toward the depositing building must
|
||||
// refuse the item; feeding through a non-output edge still works.
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tile(0, 0);
|
||||
bs.placeBelt(tile, Rotation::West);
|
||||
|
||||
// Item travelling East enters through the West (output) edge -> refused.
|
||||
REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::East));
|
||||
// Item travelling West enters through the East (back) edge -> accepted.
|
||||
REQUIRE(bs.tryPutItem(tile, makeItem("iron_ore"), Rotation::West));
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: splitter refuses an item arriving through an output edge", "[belt]")
|
||||
{
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tileSpl(1, 0);
|
||||
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||
|
||||
// Entering through the North output edge (item travelling South) -> refused.
|
||||
REQUIRE_FALSE(bs.tryPutItem(tileSpl, makeItem("iron_ore"), Rotation::South));
|
||||
// Entering through the South output edge (item travelling North) -> refused.
|
||||
REQUIRE_FALSE(bs.tryPutItem(tileSpl, makeItem("iron_ore"), Rotation::North));
|
||||
// Entering through a non-output (West) edge (item travelling East) -> accepted.
|
||||
REQUIRE(bs.tryPutItem(tileSpl, makeItem("iron_ore"), Rotation::East));
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: tunnel entry refuses an item arriving through its mouth edge", "[belt]")
|
||||
{
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint entry(0, 0);
|
||||
bs.placeTunnelEntry(entry, Rotation::East, 10);
|
||||
|
||||
// Item travelling West enters through the East mouth (output) edge -> refused.
|
||||
REQUIRE_FALSE(bs.tryPutItem(entry, makeItem("iron_ore"), Rotation::West));
|
||||
// Item travelling East enters through the back (West) edge -> accepted.
|
||||
REQUIRE(bs.tryPutItem(entry, makeItem("iron_ore"), Rotation::East));
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: tunnel exit refuses an item pushed into its output edge", "[belt]")
|
||||
{
|
||||
// A splitter's East front sits next to a tunnel exit whose output faces West
|
||||
// (back toward the splitter). The front cannot hand off into the exit's
|
||||
// output edge, so the item stays on the splitter front.
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
const QPoint tileSpl(1, 0);
|
||||
const QPoint exitTile(2, 0);
|
||||
bs.placeSplitter(tileSpl, Rotation::East, Rotation::South);
|
||||
bs.placeTunnelExit(exitTile, Rotation::West);
|
||||
|
||||
// Feed through the West edge (item travelling East); first item routes to the
|
||||
// East front (nextOutputIsA starts true).
|
||||
REQUIRE(bs.tryPutItem(tileSpl, makeItem("iron_ore"), Rotation::East));
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
bs.tick();
|
||||
}
|
||||
|
||||
// The item is stuck on the splitter's East front; the exit never received it.
|
||||
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::East}).has_value());
|
||||
REQUIRE_FALSE(bs.peekItem(Port{exitTile, Rotation::West}).has_value());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user