Freeze the vendor-portability pre-registration and the panel gate that byte-checks every packet text against its cell
This commit is contained in:
parent
7647579feb
commit
3433a85d1f
2 changed files with 2447 additions and 0 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -44,6 +44,7 @@ from __future__ import annotations
|
|||
|
||||
import importlib.util
|
||||
import json
|
||||
import re
|
||||
import statistics as st
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
|
@ -804,6 +805,166 @@ def score_read() -> None:
|
|||
print(f" контроль шума Z0/ZF на местах {order.index('Z0') + 1} и {order.index('ZF') + 1}")
|
||||
|
||||
|
||||
# ── СВОД ПАНЕЛИ «ОДНА ГЛАВА — ОДИН ПАКЕТ» ────────────────────────────────────────────────────
|
||||
# ⚠ `score_read` выше разбирает ОДИН пакет и один ключ; здесь пакетов 15, ключей 15 и раскладок 15.
|
||||
# Свод обязан (а) отказываться от главы, чей порядок не покрывает панель ровно один раз, вслух, и
|
||||
# (б) печатать контроли рядом со средним местом, а не под ним: без них порядок ничего не значит.
|
||||
# ⚠ РАЗБОР И РАНГИ БЕРУТСЯ У `chtenie` — второй реализации того же не заводится: тамошний `_ranks`
|
||||
# уже несёт правило средних мест для связок, а `orders` — две починки, купленные потерянным ответом
|
||||
# владельца. Здесь своё ТОЛЬКО правило ВЫБОРА цепочки: `orders` берёт первую и нумерует остальные
|
||||
# как разные отрывки, а в пакете «одна глава — один пакет» отрывок один, зато читатель называет
|
||||
# порядок и в заголовке, и внутри разбора («внутри семьи Т8 > Т2 > Т7»). Берём САМУЮ ПОЛНУЮ.
|
||||
def _order_of(txt: str, labels: set) -> str:
|
||||
best, best_n = "", 0
|
||||
for m in CH._ORD.findall(txt): # noqa: SLF001
|
||||
n = len({x for x in re.findall(r"[ТT]\d+", m) if x.replace("T", "Т") in labels})
|
||||
if n > best_n:
|
||||
best, best_n = m, n
|
||||
return best
|
||||
|
||||
|
||||
def _variants(pack: Path) -> dict:
|
||||
"""Тексты пакета по меткам — нужны своду, чтобы контроль тождества стоял на побайтном
|
||||
сравнении, а не на равенстве длин.
|
||||
|
||||
⚠ Разделитель `---`, стоящий ПЕРЕД следующим вариантом, обязан отрезаться: он прилипает к
|
||||
хвосту предыдущего тела и переживает `strip()` (6 знаков). Прежняя редакция его оставляла, и
|
||||
это было невидимо ровно потому, что сравнивались два ТЕЛА — артефакт стоял у обоих. Сверка с
|
||||
клеткой поймала сразу: 84 «расхождения» из 96, все ровно на 6 знаков.
|
||||
"""
|
||||
txt = pack.read_text(encoding="utf-8")
|
||||
parts = re.split(r"(?m)^## Отрывок \d+, вариант (Т\d+)\s*$", txt)
|
||||
return {lab: re.sub(r"(?m)\n-{3,}\s*$", "", body.strip()).strip()
|
||||
for lab, body in zip(parts[1::2], parts[2::2])}
|
||||
|
||||
|
||||
def verify_read(tag_: str, p: str) -> int:
|
||||
"""Сплошная сверка пакетов ПЕРЕД чтением. ⚠ Пункт «тексты РАЗЛИЧНЫ» заведён 21.08 после того,
|
||||
как читатели нашли то, чего не видела моя же «сплошная» сверка: `Z7` был побайтной копией `ZP`
|
||||
на 10 главах из 15 (Д23.6). Прежняя сверка сличала пакет с клетками, а клетки друг с другом —
|
||||
нет, и панель из 11 армов показывала 10 текстов."""
|
||||
global READ_PANEL # noqa: PLW0603
|
||||
keys = sorted((OUT / "blind-keys-rol").glob(f"rol-KEY-{tag_}-*.json"))
|
||||
bad, lays, dupes, ntx = [], set(), [], 0
|
||||
PAIRS[p]["wire"]()
|
||||
by_uid = {u["uid"][:8]: u["uid"] for u in chosen(p)}
|
||||
for kf in keys:
|
||||
uid8 = kf.stem.split(f"{tag_}-")[1]
|
||||
key = json.loads(kf.read_text(encoding="utf-8"))
|
||||
if p not in key:
|
||||
continue
|
||||
lay, pack = key[p]["1"]["метки"], READ_DIR / f"ЧТЕНИЕ-{p}-{tag_}-{uid8}.md"
|
||||
# ⚠ ГЛАВНАЯ проверка: текст под меткой — ТОТ ЖЕ, что в клетке своего арма, побайтно.
|
||||
READ_PANEL = tuple(key[p]["1"]["знаков"])
|
||||
cells = read_texts(p, by_uid[uid8])
|
||||
for lab, arm in lay.items():
|
||||
ntx += 1
|
||||
if _variants(pack).get(lab, "") != cells[arm].strip():
|
||||
bad.append(f"{uid8}: текст метки {lab} не равен клетке арма {arm}")
|
||||
if cells["_src"].strip() not in pack.read_text(encoding="utf-8"):
|
||||
bad.append(f"{uid8}: исходник в пакете не тот, что у главы")
|
||||
var = _variants(pack)
|
||||
txt = pack.read_text(encoding="utf-8")
|
||||
lays.add(tuple(sorted(lay.items())))
|
||||
if set(var) != set(lay):
|
||||
bad.append(f"{uid8}: в пакете {len(var)} вариантов, в ключе {len(lay)}")
|
||||
for arm in key[p]["1"]["знаков"]: # имя арма в пакете = утечка
|
||||
if re.search(rf"\b{arm}\b", txt):
|
||||
bad.append(f"{uid8}: имя арма {arm} стоит в пакете")
|
||||
if "торопиться не надо" not in txt:
|
||||
bad.append(f"{uid8}: нет указания владельца «торопиться не надо»")
|
||||
seen: dict = {}
|
||||
for lab, body in var.items():
|
||||
if body in seen:
|
||||
dupes.append((uid8, lay[seen[body]], lay[lab]))
|
||||
seen[body] = lab
|
||||
print(f"\n=== СВЕРКА {tag_}, пара {p} ===")
|
||||
print(f" текстов сверено побайтно с клетками: {ntx}")
|
||||
print(f" пакетов {len(keys)} · РАЗНЫХ раскладок {len(lays)}"
|
||||
f"{'' if len(lays) == len(keys) else ' ⛔ раскладки повторяются'}")
|
||||
if dupes:
|
||||
print(f" ⚠ ПОБАЙТНО СОВПАДАЮЩИЕ армы в {len(dupes)} пакетах:")
|
||||
for uid8, a, b in dupes[:6]:
|
||||
print(f" {uid8}: {a} ≡ {b}")
|
||||
print(" (не обязательно дефект — но панель показывает читателю МЕНЬШЕ текстов, "
|
||||
"чем армов, и вывод по такому арму законен только там, где он отличается)")
|
||||
for x in bad:
|
||||
print(f" ⛔ {x}")
|
||||
print(f" {'СБОРКА ГОДНА' if not bad else f'ПРОВАЛОВ {len(bad)}'}")
|
||||
return 1 if bad else 0
|
||||
|
||||
|
||||
def score_arch(tag_: str = "arch2", p: str = "en") -> int:
|
||||
keys = sorted((OUT / "blind-keys-rol").glob(f"rol-KEY-{tag_}-*.json"))
|
||||
if not keys:
|
||||
raise SystemExit(f"⛔ ключей тега {tag_} нет")
|
||||
ranks: dict = {}
|
||||
twins, decoy, ident, bad = [], [], [], []
|
||||
for kf in keys:
|
||||
uid8 = kf.stem.split(f"{tag_}-")[1]
|
||||
key = json.loads(kf.read_text(encoding="utf-8"))
|
||||
if p not in key: # ключ другой пары под тем же тегом — не наш
|
||||
continue
|
||||
lay = key[p]["1"]["метки"]
|
||||
af = READ_DIR / f"ОТВЕТ-{p}-{tag_}-{uid8}.md"
|
||||
if not af.exists():
|
||||
bad.append((uid8, "ответа нет"))
|
||||
continue
|
||||
txt = af.read_text(encoding="utf-8", errors="replace")
|
||||
places = CH._ranks(_order_of(txt, set(lay))) # noqa: SLF001
|
||||
if sorted(places) != sorted(lay):
|
||||
bad.append((uid8, f"порядок покрывает {len(places)} из {len(lay)} меток"))
|
||||
continue
|
||||
r = {lay[lab]: pos for lab, pos in places.items()}
|
||||
for arm, v in r.items():
|
||||
ranks.setdefault(arm, []).append(v)
|
||||
twins.append(abs(r["Z0"] - r["ZF"]))
|
||||
decoy.append(r["ZD"])
|
||||
# ⚠ КОНТРОЛЬ ТОЖДЕСТВА, найденный чтением: на главах без канон-щелей `Z7` — побайтная копия
|
||||
# `ZP` (фиксер не звался). Читатель, разводящий два ОДИНАКОВЫХ текста, не имеет разрешения.
|
||||
var = _variants(READ_DIR / f"ЧТЕНИЕ-{p}-{tag_}-{uid8}.md")
|
||||
rev = {v: k for k, v in lay.items()}
|
||||
if var.get(rev["Z7"]) == var.get(rev["ZP"]):
|
||||
ident.append((uid8, abs(r["Z7"] - r["ZP"])))
|
||||
n = len(twins)
|
||||
print(f"\n=== СВОД {tag_}, пара {p}: глав отсужено {n} из {len(keys)} ===")
|
||||
for uid8, why in bad:
|
||||
print(f" ⚠ {uid8}: {why}")
|
||||
if not n:
|
||||
return 1
|
||||
print(f"\n{'арм':5s} {'ср. место':>10s} {'глав':>5s} места")
|
||||
for arm, v in sorted(ranks.items(), key=lambda x: sum(x[1]) / len(x[1])):
|
||||
print(f"{arm:5s} {sum(v) / len(v):10.2f} {len(v):5d} "
|
||||
f"{' '.join(f'{x:g}' for x in v)}")
|
||||
print(f"\nКОНТРОЛЬ ШУМА |Z0−ZF| среднее {sum(twins) / n:.2f} из {len(ranks)} мест "
|
||||
f"· помест-но {' '.join(f'{x:g}' for x in twins)}")
|
||||
print(f"КОНТРОЛЬ ДЕКОЯ ZD среднее место {sum(decoy) / n:.2f} "
|
||||
f"(обязан быть внизу; {sum(1 for x in decoy if x > len(ranks) / 2)} из {n} в нижней половине)")
|
||||
# ⚠ КОНТРАСТЫ ПАРНЫЕ И МЕРИЛО У НИХ — БЛИЗНЕЦЫ, А НЕ НОЛЬ. Два прогона ОДНОГО способа расходятся
|
||||
# по главам; значит «арм A выше арма B» что-то значит, только если разрыв больше того, что тот
|
||||
# же прибор показывает между `Z0` и `ZF`. Точный знаковый тест берётся у `zsud`, своего не пишем.
|
||||
ZS = _load("zsud_r", ZONE / "zsud.py")
|
||||
tw = [a - b for a, b in zip(ranks["Z0"], ranks["ZF"])]
|
||||
print(f"\nПАРНЫЕ КОНТРАСТЫ (n={n}). Собственный пол: близнецы Z0/ZF расходятся на "
|
||||
f"{st.mean(tw):+.2f} места при пороге {2.8 * st.pstdev(tw) / n ** 0.5:.2f} — "
|
||||
f"{'НЕразличимы, как и обязаны' if abs(st.mean(tw)) <= 2.8 * st.pstdev(tw) / n ** 0.5 else '⛔ РАЗЛИЧИМЫ, пол негоден'}")
|
||||
base = [(a + b) / 2 for a, b in zip(ranks["Z0"], ranks["ZF"])]
|
||||
for arm in sorted(ranks, key=lambda x: sum(ranks[x]) / n):
|
||||
if arm in ("Z0", "ZF"):
|
||||
continue
|
||||
d = [x - y for x, y in zip(ranks[arm], base)] # >0 — арм ХУЖЕ связки
|
||||
mde = 2.8 * st.pstdev(d) / n ** 0.5 # форма порога из пре-рега Д23.3
|
||||
print(f" связка ↔ {arm:4s} разрыв {st.mean(d):+5.2f} места · порог {mde:4.2f} · "
|
||||
f"{'РАЗЛИЧИМ ' if abs(st.mean(d)) > mde else 'в пределах'} · "
|
||||
f"знаковый p={ZS._sign_p(d):.4f}") # noqa: SLF001
|
||||
if ident:
|
||||
worst = max(x for _u, x in ident)
|
||||
print(f"КОНТРОЛЬ ТОЖДЕСТВА Z7≡ZP на {len(ident)} главах · развод мест: "
|
||||
f"среднее {sum(x for _u, x in ident) / len(ident):.2f}, худший {worst:g}"
|
||||
f"{' ⛔ читатель развёл ОДИНАКОВЫЕ тексты' if worst else ' ✔ связаны знаком ='}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = sys.argv[1:] or ["--dry"]
|
||||
if a[0] == "--selftest":
|
||||
|
|
@ -843,6 +1004,12 @@ if __name__ == "__main__":
|
|||
print(f" {uid}: {why}")
|
||||
elif a[0] == "--score-read":
|
||||
score_read()
|
||||
elif a[0] == "--verify-read":
|
||||
_tag = next((x.split("=", 1)[1] for x in a if x.startswith("--tag=")), "arch2")
|
||||
sys.exit(verify_read(_tag, next((x for x in a[1:] if x in PAIRS), "en")))
|
||||
elif a[0] == "--score-arch":
|
||||
_tag = next((x.split("=", 1)[1] for x in a if x.startswith("--tag=")), "arch2")
|
||||
sys.exit(score_arch(_tag, next((x for x in a[1:] if x in PAIRS), "en")))
|
||||
elif a[0] == "--coverage":
|
||||
for p in ([x for x in a[1:] if x in PAIRS] or list(PAIRS)):
|
||||
print(f"\n=== ПОКРЫТИЕ БОЕВЫХ ПРОМТОВ, пара {p} ===")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue