# Features, worked through One sentence per feature is the exit criterion of `plan/features.md`. A sentence is not enough to argue with a fitted weight, so each feature here also has a small situation it was measured in and the number it came out at. Every figure is measured by the feature's own `measure`, through the same code the bot runs. Nothing on this page is a transcription, and `crates/sds-core/tests/vignettes.rs` fails if any of it has gone stale. All 62 of them, because a feature cannot exist without one - see `crates/sds-core/examples/vignettes/mod.rs`. ## ammo_spent `bounded`. How much of all the ammunition this unit has left this volley fires off. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![ammo_spent](features/ammo_spent.svg) Two rounds out of the nineteen aboard reads 0.11, and past nine hexes the SRM-6 is out of range and the Gauss's one round reads 0.05. The denominator is every ammunition-fed gun the **machine** carries, fired or not, so it does not move when a launcher leaves the volley. Pooled over the volley instead, the same single Gauss shot read 0.25 out there against 0.11 in close: dropping a gun made the volley look more wasteful, and one gun's full bin subsidised another's empty one. ## arc_spread `bounded`. How many different sides of each enemy we are shooting from rather than all from one. **The figure shows** four of ours ringing two of theirs, under four different assignments of who shoots whom. ![arc_spread](features/arc_spread.svg) How many distinct sides of each target we are shooting from. Attacking from several arcs denies the target one good facing: it cannot present its front to four machines at once, and whichever way it turns, somebody is looking at thin armour. This is why the assignment with the most concentration is not automatically the best one - piling four shooters into one arc scores full marks on `concentration` and poorly here, and a weight decides the trade. ## attacker_self_damage `bounded`. The chance this action puts us on the floor, by our own doing. **The figure shows** the same machine, with a kick added at the one hex a kick reaches, and a piloting base of 6 - the reading the arms found a missed kick falls at 37% of the time. ![attacker_self_damage](features/attacker_self_damage.svg) Nought everywhere except the hex the machine can actually swing at, which is the whole point: this column prices an *action*, not a position, and at seven hexes there is no action to price. Where it does bite it is the product of two things the bot could not previously see together - that the kick misses, and that a miss forces a piloting roll this machine might fail. It is a probability rather than a share of damage, and deliberately: a fall's cost is mostly the turn and the prone position rather than the points, and the points version of this column read 0.009 at its largest and could not move a decision at all. At a base of 6 a missed kick puts this machine down about one time in four; a fresh crew at 3 reads an eighth of that, which is the distinction the feature exists to make. ## charge_damage `bounded`. The share of everything we could put out that this charge lands instead. **The figure shows** one hundred-ton machine charging the same target from one, two, four and seven hexes back, against a turn of shooting that would land twenty points from where it stands. The roll gets worse as the run gets longer, because a charge is made at a run and the attacker's own movement is one of its modifiers. ![charge_damage](features/charge_damage.svg) A share of the same denominator `expected_damage` divides by - the machine's own best turn - so the two columns are directly comparable, and that comparison is the decision. From one hex a charge is worth nothing at all: MegaMek's own damage figure is ten points a hex *after the first*, so a machine that has not run reads 0.00 and can only lose the firing turn it would have spent. From four it is worth about as much as shooting, and from seven it is worth more than the machine could shoot even though it lands less than half the time. What this column deliberately does not carry is on the other side of the vector: the piloting roll the target is handed sits in `p_psr_threshold`, the one *we* are handed for making it sits in `attacker_self_damage`, and the certain points a charge costs its own attacker sit in `self_damage`. None of the three moves with the distance run, which is why they are three columns and not a correction to this one - the figure here is what the run buys, and what it costs is settled by the machine we picked to run into. ## cohesion `bounded`. How close this hex keeps us to the rest of our force, as a share of the board's longest distance. **The figure shows** two of our other machines on the right, and a row of hexes walking away from them. ![cohesion](features/cohesion.svg) The mean distance to the rest of the force, subtracted from one and divided by the board's longest hex distance, so a hex beside the lance reads high and the far corner reads 0.00 rather than going negative. A unit with nobody else on the board reads 1.00 rather than 0.50: a lone machine is not being punished for the lance it does not have. ## concentration `bounded`. How much of the force's fire is going into one enemy rather than being shared around. **The figure shows** four of ours ringing two of theirs, under four different assignments of who shoots whom. ![concentration](features/concentration.svg) A sum of squared shares, so four guns on one machine reads 1.00 and an even two-two split reads 0.50. Everyone on one machine kills it this turn instead of hurting four, which is the single highest-value thing a force can do at all, and it carries the largest force-level weight in the hand-authored set. The two 2-2 splits read the same here and `arc_spread` tells them apart: this feature is about how the fire is shared out and has no opinion about *which* target deserves it, which is what the `target_*` family is for. ## cover_distance `bounded`. How far the nearest cover is from this hex, as a share of a running move. **The figure shows** one stand of woods on the left, and a row of hexes walking away from it. ![cover_distance](features/cover_distance.svg) Standing in cover reads 0.00 - there is no distance to cover from a hex that is cover. Every other hex asks how far a run would have to carry us, so the number is about the *next* turn while every other feature here is about this one. ## cover_quality `bounded`. How much the ground would spoil the aim of the enemies who can see this hex. **The figure shows** one enemy, one of ours, and the ground between them written out hex by hex: what each hex puts on their shot, what the whole line carries, and what the feature divides that by. ![cover_quality](features/cover_quality.svg) Three is the ceiling because three is what MegaMek will still return a number for - `sds cover-probe` walks terrain up until it stops answering, and `scenarios/cover/observed.txt` is the table. Woods and smoke that do not block are worth at most two; the target's own cover adds the third point. The last panel is the one this feature used to get wrong: three light stands are worth one point each and the line is gone anyway, so there is no penalty to measure. It used to read 1.00 there - the same as the panel beside it, which is the opposite situation - and now it is left out of the mean entirely and `los_in` answers it alone. ## damage_lead `local`, not learnable. How this shot's expected damage compares with the other shots we could take right now. **The figure shows** the same column, min-maxed against itself: each range's expected damage as a share of the spread across every range on the menu. ![damage_lead](features/damage_lead.svg) The one `local` feature in this family, and the figure shows why it may never carry a fitted weight. Every number here is a position within *this* set of candidates: the best range reads 1.00 and the worst 0.00 whatever the absolute damage was, so the same situation on a board where the menu happened to be worse would produce the same column. It ranks correctly and means nothing across decisions. ## defense_1plus `bounded`. Set when our movement and the ground under us add at least one point to what the enemy needs. **The figure shows** eight candidates, one at every defence level from none to seven, with the step at 1. ![defense_1plus](features/defense_1plus.svg) A step, not a ramp, and the weight fitted against it is what the first point of defence *buys* rather than what standing on 1 of them is worth - that is what the thermometer framing means. This rung takes a gunnery-4 shot from needing 4 to needing 5, so the share of the enemy's shots it turns into misses is 0.083 off the 2d6 table. `level_tmm` and `level_terrain` carry the same total split into the two kinds of defence, because a counter to one of them is not a counter to the other. ## defense_2plus `bounded`. Set when our movement and the ground under us add at least two points to what the enemy needs. **The figure shows** eight candidates, one at every defence level from none to seven, with the step at 2. ![defense_2plus](features/defense_2plus.svg) A step, not a ramp, and the weight fitted against it is what the second point of defence *buys* rather than what standing on 2 of them is worth - that is what the thermometer framing means. This rung takes a gunnery-4 shot from needing 5 to needing 6, so the share of the enemy's shots it turns into misses is 0.111 off the 2d6 table. `level_tmm` and `level_terrain` carry the same total split into the two kinds of defence, because a counter to one of them is not a counter to the other. ## defense_3plus `bounded`. Set when our movement and the ground under us add at least three points to what the enemy needs. **The figure shows** eight candidates, one at every defence level from none to seven, with the step at 3. ![defense_3plus](features/defense_3plus.svg) A step, not a ramp, and the weight fitted against it is what the third point of defence *buys* rather than what standing on 3 of them is worth - that is what the thermometer framing means. This rung takes a gunnery-4 shot from needing 6 to needing 7, so the share of the enemy's shots it turns into misses is 0.139 off the 2d6 table. `level_tmm` and `level_terrain` carry the same total split into the two kinds of defence, because a counter to one of them is not a counter to the other. ## defense_4plus `bounded`. Set when our movement and the ground under us add at least four points to what the enemy needs. **The figure shows** eight candidates, one at every defence level from none to seven, with the step at 4. ![defense_4plus](features/defense_4plus.svg) A step, not a ramp, and the weight fitted against it is what the fourth point of defence *buys* rather than what standing on 4 of them is worth - that is what the thermometer framing means. This rung takes a gunnery-4 shot from needing 7 to needing 8, so the share of the enemy's shots it turns into misses is 0.167, the peak of the curve off the 2d6 table. `level_tmm` and `level_terrain` carry the same total split into the two kinds of defence, because a counter to one of them is not a counter to the other. ## defense_5plus `bounded`. Set when our movement and the ground under us add at least five points to what the enemy needs. **The figure shows** eight candidates, one at every defence level from none to seven, with the step at 5. ![defense_5plus](features/defense_5plus.svg) A step, not a ramp, and the weight fitted against it is what the fifth point of defence *buys* rather than what standing on 5 of them is worth - that is what the thermometer framing means. This rung takes a gunnery-4 shot from needing 8 to needing 9, so the share of the enemy's shots it turns into misses is 0.139 off the 2d6 table. `level_tmm` and `level_terrain` carry the same total split into the two kinds of defence, because a counter to one of them is not a counter to the other. ## defense_6plus `bounded`. Set when our movement and the ground under us add at least six points to what the enemy needs. **The figure shows** eight candidates, one at every defence level from none to seven, with the step at 6. ![defense_6plus](features/defense_6plus.svg) A step, not a ramp, and the weight fitted against it is what the sixth point of defence *buys* rather than what standing on 6 of them is worth - that is what the thermometer framing means. This rung takes a gunnery-4 shot from needing 9 to needing 10, so the share of the enemy's shots it turns into misses is 0.111 off the 2d6 table. `level_tmm` and `level_terrain` carry the same total split into the two kinds of defence, because a counter to one of them is not a counter to the other. ## deploy_ground `bounded`. The share of the board this hex could still be moved to from, next turn. **The figure shows** every hex of a board with a ridge across it and a deep lake in the bottom-left corner, as what deploying there would leave the machine able to reach. ![deploy_ground](features/deploy_ground.svg) A share of the whole board rather than of an open field, which is what makes it a deployment feature rather than `percent_options` under another name: at deployment nothing has moved yet, so the honest denominator is the map. The lake and the board edges both read low, and for the same reason - a corner behind water has few ways out of it. ## deploy_outlook `bounded`. The share of the board this hex has a line to, from the elevation it puts us at. **The figure shows** the same board, as how much of it a machine deployed in each hex could see. ![deploy_outlook](features/deploy_outlook.svg) The ridge reads 1.00 and the ground either side of it reads well under, which is the whole of the feature: at deployment there is nobody to have a line to, so the only thing worth asking about a hex is how much of the map it commands. Compare it with `deploy_ground` on the same board - the two disagree, and a hex that both like is a hex worth standing in. ## dfa_damage `bounded`. The share of everything we could put out that this death from above lands instead. **The figure shows** one hundred-ton machine jumping onto targets that need a 4, a 7, a 9 and an 11, against a turn of shooting that would land twenty points from where it stands. MegaMek's own figures at that weight are 30 dealt, 20 taken to the legs on a hit, and 30 taken on a miss. ![dfa_damage](features/dfa_damage.svg) A share of the same denominator `expected_damage` divides by - the machine's own best turn - so a death from above, a charge and a volley are three readings of one scale and the force picks between them by comparing numbers rather than kinds. The third line under each bar is the one that has no counterpart anywhere else in the basis. Every other attack costs less as it gets less likely to connect; this one costs *more*, because a miss is a fall with no roll to pass and a fall takes half again what landing does. At an 11 the machine expects to put about six points on somebody and take about twenty-eight back. Those points sit in `self_damage` and the floor they leave us on sits in `attacker_self_damage`, so what this column says is only what the jump buys. ## distance_to_objective `bounded`. How far this hex is from the hex the order named, as a share of the board's longest distance. **The figure shows** an empty board with one enemy on the right, and an order pointing at a hex on the left. ![distance_to_objective](features/distance_to_objective.svg) The enemy is on this figure and is not in the number. Every other distance in the basis is to something the board names for itself, and this one is to whatever the order names - so the gradient runs to the anchored hex and would run somewhere else entirely under a different order, on the same board with the same machines. The scale is `enemy_nearest`'s: hex distance over the board's own longest distance, so the two can be read against each other. A candidate with no objective is not on this scale at all - the feature is left unmeasured rather than recorded as zero, because a hex that was never asked about has not answered badly. ## edge_distance `bounded`. How far this hex is from the nearest board edge, as a share of the deepest a hex on it can be. **The figure shows** every hex of an empty board, so the shape of the measurement is the picture rather than a row of it. ![edge_distance](features/edge_distance.svg) The denominator is the deepest a hex on this board can be, which is three here and not three and a half, so the middle row reaches 1.00 instead of stopping at 0.86. Nothing about terrain is in this number: it is where the edges are, and `percent_options` is the feature that says what standing near one costs. ## elevation_gain `bounded`. How much higher this hex stands than the enemies we are facing. **The figure shows** a staircase of hexes from two levels below the enemy to four above, with the enemy on level ground. ![elevation_gain](features/elevation_gain.svg) Level with them is 0.50 rather than 0.00: the domain runs three levels each way and the middle of it is no advantage either way. Four levels up reads the same as three, which is the saturation the feature declares - a hill is worth having and a mountain is not worth twice as much. ## enemy_centroid `bounded`. How far this hex is from the centre of the enemy force, as a share of the board's longest distance. **The figure shows** the same two enemies as `enemy_nearest`, over the same row, measured against the middle of them instead of the closest. ![enemy_centroid](features/enemy_centroid.svg) The centroid is rounded to a hex before the distance is taken, because hex distance is defined between hexes and rounding afterwards would make the answer depend on the order the sightings arrived in. Compare the row with `enemy_nearest`: the hex beside one enemy is close on that feature and far on this one, and that difference is the whole reason both are in the basis. ## enemy_nearest `bounded`. How far the closest enemy is from this hex, as a share of the board's longest distance. **The figure shows** two enemies at opposite corners, and a row of hexes between them. ![enemy_nearest](features/enemy_nearest.svg) The nearest one only. A hex halfway between two enemies and a hex the same distance from one with nobody else on the board read the same number, which is why `range_spread` and `enemy_centroid` exist beside it. The denominator is the board's own longest hex distance - the two far corners of the 16x17 map set are 24 hexes apart, not 17 - so the far corner is 1.00 and nothing is over it. ## expected_criticals `bounded`. How many critical hits we expect this volley to roll on the target. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![expected_criticals](features/expected_criticals.svg) Criticals, not damage. Two terms: what gets *past* the armour, divided by what one packet carries, plus the natural 2 on the location table, which is a critical without breaching anything. It is the column `p_breach` cannot give - `p_breach` says a location was opened and stops there, this says what came through the hole. The shape follows `expected_damage` - best where both guns are in bracket, worse inside the Gauss rifle's minimum range - and the spread over it is far steeper, 0.03 at the long end against 0.24 at the best hex where expected damage only goes from 0.11 to 0.42. A location has to be opened before anything can come through it, so the second gun coming into bracket is aimed at armour the first has already been through. Against a target still behind whole armour the first term is zero and the second is not, which is why sandblasting an intact Mek is worth anything at all. ## expected_damage `bounded`. The share of everything our guns could put out that this volley lands. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![expected_damage](features/expected_damage.svg) Not a smooth falloff but a staircase with the guns' brackets in it. The step at nine hexes is the SRM-6 leaving the volley entirely, and the dip in the two hexes closest to us is the Gauss's minimum range making its own aim worse - +1 at two hexes and +2 at one, which the bands name. An enemy that walks into our face is *harder* to shoot, not easier, and this is the only figure in the set where a number falls as the target gets nearer. A share of what this machine could land at its best range, so it is comparable between a Locust and an Atlas. ## force_share_destroyed `bounded`. How much of the enemy force's battle value we expect this volley to remove. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![force_share_destroyed](features/force_share_destroyed.svg) The same expected battle value `value_destroyed` measures, over what the whole opposition is worth rather than what this target is worth. One machine faces us here, so it *is* the opposition and the two columns agree - the point of carrying both is a fight with more than one enemy in it, where finishing a light reads 1.0 on `value_destroyed` and very little here, and chipping an assault reads the reverse. The scorer is a linear sum, so neither column can be recovered from the other. ## friend_support `bounded`. The share of our other machines with a line to this hex. **The figure shows** two of ours to the north, a wall of woods across the right of the board, and the bottom row measured - U2 behind the wall, U3 in the clear. ![friend_support](features/friend_support.svg) A line, not a distance: `cohesion` asks how far the lance is and this asks how many of them could shoot at what is shooting at us. The two disagree wherever terrain does, which is the case the pair exists for. ## heat_ammo_explosion_risk `bounded`. The chance the heat a unit would end the turn on, having moved and fired, sets off its own ammunition. **The figure shows** the same six heats, as the chance the machine sets off its own ammunition. ![heat_ammo_explosion_risk](features/heat_ammo_explosion_risk.svg) Zero until 19, and zero at every heat on a machine with no bin that can go off - the feature asks the unit before it asks the chart. This one is not a risk of losing a turn but of losing the machine, which is why it is a separate column from `heat_shutdown_risk` even though the two rise together over most of the scale. ## heat_incurred `bounded`. How much of the heat this unit could generate in a turn the moving and the firing this candidate commits to would generate. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![heat_incurred](features/heat_incurred.svg) Flat at 0.71 at every range both guns can reach, and then it *drops* to 0.14 past nine hexes - not because the Gauss runs cooler out there but because the SRM-6 is out of range and is no longer fired. A cost that falls as the shot gets worse is exactly why this is priced against the damage it buys rather than on its own. The denominator is this machine's own heat ceiling - every usable gun at its rate plus the hottest gait it has - so both ends of the scale are reachable. Against `heat_capacity` instead, any design whose guns out-generate its sinks read a flat 1.00 for every volley containing them, and this figure was that flat line until the fixture was given more sinks. ## heat_mp_penalty `bounded`. How much of this machine's own running movement the heat it would end the turn on, having moved and fired, takes away. **The figure shows** one machine ending the turn on six different heats, against MegaMek's own heat chart. Each gauge says what the volley generated and what the sinks shed to leave it there. ![heat_mp_penalty](features/heat_mp_penalty.svg) A staircase with five steps in it, and nothing at all between them. Heat 13 and heat 14 read the same here because the movement row does not change between them - what changes at 14 is the reactor, which is `heat_shutdown_risk`'s line and not this one. The denominator is this machine's own running allowance, so the same heat costs a fast machine a larger share of what it had. ## heat_shutdown_risk `bounded`. The chance the heat a unit would end the turn on, having moved and fired, shuts it down. **The figure shows** the same six heats, as the chance the reactor stops. ![heat_shutdown_risk](features/heat_shutdown_risk.svg) Nothing at all below 14 and then a real number, which is the cliff no linear term can express: 13 is a machine that is merely warm and 14 is one rolling dice about whether it plays next turn. The probability is the 2d6 curve, so the steps between the 4+, 6+, 8+ and 10+ rows are not evenly spaced either. Thirty is not a roll at all - the reactor stops - so the last gauge reads exactly 1.00. ## heat_to_hit_penalty `bounded`. How much of the worst heat aim penalty the heat a unit would end the turn on, having moved and fired, brings. **The figure shows** the same six heats, priced against the worst aim penalty the chart hands out. ![heat_to_hit_penalty](features/heat_to_hit_penalty.svg) Four points of to-hit is the top of the scale, so each step is a quarter. This is the one heat feature about our own shooting rather than about the machine surviving, and it is why a hot alpha strike is a loan: the damage lands this turn and the penalty is paid on every shot of the next. ## incoming_damage `bounded`. The share of what this machine has left that it expects to lose for standing here. **The figure shows** the same two damage figures arriving at a machine with three hundred points left and at one with forty. ![incoming_damage](features/incoming_damage.svg) A share of what this machine has left, not a count of points, which is what makes the number mean the same thing to a Locust and an Atlas. Ninety points against forty saturates at 1.00 rather than reading above it: past the point where the damage kills us, more of it is not worse, and a feature that kept rising would let one hopeless hex dominate the argmax. ## indirect_share `bounded`. The share of this volley's damage thrown in an indirect firing mode, so a tactic can prefer the shot that does not need line of sight. **The figure shows** one machine's two guns against one target, four times over, with only the firing mode of each shot changed. ![indirect_share](features/indirect_share.svg) A share of the volley's **damage** and not a count of its shots, which is why the two mixed rows do not both read 0.50: the Gauss carries more of the volley than the SRM-6 does. `Engage` leaves this column at nought - the indirect shot is the worse one, by the `+1 (indirect fire)` modifier MegaMek charges, and there is no general reason to take it. `Bombard` pays for it, and pays a bonus rather than a rule, so a direct volley that is enough better on damage still wins. ## infantry_open `bounded`. Whether this hex leaves a conventional platoon in the open, where MegaMek doubles every volley that lands on it. **The figure shows** one stand of light woods and one of heavy in an open field, with what each hex would leave a platoon standing in. ![infantry_open](features/infantry_open.svg) Two values and nothing between them, and the two stands read the same. `ServerHelper.infantryInOpen` asks `containsTerrain` for eight kinds and stops at the first that answers, so a point of woods is worth exactly what two are - which is where this column and `level_terrain` part company, because that one grades the same two stands one apart. Rough, rubble, swamp, a building and a fortified hex are in the set too and none of them adds anything to the to-hit roll at all. `scenarios/infantry/observed.txt` is the table, and the volley it measures takes twice as many men in the pale hexes as in the dark ones. ## level_terrain `bounded`. The to-hit modifier the woods or jungle in our own hex adds to every shot taken at us. **The figure shows** the hull over bare ground, light woods, heavy woods and one hex of ultra-heavy. ![level_terrain](features/level_terrain.svg) Four steps and nothing between them: the modifier is 0, 1, 2 or 3 and `MAX_TARGET_TERRAIN_MODIFIER` is the 3. Where the enemy is does not appear in it at all - that is the difference from `cover_quality`, which is the woods a line crosses and moves when they do. ## level_tmm `bounded`. The target movement modifier this move would earn us, as a share of the most a real move reaches. **The figure shows** one hex, reached by walks and jumps of different lengths. ![level_tmm](features/level_tmm.svg) A staircase, not a ramp: MegaMek's table steps at 3, 5, 7, 10 and 18 hexes and does nothing in between, so two candidates a hex apart in distance are usually the same number here. A jump of `n` hexes is harder to hit than a walk of `n`, which is the only thing `jumped` changes. The scale is the highest modifier a real movement allowance reaches and not the table's own ceiling of seven, which would need a twenty-five hex jump. ## los_in `bounded`. The share of the enemy force that could see us in this hex. **The figure shows** two enemies and a stand of woods that blocks one of them, over a row of hexes that pass behind it. ![los_in](features/los_in.svg) A share of the enemy force, so with two of them the only values possible are 0.00, 0.50 and 1.00. That coarseness is the feature: it counts machines that could shoot, and does not care how hard. ## los_out `bounded`. The share of the enemy force we could see from this hex. **The figure shows** the same board as `los_in`, measured the other way: how many of them we could shoot from each hex. ![los_out](features/los_out.svg) Identical to `los_in` here, and that is the point of showing both on one board. MegaMek's trace is only asymmetric when the two units differ in height or elevation, so on flat ground the pair is one column twice - which is exactly the shape a fit puts a large weight on with the wrong sign. Where a ridge does separate them, this pair is the only thing in the basis that says so. ## overkill `bounded`. The chance this volley puts more damage into some location than that location can absorb. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![overkill](features/overkill.svg) `P(some location takes more than it can absorb)`, which is what the name says and what a bot deciding whether to point one more gun at a dying target needs. It used to be `waste / mean`, a share of damage: an honest quantity whose sentence described it correctly, but not this one, and it barely used its range. Over volleys of one to six fifteen-point guns the share spans 0.006 to 0.070 against a whole 55-tonner and 0.33 to 0.48 against a mauled one; the probability spans 0.02 to 0.56 and 0.46 to 0.98 over the same twelve volleys. ## overlook `bounded`. The share of the board this hex has a line to, ignoring who is standing where. **The figure shows** a ridge five levels high across the middle of the board, with every hex measured. ![overlook](features/overlook.svg) Not about the enemy at all - it is how much of the *board* a hex can see, sampled on a lattice, which makes it a property of the ground rather than of where anybody is standing this turn. The ridge line reads 1.00 and everything either side of it reads well under: a hex on the crest sees both halves of the map, and one at the foot sees one. Woods do not appear in this figure because a stand you are standing in does not blind you - only what a line has to cross does. ## p_breach `bounded`. The chance this volley strips some part of the target down to bare structure. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![p_breach](features/p_breach.svg) The chance of opening a location to its structure, which is where a kill comes from. This is the feature the two-gun loadout exists for: the Gauss puts fifteen points in one place and the SRM-6 scatters six two-point missiles that mostly do not land together, so the same total damage breaches at very different rates. `weapon_concentration` asks the same question of the guns; this one asks it of the target and answers from the distribution. ## p_kill `bounded`. The chance the head or centre torso is destroyed, counting only the damage that lands there. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![p_kill](features/p_kill.svg) Small everywhere, and that is the honest answer: two medium guns do not often kill a fresh 55-tonner in one volley. It is measured against the *location* the damage reaches rather than against the target's total - a centre torso takes 7 rolls in 36 and a leg takes 4 - which is what stopped this feature being overconfident by a factor that swung with the shape of the volley. ## p_mission_kill `bounded`. The chance this volley destroys a leg, counting only the damage that lands there. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![p_mission_kill](features/p_mission_kill.svg) `P(a leg is destroyed)` and nothing else. It used to be `P(a leg goes or the target dies)`, a union strictly containing `p_kill`, so the two could not be weighted apart however much corpus was recorded. Read this figure against `p_kill`'s: they now differ at every range, which is the whole point of the change. ## p_psr_threshold `bounded`. The chance we hit the target hard enough this turn to make it roll not to fall over. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![p_psr_threshold](features/p_psr_threshold.svg) Twenty points in a turn is the line, and among machines that fall it is a property of the *volley* rather than of the target - a Locust and an Atlas both roll at twenty. *Whether* the target falls is a property of the target, and a combat vehicle does not, so the column is undefined for one rather than small. A Gauss rifle and an SRM-6 can reach it: fifteen and twelve is twenty-seven, so the chance is real at the ranges both guns bear and collapses past nine hexes where the launcher stops. A PPC and an SRM-4 were on this scene first and topped out at eighteen between them, which made this column a structural zero at every range rather than merely a small number. ## percent_options `bounded`. The share of its open-field movement this hex leaves the machine. **The figure shows** a deep lake against the left edge, and a row of hexes walking out of it. ![percent_options](features/percent_options.svg) The denominator is what the same allowance is worth on flat empty ground, so a hex in open country in the middle of the board reads near 1.00 and the corner behind the lake reads far less. This is the only feature here that can see that a hex is a trap: a hollow, a cliff and a lake all read as ordinary ground to `cover_quality` and `level_tmm`. ## range_band_fit `bounded`. How much of this unit's best firepower it could bring to bear at this range, with minimum ranges and each weapon's own to-hit modifier charged. **The figure shows** one enemy at the top of the board and a column of hexes closing on it, measured against the loadout in `examples/common/mod.rs` - an AC/20, a PPC and an LRM-10. ![range_band_fit](features/range_band_fit.svg) The curve is the loadout's, not the rules'. Every band change is a gun entering or leaving its bracket, so a machine with different guns has a different picture on this same board. The denominator is the most this machine could land at any range, which is what keeps the number falling as a hex leaves the fight rather than normalising the distance back out. ## range_spread `bounded`. How far apart the ranges to the enemy are from this hex, as a share of the board's longest distance. **The figure shows** the same two enemies again, and how far apart the two ranges are from each hex of the row. ![range_spread](features/range_spread.svg) Low in the middle, where both are the same distance away and one range bracket serves both guns; high at either end, where closing on one leaves the other at a range nothing is set up for. A machine cannot be at short range to two enemies in opposite directions, and this is the only column that says so. ## rear_arc_exposure `bounded`. The share of the enemy force that would be shooting us in the back from this hex. **The figure shows** one hex, one enemy due north of it, and the same candidate turned through all six facings. ![rear_arc_exposure](features/rear_arc_exposure.svg) A hex is not a posture. The same ground with the enemy off the nose and off the back are different situations, and this is the feature that tells them apart - which is why `Posture` carries a facing at all. Only an enemy that can actually shoot us counts, so an enemy with no line never puts a candidate in the red however it is turned. ## rear_arc_gain `bounded`. The share of the enemy force we would be shooting in the back from this hex. **The figure shows** one enemy facing north, and every hex of the board measured for whether a shot from it arrives in that enemy's back. ![rear_arc_gain](features/rear_arc_gain.svg) The arc is theirs and the facing is theirs: our own facing does not appear in this number at all. With one enemy the only values are 0.00 and 1.00, and the wedge that reads 1.00 is exactly MegaMek's rear arc. A hex with no line to them reads 0.00 however far behind it is - standing behind something you cannot see is worth nothing. ## self_damage `bounded`. The share of what this machine has left that it spends on making this attack. **The figure shows** a Locust and an Atlas each charging a Locust and an Atlas, on the same roll, with MegaMek's own self-damage figure and each attacker's own armour and structure. ![self_damage](features/self_damage.svg) The column the attacker charges *into* is the one that moves the figure, and that is the fact the bot could not previously see: `ChargeAttackAction.getDamageTakenBy` reads the target's weight and nothing of ours, so running into an Atlas costs five times what running into a Locust does whoever is doing it. The other half is ours - the same ten points are a tenth of a Locust and a fortieth of an Atlas - and the two together span twenty-six times across four bars that differ in nothing else. It is a share of armour plus structure remaining, which is `incoming_damage`'s denominator and not `expected_damage`'s: this is damage arriving at the machine, and what a machine can put out is not what it can absorb. `attacker_self_damage` beside it prices a different event entirely - the chance of a fall, which carries no points and costs a turn - so a charge is priced by both and counted twice by neither. ## submerged `bounded`. How much of the machine this hex puts under water: legs at depth one, torso at two. **The figure shows** dry ground, one hex of shallow water and one of deep, across a row. ![submerged](features/submerged.svg) Three values and nothing between them, because the rule has three cases and no gradient: dry, legs wet, hull under. Depth one costs the jump jets and reads 0.25; depth two is a different situation entirely and reads 1.00. Nothing here interpolates, and a feature that did would be describing a rule that does not exist. ## target_breach `bounded`. The chance the damage coming in strips one of the target's locations down to bare metal. **The figure shows** one target, with the volley from the firing scene arriving from every range down the column and the board tinted into its bracket regions. ![target_breach](features/target_breach.svg) The only target feature that reads the volley: it is the chance *this damage* opens a location to structure, so it changes with the shot and not only with the target. It reads 0.00 flat before a volley has been built - nothing is known, which is not the same as nothing happening - and that is why the figure varies range rather than varying the target. ## target_current_bv `bounded`. How much of its undamaged battle value this target still has. **The figure shows** our machine looking at four enemies at once, with each one's reading on its own hex. ![target_current_bv](features/target_current_bv.svg) `bounded` where `target_original_bv` is a `rank`, because dividing a machine's current value by its own undamaged value *does* have a domain a rule gives. It is not monotonic over a match: heat and movement move battle value, so a unit can be worth more than it was last round. ## target_described `bounded`. Whether the hit tables describe this target, so the columns that come off its locations can be told from the ones that are simply absent. **The figure shows** four machines MegaMek fields, and whether the hit tables in `hitloc` describe any of them. Nothing is fired: the reading is a property of the target. ![target_described](features/target_described.svg) One column in the basis that is about the other columns. It is not "is this a Mek": the Atlas and the Manticore both read 1.00, because `hitloc` has a body plan with its own tables for each, and `value_destroyed`, `p_breach` and `at_stake` mean what they say against a tank. The quadruped and the battle armour read 0.00, and those columns read zero as well - not because the volley achieves nothing but because no plan here rolls for that body, and most of them have no gate to say so. The quadruped is the one worth looking at twice: it is a `Mek` with eight locations and it still fails, because four of its names are legs the biped table has never heard of. `expected_criticals` is the one this column does not explain - it reads zero against the Manticore too, because a vehicle is described by a hit table and still has no critical model. What this buys a fit is a constant offset on those rows, not a different slope - better bookkeeping, and not a model of what a vehicle is worth. ## target_gunnery `bounded`. How well the target shoots, on the game's zero-to-eight gunnery scale, inverted so a better gunner reads higher. **The figure shows** our machine looking at four enemies at once, with each one's reading on its own hex. ![target_gunnery](features/target_gunnery.svg) Inverted on purpose, so every feature in the basis points the same way: a bigger number is a more dangerous enemy, so the gunnery-3 pilot reads highest. Without the inversion a fit would have to learn a negative weight to say the obvious thing, and a sign that has to be learned is a sign that can come out wrong. ## target_health `bounded`. How much armour and structure the target has left, against what it started with. **The figure shows** our machine looking at one chassis at three states of repair. ![target_health](features/target_health.svg) Armour and structure are one number here, not two. The weight on this feature is negative in the hand-authored set - a low reading is a target closer to being removed - so the darkest hex on this figure is the one worth shooting least. ## target_original_bv `rank`. How valuable this target was undamaged, ranked against the other enemies on the board. **The figure shows** our machine looking at four enemies at once, with each one's reading on its own hex. ![target_original_bv](features/target_original_bv.svg) The one target-worth feature still a `rank`, and deliberately a separate column from `target_tonnage`: battle value prices the pilot and the guns, and tonnage does not. E3 is lighter than E2 and worth less; a well-piloted light can outrank a heavy, and only this feature would see it. Undamaged value, so it does not move as the match goes on. ## target_piloting `bounded`. How well the target keeps its feet, on the game's zero-to-eight piloting scale, inverted so a better pilot reads higher. **The figure shows** our machine looking at four enemies at once, with each one's reading on its own hex. ![target_piloting](features/target_piloting.svg) The same scale and the same inversion as `target_gunnery`, read off the other half of the crew. E4 shoots worst of the four and pilots best, so the two figures disagree about it - which is why one column could not carry both. Piloting is what a PSR is rolled against, so a low reading here marks a machine worth knocking down rather than one worth trading fire with. ## target_tonnage `bounded`. How heavy this target is, against the two hundred tons of the heaviest ground unit MegaMek fights with. **The figure shows** our machine looking at four enemies at once, with each one's reading on its own hex. ![target_tonnage](features/target_tonnage.svg) Tons over the two hundred of the heaviest ground unit MegaMek fights with, so the number means tonnage and not position: the 75-tonner reads 0.38 and the 20-tonner 0.10 whoever else is on the board. It was a `rank`, which said only which machine was heavier - and moved when a heavier enemy died, without anything about this one changing. Melee, charge and death-from-above damage all scale with tonnage directly, and a rank had none of that in it. Four ordinary Meks sit in the bottom quarter of the scale on purpose: halving every reading is undone by a fitted weight twice the size, where a ceiling of 100 would clamp a superheavy onto an Atlas and no weight could undo that. ## troopers_removed `bounded`. Share of a conventional platoon's remaining troopers this volley is expected to remove. **The figure shows** one volley from one gun at a 28-strong platoon, with the gun, the ground and the platoon changed one at a time. ![troopers_removed](features/troopers_removed.svg) Not damage, and not monotone in it. The Flamer carries two points and the autocannon twenty, and the Flamer is worth seven times as much - enough to expect the whole platoon, which is why the first card saturates. MegaMek converts a weapon's damage into troopers by a table keyed on the weapon's infantry damage class, and for the burst classes the damage figure is not read at all: a Flamer is 4d6 men whatever it says on the sheet. Priced off `expected_damage` the bot fires the autocannon, and `value_destroyed` cannot correct it - that column reads a flat 0.00 against every platoon on the board, because a platoon has no body plan for it to sum over. The ground halves the Flamer: a hex with woods in it stops the doubling every volley against a platoon in the open gets. Riding costs it three quarters, because the burst classes are halved against mechanised infantry and the armour kit then halves what is left. The autocannon does not move at all between the first card and the last, because those same two steps *double* the direct-fire classes and then halve them back - so the gap closes from sevenfold to under two, and a bot that could not read this column would have no way to notice either fact. ## value_destroyed `bounded`. How close this volley comes to killing the target, as a share of what it is worth. **The figure shows** one machine of ours standing still with a Gauss rifle and an SRM-6, and a column of hexes an enemy could be standing in, with the board tinted into the regions where both guns are doing one thing throughout. ![value_destroyed](features/value_destroyed.svg) How close the volley comes to killing this target, as a share of the battle value it is worth. A location whose loss ends the machine is worth the whole machine, so the column climbs where a head or centre torso is in reach and reads what a limb was carrying elsewhere. Scale-free by construction: finishing a light reads the same as finishing an assault, and how much a volley takes off the board is a separate column.