The situational basis #
A tactic is chosen at the top of a turn, before movement. That timing decides what may be measured, and it is the whole design of this basis: no hex has been proposed yet, no volley has been priced, so a feature that reached for one would be answering about a future the tactic is itself about to change.
So the tactic layer has its own input - situation::Prospect, holding our units, theirs, what this force was built as, the round, and the tactic being considered. There is no board in it, no reachable set, no stand and no candidate. A feature that needs one cannot be written against this input; it fails to compile. That is the same kind of guarantee features::score gives about normalisation, and it is why the boundary is a type rather than a comment.
It is also a separate registry from for_each_feature!. One list holding both would let a task weight be fitted against a column that is constant across every candidate in a decision, or a tactic weight against a column that does not exist yet when a tactic is chosen.
The basis #
| feature | what it reads |
|---|---|
crippled_share |
How many of this force MegaMek calls crippled, as a share of those still standing. |
enemy_share |
How many of the machines still standing are theirs. A half is an even fight. |
force_intact |
How many of this force are still standing, as a share of the strength it started at. |
heat_load |
Heat on the scale against what this force can shed in a turn, averaged and capped at one. |
is_our_opening |
Whether this is the tactic this force's formation opens on. |
our_dispersion |
How far this force's machines average from their own centre, out to fifteen hexes. |
our_health |
Armour and structure left across this force, averaged over its machines. |
reach_edge |
Our longest guns against theirs. Above a half is a force that outranges the enemy. |
separation |
How far our centre is from theirs, in hexes, out to thirty. |
speed_edge |
Our running speed against theirs. Above a half is a force that can choose the range. |
their_dispersion |
How far the enemy's machines average from their own centre, out to fifteen hexes. |
their_health |
Armour and structure left across the enemy we can see, averaged over their machines. |
Every one is Bounded: a rule or a stated domain gives the range, so a weight fitted against it means the same thing on every board. separation runs out to thirty hexes and the two dispersions to fifteen, which are the domains situation::FAR and situation::SCATTERED name.
Exactly one of them reads the tactic rather than only the fight: is_our_opening. That is what lets one weight vector rank every orderable tactic instead of needing a vector apiece - the basis measures a (situation, tactic) pair, the same way the task basis measures a candidate.
What a force proposes today #
TacticWeights::hand_authored is one number: is_our_opening at 1.0, and every other feature measured, recorded and weighted at nought. So a force proposes the tactic its formation opens on - exactly what it did before this layer existed - and the mechanism can be landed and read before any weight is argued about. Everything else is a fit waiting to happen rather than a table waiting to be edited.
A force reports every tactic it scored, not just the one it proposes. That is the rule a unit's menu already follows, and the coordinator needs the losers anyway, because harmonising is choosing among them.
The two shapes, and which this is #
plan/tactics.md leaves two models open: a weight over which tactic to hold now, or a weight over which transition to take. This is the first, and the reason is the division of labour rather than a judgement about which model is better. Hysteresis belongs to the planner, not to a force - so a force has no switching cost to price, and a per-transition model inside one would be pricing a cost that is not its own. A force proposes on merit; the coordinator decides on cost.
The second shape is one feature away. Prospect::held carries what the force is on now and nothing reads it, deliberately: reading it here is the hysteresis that belongs upstairs. A transition model is a feature of (situation, held, proposed) added to the list above, and the input already carries all three.