9ae7b88b89
Fuse the standalone ship-class designer (Phases 17/18) into a sidebar calculator: live mass/speed/attack/defence/bombing results, a planet build-rate readout, single-target goal-seek, a modernization-cost mode, and auto reach circles on the map for the selected planet. pkg/calc becomes the single source for the new math (no mirroring): extract BombingPower from the engine model and the per-turn ship-production loop from controller.ProduceShip into pkg/calc (engine now delegates), and add inverse goal-seek solvers in pkg/calc/solve.go. Thin-bridge the combat, planet-build, and solver functions through ui/core/calc + ui/wasm and rebuild core.wasm. Remove the standalone designer view/route; the ship-classes table and the view/bottom menus open the calculator via a shared request store. Docs: rewrite ui/PLAN.md Phase 30, adjust Phase 34 (realistic forecast + CAP/COL ownership), add ui/docs/calculator-ux.md, extend calc-bridge.md, fix navigation.md; remove ui/CALCULATOR.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
77 lines
2.7 KiB
Go
77 lines
2.7 KiB
Go
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++
|
|
}
|
|
}
|