fix(engine): implement input batch reservation to prevent finished goods disappearance on input shortage
This commit is contained in:
parent
da5435afbc
commit
007fee47d4
2 changed files with 60 additions and 48 deletions
|
|
@ -269,30 +269,33 @@ def process_offline_and_ticks(state: dict) -> dict:
|
||||||
p_state['stock'] = p_state.get('stock', 0.0) + produced
|
p_state['stock'] = p_state.get('stock', 0.0) + produced
|
||||||
p_state['totalProduced'] = p_state.get('totalProduced', 0.0) + produced
|
p_state['totalProduced'] = p_state.get('totalProduced', 0.0) + produced
|
||||||
else:
|
else:
|
||||||
# Manufacturing requiring inputs
|
# Manufacturing logic with input batch reservation
|
||||||
max_cycles = int(min(
|
needed = [(inp['id'], inp['amount'] * lines) for inp in inputs]
|
||||||
state['products'].get(inp['id'], {}).get('stock', 0.0) // (inp['amount'] * lines)
|
can_start = all(state['products'].get(item_id, {}).get('stock', 0.0) >= req for item_id, req in needed)
|
||||||
for inp in inputs
|
|
||||||
)) if inputs else 0
|
|
||||||
|
|
||||||
if max_cycles <= 0:
|
if p_state.get('cycleProgress', 0.0) <= 0.0:
|
||||||
p_state['isBottlenecked'] = True
|
if not can_start:
|
||||||
|
p_state['isBottlenecked'] = True
|
||||||
|
else:
|
||||||
|
p_state['isBottlenecked'] = False
|
||||||
|
for item_id, req in needed:
|
||||||
|
state['products'][item_id]['stock'] = max(0.0, state['products'][item_id].get('stock', 0.0) - req)
|
||||||
|
p_state['cycleProgress'] = p_state.get('cycleProgress', 0.0) + (cycle_rate * dt)
|
||||||
else:
|
else:
|
||||||
p_state['isBottlenecked'] = False
|
p_state['isBottlenecked'] = False
|
||||||
p_state['cycleProgress'] = p_state.get('cycleProgress', 0.0) + (cycle_rate * dt)
|
p_state['cycleProgress'] = p_state.get('cycleProgress', 0.0) + (cycle_rate * dt)
|
||||||
if p_state['cycleProgress'] >= 1.0:
|
if p_state['cycleProgress'] >= 1.0:
|
||||||
cycles_to_run = min(int(p_state['cycleProgress']), max_cycles)
|
p_state['cycleProgress'] = 0.0
|
||||||
if cycles_to_run > 0:
|
produced = p_cfg['outputAmount'] * lines
|
||||||
p_state['cycleProgress'] -= cycles_to_run
|
p_state['stock'] = p_state.get('stock', 0.0) + produced
|
||||||
for inp in inputs:
|
p_state['totalProduced'] = p_state.get('totalProduced', 0.0) + produced
|
||||||
if inp['id'] in state['products']:
|
|
||||||
state['products'][inp['id']]['stock'] = max(
|
# Check next batch
|
||||||
0.0,
|
if can_start:
|
||||||
state['products'][inp['id']]['stock'] - (inp['amount'] * lines * cycles_to_run)
|
for item_id, req in needed:
|
||||||
)
|
state['products'][item_id]['stock'] = max(0.0, state['products'][item_id].get('stock', 0.0) - req)
|
||||||
produced = cycles_to_run * p_cfg['outputAmount'] * lines
|
else:
|
||||||
p_state['stock'] = p_state.get('stock', 0.0) + produced
|
p_state['isBottlenecked'] = True
|
||||||
p_state['totalProduced'] = p_state.get('totalProduced', 0.0) + produced
|
|
||||||
|
|
||||||
# Passive Logistics Sales
|
# Passive Logistics Sales
|
||||||
logistics = p_state.get('logistics', 0)
|
logistics = p_state.get('logistics', 0)
|
||||||
|
|
|
||||||
|
|
@ -67,45 +67,54 @@ export class SimulationEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Check if we have required input stock
|
// Manufacturing logic with input batch reservation:
|
||||||
const maxCyclesPossible = Math.min(
|
// Check if factory is currently executing an in-progress cycle or needs to start one
|
||||||
...p.inputs.map(inp => {
|
const neededInputs = p.inputs.map(inp => ({
|
||||||
const inputStock = state.products[inp.id]?.stock || 0;
|
id: inp.id,
|
||||||
const reqPerCycle = inp.amount * lines;
|
amount: inp.amount * lines
|
||||||
return reqPerCycle > 0 ? Math.floor(inputStock / reqPerCycle) : 0;
|
}));
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (maxCyclesPossible <= 0) {
|
const canStartBatch = neededInputs.every(inp => (state.products[inp.id]?.stock || 0) >= inp.amount);
|
||||||
pState.isBottlenecked = true;
|
|
||||||
// Retain existing cycle progress until inputs arrive
|
if (pState.cycleProgress <= 0.0) {
|
||||||
|
// Not currently running a cycle
|
||||||
|
if (!canStartBatch) {
|
||||||
|
pState.isBottlenecked = true;
|
||||||
|
} else {
|
||||||
|
// Start batch by reserving required inputs
|
||||||
|
pState.isBottlenecked = false;
|
||||||
|
for (const inp of neededInputs) {
|
||||||
|
if (state.products[inp.id]) {
|
||||||
|
state.products[inp.id].stock = Math.max(0, state.products[inp.id].stock - inp.amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pState.cycleProgress += cycleRate * dt;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Cycle already in progress (inputs already secured)
|
||||||
pState.isBottlenecked = false;
|
pState.isBottlenecked = false;
|
||||||
pState.cycleProgress += cycleRate * dt;
|
pState.cycleProgress += cycleRate * dt;
|
||||||
|
|
||||||
if (pState.cycleProgress >= 1.0) {
|
if (pState.cycleProgress >= 1.0) {
|
||||||
const cyclesToRun = Math.min(Math.floor(pState.cycleProgress), maxCyclesPossible);
|
// Finished cycle, deliver produced goods
|
||||||
if (cyclesToRun > 0) {
|
pState.cycleProgress = 0.0;
|
||||||
pState.cycleProgress -= cyclesToRun;
|
const produced = p.outputAmount * lines;
|
||||||
|
pState.stock += produced;
|
||||||
|
pState.totalProduced = (pState.totalProduced || 0) + produced;
|
||||||
|
state.stats.totalProduced = (state.stats.totalProduced || 0) + produced;
|
||||||
|
if (this._sectorAccumulators[p.sector]) {
|
||||||
|
this._sectorAccumulators[p.sector].units += produced;
|
||||||
|
}
|
||||||
|
|
||||||
// Deduct inputs
|
// Immediately check if next batch can start
|
||||||
for (const inp of p.inputs) {
|
if (canStartBatch) {
|
||||||
|
for (const inp of neededInputs) {
|
||||||
if (state.products[inp.id]) {
|
if (state.products[inp.id]) {
|
||||||
state.products[inp.id].stock = Math.max(
|
state.products[inp.id].stock = Math.max(0, state.products[inp.id].stock - inp.amount);
|
||||||
0,
|
|
||||||
state.products[inp.id].stock - (inp.amount * lines * cyclesToRun)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Credit finished goods
|
pState.isBottlenecked = true;
|
||||||
const produced = cyclesToRun * p.outputAmount * lines;
|
|
||||||
pState.stock += produced;
|
|
||||||
pState.totalProduced = (pState.totalProduced || 0) + produced;
|
|
||||||
state.stats.totalProduced = (state.stats.totalProduced || 0) + produced;
|
|
||||||
if (this._sectorAccumulators[p.sector]) {
|
|
||||||
this._sectorAccumulators[p.sector].units += produced;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue