#include "FactoryChecksum.h" #include #include #include "BuildingBuffers.h" #include "FactoryState.h" #include "Item.h" #include "ItemType.h" #include "StateChecksum.h" namespace { void appendItems(Hasher& hasher, const std::vector& items) { hasher.append(items.size()); for (const Item& item : items) { hasher.append(item.type.id); } } // std::map iterates in sorted-id order (ItemType::operator<), so both // buffer sides hash the same way in every run. void appendItemCounts(Hasher& hasher, const std::map& counts) { hasher.append(counts.size()); for (const std::pair& entry : counts) { hasher.append(entry.first.id); hasher.append(entry.second); } } void appendInputBuffer(Hasher& hasher, const InputBuffer& buffer) { appendItemCounts(hasher, buffer.counts); appendItemCounts(hasher, buffer.caps); } } // namespace void appendChecksum(const FactoryState& state, Hasher& hasher) { // state.buildings keeps a stable, deterministic order (append on build, swap-free // erase aside -- both runs perform identical operations, so order matches). hasher.append(state.buildings.size()); for (const Building& b : state.buildings) { hasher.append(b.id); hasher.append(b.anchor); hasher.append(b.footprint.width()); hasher.append(b.footprint.height()); hasher.append(b.rotation); hasher.append(b.type); hasher.append(b.recipeId); appendInputBuffer(hasher, b.inputBuffer); appendItems(hasher, b.outputBuffer.items); appendItemCounts(hasher, b.outputBuffer.caps); hasher.append(b.emergingItems.size()); for (const std::vector& lane : b.emergingItems) { hasher.append(lane.size()); for (const BeltItemSlot& slot : lane) { hasher.append(slot.item.type.id); hasher.append(slot.progress); } } hasher.append(b.incomingItems.size()); for (const std::vector& lane : b.incomingItems) { hasher.append(lane.size()); for (const BeltItemSlot& slot : lane) { hasher.append(slot.item.type.id); hasher.append(slot.progress); } } hasher.append(b.production.has_value()); if (b.production.has_value()) { hasher.append(b.production->recipeId); hasher.append(b.production->completesAt); appendItems(hasher, b.production->chosenOutputs); } hasher.append(b.shipLayout.has_value()); hasher.append(b.queuedForDeconstruction); } hasher.append(state.constructionQueue.size()); for (const ConstructionSite& s : state.constructionQueue) { hasher.append(s.id); hasher.append(s.anchor); hasher.append(s.footprint.width()); hasher.append(s.footprint.height()); hasher.append(s.rotation); hasher.append(s.type); hasher.append(s.recipeId); hasher.append(s.completesAt); hasher.append(s.shipLayout.has_value()); hasher.append(s.splitterFilterA.size()); for (const ItemType& type : s.splitterFilterA) { hasher.append(type.id); } hasher.append(s.splitterFilterB.size()); for (const ItemType& type : s.splitterFilterB) { hasher.append(type.id); } } hasher.append(state.deconstructionQueue.size()); for (const DeconstructionEntry& e : state.deconstructionQueue) { hasher.append(e.id); hasher.append(e.completesAt); hasher.append(e.splitterFilterA.size()); for (const ItemType& type : e.splitterFilterA) { hasher.append(type.id); } hasher.append(e.splitterFilterB.size()); for (const ItemType& type : e.splitterFilterB) { hasher.append(type.id); } } state.grid.appendChecksum(hasher); }