Skip to main content

add-boat-strategy

Add a new tack/gybe decision strategy or a new boat polar to shiftsim. Use when the user wants to model a new tactical rule (when to tack/gybe) or a new boat's performance.

Ir para a instalação

Informações da origem

Repositório
weaties/shiftsimulator
Última atividade na origem
25 de junho de 2026 às 15:41
Idioma detectado do SKILL.md
inglês
Estrelas
0
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
add-boat-strategy
description
Add a new tack/gybe decision strategy or a new boat polar to shiftsim. Use when the user wants to model a new tactical rule (when to tack/gybe) or a new boat's performance.
# Add a boat / strategy Two related tasks: **(A)** add a tack/gybe `Strategy`, or **(B)** add a boat polar. Read `CLAUDE.md` first — especially the decision pipeline and conventions. The key fact: a strategy only decides **whether to maneuver now**; the simulator handles laylines, the maneuver cost, and the safety nets. So strategies stay small and focused on the *tactic*. ## A. Add a Strategy In `src/shiftsim/strategy.py`, subclass `Strategy`: ```python class MyRule(Strategy): name = "my_rule" def __init__(self, threshold: float = 8.0): self.threshold = float(threshold) def reset(self): # clear per-run state at the start of each run ... def decide(self, ctx: StrategyContext) -> bool: # return True to tack/gybe now ... def to_dict(self): return {"name": self.name, "threshold": self.threshold} ``` `StrategyContext` (read-only, given each step) has: `t, dt, point_of_sail` (`"upwind"`/`"downwind"`), `tack`, `twd, tws, twa`, `heading_current`, `heading_other`, `bearing_to_mark`, `dist_to_mark`, and `ctx.err(heading)` = angular error of a heading vs the mark bearing (smaller = points closer). Useful idioms (see existing presets): - **Point-closest-to-mark** (the lifted tack): tack when `ctx.err(ctx.heading_other) < ctx.err(ctx.heading_current) - threshold`. This is `TackOnHeader`. The mirror is `TackOnLift` (a control that loses). - **Keep your own state** across steps (e.g. a wind-direction EMA, a tack timer) on the instance; clear it in `reset()`. `FixedInterval` shows a simple timer. - Branch on `ctx.point_of_sail` if upwind vs downwind logic should differ. Also implement `explain(self, ctx) -> dict` (read-only; runs *after* `decide` when a maneuver fires) returning the numbers behind the decision — this is what the web viewer's per-maneuver calculation panel shows. Include a `"rule"` key and the relevant values (thresholds, errors, the header amount, etc.). Then: 1. Register in `_REGISTRY` at the bottom of the file (key = `name`). 2. Expose it in the web UI: add an entry to the `STRATS` map in `web/index.html` (`name: [[paramKey, label, default, docAnchor], ...]`) so it appears in the boat editor's strategy dropdown with the right parameter inputs. 2a. **Document it (required):** add a section + anchor for the strategy and each of its parameters in `web/docs.html`, and a `HELP` map entry in `web/index.html` for each new `data-help` key (short tooltip + `docs.html#anchor`). See "Keep the documentation in sync" in `CLAUDE.md`. Verify every tooltip anchor resolves with the `comm` check documented there. 3. Use it from a scenario: `"strategy": {"name": "my_rule", "threshold": 6}`. 4. Add a test in `tests/test_simulator.py` asserting the *expected tactical outcome* (e.g. it beats / loses to a baseline in a given wind). Run `python3 tests/run_all.py`. **Sanity:** if your new strategy ever makes a boat DNF, check `n_struggled` in the summary — a high value means it's thrashing and the thrash-breaker rescued it. That usually means the rule oscillates; add hysteresis (a `threshold`) or a minimum interval. A *good* strategy should rarely trigger the safety nets. ## B. Add a boat polar A boat's speed model is a `Polar`. Three ways to provide one in a scenario's `"polar"` block: - **Synthetic:** `{"type":"synthetic","max_speed":7.5,"pointing":1.1,"light_air_factor":8}` — tune `max_speed` (top speed), `pointing` (>1 points higher / tighter upwind angle), `light_air_factor` (smaller = powers up sooner). Inspect with `python -m shiftsim polar --max-speed 7.5 --pointing 1.1`. - **Real data:** `{"type":"csv","path":"path/to/polar.csv"}` — first row TWS, first column TWA, body = boat speed (knots). The common VPP/ORC export form. - **Inline:** a full `{"twa":[...],"tws":[...],"table":[[...]]}` dict. To give boats genuinely different behaviour for a comparison, vary `pointing` and `max_speed`, or hand different boats different CSVs. ## Done when - The strategy is registered, usable from a scenario, and has a test asserting its tactical outcome; **or** the polar loads and its `best_upwind`/ `best_downwind` angles look realistic (≈30–55° upwind, ≈120–180° downwind).
Ver no GitHub