From b30addab3d6ba60ee338f5493283cb4fc001e6f4 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Fri, 1 May 2026 21:19:07 +0200 Subject: [PATCH] implement 4 items on belt tile --- src/lib/sim/BeltSystem.cpp | 387 ++++++++++++++++-------------------- src/lib/sim/BeltSystem.h | 17 +- src/test/BeltSystemTest.cpp | 43 ++-- 3 files changed, 204 insertions(+), 243 deletions(-) diff --git a/src/lib/sim/BeltSystem.cpp b/src/lib/sim/BeltSystem.cpp index 78985ad..46dcbcb 100644 --- a/src/lib/sim/BeltSystem.cpp +++ b/src/lib/sim/BeltSystem.cpp @@ -66,7 +66,6 @@ void BeltSystem::placeSplitter(QPoint tile, Rotation outputA, Rotation outputB) st.outputA = outputA; st.outputB = outputB; st.nextOutputIsA = true; - st.backDir = Rotation::North; // irrelevant until back is set m_splitters[key(tile)] = st; } @@ -211,10 +210,10 @@ bool BeltSystem::tryPutItem(QPoint tile, Item item, Rotation fromDir) m_splitters.find(key(tile)); if (splIt != m_splitters.end()) { - if (!splIt->second.back) + if (splIt->second.back.size() < 2) { - splIt->second.back = BeltItemSlot{item, 0.0}; - splIt->second.backDir = fromDir; + splIt->second.back.push_back(BeltItemSlot{item, 0.0}); + splIt->second.backDir.push_back(fromDir); return true; } return false; @@ -224,15 +223,9 @@ bool BeltSystem::tryPutItem(QPoint tile, Item item, Rotation fromDir) m_tunnelEntries.find(key(tile)); if (teIt != m_tunnelEntries.end()) { - TunnelEntryTile& te = teIt->second; - if (!te.front) + if (teIt->second.itemSlots.size() < 4) { - te.front = BeltItemSlot{item, 0.0}; - return true; - } - if (!te.back) - { - te.back = BeltItemSlot{item, 0.0}; + teIt->second.itemSlots.push_back(BeltItemSlot{item, 0.0}); return true; } return false; @@ -251,11 +244,10 @@ std::optional BeltSystem::tryTakeItem(Port port) return std::nullopt; } BeltTile& bt = beltIt->second; - if (bt.front && bt.front->progress >= 1.0) + if (!bt.itemSlots.empty() && bt.itemSlots.front().progress >= 1.0) { - const Item taken = bt.front->item; - bt.front = bt.back; - bt.back = std::nullopt; + const Item taken = bt.itemSlots.front().item; + bt.itemSlots.erase(bt.itemSlots.begin()); return taken; } return std::nullopt; @@ -285,11 +277,11 @@ std::optional BeltSystem::tryTakeItem(Port port) if (txIt != m_tunnelExits.end()) { TunnelExitTile& tx = txIt->second; - if (tx.direction == port.direction && tx.front && tx.front->progress >= 1.0) + if (tx.direction == port.direction && !tx.itemSlots.empty() + && tx.itemSlots.front().progress >= 1.0) { - const Item taken = tx.front->item; - tx.front = tx.back; - tx.back = std::nullopt; + const Item taken = tx.itemSlots.front().item; + tx.itemSlots.erase(tx.itemSlots.begin()); return taken; } } @@ -308,9 +300,9 @@ std::optional BeltSystem::peekItem(Port port) const return std::nullopt; } const BeltTile& bt = beltIt->second; - if (bt.front && bt.front->progress >= 1.0) + if (!bt.itemSlots.empty() && bt.itemSlots.front().progress >= 1.0) { - return bt.front->item.type; + return bt.itemSlots.front().item.type; } return std::nullopt; } @@ -335,9 +327,10 @@ std::optional BeltSystem::peekItem(Port port) const if (txIt != m_tunnelExits.end()) { const TunnelExitTile& tx = txIt->second; - if (tx.direction == port.direction && tx.front && tx.front->progress >= 1.0) + if (tx.direction == port.direction && !tx.itemSlots.empty() + && tx.itemSlots.front().progress >= 1.0) { - return tx.front->item.type; + return tx.itemSlots.front().item.type; } } @@ -355,14 +348,14 @@ void BeltSystem::clearTiles(const std::vector& tiles) const std::map, BeltTile>::iterator bIt = m_belts.find(key(tile)); if (bIt != m_belts.end()) { - bIt->second.front = std::nullopt; - bIt->second.back = std::nullopt; + bIt->second.itemSlots.clear(); } const std::map, SplitterTile>::iterator sIt = m_splitters.find(key(tile)); if (sIt != m_splitters.end()) { - sIt->second.back = std::nullopt; + sIt->second.back.clear(); + sIt->second.backDir.clear(); sIt->second.frontA = std::nullopt; sIt->second.frontB = std::nullopt; } @@ -371,8 +364,7 @@ void BeltSystem::clearTiles(const std::vector& tiles) m_tunnelEntries.find(key(tile)); if (teIt != m_tunnelEntries.end()) { - teIt->second.front = std::nullopt; - teIt->second.back = std::nullopt; + teIt->second.itemSlots.clear(); for (TunnelLink& link : m_tunnelLinks) { if (link.entryTile == tile) @@ -386,8 +378,7 @@ void BeltSystem::clearTiles(const std::vector& tiles) m_tunnelExits.find(key(tile)); if (txIt != m_tunnelExits.end()) { - txIt->second.front = std::nullopt; - txIt->second.back = std::nullopt; + txIt->second.itemSlots.clear(); for (TunnelLink& link : m_tunnelLinks) { if (link.exitTile == tile) @@ -419,33 +410,26 @@ void BeltSystem::advanceProgress() { BeltTile& bt = it->second; - if (bt.front) + for (std::size_t i = 0; i < bt.itemSlots.size(); ++i) { - bt.front->progress += m_progressPerTick; - if (bt.front->progress > 1.0) + bt.itemSlots[i].progress += m_progressPerTick; + + // Absolute cap: slot i cannot exceed 1.0 - i * 0.25. + const double absoluteCap = 1.0 - i * 0.25; + if (bt.itemSlots[i].progress > absoluteCap) { - bt.front->progress = 1.0; + bt.itemSlots[i].progress = absoluteCap; } - } - if (bt.back) - { - bt.back->progress += m_progressPerTick; - - // Back must not overtake front. - if (bt.front && bt.back->progress >= bt.front->progress) + // Gap constraint: must stay 0.25 behind the slot ahead. + if (i > 0) { - bt.back->progress = bt.front->progress - m_progressPerTick; - if (bt.back->progress < 0.0) + const double gapCap = bt.itemSlots[i - 1].progress - 0.25; + if (bt.itemSlots[i].progress > gapCap) { - bt.back->progress = 0.0; + bt.itemSlots[i].progress = (gapCap < 0.0 ? 0.0 : gapCap); } } - - if (bt.back->progress > 0.5) - { - bt.back->progress = 0.5; - } } } @@ -454,12 +438,26 @@ void BeltSystem::advanceProgress() { SplitterTile& st = it->second; - if (st.back) + for (std::size_t i = 0; i < st.back.size(); ++i) { - st.back->progress += m_progressPerTick; - if (st.back->progress > 0.5) + st.back[i].progress += m_progressPerTick; + const double absoluteCap = 0.5 - i * 0.25; + if (st.back[i].progress > absoluteCap) { - st.back->progress = 0.5; + st.back[i].progress = absoluteCap; + } + + if (i > 0) + { + const double gapCap = st.back[i - 1].progress - 0.25; + if (gapCap < 0.0) + { + st.back[i].progress = 0.0; + } + else if (st.back[i].progress > gapCap) + { + st.back[i].progress = gapCap; + } } } @@ -490,29 +488,23 @@ void BeltSystem::advanceTunnelProgress() { TunnelEntryTile& te = it->second; - if (te.front) + for (std::size_t i = 0; i < te.itemSlots.size(); ++i) { - te.front->progress += m_progressPerTick; - if (te.front->progress > 1.0) - { - te.front->progress = 1.0; - } - } + te.itemSlots[i].progress += m_progressPerTick; - if (te.back) - { - te.back->progress += m_progressPerTick; - if (te.front && te.back->progress >= te.front->progress) + const double absoluteCap = 1.0 - i * 0.25; + if (te.itemSlots[i].progress > absoluteCap) { - te.back->progress = te.front->progress - m_progressPerTick; - if (te.back->progress < 0.0) - { - te.back->progress = 0.0; - } + te.itemSlots[i].progress = absoluteCap; } - if (te.back->progress > 0.5) + + if (i > 0) { - te.back->progress = 0.5; + const double gapCap = te.itemSlots[i - 1].progress - 0.25; + if (te.itemSlots[i].progress > gapCap) + { + te.itemSlots[i].progress = (gapCap < 0.0 ? 0.0 : gapCap); + } } } } @@ -522,29 +514,23 @@ void BeltSystem::advanceTunnelProgress() { TunnelExitTile& tx = it->second; - if (tx.front) + for (std::size_t i = 0; i < tx.itemSlots.size(); ++i) { - tx.front->progress += m_progressPerTick; - if (tx.front->progress > 1.0) - { - tx.front->progress = 1.0; - } - } + tx.itemSlots[i].progress += m_progressPerTick; - if (tx.back) - { - tx.back->progress += m_progressPerTick; - if (tx.front && tx.back->progress >= tx.front->progress) + const double absoluteCap = 1.0 - i * 0.25; + if (tx.itemSlots[i].progress > absoluteCap) { - tx.back->progress = tx.front->progress - m_progressPerTick; - if (tx.back->progress < 0.0) - { - tx.back->progress = 0.0; - } + tx.itemSlots[i].progress = absoluteCap; } - if (tx.back->progress > 0.5) + + if (i > 0) { - tx.back->progress = 0.5; + const double gapCap = tx.itemSlots[i - 1].progress - 0.25; + if (tx.itemSlots[i].progress > gapCap) + { + tx.itemSlots[i].progress = (gapCap < 0.0 ? 0.0 : gapCap); + } } } } @@ -561,7 +547,7 @@ void BeltSystem::advanceTunnelProgress() } if (i > 0) { - const double maxProgress = link.items[i - 1].progress - 0.5; + const double maxProgress = link.items[i - 1].progress - 0.25; if (ti.progress > maxProgress) { ti.progress = maxProgress; @@ -582,7 +568,7 @@ void BeltSystem::moveItemsToNextTile() it != m_belts.end(); ++it) { BeltTile& bt = it->second; - if (!bt.front || bt.front->progress < 1.0) + if (bt.itemSlots.empty() || bt.itemSlots.front().progress < 1.0) { continue; } @@ -595,39 +581,31 @@ void BeltSystem::moveItemsToNextTile() if (nextBelt != m_belts.end()) { - if (tryPlaceOnBelt(next, bt.front->item)) + if (tryPlaceOnBelt(next, bt.itemSlots.front().item)) { - bt.front = bt.back; - bt.back = std::nullopt; + bt.itemSlots.erase(bt.itemSlots.begin()); } // else: next belt is full — item stays blocked at progress 1.0. } else if (nextSplitter != m_splitters.end()) { - if (!nextSplitter->second.back) + if (nextSplitter->second.back.size() < 2) { - nextSplitter->second.back = BeltItemSlot{bt.front->item, 0.0}; - nextSplitter->second.backDir = bt.direction; - bt.front = bt.back; - bt.back = std::nullopt; + nextSplitter->second.back.push_back(BeltItemSlot{bt.itemSlots.front().item, 0.0}); + nextSplitter->second.backDir.push_back(bt.direction); + bt.itemSlots.erase(bt.itemSlots.begin()); } } else { const std::map, TunnelEntryTile>::iterator nextEntry = m_tunnelEntries.find(key(next)); - if (nextEntry != m_tunnelEntries.end() && !nextEntry->second.back) + if (nextEntry != m_tunnelEntries.end() + && nextEntry->second.itemSlots.size() < 4) { - if (!nextEntry->second.front) - { - nextEntry->second.front = BeltItemSlot{bt.front->item, 0.0}; - } - else - { - nextEntry->second.back = BeltItemSlot{bt.front->item, 0.0}; - } - bt.front = bt.back; - bt.back = std::nullopt; + nextEntry->second.itemSlots.push_back( + BeltItemSlot{bt.itemSlots.front().item, 0.0}); + bt.itemSlots.erase(bt.itemSlots.begin()); } } } @@ -663,17 +641,16 @@ void BeltSystem::moveItemsToNextTile() it != m_tunnelExits.end(); ++it) { TunnelExitTile& tx = it->second; - if (!tx.front || tx.front->progress < 1.0) + if (tx.itemSlots.empty() || tx.itemSlots.front().progress < 1.0) { continue; } const QPoint here = QPoint(it->first.first, it->first.second); const QPoint next = adjacentTile(here, tx.direction); - if (tryPushToTile(next, tx.front->item, tx.direction)) + if (tryPushToTile(next, tx.itemSlots.front().item, tx.direction)) { - tx.front = tx.back; - tx.back = std::nullopt; + tx.itemSlots.erase(tx.itemSlots.begin()); } } } @@ -688,23 +665,22 @@ void BeltSystem::moveTunnelItems() if (teIt != m_tunnelEntries.end()) { TunnelEntryTile& te = teIt->second; - if (te.front && te.front->progress >= 1.0) + if (!te.itemSlots.empty() && te.itemSlots.front().progress >= 1.0) { const bool canEnter = link.items.empty() - || link.items.back().progress >= 0.5; + || link.items.back().progress >= 0.25; if (canEnter) { TunnelTransitItem ti; - ti.item = te.front->item; + ti.item = te.itemSlots.front().item; ti.progress = 0.0; link.items.push_back(ti); - te.front = te.back; - te.back = std::nullopt; + te.itemSlots.erase(te.itemSlots.begin()); } } } - // Transit front → exit back + // Transit front → exit if (!link.items.empty() && link.items.front().progress >= link.length) { const std::map, TunnelExitTile>::iterator txIt = @@ -712,14 +688,9 @@ void BeltSystem::moveTunnelItems() if (txIt != m_tunnelExits.end()) { TunnelExitTile& tx = txIt->second; - if (!tx.back && !tx.front) + if (tx.itemSlots.size() < 4) { - tx.front = BeltItemSlot{link.items.front().item, 0.0}; - link.items.erase(link.items.begin()); - } - else if (!tx.back && tx.front) - { - tx.back = BeltItemSlot{link.items.front().item, 0.0}; + tx.itemSlots.push_back(BeltItemSlot{link.items.front().item, 0.0}); link.items.erase(link.items.begin()); } } @@ -733,24 +704,26 @@ void BeltSystem::routeSplitterItems() it != m_splitters.end(); ++it) { SplitterTile& st = it->second; - if (!st.back || st.back->progress < 0.5) + if (st.back.empty() || st.back.front().progress < 0.5) { continue; } - const Item& item = st.back->item; + const Item& item = st.back.front().item; const bool matchesA = st.filterA.empty() || std::find(st.filterA.begin(), st.filterA.end(), item.type) != st.filterA.end(); const bool matchesB = st.filterB.empty() || std::find(st.filterB.begin(), st.filterB.end(), item.type) != st.filterB.end(); + bool routed = false; + if (matchesA && !matchesB) { if (!st.frontA) { st.frontA = BeltItemSlot{item, 0.0}; - st.back = std::nullopt; + routed = true; } } else if (matchesB && !matchesA) @@ -758,7 +731,7 @@ void BeltSystem::routeSplitterItems() if (!st.frontB) { st.frontB = BeltItemSlot{item, 0.0}; - st.back = std::nullopt; + routed = true; } } else if (matchesA && matchesB) @@ -769,30 +742,36 @@ void BeltSystem::routeSplitterItems() if (preferA && !st.frontA) { st.frontA = BeltItemSlot{item, 0.0}; - st.back = std::nullopt; st.nextOutputIsA = false; + routed = true; } else if (!preferA && !st.frontB) { st.frontB = BeltItemSlot{item, 0.0}; - st.back = std::nullopt; st.nextOutputIsA = true; + routed = true; } else if (preferA && !st.frontB) { // Preferred (A) is full — fall back to B; nextOutputIsA stays. st.frontB = BeltItemSlot{item, 0.0}; - st.back = std::nullopt; + routed = true; } else if (!preferA && !st.frontA) { // Preferred (B) is full — fall back to A; nextOutputIsA stays. st.frontA = BeltItemSlot{item, 0.0}; - st.back = std::nullopt; + routed = true; } // else both fronts occupied — back stays. } // else (!matchesA && !matchesB): stall — back stays. + + if (routed) + { + st.back.erase(st.back.begin()); + st.backDir.erase(st.backDir.begin()); + } } } @@ -805,24 +784,12 @@ bool BeltSystem::tryPlaceOnBelt(QPoint tile, Item item) } BeltTile& bt = it->second; - - if (!bt.front) + if (bt.itemSlots.size() < 4) { - bt.front = BeltItemSlot{item, 0.0}; + bt.itemSlots.push_back(BeltItemSlot{item, 0.0}); return true; } - if (!bt.back) - { - bt.back = BeltItemSlot{item, 0.0}; - - // Ensure ordering invariant: front has higher progress. - if (bt.back->progress > bt.front->progress) - { - std::swap(bt.front, bt.back); - } - return true; - } - return false; // both slots occupied + return false; // all slots occupied } bool BeltSystem::tryPushToTile(QPoint dest, Item item, Rotation fromDir) @@ -836,10 +803,10 @@ bool BeltSystem::tryPushToTile(QPoint dest, Item item, Rotation fromDir) m_splitters.find(key(dest)); if (splIt != m_splitters.end()) { - if (!splIt->second.back) + if (splIt->second.back.size() < 2) { - splIt->second.back = BeltItemSlot{item, 0.0}; - splIt->second.backDir = fromDir; + splIt->second.back.push_back(BeltItemSlot{item, 0.0}); + splIt->second.backDir.push_back(fromDir); return true; } return false; @@ -849,15 +816,9 @@ bool BeltSystem::tryPushToTile(QPoint dest, Item item, Rotation fromDir) m_tunnelEntries.find(key(dest)); if (teIt != m_tunnelEntries.end()) { - TunnelEntryTile& te = teIt->second; - if (!te.front) + if (teIt->second.itemSlots.size() < 4) { - te.front = BeltItemSlot{item, 0.0}; - return true; - } - if (!te.back) - { - te.back = BeltItemSlot{item, 0.0}; + teIt->second.itemSlots.push_back(BeltItemSlot{item, 0.0}); return true; } return false; @@ -867,15 +828,9 @@ bool BeltSystem::tryPushToTile(QPoint dest, Item item, Rotation fromDir) m_tunnelExits.find(key(dest)); if (txIt != m_tunnelExits.end()) { - TunnelExitTile& tx = txIt->second; - if (!tx.front) + if (txIt->second.itemSlots.size() < 4) { - tx.front = BeltItemSlot{item, 0.0}; - return true; - } - if (!tx.back) - { - tx.back = BeltItemSlot{item, 0.0}; + txIt->second.itemSlots.push_back(BeltItemSlot{item, 0.0}); return true; } return false; @@ -901,19 +856,12 @@ void BeltSystem::forEachVisualItem(QRect viewportTiles, const BeltTile& bt = entry.second; - if (bt.front) + // Render least-progressed first (bottom) → most-progressed last (top). + for (int i = static_cast(bt.itemSlots.size()) - 1; i >= 0; --i) { VisualItem vi; - vi.type = bt.front->item.type; - vi.worldPos = slotWorldPos(tile, bt.direction, bt.front->progress); - visit(vi); - } - - if (bt.back) - { - VisualItem vi; - vi.type = bt.back->item.type; - vi.worldPos = slotWorldPos(tile, bt.direction, bt.back->progress); + vi.type = bt.itemSlots[i].item.type; + vi.worldPos = slotWorldPos(tile, bt.direction, bt.itemSlots[i].progress); visit(vi); } } @@ -928,28 +876,51 @@ void BeltSystem::forEachVisualItem(QRect viewportTiles, const SplitterTile& st = entry.second; - if (st.back) + // Unassigned items: least-progressed first (bottom), then higher-progressed (top). + for (int i = static_cast(st.back.size()) - 1; i >= 0; --i) { VisualItem vi; - vi.type = st.back->item.type; - vi.worldPos = slotWorldPos(tile, st.backDir, st.back->progress); + vi.type = st.back[i].item.type; + vi.worldPos = slotWorldPos(tile, st.backDir[i], st.back[i].progress); visit(vi); } - if (st.frontA) + // Output-slot items rendered on top of unassigned, in clockwise order from East. + // East=0, South=1, West=2, North=3 — lower rank rendered first (bottom). + auto clockwiseRank = [](Rotation r) -> int { - VisualItem vi; - vi.type = st.frontA->item.type; - vi.worldPos = slotWorldPos(tile, st.outputA, st.frontA->progress); - visit(vi); - } + switch (r) + { + case Rotation::East: return 0; + case Rotation::South: return 1; + case Rotation::West: return 2; + case Rotation::North: return 3; + } + return 0; + }; - if (st.frontB) + const bool aBeforeB = clockwiseRank(st.outputA) <= clockwiseRank(st.outputB); + + auto renderFront = [&](const std::optional& slot, Rotation dir) { - VisualItem vi; - vi.type = st.frontB->item.type; - vi.worldPos = slotWorldPos(tile, st.outputB, st.frontB->progress); - visit(vi); + if (slot) + { + VisualItem vi; + vi.type = slot->item.type; + vi.worldPos = slotWorldPos(tile, dir, slot->progress); + visit(vi); + } + }; + + if (aBeforeB) + { + renderFront(st.frontA, st.outputA); + renderFront(st.frontB, st.outputB); + } + else + { + renderFront(st.frontB, st.outputB); + renderFront(st.frontA, st.outputA); } } @@ -962,18 +933,11 @@ void BeltSystem::forEachVisualItem(QRect viewportTiles, } const TunnelEntryTile& te = entry.second; - if (te.front) + for (int i = static_cast(te.itemSlots.size()) - 1; i >= 0; --i) { VisualItem vi; - vi.type = te.front->item.type; - vi.worldPos = slotWorldPos(tile, te.direction, te.front->progress); - visit(vi); - } - if (te.back) - { - VisualItem vi; - vi.type = te.back->item.type; - vi.worldPos = slotWorldPos(tile, te.direction, te.back->progress); + vi.type = te.itemSlots[i].item.type; + vi.worldPos = slotWorldPos(tile, te.direction, te.itemSlots[i].progress); visit(vi); } } @@ -987,18 +951,11 @@ void BeltSystem::forEachVisualItem(QRect viewportTiles, } const TunnelExitTile& tx = entry.second; - if (tx.front) + for (int i = static_cast(tx.itemSlots.size()) - 1; i >= 0; --i) { VisualItem vi; - vi.type = tx.front->item.type; - vi.worldPos = slotWorldPos(tile, tx.direction, tx.front->progress); - visit(vi); - } - if (tx.back) - { - VisualItem vi; - vi.type = tx.back->item.type; - vi.worldPos = slotWorldPos(tile, tx.direction, tx.back->progress); + vi.type = tx.itemSlots[i].item.type; + vi.worldPos = slotWorldPos(tile, tx.direction, tx.itemSlots[i].progress); visit(vi); } } diff --git a/src/lib/sim/BeltSystem.h b/src/lib/sim/BeltSystem.h index 126907f..48feda3 100644 --- a/src/lib/sim/BeltSystem.h +++ b/src/lib/sim/BeltSystem.h @@ -123,8 +123,8 @@ private: struct BeltTile { Rotation direction; - std::optional front; // higher progress; closer to output - std::optional back; // lower progress; closer to input + // front (highest progress) at index 0; back (just entered) at end. Max 4. + std::vector itemSlots; }; struct SplitterTile @@ -134,8 +134,9 @@ private: std::vector filterA; // empty = accept all std::vector filterB; bool nextOutputIsA; // alternation state - std::optional back; // progress [0, 0.5]; entering from input belt - Rotation backDir; // direction of the feeding belt (for animation) + // Unassigned items: [0] = routing candidate (higher progress, caps at 0.5). Max 2. + std::vector back; + std::vector backDir; // feeding belt direction, parallel to back std::optional frontA; // progress [0, 1]; routed to outputA std::optional frontB; // progress [0, 1]; routed to outputB }; @@ -144,15 +145,15 @@ private: { Rotation direction; int maxDistance; - std::optional front; - std::optional back; + // front (highest progress) at index 0; back at end. Max 4. + std::vector itemSlots; }; struct TunnelExitTile { Rotation direction; - std::optional front; - std::optional back; + // front (highest progress) at index 0; back at end. Max 4. + std::vector itemSlots; }; struct TunnelTransitItem diff --git a/src/test/BeltSystemTest.cpp b/src/test/BeltSystemTest.cpp index 7c1360a..0b425b3 100644 --- a/src/test/BeltSystemTest.cpp +++ b/src/test/BeltSystemTest.cpp @@ -66,17 +66,19 @@ TEST_CASE("BeltSystem: tryPutItem fails after removeTile", "[belt]") // Capacity // --------------------------------------------------------------------------- -TEST_CASE("BeltSystem: two items fit in one tile", "[belt]") +TEST_CASE("BeltSystem: four items fit in one tile", "[belt]") { BeltSystem bs(kFastBeltSpeed); const QPoint tile(0, 0); bs.placeBelt(tile, Rotation::East); - REQUIRE(bs.tryPutItem(tile, makeItem("iron_ore"))); - REQUIRE(bs.tryPutItem(tile, makeItem("copper_ore"))); + REQUIRE(bs.tryPutItem(tile, makeItem("a"))); + REQUIRE(bs.tryPutItem(tile, makeItem("b"))); + REQUIRE(bs.tryPutItem(tile, makeItem("c"))); + REQUIRE(bs.tryPutItem(tile, makeItem("d"))); } -TEST_CASE("BeltSystem: third tryPutItem on full tile returns false", "[belt]") +TEST_CASE("BeltSystem: fifth tryPutItem on full tile returns false", "[belt]") { BeltSystem bs(kFastBeltSpeed); const QPoint tile(0, 0); @@ -84,8 +86,10 @@ TEST_CASE("BeltSystem: third tryPutItem on full tile returns false", "[belt]") bs.tryPutItem(tile, makeItem("a")); bs.tryPutItem(tile, makeItem("b")); + bs.tryPutItem(tile, makeItem("c")); + bs.tryPutItem(tile, makeItem("d")); - REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("c"))); + REQUIRE_FALSE(bs.tryPutItem(tile, makeItem("e"))); } // --------------------------------------------------------------------------- @@ -217,6 +221,8 @@ TEST_CASE("BeltSystem: item stays blocked when next tile is full", "[belt]") // Fill tileB to capacity. bs.tryPutItem(tileB, makeItem("b1")); bs.tryPutItem(tileB, makeItem("b2")); + bs.tryPutItem(tileB, makeItem("b3")); + bs.tryPutItem(tileB, makeItem("b4")); // Place item in tileA — should be blocked. bs.tryPutItem(tileA, makeItem("a1")); @@ -226,11 +232,11 @@ TEST_CASE("BeltSystem: item stays blocked when next tile is full", "[belt]") REQUIRE(bs.tryTakeItem(eastPort(tileA)).has_value()); } -TEST_CASE("BeltSystem: belt back slot is capped at progress 0.5", "[belt]") +TEST_CASE("BeltSystem: belt second slot is capped at progress 0.75", "[belt]") { - // Use progress/tick = 0.4 so the cap is observable: without it, back would - // advance to 0.8 while front is stuck at 1.0, and then need only 1 more tick - // after being promoted. With the cap it stays at 0.5 and needs 2 more ticks. + // Use progress/tick = 0.4 so the cap is observable: without it, slot[1] would + // advance to 0.8 while slot[0] is stuck at 1.0. With the 0.75 cap it stays + // at 0.75 and needs exactly 1 more tick after promotion. const double medBeltSpeed = 0.4 * static_cast(kTickRateHz); BeltSystem bs(medBeltSpeed); @@ -239,21 +245,18 @@ TEST_CASE("BeltSystem: belt back slot is capped at progress 0.5", "[belt]") // Advance front item to the output edge; it stays there (no next tile). bs.tryPutItem(tile, makeItem("front_item")); - bs.tick(); // front: 0.4 - bs.tick(); // front: 0.8 - bs.tick(); // front: 1.0 (capped, stuck) + bs.tick(); // slot[0]: 0.4 + bs.tick(); // slot[0]: 0.8 + bs.tick(); // slot[0]: 1.0 (capped, stuck) - // Place back item; front is at 1.0 and not blocking (back < 1.0). + // Place second item; slot[0] is at 1.0. bs.tryPutItem(tile, makeItem("back_item")); - bs.tick(); // back: 0.4 - bs.tick(); // back would reach 0.8 — must be capped at 0.5 + bs.tick(); // slot[1]: 0.4 + bs.tick(); // slot[1] would reach 0.8 — capped at 0.75 - // Remove front; back (now promoted to front) must be at 0.5, not 0.8. + // Remove front; slot[1] (now promoted to slot[0]) must be at 0.75. REQUIRE(bs.tryTakeItem(eastPort(tile)).has_value()); - // At 0.4/tick, 0.5 → 0.9 after one tick — not at 1.0 yet. - bs.tick(); - REQUIRE_FALSE(bs.tryTakeItem(eastPort(tile)).has_value()); - // 0.9 → 1.0 after a second tick — now available. + // At 0.4/tick, 0.75 → 1.0 (capped) after one tick — available. bs.tick(); REQUIRE(bs.tryTakeItem(eastPort(tile)).has_value()); }