fix bug where items get already consumed right after being placed on a belt

This commit is contained in:
2026-04-20 22:14:55 +02:00
parent a924877e20
commit 35dd81748e
2 changed files with 32 additions and 23 deletions

View File

@@ -119,19 +119,13 @@ std::optional<Item> BeltSystem::tryTakeItem(Port port)
} }
BeltTile& bt = it->second; BeltTile& bt = it->second;
if (bt.front) if (bt.front && bt.front->progress >= 1.0)
{ {
const Item taken = bt.front->item; const Item taken = bt.front->item;
bt.front = bt.back; bt.front = bt.back;
bt.back = std::nullopt; bt.back = std::nullopt;
return taken; return taken;
} }
if (bt.back)
{
const Item taken = bt.back->item;
bt.back = std::nullopt;
return taken;
}
return std::nullopt; return std::nullopt;
} }
@@ -149,14 +143,10 @@ std::optional<ItemType> BeltSystem::peekItem(Port port) const
} }
const BeltTile& bt = it->second; const BeltTile& bt = it->second;
if (bt.front) if (bt.front && bt.front->progress >= 1.0)
{ {
return bt.front->item.type; return bt.front->item.type;
} }
if (bt.back)
{
return bt.back->item.type;
}
return std::nullopt; return std::nullopt;
} }

View File

@@ -101,12 +101,13 @@ TEST_CASE("BeltSystem: third tryPutItem on full tile returns false", "[belt]")
// tryTakeItem // tryTakeItem
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
TEST_CASE("BeltSystem: tryTakeItem returns placed item", "[belt]") TEST_CASE("BeltSystem: tryTakeItem returns placed item after reaching output edge", "[belt]")
{ {
BeltSystem bs(kFastBeltSpeed); BeltSystem bs(kFastBeltSpeed);
const QPoint tile(0, 0); const QPoint tile(0, 0);
bs.placeBelt(tile, Rotation::East); bs.placeBelt(tile, Rotation::East);
bs.tryPutItem(eastPort(tile), makeItem("iron_ore")); bs.tryPutItem(eastPort(tile), makeItem("iron_ore"));
bs.tick(); // advance to output edge
const std::optional<Item> taken = bs.tryTakeItem(eastPort(tile)); const std::optional<Item> taken = bs.tryTakeItem(eastPort(tile));
@@ -114,7 +115,23 @@ TEST_CASE("BeltSystem: tryTakeItem returns placed item", "[belt]")
REQUIRE(taken->type.id == "iron_ore"); REQUIRE(taken->type.id == "iron_ore");
} }
TEST_CASE("BeltSystem: tryTakeItem with two items returns both in sequence", "[belt]") TEST_CASE("BeltSystem: tryTakeItem requires item to reach output edge before yielding", "[belt]")
{
BeltSystem bs(kFastBeltSpeed);
const QPoint tile(0, 0);
bs.placeBelt(tile, Rotation::East);
bs.tryPutItem(eastPort(tile), makeItem("iron_ore"));
// Item placed but not yet at output edge — must not be available.
REQUIRE_FALSE(bs.tryTakeItem(eastPort(tile)).has_value());
REQUIRE_FALSE(bs.peekItem(eastPort(tile)).has_value());
// After one tick the item has reached progress 1.0 and is available.
bs.tick();
REQUIRE(bs.tryTakeItem(eastPort(tile)).has_value());
}
TEST_CASE("BeltSystem: tryTakeItem with two items returns both after each reaches output edge", "[belt]")
{ {
BeltSystem bs(kFastBeltSpeed); BeltSystem bs(kFastBeltSpeed);
const QPoint tile(0, 0); const QPoint tile(0, 0);
@@ -122,15 +139,16 @@ TEST_CASE("BeltSystem: tryTakeItem with two items returns both in sequence", "[b
bs.tryPutItem(eastPort(tile), makeItem("first")); bs.tryPutItem(eastPort(tile), makeItem("first"));
bs.tryPutItem(eastPort(tile), makeItem("second")); bs.tryPutItem(eastPort(tile), makeItem("second"));
// First take returns front item (first placed, higher progress). // Front item reaches output edge after one tick.
bs.tick();
const std::optional<Item> taken1 = bs.tryTakeItem(eastPort(tile)); const std::optional<Item> taken1 = bs.tryTakeItem(eastPort(tile));
REQUIRE(taken1.has_value()); REQUIRE(taken1.has_value());
// Second take returns the remaining item. // Back item (now promoted to front) needs another tick to reach output edge.
bs.tick();
const std::optional<Item> taken2 = bs.tryTakeItem(eastPort(tile)); const std::optional<Item> taken2 = bs.tryTakeItem(eastPort(tile));
REQUIRE(taken2.has_value()); REQUIRE(taken2.has_value());
// Tile is now empty.
REQUIRE_FALSE(bs.tryTakeItem(eastPort(tile)).has_value()); REQUIRE_FALSE(bs.tryTakeItem(eastPort(tile)).has_value());
} }
@@ -156,7 +174,7 @@ TEST_CASE("BeltSystem: tryTakeItem returns nullopt on direction mismatch", "[bel
// tick() — item advancement // tick() — item advancement
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
TEST_CASE("BeltSystem: one tick moves item from tile A to tile B in a 2-tile chain", "[belt]") TEST_CASE("BeltSystem: item transfers from tile A to tile B and becomes available after two ticks", "[belt]")
{ {
BeltSystem bs(kFastBeltSpeed); BeltSystem bs(kFastBeltSpeed);
const QPoint tileA(0, 0); const QPoint tileA(0, 0);
@@ -165,9 +183,9 @@ TEST_CASE("BeltSystem: one tick moves item from tile A to tile B in a 2-tile cha
bs.placeBelt(tileB, Rotation::East); bs.placeBelt(tileB, Rotation::East);
bs.tryPutItem(eastPort(tileA), makeItem("iron_ore")); bs.tryPutItem(eastPort(tileA), makeItem("iron_ore"));
bs.tick(); bs.tick(); // item reaches output edge of A, moves to B at progress 0
bs.tick(); // item reaches output edge of B
// Item should have moved to tileB.
REQUIRE_FALSE(bs.tryTakeItem(eastPort(tileA)).has_value()); REQUIRE_FALSE(bs.tryTakeItem(eastPort(tileA)).has_value());
const std::optional<Item> inB = bs.tryTakeItem(eastPort(tileB)); const std::optional<Item> inB = bs.tryTakeItem(eastPort(tileB));
REQUIRE(inB.has_value()); REQUIRE(inB.has_value());
@@ -187,7 +205,7 @@ TEST_CASE("BeltSystem: item stays at progress 1.0 when next tile is absent", "[b
REQUIRE(bs.tryTakeItem(eastPort(tileA)).has_value()); REQUIRE(bs.tryTakeItem(eastPort(tileA)).has_value());
} }
TEST_CASE("BeltSystem: item traverses 3-tile chain in 2 ticks", "[belt]") TEST_CASE("BeltSystem: item traverses 3-tile chain in 3 ticks (one per tile)", "[belt]")
{ {
BeltSystem bs(kFastBeltSpeed); BeltSystem bs(kFastBeltSpeed);
const QPoint tileA(0, 0); const QPoint tileA(0, 0);
@@ -198,8 +216,9 @@ TEST_CASE("BeltSystem: item traverses 3-tile chain in 2 ticks", "[belt]")
bs.placeBelt(tileC, Rotation::East); bs.placeBelt(tileC, Rotation::East);
bs.tryPutItem(eastPort(tileA), makeItem("iron_ore")); bs.tryPutItem(eastPort(tileA), makeItem("iron_ore"));
bs.tick(); // A -> B bs.tick(); // A output edge → moves to B at progress 0
bs.tick(); // B -> C bs.tick(); // B output edge → moves to C at progress 0
bs.tick(); // C output edge → available for pickup
REQUIRE_FALSE(bs.tryTakeItem(eastPort(tileA)).has_value()); REQUIRE_FALSE(bs.tryTakeItem(eastPort(tileA)).has_value());
REQUIRE_FALSE(bs.tryTakeItem(eastPort(tileB)).has_value()); REQUIRE_FALSE(bs.tryTakeItem(eastPort(tileB)).has_value());