# Second organe declaratif separe (REPAIR c.1013 #15175, DM ai-01 `inaxqo`)
#
# REPAIR c.1013 : la fonction `_spec_action` precedente recopait la logique de
# string-match du moteur principal (`'return "C"' in src`, `DefectBot_toy in src`,
# etc.) -- c'etait l'organe declaratif qui re-encodait la meme regle que l'implementation,
# pas un oracle independant. Remplacement par une **table de verite litterale**,
# encodee a la main : 25 entrees (a, b) -> (action_a, action_b, payoff), derivees
# par lecture de la specification documentee en cellule 11 et application mentale
# des regles (PAS par appel des fonctions bot, PAS par string-match du source).
#
# L'independance est desormais **structurelle** : l'oracle est une **table de
# donnees**, pas une fonction logique. Si le moteur boguait (par ex. FairBot copiait
# une mauvaise regex), la table continuerait a rendre le bon verdict car elle ne
# depend d'aucune logique du moteur. La seule dependance est la **constante** de
# payoff (PD : T=5, R=3, P=1, S=0) -- si la constante de payoff change, l'oracle
# change avec, ce qui est la definition d'un oracle coherent.
#
# **Troisieme organe : verification EXTERNE** (Tranche A #15173, c.1109, lane
# myia-po-2026:CoursIA-2) -- la table `EXPECTED_TABLE_V2` ci-dessous reste dans
# le notebook pour reference pedagogique, mais la **verification decisive** est
# delestee a `scripts/notebook_tools/verify_program_games_table.py`, execute dans
# un **process Python separe**. L'independance devient triple : (i) moteur vs
# (ii) oracle in-notebook (table de verite) vs (iii) oracle externe (autre
# process, autre chemin, autre parsing de la sortie de cellule 14).
#
# L'execution du verificateur externe (subprocess) produit un verdict binaire :
# 25/25 agree OU mismatch detaille. Si rc=0, les trois organes sont en accord.
# Si rc != 0, le moteur (organe 1) et un des deux oracles sont en desaccord --
# le mismatch est diagnostique.
PAYOFFS_V2 = {
('C', 'C'): (3, 3),
('C', 'D'): (0, 5),
('D', 'C'): (5, 0),
('D', 'D'): (1, 1),
}
# Table de verite 25 entrees, derivee a la main de la spec cellule 11.
# Format : (bot_a, bot_b) -> (action_a, action_b, payoff).
# Chaque entree documente le raisonnement COURT qui justifie le verdict,
# pour que l'independance soit auditable.
EXPECTED_TABLE_V2 = {
# --- Confrontations CooperateBot_toy ---
# Spec : CooperateBot_toy(_) -> 'C' inconditionnel.
('CooperateBot_toy', 'CooperateBot_toy'): ('C', 'C', (3, 3)), # C/C -> (3,3)
('CooperateBot_toy', 'DefectBot_toy'): ('C', 'D', (0, 5)), # C/D -> (0,5)
('CooperateBot_toy', 'FairBot_toy'): ('C', 'C', (3, 3)), # Fair voit 'return "C"' dans src Coop -> C
('CooperateBot_toy', 'CUPOD_toy'): ('C', 'C', (3, 3)), # CUPOD voit 'return "C"' dans src Coop -> C
('CooperateBot_toy', 'PrudentBot_toy'): ('C', 'C', (3, 3)), # Prudent : pas Defect-Bot pur, voit 'return "C"' -> C
# --- Confrontations DefectBot_toy ---
# Spec : DefectBot_toy(_) -> 'D' inconditionnel.
('DefectBot_toy', 'CooperateBot_toy'): ('D', 'C', (5, 0)), # D/C -> (5,0)
('DefectBot_toy', 'DefectBot_toy'): ('D', 'D', (1, 1)), # D/D -> (1,1)
('DefectBot_toy', 'FairBot_toy'): ('D', 'D', (1, 1)), # Fair voit 'return "D"' dans src Defect -> D
('DefectBot_toy', 'CUPOD_toy'): ('D', 'D', (1, 1)), # CUPOD voit 'return "D"' dans src Defect -> D
('DefectBot_toy', 'PrudentBot_toy'): ('D', 'D', (1, 1)), # Prudent : src contient DefectBot_toy seul -> D
# --- Confrontations FairBot_toy ---
# Spec : FairBot_toy(src) -> 'C' si 'return "C"' dans src, sinon 'D'.
('FairBot_toy', 'CooperateBot_toy'): ('C', 'C', (3, 3)), # Fair voit 'return "C"' dans Coop -> C
('FairBot_toy', 'DefectBot_toy'): ('D', 'D', (1, 1)), # Fair voit 'return "D"' dans Defect -> D
('FairBot_toy', 'FairBot_toy'): ('C', 'C', (3, 3)), # Fair voit son propre 'return "C"' -> C
('FairBot_toy', 'CUPOD_toy'): ('C', 'C', (3, 3)), # Fair voit 'return "C"' dans CUPOD -> C
('FairBot_toy', 'PrudentBot_toy'): ('C', 'C', (3, 3)), # Fair voit 'return "C"' dans Prudent -> C
# --- Confrontations CUPOD_toy ---
# Spec : CUPOD_toy(src) -> 'C' si 'return "C"' dans src, sinon 'D'.
('CUPOD_toy', 'CooperateBot_toy'): ('C', 'C', (3, 3)), # CUPOD voit 'return "C"' dans Coop -> C
('CUPOD_toy', 'DefectBot_toy'): ('D', 'D', (1, 1)), # CUPOD voit 'return "D"' dans Defect -> D
('CUPOD_toy', 'FairBot_toy'): ('C', 'C', (3, 3)), # CUPOD voit 'return "C"' dans Fair -> C
('CUPOD_toy', 'CUPOD_toy'): ('C', 'C', (3, 3)), # CUPOD voit son propre 'return "C"' -> C
('CUPOD_toy', 'PrudentBot_toy'): ('C', 'C', (3, 3)), # CUPOD voit 'return "C"' dans Prudent -> C
# --- Confrontations PrudentBot_toy ---
# Spec : si 'DefectBot_toy' dans src ET pas 'CUPOD_toy'/pas 'FairBot_toy' -> 'D'
# sinon si 'return "C"' dans src -> 'C' sinon 'D'.
('PrudentBot_toy', 'CooperateBot_toy'): ('C', 'C', (3, 3)), # Prudent voit 'return "C"' dans Coop -> C
('PrudentBot_toy', 'DefectBot_toy'): ('D', 'D', (1, 1)), # Prudent : Defect pur -> D ; Defect -> D
('PrudentBot_toy', 'FairBot_toy'): ('C', 'C', (3, 3)), # Prudent voit 'FairBot_toy' -> pas condition D ; voit 'return "C"' -> C
('PrudentBot_toy', 'CUPOD_toy'): ('C', 'C', (3, 3)), # Prudent voit 'CUPOD_toy' -> pas condition D ; voit 'return "C"' -> C
('PrudentBot_toy', 'PrudentBot_toy'): ('C', 'C', (3, 3)), # Prudent voit 'return "C"' dans Prudent -> C
}
def independent_pair_v2(name_a, name_b):
'''Verificateur strictement independant (REPAIR c.1013 #15175).'''
key = (name_a, name_b)
if key not in EXPECTED_TABLE_V2:
raise KeyError(f"cle absente de l'oracle : {key}")
return EXPECTED_TABLE_V2[key]
ok_v2 = 0
for r in rows:
a2, b2, pay2 = independent_pair_v2(r['A'], r['B'])
same = (a2, b2) == tuple(r['act'].split('/')) and pay2 == r['payoff']
assert same, (f"Mismatch independent_v2 sur {r['A']} vs {r['B']}: "
f"moteur={r['act']}, oracle=({a2},{b2},{pay2})")
ok_v2 += 1
assert ok_v2 == 25, f"independent_v2: attendu 25/25, obtenu {ok_v2}/25"
print(f"Matrice reproductible (oracle declaratif v2) : {ok_v2}/25 confrontations agree.")
print("Deux organes de verification : moteur (cellule 18) ET oracle declarative (cette cellule) rendent le meme verdict.")
print(f"Table oracle : {len(EXPECTED_TABLE_V2)} paires, encodees a la main depuis la spec (independance structurelle).")
# Troisieme organe : verificateur externe (Tranche A #15173, c.1109)
# ---------------------------------------------------------------------------
# Le script `scripts/notebook_tools/verify_program_games_table.py` parse la
# sortie de cellule 14 (moteur) en isolation -- un autre process Python, un
# autre chemin de parsing, une autre table de verite (independante des
# constantes ci-dessus, sauf la constante PD canonique T=5,R=3,P=1,S=0).
#
# Si l'execution externe rend rc=0 (25/25 agree), le moteur et les DEUX oracles
# sont en accord. Si rc != 0, le moteur et l'un des oracles sont en desaccord.
import subprocess
import sys as _sys
from pathlib import Path as _Path
def _resolve_paths():
candidates = [_Path.cwd()]
for _ in range(5):
candidates.append(candidates[-1].parent)
for base in candidates:
nb = base / "MyIA.AI.Notebooks" / "GameTheory" / "GameTheory-06e-Open-Source-Game-Theory-Python.ipynb"
ver = base / "scripts" / "notebook_tools" / "verify_program_games_table.py"
if nb.exists() and ver.exists():
return nb, ver
raise FileNotFoundError(
"verifier_introuvable depuis cwd=" + str(_Path.cwd())
)
NOTEBOOK_PATH, VERIFIER = _resolve_paths()
proc = subprocess.run(
[_sys.executable, str(VERIFIER), "--notebook", str(NOTEBOOK_PATH), "--json"],
capture_output=True, text=True,
)
print(f"--- Verificateur externe : rc={proc.returncode} ---")
print(proc.stdout)
if proc.stderr:
print(f"stderr : {proc.stderr}")
assert proc.returncode == 0, (
f"Verificateur externe en desaccord avec le moteur (rc={proc.returncode}). "
f"Voir stdout ci-dessus pour le detail des mismatches."
)
print("Trois organes de verification en accord : moteur + oracle in-notebook + oracle externe.")