package calc func ShipProductionCost(shipEmptyMass float64) float64 { return shipEmptyMass * 10. } func PlanetProduceShipMass(L, Mat, Res float64) float64 { result := L / 10 if result <= Mat { return result } return (L + Mat/Res) / (10 + 1/Res) } // ShipBuildCost returns the total per-turn cost (production units) to // build one ship of empty mass shipMass on a planet that currently // holds material stockpile and has natural resources. The cost is the // ship's production cost ([ShipProductionCost]) plus the cost of // farming any missing material from the planet (the missing-material // volume divided by the planet's resources rating). // // resources is expected to be positive in normal play; the helper // guards against a non-positive value by collapsing the material- // farming term to zero, which keeps callers numerically stable on // pathological synthetic data. [ProduceShipsInTurn] composes this cost // into the per-turn build loop that the engine's controller.ProduceShip // delegates to, so the engine, the calculator, and the // legacy-report-to-json dev tool (which derives prod_used from percent) // all share one formula. func ShipBuildCost(shipMass, material, resources float64) float64 { matNeed := shipMass - material if matNeed < 0 { matNeed = 0 } matFarm := 0. if resources > 0 { matFarm = matNeed / resources } return ShipProductionCost(shipMass) + matFarm } // ProduceShipsInTurn simulates one turn of ship production on a planet // that has productionAvailable production units to spend, a material // stockpile, a resources rating, building ships of empty mass shipMass. // It returns the number of whole ships completed this turn, the material // left afterwards, the production units spent on the next (still // incomplete) ship, and that ship's progress fraction in [0, 1). // // Each ship consumes shipMass units of material; any shortfall is farmed // through [ShipBuildCost] at the planet's resources rating, draining the // stockpile to zero before farming. The loop mirrors the engine's // per-turn build step so the calculator and the turn generator agree on // how many ships a planet yields. productionAvailable or shipMass that is // non-positive yields no ships and leaves the stockpile untouched. func ProduceShipsInTurn( productionAvailable, material, resources, shipMass float64, ) (ships uint, materialLeft, productionUsed, progress float64) { if productionAvailable <= 0 || shipMass <= 0 { return 0, material, 0, 0 } pa := productionAvailable mat := material for { matNeed := shipMass - mat if matNeed < 0 { matNeed = 0 } totalCost := ShipBuildCost(shipMass, mat, resources) if pa < totalCost { return ships, mat, pa, pa / totalCost } pa -= totalCost mat = mat - shipMass + matNeed ships++ } }