Something went wrong. Try again.
bayes for days
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120"""What one step costs each mover, read off the probe.
`sds step-cost` walks every step of the four pathfind boards with each specimenrated past anything they can charge - so nothing is refused for cost and whatcomes back is the price - and tabulates `MovePath`'s own figure against thedestination's terrain column, its depth, and the level the mover's feet changeby. These tests read that table rather than a rulebook.
It answered two questions the epic had been carrying as open:
- **A Mek pays one point a level and everything else pays two**, climbing or dropping alike. Not the hovercraft rule it was taken for: tracked, hover and both kinds of infantry all pay two, and the Mek is the exception. That was 36 of the 44 routes `routes-check` refused.- **Infantry pay the terrain instead of the step.** Light woods costs a platoon 1 where it costs a Mek 2."""
import reimport unittestfrom pathlib import Path
TABLE = Path(__file__).resolve().parent.parent / "scenarios" / "move" / "stepcost.txt"
def rows() -> list[dict]: out = [] for line in TABLE.read_text().splitlines(): if not line.startswith("cost "): continue row = dict(re.findall(r"(\w+)=(\S+)", line)) for key in ("terrain", "depth", "dlevel", "predicted", "steps"): row[key] = int(row[key]) row["megamek"] = [int(n) for n in re.findall(r"\d+", row["megamek"])] row["verdict"] = line.split()[-1] out.append(row) return out
def totals() -> tuple[int, int, int]: for line in TABLE.read_text().splitlines(): found = re.match(r"stepcost total: (\d+) rows, (\d+) ambiguous, (\d+) mismatched", line) if found: return tuple(int(found.group(n)) for n in (1, 2, 3)) raise AssertionError(f"{TABLE} has no total line; run `sds step-cost`")
class TestStepCost(unittest.TestCase): def setUp(self) -> None: self.rows = rows() self.assertTrue(self.rows, f"{TABLE} has no rows; run `sds step-cost`")
def cost(self, mode: str, terrain: int, depth: int, dlevel: int) -> int: for row in self.rows: if ( row["mode"] == mode and row["terrain"] == terrain and row["depth"] == depth and row["dlevel"] == dlevel ): self.assertEqual(len(row["megamek"]), 1, row) return row["megamek"][0] raise AssertionError(f"no {mode} row for terrain={terrain} depth={depth} dlevel={dlevel}")
def test_terrain_depth_and_level_determine_the_price(self) -> None: """No row carries two prices.
This is the claim that makes the rest of the table mean anything: if these three numbers did not settle the cost, a model built on them could only ever be an average of something else. """ _, ambiguous, _ = totals() self.assertEqual(ambiguous, 0, "a key that does not determine the price")
def test_a_mek_pays_one_a_level_and_everything_else_two(self) -> None: """The rule that was 36 of the 44 refusals, and it is not hover-only.""" for mode in ("TRACKED", "HOVER", "INF_LEG", "INF_JUMP"): flat = self.cost(mode, 0, 0, 0) self.assertEqual(self.cost(mode, 0, 0, +1) - flat, 2, f"{mode} climbing one level") self.assertEqual(self.cost(mode, 0, 0, -1) - flat, 2, f"{mode} dropping one level") flat = self.cost("BIPED", 0, 0, 0) self.assertEqual(self.cost("BIPED", 0, 0, +1) - flat, 1, "a Mek climbing") self.assertEqual(self.cost("BIPED", 0, 0, -1) - flat, 1, "a Mek dropping")
def test_infantry_pay_the_terrain_and_not_the_step(self) -> None: """Light woods costs a platoon what it costs, not a point more.""" for mode in ("INF_LEG", "INF_JUMP"): self.assertEqual(self.cost(mode, 0, 0, 0), 1, f"{mode} on clear ground") self.assertEqual(self.cost(mode, 1, 0, 0), 1, f"{mode} in light woods") self.assertEqual(self.cost(mode, 2, 0, 0), 2, f"{mode} in heavy woods") # The Mek, for contrast: a point for the step and the terrain on top. self.assertEqual(self.cost("BIPED", 1, 0, 0), 2) self.assertEqual(self.cost("BIPED", 2, 0, 0), 3)
def test_a_hovercraft_pays_nothing_for_depth(self) -> None: """It is on the surface of a pond, not on the bottom of it.""" self.assertEqual(self.cost("HOVER", 0, 3, 0), 1, "three deep, and still a point") self.assertEqual(self.cost("BIPED", 0, 3, 0), 4, "a Mek wades it")
def test_what_the_model_still_misses_is_written_down(self) -> None: """One corner, and it is named rather than rounded off.
A Mek entering water exactly two deep is charged one more than the model predicts - 18 steps of the 4,500-odd it takes on these boards. Depth 1 and depth 3 both fit, which is what makes it a corner rather than a term. It is *under*-predicted, which is the direction that can send the server a move it refuses, so it is pinned here to stay small rather than left to grow. """ rows_, _, mismatched = totals() self.assertEqual(mismatched, 2, "the set of rows the model misses has moved") missed = [row for row in self.rows if row["verdict"] == "MISMATCH"] self.assertTrue(all(row["mode"] == "BIPED" for row in missed), missed) self.assertTrue(all(row["depth"] == 2 for row in missed), missed) self.assertLess(sum(row["steps"] for row in missed), 50, missed)
if __name__ == "__main__": unittest.main()