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>
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package calc_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
source "galaxy/calc"
|
|
bridge "galaxy/core/calc"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestWeaponsForAttackParity(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct{ targetAttack, weaponsTech float64 }{
|
|
{18, 1.5}, {0, 1}, {10, 0},
|
|
}
|
|
for _, c := range cases {
|
|
wantV, wantOk := source.WeaponsForAttack(c.targetAttack, c.weaponsTech)
|
|
gotV, gotOk := bridge.WeaponsForAttack(c.targetAttack, c.weaponsTech)
|
|
assert.Equal(t, wantOk, gotOk)
|
|
assert.Equal(t, wantV, gotV)
|
|
}
|
|
}
|
|
|
|
func TestDriveForSpeedParity(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct{ targetSpeed, driveTech, restMass float64 }{
|
|
{5, 1.2, 35}, {24, 1.2, 35}, {0, 1, 10},
|
|
}
|
|
for _, c := range cases {
|
|
wantV, wantOk := source.DriveForSpeed(c.targetSpeed, c.driveTech, c.restMass)
|
|
gotV, gotOk := bridge.DriveForSpeed(c.targetSpeed, c.driveTech, c.restMass)
|
|
assert.Equal(t, wantOk, gotOk)
|
|
assert.Equal(t, wantV, gotV)
|
|
}
|
|
}
|
|
|
|
func TestShieldsForDefenceParity(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct{ targetDefence, shieldsTech, restMass float64 }{
|
|
{5, 1, 40}, {0, 1, 40}, {3, 0, 40},
|
|
}
|
|
for _, c := range cases {
|
|
wantV, wantOk := source.ShieldsForDefence(c.targetDefence, c.shieldsTech, c.restMass)
|
|
gotV, gotOk := bridge.ShieldsForDefence(c.targetDefence, c.shieldsTech, c.restMass)
|
|
assert.Equal(t, wantOk, gotOk)
|
|
assert.Equal(t, wantV, gotV)
|
|
}
|
|
}
|
|
|
|
func TestCargoForEmptyMassParity(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct{ targetEmptyMass, restMass float64 }{
|
|
{42, 30}, {29, 30},
|
|
}
|
|
for _, c := range cases {
|
|
wantV, wantOk := source.CargoForEmptyMass(c.targetEmptyMass, c.restMass)
|
|
gotV, gotOk := bridge.CargoForEmptyMass(c.targetEmptyMass, c.restMass)
|
|
assert.Equal(t, wantOk, gotOk)
|
|
assert.Equal(t, wantV, gotV)
|
|
}
|
|
}
|
|
|
|
func TestLoadForFullMassParity(t *testing.T) {
|
|
t.Parallel()
|
|
cases := []struct{ targetFullMass, emptyMass, cargoTech float64 }{
|
|
{65, 45, 1}, {44, 45, 1}, {65, 45, 0},
|
|
}
|
|
for _, c := range cases {
|
|
wantV, wantOk := source.LoadForFullMass(c.targetFullMass, c.emptyMass, c.cargoTech)
|
|
gotV, gotOk := bridge.LoadForFullMass(c.targetFullMass, c.emptyMass, c.cargoTech)
|
|
assert.Equal(t, wantOk, gotOk)
|
|
assert.Equal(t, wantV, gotV)
|
|
}
|
|
}
|