| name | validate-physics |
| description | Sanity-check the shiftsim physics/model after changes — run the regression suite and the known-result tactical checks so realism doesn't silently break. Use after editing the simulator, polar, wind, or strategy code. |
Validate the physics
Run after any change to simulator.py, polar.py, wind.py, strategy.py, or
the RunConfig knobs. The point is to catch silent realism regressions — code
that runs fine but no longer behaves like sailing.
Run the suite
python3 tests/run_all.py
All tests must pass. The load-bearing ones:
| test | what it protects |
|---|
test_geometry.* | angle conventions (TWD-from, bearings, heading = TWD±TWA) |
test_polar.best_upwind/downwind_angle_realistic | optimal angles stay ≈30–55° up, ≈120–180° down |
test_physics.test_vmg_optimum_beats_neighbours | chosen angle really maximises VMG |
test_physics.test_boat_sails_the_shift_keeps_twa | boats hold TWA, heading rotates with the wind |
test_physics.test_extra_tacks_cost_distance | maneuvers cost time/distance |
test_simulator.test_tack_on_header_beats_minimize_in_oscillation | the core result: tacking on headers wins in oscillation |
test_simulator.test_favoured_side_wins_persistent | committing to the favoured side wins a persistent shift |
test_simulator.test_determinism | same scenario ⇒ identical result |
test_simulator.test_all_boats_finish_demos | no livelock / divergence |
If you changed behaviour intentionally, update the test and say why in the diff —
don't delete a tactical regression to make red go green.
Manual sanity checks (when adding to the model)
- Optimal angles:
python -m shiftsim polar prints best upwind/downwind
angles for a synthetic polar — they should look like a real boat.
- Sailing the shift: in a pure oscillation, a single
minimize_tacks boat's
heading should swing by the wind's amplitude while its TWA stays constant
(that's test_boat_sails_the_shift_keeps_twa).
- Tracks look right: open
out/tracks.svg — beats should zig-zag up toward
the windward mark, runs fan down to the leeward mark; no boat should sail off
to infinity or loop near a mark.
- No spurious struggling: check
n_struggled for the good strategies
(clean one-tack / well-tuned tack-on-header). It should be 0 or near 0. If a
sensible strategy is being rescued by the thrash-breaker, the layline/EMA
tuning regressed — investigate before shipping.
Adding a new known-result test
When you add a wind type or strategy, add a test that asserts the tactically
correct outcome (who should win and why), not just "it runs". That's what keeps
the simulator trustworthy. Put physics invariants in tests/test_physics.py and
outcome checks in tests/test_simulator.py.
Done when
tests/run_all.py is fully green, the core tactical results still hold, and any
intentional behaviour change is reflected in an updated test.