fix(map): load_map rejects two deployment sides bound to the same zone letter (build_states resets its placed set per side, so shared letters deployed camps on top of each other) - fail-fast MapValidationError + README rule, reject/accept tests

This commit is contained in:
2026-09-19 17:12:42 +02:00
parent 2ee19d34e4
commit ba5eb4c95f
3 changed files with 28 additions and 0 deletions
+3
View File
@@ -136,6 +136,9 @@ Règles de construction d'une rencontre :
- Les combattants d'un camp remplissent les cases de leur zone dans l'ordre - Les combattants d'un camp remplissent les cases de leur zone dans l'ordre
(ligne, colonne). Une zone trop petite pour le nombre de combattants est une (ligne, colonne). Une zone trop petite pour le nombre de combattants est une
erreur. erreur.
- Chaque camp du déploiement doit être lié à une lettre de zone **distincte** :
deux camps partageant la même lettre se déploieraient l'un sur l'autre, ce
qui est une erreur de carte (`share zone letters`).
## Interpréter le rapport ## Interpréter le rapport
+8
View File
@@ -140,6 +140,14 @@ def _check_deployment(
if missing: if missing:
msg = f"deployment zones absent from zones layer: {missing}" msg = f"deployment zones absent from zones layer: {missing}"
raise MapValidationError(path, msg) raise MapValidationError(path, msg)
letters = list(deployment.values())
shared = sorted({letter for letter in letters if letters.count(letter) > 1})
if shared:
msg = (
"deployment sides share zone letters (each side needs its own "
f"distinct zone, or they deploy on top of each other): {shared}"
)
raise MapValidationError(path, msg)
def load_map(path: Path) -> MapSpec: def load_map(path: Path) -> MapSpec:
+17
View File
@@ -109,3 +109,20 @@ def test_rejects_deployment_without_zone(tmp_path: Path) -> None:
body = _minimal_map() + "zones: |\n ...\n .A.\n ...\ndeployment:\n players: B\n" body = _minimal_map() + "zones: |\n ...\n .A.\n ...\ndeployment:\n players: B\n"
with pytest.raises(MapValidationError): with pytest.raises(MapValidationError):
load_map(_write(tmp_path, body)) load_map(_write(tmp_path, body))
def test_rejects_shared_deployment_zone_letter(tmp_path: Path) -> None:
"""Two sides bound to the same zone letter would deploy on top of each
other (build_states resets its placed set per side): a data error."""
body = _minimal_map() + (
"zones: |\n ...\n .A.\n ...\ndeployment:\n players: A\n monsters: A\n"
)
with pytest.raises(MapValidationError, match="share zone letters"):
load_map(_write(tmp_path, body))
def test_accepts_distinct_deployment_zone_letters(tmp_path: Path) -> None:
body = _minimal_map() + (
"zones: |\n ...\n AB.\n ...\ndeployment:\n players: A\n monsters: B\n"
)
assert load_map(_write(tmp_path, body)).deployment == {"players": "A", "monsters": "B"}