Record the decoy base arm in the blind key so the mandatory control is computed for any panel, not only the first pack

This commit is contained in:
Claude (backend session) 2026-08-08 22:16:20 +03:00
parent ed9e049248
commit 784e412c24

View file

@ -169,6 +169,7 @@ def build_unit(u: dict) -> tuple[str, dict]:
kinds: dict[str, str] = {a: "боевой" for a in slots}
clean = DECOY_HOOK(slots) if DECOY_HOOK is not None else (slots.get("A") or slots["A_law"])
decoy_from = next((a for a, t in slots.items() if t == clean), "")
planted, n = _plant(clean)
if n >= 2:
slots["CTRLdecoy"], kinds["CTRLdecoy"] = planted, "контроль-декой"
@ -206,8 +207,12 @@ def build_unit(u: dict) -> tuple[str, dict]:
"", "=" * 60, "ИСХОДНИК:", u["source"]]
for n in order:
body += ["", "=" * 60, f"{label[n]}:", slots[n]]
# ⚠ `decoy_from` пишется В КЛЮЧ. Без него контроль декоя считался ПРОТИВ ЖЁСТКО ЗАШИТОГО арма
# `A`, которого у другой панели нет, и обязательный контроль молча не считался вовсе
# (поймано эксп-22 чтением контролей перед боевыми числами).
key = {label[n]: dict(arm=n, kind=kinds[n], uid=u["uid"],
sig=hashlib.sha1(slots[n].encode()).hexdigest()[:12]) # noqa: S324
sig=hashlib.sha1(slots[n].encode()).hexdigest()[:12], # noqa: S324
**({"decoy_from": decoy_from} if n == "CTRLdecoy" else {}))
for n in order}
return "\n".join(body), key
@ -617,8 +622,10 @@ def controls() -> None:
print("голосов нет — сначала --ingest")
return
print("КОНТРОЛИ САМОГО СУДЬИ — читать ДО боевых чисел\n")
dec = [d["A"]["errors"] - d["CTRLdecoy"]["errors"]
for d in data.values() if "CTRLdecoy" in d and "A" in d]
# База декоя берётся ИЗ КЛЮЧА (`decoy_from`), а не из имени арма: панель у каждого пака своя.
dec = [d[d["CTRLdecoy"].get("decoy_from", "A")]["errors"] - d["CTRLdecoy"]["errors"]
for d in data.values()
if "CTRLdecoy" in d and d["CTRLdecoy"].get("decoy_from", "A") in d]
if dec:
print(f" ДЕКОЙ посаженная деградация против чистого: {st.mean(dec):+.2f} "
f"· поймано {sum(1 for x in dec if x < 0)}/{len(dec)} (нужен отрицательный знак)")