package world import ( "math" "testing" "wh/config" "wh/fixed" ) func TestOrbitsAreCirclesAtCommonSpeed(t *testing.T) { cfg := config.Default() asts, _ := Generate(cfg) want := cfg.OrbitSpeed.Float64() for _, a := range asts { o := a.Orbit for _, ts := range []int64{0, 100_000, 777_777, o.Period / 3, o.Period - 1} { p, v := o.State(ts) if r := p.Len().Float64(); math.Abs(r-o.A.Float64()) > 1e-3 { t.Fatalf("asteroid %d t=%d: radius %g, want %g", a.ID, ts, r, o.A.Float64()) } if s := v.Len().Float64(); math.Abs(s-want)/want > 1e-5 { t.Fatalf("asteroid %d t=%d: speed %g, want %g", a.ID, ts, s, want) } if d := p.Dot(v).Float64() / (p.Len().Float64() * want); math.Abs(d) > 1e-4 { t.Fatalf("asteroid %d: velocity not tangent (cos=%g)", a.ID, d) } // Inclination bounds the height above the ecliptic. if z := p.Z.Abs().Float64(); z > o.A.Float64()*math.Sin(o.Inc.Float64())*1.001+1e-3 { t.Fatalf("asteroid %d: |z|=%g exceeds inclination bound", a.ID, z) } } // Periodic. p1, _ := o.State(12345) p2, _ := o.State(12345 + o.Period) if d := p1.Sub(p2).Len().Float64(); d > 1 { t.Fatalf("asteroid %d not periodic, off by %g km", a.ID, d) } } } func TestOrbitPlaneMatchesInclination(t *testing.T) { o := Orbit{A: fixed.FromInt(2_000_000), Inc: fixed.FromRatio(3, 10), Node: fixed.FromInt(2)} o.Period = PeriodFor(o.A, fixed.FromInt(3)) // The orbit normal is Rz(Node)Rx(Inc) applied to +Z; positions must be // perpendicular to it, and the maximum height is A*sin(Inc). n := fixed.Vec{Z: fixed.One}.RotateX(o.Inc).RotateZ(o.Node) var maxZ float64 for k := int64(0); k < 50; k++ { p, _ := o.State(k * o.Period / 50) if d := p.Dot(n).Float64(); math.Abs(d) > 1e-2 { t.Fatalf("position not in the orbital plane: p.n = %g", d) } maxZ = math.Max(maxZ, p.Z.Float64()) } if want := o.A.Float64() * math.Sin(0.3); math.Abs(maxZ-want)/want > 0.01 { t.Fatalf("max z = %g, want %g", maxZ, want) } } func TestGenerateDeterministic(t *testing.T) { cfg := config.Default() a1, s1 := Generate(cfg) a2, s2 := Generate(cfg) if len(a1) != cfg.AsteroidCount || s1 != s2 { t.Fatal("mismatch") } for i := range a1 { if a1[i] != a2[i] { t.Fatalf("asteroid %d differs", i) } } cfg.Seed = 2 a3, _ := Generate(cfg) if a3[0] == a1[0] { t.Fatal("different seeds should differ") } }