Something went wrong. Try again.
bayes for days
Something went wrong. Try again.
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144"""The suite's maps must let the two sides reach each other.
`bridge/sds/Crossing.java` is what enforces this - it runs inside `sds validate`against MegaMek's own `Board`, which is the only thing that knows what a hexholds at match time. This file states the fact that check exists to protect, anddoes it without a JVM: the flood fill below reads the `.board` files directly,the way `bots/hexes.py` reimplements MegaMek's hex geometry outside the JVM.
The fact: of the twelve boards the generator draws from, exactly one -`Map Set 4/16x17 River Delta 1` - has no water-free path between the north andsouth deployment edges. Its two 1v1 scenarios ran to the round limit at 100% BVin every game of the 40-game control, never making contact. Water fraction isnot the test: `Large Lakes 1` is wetter and plays fine."""
import osimport reimport sysimport unittestfrom pathlib import Path
_REPO = Path(__file__).resolve().parent.parentsys.path.insert(0, str(_REPO / "bots"))
from hexes import translated # noqa: E402
from sds.scenario import MAPS # noqa: E402
MM_HOME = Path(os.environ.get("MM_HOME", Path.home() / ".cache" / "mul-build" / "megamek"))BOARDS = MM_HOME / "data" / "boards"
HEX = re.compile(r'^hex (\d+) (-?\d+) "([^"]*)"', re.M)SIZE = re.compile(r"^size (\d+) (\d+)", re.M)
def read_board(path: Path) -> tuple[int, int, dict[tuple[int, int], dict[str, int]]]: """The terrain of every hex, keyed by zero-based (x, y).
MegaMek writes the coordinate as two equal halves, one-based, so `0916` is column 9 row 16 and a board wider than 99 uses three digits a side. """ text = path.read_text(errors="replace") width, height = (int(n) for n in SIZE.search(text).groups()) hexes = {} for coord, _level, terrain in HEX.findall(text): half = len(coord) // 2 key = (int(coord[:half]) - 1, int(coord[half:]) - 1) parsed = {} for entry in terrain.split(";"): name, _, level = entry.partition(":") if level.lstrip("-").isdigit(): parsed[name] = int(level) hexes[key] = parsed return width, height, hexes
def connects(open_hexes: set, start: set, goal: set) -> bool: """Flood fill over MegaMek's adjacency: is any goal hex reachable?""" seen = set(start) & open_hexes stack = list(seen) while stack: here = stack.pop() if here in goal: return True for direction in range(6): nxt = translated(here[0], here[1], direction) if nxt in open_hexes and nxt not in seen: seen.add(nxt) stack.append(nxt) return False
class TestConnects(unittest.TestCase): """The search, on a grid that needs no MegaMek."""
WIDTH, HEIGHT = 8, 8
def grid(self, blocked: set) -> set: return { (x, y) for x in range(self.WIDTH) for y in range(self.HEIGHT) if (x, y) not in blocked }
def edges(self) -> tuple[set, set]: top = {(x, 0) for x in range(self.WIDTH)} bottom = {(x, self.HEIGHT - 1) for x in range(self.WIDTH)} return top, bottom
def test_open_grid_connects(self): top, bottom = self.edges() self.assertTrue(connects(self.grid(set()), top, bottom))
def test_full_band_blocks(self): top, bottom = self.edges() band = {(x, 4) for x in range(self.WIDTH)} self.assertFalse(connects(self.grid(band), top, bottom))
def test_one_gap_is_enough(self): """The gap is on an odd column, where a mis-ported parity term fails.""" top, bottom = self.edges() band = {(x, 4) for x in range(self.WIDTH) if x != 3} self.assertTrue(connects(self.grid(band), top, bottom))
def test_a_start_inside_the_wall_is_not_a_start(self): top, bottom = self.edges() band = {(x, 4) for x in range(self.WIDTH)} blocked = band | top self.assertFalse(connects(self.grid(blocked), top, bottom))
class TestSuiteMaps(unittest.TestCase): # A third of the board deep at each edge, which is more generous than any # deployment zone MegaMek hands out for an edge start. A path that has to # squeeze past a wider start zone is not one the check would miss. ZONE = 3
def setUp(self): if not BOARDS.is_dir(): self.skipTest(f"no MegaMek boards under {BOARDS}")
def crossable(self, name: str) -> bool: width, height, hexes = read_board(BOARDS / f"{name}.board") # Any water at all is closed. Depth-1 water is legal, passable terrain # and the map is not broken - but no bot in this harness routes through # it, so a map that requires a crossing measures water aversion rather # than tactics. See Crossing.java for the whole argument. dry = {c for c, terrain in hexes.items() if terrain.get("water", 0) == 0} top = {(x, y) for x in range(width) for y in range(self.ZONE)} bottom = {(x, y) for x in range(width) for y in range(height - self.ZONE, height)} return connects(dry, top, bottom)
def test_every_map_in_the_pool_is_crossable(self): blocked = sorted(name for name in MAPS if not self.crossable(name)) self.assertEqual(blocked, [])
def test_river_delta_is_why_the_pool_excludes_it(self): # Named rather than drawn from MAPS: it was removed from the pool, and # the reason it was removed is the thing worth keeping a test on. self.assertFalse(self.crossable("Map Set 4/16x17 River Delta 1")) self.assertNotIn("Map Set 4/16x17 River Delta 1", MAPS)
if __name__ == "__main__": unittest.main()