fix bug where splitters reduce belt throughput, even if one side is blocked
This commit is contained in:
@@ -756,13 +756,13 @@ void BeltSystem::routeSplitterItems()
|
|||||||
else if (preferA && !st.frontB)
|
else if (preferA && !st.frontB)
|
||||||
{
|
{
|
||||||
// Preferred (A) is full — fall back to B; nextOutputIsA stays.
|
// Preferred (A) is full — fall back to B; nextOutputIsA stays.
|
||||||
st.frontB = BeltItemSlot{item, 0.0};
|
st.frontB = BeltItemSlot{item, 0.75};
|
||||||
routed = true;
|
routed = true;
|
||||||
}
|
}
|
||||||
else if (!preferA && !st.frontA)
|
else if (!preferA && !st.frontA)
|
||||||
{
|
{
|
||||||
// Preferred (B) is full — fall back to A; nextOutputIsA stays.
|
// Preferred (B) is full — fall back to A; nextOutputIsA stays.
|
||||||
st.frontA = BeltItemSlot{item, 0.0};
|
st.frontA = BeltItemSlot{item, 0.75};
|
||||||
routed = true;
|
routed = true;
|
||||||
}
|
}
|
||||||
// else both fronts occupied — back stays.
|
// else both fronts occupied — back stays.
|
||||||
@@ -963,3 +963,4 @@ void BeltSystem::forEachVisualItem(QRect viewportTiles,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
@@ -553,6 +555,64 @@ TEST_CASE("BeltSystem: splitter falls back to other output when preferred is blo
|
|||||||
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
|
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("BeltSystem: splitter fallback enters the open output at progress 0.75", "[belt]")
|
||||||
|
{
|
||||||
|
// When the preferred output is blocked, the diverted item is dropped onto the
|
||||||
|
// open output near its edge (progress 0.75) instead of at progress 0.0. This
|
||||||
|
// closes the large gap that would otherwise appear between items leaving the
|
||||||
|
// open side of a half-blocked splitter.
|
||||||
|
//
|
||||||
|
// Progress/tick = 0.25 so the 0.0-vs-0.75 entry position is observable: a
|
||||||
|
// normally-routed item starts at 0.0, a fallback item starts at 0.75.
|
||||||
|
const double quarterSpeed = 0.25 * static_cast<double>(kTickRateHz);
|
||||||
|
BeltSystem bs(quarterSpeed);
|
||||||
|
|
||||||
|
const QPoint tileSpl(1, 0);
|
||||||
|
const QPoint tileB(1, 1); // South output belt; North output has no belt (blocked).
|
||||||
|
|
||||||
|
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
|
||||||
|
bs.placeBelt(tileB, Rotation::South);
|
||||||
|
|
||||||
|
// Reads a named item's progress along the South output via the rendering contract.
|
||||||
|
// slotWorldPos maps a South-bound slot on tileSpl (y = 0) to worldPos.y == progress.
|
||||||
|
// Matching by id avoids the blocked North item, which also renders at worldPos.y 0.
|
||||||
|
auto southProgressOf = [&bs](const std::string& id) -> std::optional<double>
|
||||||
|
{
|
||||||
|
std::optional<double> progress;
|
||||||
|
bs.forEachVisualItem(QRect(-5, -5, 20, 20), [&](VisualItem vi)
|
||||||
|
{
|
||||||
|
if (vi.type.id == id)
|
||||||
|
{
|
||||||
|
progress = vi.worldPos.y();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return progress;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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.tick(); // back: 0.25
|
||||||
|
bs.tick(); // back: 0.5 -> frontA at 0.0 (preferred A), nextOutputIsA = false
|
||||||
|
bs.tick(); bs.tick(); bs.tick(); bs.tick(); // frontA: 0.25 -> 0.5 -> 0.75 -> 1.0 (stuck)
|
||||||
|
|
||||||
|
// Item routed to B as the *preferred* output enters at progress 0.0.
|
||||||
|
bs.tryPutItem(tileSpl, makeItem("toB_pref"));
|
||||||
|
bs.tick(); // back: 0.25
|
||||||
|
bs.tick(); // back: 0.5 -> frontB at 0.0 (preferred B), nextOutputIsA = true
|
||||||
|
REQUIRE(southProgressOf("toB_pref") == Approx(0.0));
|
||||||
|
|
||||||
|
// Let it traverse and hand off to the downstream belt, freeing frontB.
|
||||||
|
bs.tick(); bs.tick(); bs.tick(); bs.tick(); // frontB: 0.25 -> 0.5 -> 0.75 -> 1.0 -> tileB
|
||||||
|
|
||||||
|
// 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.tick(); // back: 0.25
|
||||||
|
bs.tick(); // back: 0.5 -> fallback routes to frontB at 0.75
|
||||||
|
REQUIRE(southProgressOf("toB_fallback") == Approx(0.75));
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Splitter — direct building input (no output belts)
|
// Splitter — direct building input (no output belts)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user