1. Profit per Item
For every item, we compute net profit per batch and the time to make one, tracing its full ingredient chain back to whatever facilities produce the raw materials.
\[ \text{profit}_{\text{batch}} = (\text{yield} \times \text{sell_price}) - \text{raw_cost} \]
Dividing by batch time gives a per-second rate, but that rate alone doesn't say how much of each facility one batch actually needs. For that we track utilization: batches per second at this item's own facility, plus the same for every facility touched anywhere upstream in its ingredient tree (e.g. soy_sauce_tofu pulls from both Bouncy Brew Keg and Carousel Mill, which both draw soybean from Farmland).
2. Environment Coverage
Some crops need a growing environment (Cool/Warm/Freeze/Scorching/Adequate), supplied by Heat Furnace/Cooling Unit/Sunlamp buildings. Each building covers a fixed 2D area around it, so how many plots of each facility type it can cover is an exact geometric packing problem, solved once up front per owned building using each candidate item's standalone profit-per-plot to prioritize which types get coverage. This produces a hard cap on how much of each covered facility type is usable, feeding into the allocation below as an extra capacity limit.
3. Joint Facility Allocation
Every owned facility can be shared by several candidate items at once, so picking each item's rate independently would double-count shared capacity (two recipes both claiming the same Farmland soybean plots as if the other didn't exist). A facility can also be owned across multiple levels (e.g. some plots upgraded further than others); a higher-level plot can run a lower-level recipe too, so capacity is tracked per level threshold rather than as one flat total. Instead of picking rates independently, we solve one linear program across every candidate item and every owned facility simultaneously:
\[ \max \sum_i \text{profit}_{\text{batch},i} \cdot x_i \quad \text{s.t.} \quad \sum_i \text{utilization}_{i,f} \cdot x_i \leq \text{capacity}_f \ \ \forall f \]
Where \(x_i\) is item \(i\)'s batches/sec and \(f\) ranges over every owned facility and level threshold, plus each environment coverage cap from step 2. This is solved exactly (not greedily) with an off-the-shelf simplex solver, so the result is the provably-best combination of rates achievable given shared upstream supply and coverage.
4. Rounding to Whole Units: Growers
The linear program's solution is continuous, e.g. "62% of Farmland's soybean plots." A grower facility (Farmland, Woodland, Mineral Pile, and similar) can't actually run a fractional plot; each unit commits to one crop for its whole cycle. So every grower facility's fractional shares get converted to whole counts using the largest-remainder method (the same apportionment technique used to allocate parliament seats), landing as close to the true fractions as an integer solution allows.
Each item's final rate is then capped by what its grower facilities can actually supply once rounded, taking the minimum against the continuous rate rather than recomputing from scratch, since the continuous solve already accounted for fair sharing at every processor a chain passes through.
5. Whole-Unit Dedication: Processors
A processor facility (Carousel Mill, Claw Game Cooker, and similar) isn't tied to one crop for a whole cycle the way a grower is, but it still can't be time-shared between recipes in practice. A player sets one up to run one recipe continuously, not a "38% of the time" split. If a chain needs two different items made at the same facility type (an intermediate step plus the final item), each one needs its own dedicated unit, not one unit alternating. When the linear program's solution would ask a processor facility to serve more distinct recipes than it has units, we keep only the most profitable candidates (one dedicated unit each) and drop the rest, then re-solve so their freed-up upstream supply finds a genuine next-best use rather than being silently discarded.
6. Refining the Choice
Environment coverage (step 2) is priced from each item's own standalone economics, before the full joint solve runs. Occasionally that naive pricing favors a chain that turns out worse once every other constraint is accounted for, either because it never ends up producing anything once the rest of the plan settles, or because a competing chain would have used the same scarce coverage more effectively overall. After solving once, the algorithm checks which chains actually end up producing something, then tries excluding a losing candidate and re-solving; the exclusion is kept only if the resulting plan's real total is genuinely better.
7. Time to Reach a Goal
Once the plan's rates are settled, each item contributes nothing until its own lead time has passed (time for its first batch to clear the chain), then its steady rate after that:
\[ \text{amount}(t) = \sum_i \text{rate}_i \cdot \max(0,\ t - \text{lead}_i) \]
This is monotonically non-decreasing in \(t\), so the smallest \(t\) reaching your target amount is found with a binary search rather than solving anything analytically.