Testing Deterministic Math for Strategy Games

Networked multiplayer strategy games rely heavily on deterministic lockstep architecture, where game states remain synchronized across different machines solely by sharing player inputs. Because standard IEEE 754 floating-point operations can produce subtle platform-dependent discrepancies, developers implement custom fixed-point or soft-float math libraries. Testing these libraries requires a rigorous approach to ensure bit-level parity, prevent desynchronization, and maintain mathematical correctness across varied hardware, operating systems, and compiler optimizations.

1. Cross-Platform Bit-Exactness and Golden Master Tests

The primary failure mode of a deterministic math library is cross-platform divergence. Developers structure tests to verify that computations produce identical raw bit outputs across different target platforms (x86_64, ARM64, WASM) and compilers (MSVC, Clang, GCC).

  • Golden Master Data: Test suites often compare runtime results against pre-generated binary or hex dumps of expected outputs.
  • Continuous Integration Matrix: Unit tests run automatically in CI pipelines across all target architectures and platforms to catch compiler-specific optimizations or instruction set quirks that break determinism.
  • Serialization Verification: Tests serialize math types (e.g., vectors, matrices, angles) to raw byte streams and verify that deserialization reconstitutes the exact state without loss of precision.

2. Primitive Arithmetic and Boundary Verification

Basic operations (+, -, *, /, bit shifts) form the foundation of fixed-point arithmetic and require thorough unit coverage for edge cases:

  • Overflow and Underflow: Tests specifically target upper and lower limits of the underlying integer representations (e.g., INT32_MAX, INT32_MIN) to verify wrapping, clamping, or saturation logic.
  • Rounding Modes: Developers write tests to validate exact rounding rules (truncation, round-to-nearest, or ceiling) across both positive and negative values.
  • Division by Zero: Tests ensure robust, deterministic fallback behavior or explicit assertions when dividing by zero.

3. Trigonometric and Transcendental Approximations

Strategy games rely heavily on trigonometry for line-of-sight, pathfinding, and projectile trajectories. Since hardware-accelerated functions like sin or sqrt are non-deterministic across platforms, custom implementations (Lookup Tables, CORDIC algorithms, or polynomial approximations) must be rigorously validated.

  • Exhaustive Domain Sweeps: For lookup-table-based functions with constrained input ranges (such as angles from 0 to 360 degrees), tests sweep every discrete integer step to compare values against baseline tables.
  • Approximation Tolerance Checks: While the math must be deterministic across machines, it must also be sufficiently accurate relative to true mathematical values. Tests assert that the maximum absolute error stays within defined thresholds.
  • Symmetry and Identity Tests: Tests verify mathematical identities, such as sin(-x) == -sin(x) and sin^2(x) + cos^2(x) == 1 (within acceptable fixed-point precision).

4. Vector, Matrix, and Spatial Math Layers

Higher-level geometric types built on top of the scalar fixed-point types require integration tests focused on spatial calculations:

  • Vector Normalization: Tests ensure normalization algorithms avoid standard floating-point inverse square roots and properly handle zero-length vectors.
  • Dot and Cross Products: Tests confirm geometric orthogonality, collinearity, and sign correctness under varied orientations.
  • Matrix Transformations: Tests validate translation, rotation, and scaling matrices by verifying that applying and inverting transformations yields the original coordinates within expected rounding errors.

5. Property-Based and Fuzz Testing

Beyond standard deterministic examples, developers use property-based testing to throw millions of pseudorandom inputs at the math library to discover unhandled edge cases.

  • Algebraic Invariants: Randomized inputs are checked for algebraic properties such as commutativity (a + b == b + a), associativity, and distributivity.
  • Monotonicity Tests: Tests verify that strictly increasing functions (such as square roots) maintain a monotonically increasing output for sequential inputs without inversion anomalies.
  • Fuzzing Internal State: Fuzzers supply pseudo-random bit patterns to fixed-point structures to ensure no combination of bits leads to infinite loops, crashes, or unhandled numeric states.