Versant D4 du chantier #12207. Les notebooks GT-3, GT-3b et GT-3h ont fait de l’espace des jeux 2x2 ordinaux un univers fini manipulable : 576 chambres strictes, six swaps generateurs, des murs ou vivent les egalites. Ce notebook monte l’echelle d’un cran : les swaps y deviennent des actions que l’agent paie.
Plan
Marche 1 – l’agent joue : equilibres de Nash purs et dynamique de meilleure reponse sur les 576 jeux
Marche 2 – l’agent deplace le jeu : meta-actions tarifees, un cout mesure en echelons de rang, seuil de migration
Marche 3 – deux agents en desaccord : le meta-jeu 4x4, ses equilibres, et la question de l’accord
La question
La strate 6 de la theorie des jeux dit ce qu’un agent fait dans des regles donnees. La strate 7 demande ce qui se passe quand modifier les regles est lui-meme une action – avec un cout, donc un arbitrage, donc un equilibre au niveau superieur. Rendu operationnel sur un univers fini, le franchissement devient mesurable : a quel prix un agent accepte-t-il de deplacer son propre jeu, et que devient cet arbitrage quand les deux joueurs le jouent simultanement ?
Fusion (2026-09-19, #16231 chantier C) : ce notebook absorbe l’ex-GameTheory-03f-Parcours-Complet — la partie « parcours complet » (sections 4-8) ci-dessous. L’arc méta-actions (marches 1-3) puis l’intégration (chemin, murs, coûts) se lisent d’un seul tenant.
0. Le substrat, herite de GT-3b
Un jeu = deux tables de rangs, une par joueur, chacune un 4-uplet ordonnant strictement les quatre cases (haut-gauche, haut-droite, bas-gauche, bas-droite) – rang 4 = meilleur. Les swapsR1, R2, R3 (cote Ligne) et C1, C2, C3 (cote Colonne) echangent les cases portant deux rangs adjacents k et k+1 : ce sont les six generateurs de l’espace, chacun traverse un mur du monde de Bruns-Kimmich. Toute la mecanique est reprise tel quel de GT-3b, sans modification – ce notebook est un consommateur du substrat, pas une refonte.
# Substrat GT-3b, repris tel quel -- pur stdlibfrom itertools import productfrom collections import Counter, dequedef swap_val(t, k):"""Echange les cases portant les rangs k et k+1 (traversee de la facette k<->k+1).""" pk, pk1 = t.index(k), t.index(k +1) l =list(t); l[pk], l[pk1] = l[pk1], l[pk];returntuple(l)chambres =sorted({tuple(p) for p in product(range(1, 5), repeat=4) iflen(set(p)) ==4})jeux = [(r, c) for r in chambres for c in chambres]print("Chambres strictes par joueur :", len(chambres), "| jeux 2x2 stricts :", len(jeux))print("Generateurs : R1 R2 R3 (Ligne) x C1 C2 C3 (Colonne)")# Le Dilemme du Prisonnier, encodage GT-21 / GT-3bPD = ((3, 1, 4, 2), (3, 4, 1, 2))print("PD : Ligne", PD[0], "Colonne", PD[1], "(rang 4 = meilleur, cases TG TD BG BD)")
Avant de changer les regles, il faut les subir. Pour chaque jeu, deux instruments :
les equilibres de Nash purs : cases ou personne ne prefere devier unilateralement ;
la dynamique de meilleure reponse depuis la case haut-gauche : Ligne ajuste, puis Colonne, et on repete – c’est la facon la plus simple dont des agents “jouent” quand personne ne calcule d’equilibre.
Sur des rangs ordinaux, l’equilibre mixte n’a pas de sens (on ne peut pas moyenner des rangs) : un jeu sans equilibre pur est ici injouable – la dynamique cycle sans fin. C’est un fait, pas une anomalie a corriger.
# Marche 1 : NE purs + dynamique BR depuis la case haut-gauchedef ne_purs(row, col): nes = []for r in (0, 1):for c in (0, 1):if row[2*r+c] > row[2*(1-r)+c] and col[2*r+c] > col[2*r+(1-c)]: nes.append((r, c))return nesdef br_dyn(row, col, start=(0, 0), max_steps=16):"""BR alterne (Ligne d'abord). ('cycle'|'ok', case finale).""" r, c = startfor _ inrange(max_steps): moved =False nr = r if row[2*r+c] > row[2*(1-r)+c] else (1- r)if nr != r: r, moved = nr, True nc = c if col[2*r+c] > col[2*r+(1-c)] else (1- c)if nc != c: c, moved = nc, Trueifnot moved: return ('ok', (r, c))return ('cycle', (r, c))dist_ne = Counter(len(ne_purs(r, c)) for r, c in jeux)stats = Counter(br_dyn(r, c)[0] for r, c in jeux)print("NE purs par jeu :", dict(sorted(dist_ne.items())), "sur", len(jeux))print("Dynamique BR depuis TG : converge", stats['ok'], "| cycle", stats['cycle'])croisement = Counter((len(ne_purs(r, c)), br_dyn(r, c)[0]) for r, c in jeux)print("Croisement (#NE, BR) :", dict(sorted(croisement.items())))print()print("PD : NE purs =", ne_purs(*PD), "-> (bas,droite) = defection mutuelle, rang", PD[0][3], "chacun")
NE purs par jeu : {0: 72, 1: 432, 2: 72} sur 576
Dynamique BR depuis TG : converge 504 | cycle 72
Croisement (#NE, BR) : {(0, 'cycle'): 72, (1, 'ok'): 432, (2, 'ok'): 72}
PD : NE purs = [(1, 1)] -> (bas,droite) = defection mutuelle, rang 2 chacun
Lecture de la marche 1
La distribution est remarquablement symetrique : 72 jeux sans equilibre pur, 432 avec un seul, 72 avec deux (les coordinations). Et le croisement est exact : la dynamique converge si et seulement si un equilibre pur existe – les 72 cycles sont precisement les 72 jeux sans equilibre. Aucun jeu a deux equilibres ne pose ici de probleme de selection dynamique : parti de haut-gauche, le jeu se stabilise toujours quelque part.
Le Dilemme du Prisonnier fait ce qu’on attend de lui : equilibre unique a la defection mutuelle, rang 2 chacun – le rang 4 de la cooperation existe mais n’est pas stable. Retenons ce chiffre, 2 : c’est ce que vaut le Dilemme pour celui qui y est piege.
# Le fait brut : exhiber un jeu injouable (cycle BR complet)ex_injouable = ((2, 3, 1, 4), (1, 2, 4, 3))row, col = ex_injouableprint("Jeu sans NE pur : Ligne", row, "Colonne", col)NOMS_CASES = ("TG", "TD", "BG", "BD")r, c =0, 0trace = [(r, c)]for _ inrange(6): nr = r if row[2*r+c] > row[2*(1-r)+c] else (1- r)if nr != r: r = nr; trace.append((r, c)) nc = c if col[2*r+c] > col[2*r+(1-c)] else (1- c)if nc != c: c = nc; trace.append((r, c))print("Trajectoire BR depuis TG :", " -> ".join(NOMS_CASES[2*tr+tc] for tr, tc in trace[:6]) +" -> ... (cycle)")print("Boucle :", " -> ".join(NOMS_CASES[2*tr+tc] for tr, tc in trace[1:5]) +" -> ... repetee indefiniment")# Convention de la suite : un jeu injouable vaut 0 pour chacun (pire que tout rang jouable)def payoff(row, col, cote): st, (r, c) = br_dyn(row, col)if st =='cycle': return0return row[2*r+c] if cote ==0else col[2*r+c]print()print("Convention : gain au sorti de BR ; jeu injouable -> 0 pour chacun (pire que tout rang >= 1)")
Jeu sans NE pur : Ligne (2, 3, 1, 4) Colonne (1, 2, 4, 3)
Trajectoire BR depuis TG : TG -> TD -> BD -> BG -> TG -> TD -> ... (cycle)
Boucle : TD -> BD -> BG -> TG -> ... repetee indefiniment
Convention : gain au sorti de BR ; jeu injouable -> 0 pour chacun (pire que tout rang >= 1)
2. Marche 2 – l’agent deplace le jeu, et paie
L’agent Ligne recoit desormais des meta-actions : appliquer l’un de ses trois swaps R1, R2, R3 – reecrire ses propres preference declarees – puis jouer le jeu deplace. Chaque swap coute c echelons de rang. Cette unite n’est pas un artifice : payer 1 signifie renoncer a un echelon de preference pour l’atteindre, ce qui rend la soustraction rang final - cout honnetement interpretable dans un monde purement ordinal.
Ligne arbitre : rester et toucher son rang courant, ou migrer vers une table distante de d swaps et toucher rang(final) - c*d. Comme l’espace est fini, l’optimum se calcule par parcours exhaustif – BFS depuis sa propre table (le graphe de Cayley des 24 tables, diametre 6, deja mesure par GT-3b).
# Marche 2 : optimum de migration par jeu, puis sweep du coutdef bfs_from(t0): d = {t0: 0}; q = deque([t0])while q: u = q.popleft()for k in (1, 2, 3): v = swap_val(u, k)if v notin d: d[v] = d[u] +1; q.append(v)return dbest_by_dist = {}for row_t, col_t in jeux: m = {}for t2, d in bfs_from(row_t).items(): p = payoff(t2, col_t, 0) m[d] =max(m.get(d, -1), p) best_by_dist[(row_t, col_t)] = m # m[0] = rang courant, m[d] = meilleur rang atteignable a distance dprint("c | %migrer | dist.opt moy | gain net moy (migrants) | fuient un cycle")print("-"*72)for c in (0, 1, 2, 3): n_mig = tot_d = tot_gain = fuit =0for (r_t, c_t), m in best_by_dist.items(): best =max(m[d] - c * d for d in m) bd =min(d for d in m if m[d] - c * d == best)if bd >0and best > m[0]: # migrer ssi strictement meilleur que rester n_mig +=1; tot_d += bd; tot_gain += best - m[0]if payoff(r_t, c_t, 0) ==0: fuit +=1 n =len(jeux)print(f"{c} | {100*n_mig//n:3d}% | {tot_d/max(n_mig,1):.2f} | {tot_gain/max(n_mig,1):+.2f} | {fuit}")
Lecture du sweep : un seuil monotone, une distance qui se contracte
Quatre faits mesures :
La migration s’effondre avec le cout : 56 % des jeux voient Ligne migrer a cout nul, 16 % a cout 1, 8 % a cout 2, 4 % a cout 3. A un echelon par swap, six jeux sur sept restent – le privilege de reecrire ses preferences est cher.
La distance optimale se contracte de 1,33 vers 1,00 : plus le km de swap est cher, plus on ne paie que le deplacement minimal – a cout eleve, seuls les gains de proximite immediate survivent.
Le gain net moyen des migrants monte puis redescend (+1,93 a c=0, +2,00 a c=1, +1,50 a c=2, +1,00 a c=3) : a c=0 partent aussi les migrations gadgets ; a c=1 ne restent que les migrations de fond ; au-dela, le cout ronge le gain.
Les 72 jeux injouables se vident a leur rythme : meme a cout 1, les 72 fuient tous (quitter un gain nul vaut n’importe quel prix raisonnable) ; a cout 2 il en reste 48, a cout 3 seulement 24 – les autres preferent le cycle gratuit au deplacement trop cher. Meme l’injouable a un prix au-dessus duquel on le tolere.
# Le Dilemme sous les meta-actions : que peut acheter Ligne ?m_pd = best_by_dist[PD]print("PD : rang courant de Ligne (defection mutuelle) =", m_pd[0])print("Meilleur rang atteignable par distance :")for d insorted(m_pd):print(f" a distance {d} : rang {m_pd[d]}"+ (f" -> net a c=1 : {m_pd[d]-d}"if d >0else" (rester)"))print()for c in (0, 1, 2): best =max(m_pd[d] - c * d for d in m_pd) bd =min(d for d in m_pd if m_pd[d] - c * d == best) verdict ="RESTE"if bd ==0elsef"MIGRE a distance {bd}"print(f"c = {c} : net optimal {best} (courant {m_pd[0]}) -> Ligne {verdict}")
PD : rang courant de Ligne (defection mutuelle) = 2
Meilleur rang atteignable par distance :
a distance 0 : rang 2 (rester)
a distance 1 : rang 3 -> net a c=1 : 2
a distance 2 : rang 4 -> net a c=1 : 2
a distance 3 : rang 4 -> net a c=1 : 1
a distance 4 : rang 4 -> net a c=1 : 0
a distance 5 : rang 4 -> net a c=1 : -1
a distance 6 : rang 4 -> net a c=1 : -2
c = 0 : net optimal 4 (courant 2) -> Ligne MIGRE a distance 2
c = 1 : net optimal 2 (courant 2) -> Ligne RESTE
c = 2 : net optimal 2 (courant 2) -> Ligne RESTE
Lecture : le Dilemme exactement indifferent
A cout nul, Ligne quitte le Dilemme : deux swaps l’amennent a un jeu ou sa table lui donne le rang 4, net +2. Mais a cout 1, l’indifference est exacte : le rang 3 a distance 1 et le rang 4 a distance 2 donnent tous deux un net de 2 – strictement egal a rester. Le gain de fuite du Dilemme est entierement mange par le cout du deplacement. Ce n’est pas un accident d’arrondi : sur l’echelle des rangs, la fuite solitaire du Dilemme coute precisement ce qu’elle rapporte. Le piege de la strate 6 a une propriete economique de plus : il est juste assez solide pour retenir un agent solitaire qui doit payer son deplacement – ce qui rend d’autant plus interessante la question des deux agents, en marche 3.
# Les migrants a cout 1 : qui part, et la fuite des injouablesmig = []for (r_t, c_t), m in best_by_dist.items(): best =max(m[d] - d for d in m) bd =min(d for d in m if m[d] - d == best)if bd >0and best > m[0]: mig.append((r_t, c_t, bd, m[0], m[bd]))print("Migrants a c=1 :", len(mig), "jeux sur", len(jeux), "dont", sum(1for x in mig if x[3] ==0), "refugies (partent d'un rang nul)")opp = [x for x in mig if x[3] >0]r_t, c_t, bd, p0, p1 = opp[0]t_cible = [t for t, d in bfs_from(r_t).items() if d == bd and payoff(t, c_t, 0) == p1][0]print("Opportuniste : jeu", (r_t, c_t), "| rang", p0, "-> rang", p1, "en", bd, "swap (table cible", t_cible, ")")inj = [g for g in jeux ifnot ne_purs(*g)]rep1 =sum(1for g in inj ifmin(d for d in best_by_dist[g] if best_by_dist[g][d] >0) ==1)print()print("Jeux injouables :", len(inj), "| reparables en 1 swap :", rep1, "| autres :", len(inj) - rep1)row_i, col_i = inj[0]jouables_d1 = [t for t, d in bfs_from(row_i).items() if d ==1and payoff(t, col_i, 0) >0]meilleur =max(payoff(t, col_i, 0) for t in jouables_d1) if jouables_d1 elseNoneprint("Exemple", (row_i, col_i), "-> tables jouables a distance 1 :", jouables_d1,"| meilleur rang", meilleur)
Migrants a c=1 : 96 jeux sur 576 dont 72 refugies (partent d'un rang nul)
Opportuniste : jeu ((1, 4, 2, 3), (1, 2, 4, 3)) | rang 2 -> rang 4 en 1 swap (table cible (2, 4, 1, 3) )
Jeux injouables : 72 | reparables en 1 swap : 48 | autres : 24
Exemple ((1, 3, 4, 2), (2, 1, 3, 4)) -> tables jouables a distance 1 : [(1, 2, 4, 3)] | meilleur rang 3
Lecture : deux raisons de partir
Les migrants a cout 1 ne sont pas une population homogene. Il y a les opportunistes – des jeux jouables ou un seul swap proche decalle l’equilibre vers une case de meilleur rang (l’exemple affiche saute de deux echelons en un swap) – et les refugies : les 72 jeux sans equilibre, dont 48 se reparent a distance 1. Pour un refugie, la meta-action n’est pas du luxe strategique, c’est la condition meme de jouabilite : sans elle, le jeu ne produit aucun resultat – c’est le refugie affiche plus bas, parti du rang nul. La strate 7 commence deja ici – changer les regles pour que le jeu existe – avant meme toute consideration d’amelioration.
Le mur traversé — ce que le migrant coupe (critère de clôture #12207)
Le chantier #12207 se clôt quand un lecteur peut, dans un notebook exécuté : partir d’un jeu nommé, atteindre un autre jeu nommé par un chemin qu’il n’a pas écrit lui-même, voir le mur qu’il traverse, et lire le coût de la méta-action qui l’y a mené. Les marches 1-2 ont nommé les jeux, calculé les chemins optimaux et balayé les coûts — mais aucune sortie ne montrait le mur. Entre deux chambres strictes adjacentes, l’égalité des deux cases permutées est un mur de codimension 1 : la lecture Bruns-Kimmich de GT-3b, rendue visible ici dans le flot de migration payée.
# Le mur traverse : la meta-action echange les rangs k et k+1 de deux cases. Dans l'espace# des utilites (representant lineaire u = valeur du rang), la deformation# u(lambda) = (1-lambda)*u_depart + lambda*u_cible coupe l'hyperplan d'egalite des deux# cases permutees. De part et d'autre, la table ordinale est CONSTANTE : c'est la chambre.positions = [i for i inrange(4) if r_t[i] != t_cible[i]]assertlen(positions) ==2, "une meta-action = un swap de deux cases"iA, iB = positionsnoms = ["TG", "TD", "BG", "BD"]def table_depuis_u(u):"""La table ordinale (rangs 1..4) induite par un profile d'utilites.""" ordre =sorted(range(4), key=lambda j: u[j]) t = [0] *4for rang, j inenumerate(ordre, start=1): t[j] = rangreturntuple(t)print(f"Jeu depart (Ligne) : {r_t} -> cible : {t_cible} ({bd} meta-action, rang {p0} -> {p1})")print(f"Cases permutees : {noms[iA]} (rang {r_t[iA]}) <-> {noms[iB]} (rang {r_t[iB]})")print()print("lambda | u_TG u_TD u_BG u_BD | table ordinale induite")print("-"*66)for lam in (0.0, 0.25, 0.5, 0.75, 1.0): u =tuple((1- lam) * r_t[j] + lam * t_cible[j] for j inrange(4))ifabs(u[iA] - u[iB]) <1e-12: classe =f"MUR : u_{noms[iA]} = u_{noms[iB]} -- codimension 1"else: t = table_depuis_u(u) classe ="chambre de depart"if t == r_t else ("chambre cible"if t == t_cible elsestr(t))print(f" {lam:.2f} | "+" ".join(f"{x:5.2f}"for x in u) +f" | {classe}")print()print(f"Le migrant coupe UN mur (l'egalite {noms[iA]}/{noms[iB]} des rangs {min(r_t[iA], r_t[iB])} et {min(r_t[iA], r_t[iB]) +1})")print(f"a lambda = 0.50, et l'a paye c = {bd} : rang {p0} -> rang {p1}, gain net {p1 - p0 - bd:+d}.")
Jeu depart (Ligne) : (1, 4, 2, 3) -> cible : (2, 4, 1, 3) (1 meta-action, rang 2 -> 4)
Cases permutees : TG (rang 1) <-> BG (rang 2)
lambda | u_TG u_TD u_BG u_BD | table ordinale induite
------------------------------------------------------------------
0.00 | 1.00 4.00 2.00 3.00 | chambre de depart
0.25 | 1.25 4.00 1.75 3.00 | chambre de depart
0.50 | 1.50 4.00 1.50 3.00 | MUR : u_TG = u_BG -- codimension 1
0.75 | 1.75 4.00 1.25 3.00 | chambre cible
1.00 | 2.00 4.00 1.00 3.00 | chambre cible
Le migrant coupe UN mur (l'egalite TG/BG des rangs 1 et 2)
a lambda = 0.50, et l'a paye c = 1 : rang 2 -> rang 4, gain net +1.
Lecture : les quatre maillons enchaînés
La sortie ci-dessus est le critère de clôture de #12207 réalisé en un seul flot exécuté : un jeu nommé (le premier migrant opportuniste, tables affichées), rejoint par un chemin qu’il n’a pas écrit (le BFS optimal de la marche 2), en traversant un mur visible — la table ordinale reste celle de la chambre de départ pour tout λ < 0.5, celle de la chambre cible pour tout λ > 0.5, et l’égalité des deux cases permutées à λ = 0.50 exactement est le mur de codimension 1 — et le coût lu : c = 1 pour cette traversée, gain net +1 en rang.
Le passage payé se lit désormais chambre -> mur -> chambre voisine : la méta-action tarifée achète exactement une traversée de mur, et rien d’autre — un échange de rangs qui ne modifie l’ordre d’aucune autre case ne peut couper plus d’un mur.
3. Marche 3 – deux agents, un meta-jeu
Derniere marche : Ligne et Colonne disposent simultanement de leurs meta-actions. Chacun choisit une action dans {rester, R1, R2, R3} x {rester, C1, C2, C3} – chacun ne reecrit que sa table, payer ses propres echelons – puis le jeu deplace est joue (dynamique BR depuis haut-gauche, convention de marche 1), et chacun touche son rang final moins son cout. Seize profils par jeu de base : un meta-jeu 4x4 dont on peut chercher les equilibres de Nash purs, exactement comme a la marche 1.
La question du desaccord devient calculable : existe-t-il un equilibre du meta-jeu, bouge-t-on a l’equilibre, et cet equilibre est-il bon pour les deux ?
# Marche 3 : le meta-jeu 4x4 et ses equilibres (cout = 1)def meta_profil(row_t, col_t, a1, a2, cout=1): r2 = swap_val(row_t, a1) if a1 >0else row_t c2 = swap_val(col_t, a2) if a2 >0else col_t p1 = payoff(r2, c2, 0); p2 = payoff(r2, c2, 1)return p1 - (cout if a1 >0else0), p2 - (cout if a2 >0else0)def meta_ne(row_t, col_t, cout=1): profils = {(a1, a2): meta_profil(row_t, col_t, a1, a2, cout)for a1 inrange(4) for a2 inrange(4)} nes = []for (a1, a2), (u1, u2) in profils.items():ifall(profils[(a1b, a2)][0] <= u1 for a1b inrange(4)) and\all(profils[(a1, a2b)][1] <= u2 for a2b inrange(4)): nes.append((a1, a2))return nes, profilsst_ne = Counter()tous_restant = ambigu = aucun_restant =0for row_t, col_t in jeux: nes, _ = meta_ne(row_t, col_t) st_ne[len(nes)] +=1ifnot nes: continueifall(n == (0, 0) for n in nes): tous_restant +=1elif (0, 0) in nes: ambigu +=1else: aucun_restant +=1print("Jeux avec au moins un meta-NE pur :", sum(v for k, v in st_ne.items() if k >0), "/", len(jeux))print("Distribution du nombre de meta-NE :", dict(sorted(st_ne.items())))print()print("Statut du mouvement a l'equilibre :")print(" tous les meta-NE restent sur place :", tous_restant)print(" (rester) coexiste avec des NE mobiles :", ambigu, "(selection d'equilibre)")print(" aucun meta-NE ne reste sur place :", aucun_restant, "(bouger est necessaire)")
Jeux avec au moins un meta-NE pur : 572 / 576
Distribution du nombre de meta-NE : {0: 4, 1: 166, 2: 266, 3: 16, 4: 116, 5: 6, 6: 2}
Statut du mouvement a l'equilibre :
tous les meta-NE restent sur place : 134
(rester) coexiste avec des NE mobiles : 332 (selection d'equilibre)
aucun meta-NE ne reste sur place : 106 (bouger est necessaire)
Lecture : le meta-jeu a presque toujours des equilibres
Premiere surprise : 572 jeux sur 576 ont au moins un equilibre pur au niveau meta – ajouter des meta-actions stabilise plutot qu’elle ne destabilise. Le detail du statut est plus riche :
134 jeux : tous les equilibres restent sur place – la meta-action existe mais personne n’a interet a s’en servir a l’equilibre (l’immobilisme est stable) ;
332 jeux : rester et bouger coexistent comme equilibres – c’est le domaine de la selection d’equilibre : plusieurs futurs possibles, aucun critere ordinal n’en designe un ;
106 jeux : aucun equilibre sur place – bouger est une necessite d’equilibre. Dans pres d’un jeu sur cinq, l’equilibre de strate 6 n’est plus tenable des que reecrire les regles est possible : l’agent qui refuse de payer reste piege pendant que l’autre reconfigure.
La marche 2 disait quand un agent solitaire part ; la marche 3 dit que des qu’on ouvre la porte aux deux, dans un cinquieme des jeux, partir est force.
# Le meta-jeu du Dilemme, en entiernes_pd, prof_pd = meta_ne(*PD)noms = ["reste", "R1", "R2", "R3"]noms_c = ["reste", "C1", "C2", "C3"]print("Meta-jeu du PD (Ligne en lignes, Colonne en colonnes) :")print(" "+"".join(f"{n:>10s}"for n in noms_c))for a1 inrange(4): ligne =f"{noms[a1]:>8s} "for a2 inrange(4): u1, u2 = prof_pd[(a1, a2)] marque =" *"if (a1, a2) in nes_pd else" " ligne +=f"({u1},{u2}){marque} "print(ligne)print()print("* = equilibre de Nash pur du meta-jeu :", nes_pd)
Meta-jeu du PD (Ligne en lignes, Colonne en colonnes) :
reste C1 C2 C3
reste (2,2) * (4,1) (2,2) * (2,1)
R1 (1,4) (3,1) (1,3) (-1,-1)
R2 (2,2) * (3,1) (2,2) * (2,1)
R3 (1,2) (-1,-1) (1,2) (3,3) *
* = equilibre de Nash pur du meta-jeu : [(0, 0), (0, 2), (2, 0), (2, 2), (3, 3)]
Lecture : le Dilemme se resout – a deux, et par equilibre
Le meta-jeu du Dilemme porte cinq equilibres. Quatre laissent les deux prisonniers a (2,2) – le statu quo auquel on pense toujours. Mais le cinquieme, (R3, C3), paie (3,3) : chacun reecrit sa propre preference au niveau 3-4, le jeu deplace devient une coordination dont l’equilibre selectionne par la dynamique est la case mutuellement meilleure (rang 4 chacun), et meme apres avoir paye un echelon chacun, il reste (3,3) > (2,2). Trois proprietes remarquables :
L’evasion est unilateralement stable : si un seul migre, la dynamique retombe sur la defection mutuelle et le migrant a paye pour rien – les profils (R3, reste) et (reste, C3) du tableau paient (1, 2) et (2, 1) : moins que (3, 3) pour chacun – personne ne peut profiter du départ de l’autre ;
Elle est symetrique : chacun ne touche qu’a sa table ; personne ne reecrit les preferences de l’autre ;
Elle n’est pas unique : quatre autres equilibres restent au (2,2). L’evasion existe et est stable, mais rien ne la selectionne – c’est un probleme de focalisation, plus de stabilite.
C’est la reponse mesuree a la question de la marche : modifier les regles est une action, et dans le Dilemme cette action est un equilibre qui domine le piege – a condition que les deux la jouent ensemble.
# L'echec de coordination : quand AUCUN equilibre n'est Pareto-optimaldef pareto_dominated(profils, p):returnany(v[0] > p[0] and v[1] > p[1] for v in profils.values())au_moins_un_bon = aucun_bon =0ex_dur =Nonefor row_t, col_t in jeux: nes, profils = meta_ne(row_t, col_t)ifnot nes: continueifany(not pareto_dominated(profils, profils[n]) for n in nes): au_moins_un_bon +=1else: aucun_bon +=1if ex_dur isNone: ex_dur = (row_t, col_t, nes, profils)print("Jeux (avec meta-NE) dont AU MOINS UN equilibre est Pareto-optimal :", au_moins_un_bon)print("Jeux ou AUCUN equilibre n'est Pareto-optimal :", aucun_bon, "-- l'echec de coordination dur")row_t, col_t, nes, profils = ex_durprint()print("Exemple", (row_t, col_t), ": les meta-NE paient", [profils[n] for n in nes])frontiere = {k: v for k, v in profils.items() ifnot pareto_dominated(profils, v)}print("Frontiere de Pareto du meta-jeu :", list(frontiere.values())[:5], "...")
Jeux (avec meta-NE) dont AU MOINS UN equilibre est Pareto-optimal : 568
Jeux ou AUCUN equilibre n'est Pareto-optimal : 4 -- l'echec de coordination dur
Exemple ((1, 3, 2, 4), (3, 4, 2, 1)) : les meta-NE paient [(2, 2), (2, 2), (2, 2), (2, 2)]
Frontiere de Pareto du meta-jeu : [(4, 1), (3, 1), (1, 3), (3, 1), (3, 3)] ...
Lecture : la limite du decentralise
Dans 568 jeux sur 572, au moins un equilibre du meta-jeu est Pareto-optimal : en general, le meilleur a deux est stable, le probleme est seulement de le choisir parmi les equilibres. Mais 4 jeux realisent l’echec de coordination pur : tous leurs equilibres paient (2,2) tandis que la frontiere de Pareto contient des profils mutuellement meilleurs – des regles reecrites dont les deux joueurs profiteraient, et qui ne sont pas stables : chacun serait tente d’en devier. L’exhibe le montre chiffres en main : l’accord existe dans la matrice, il n’existe pas dans les incitations.
C’est exactement la jonction annoncee par le chantier : la ou la strate 7 rencontre la theorie des mecanismes. Quand changer les regles est une action decentralisee, l’accord mutuel sur le changement reste un equilibre a produire – le probleme ne fait que remonter d’un etage. Un protocole d’engagement (le versant commitment de GT-9b) est le candidat naturel, et l’exercice 3 le fait toucher du doigt.
Limites et conventions (lues avant d’etendre)
Le cout en echelons de rang est une convention locale, documentee : elle rend rang - cout interpretable sans cardinaliser les preferences. Un cout en energie, en temps ou en argent exigerait une echelle cardinale et changerait les seuils – pas la forme du protocole.
La regle de jeu est la dynamique BR depuis la case haut-gauche. Une autre regle de selection (depuis une autre case, tirage aleatoire, dynamique simultanee) deplace certains equilibres selectionnes ; les comptages d’equilibres purs (marche 1) et de meta-NE (marche 3) n’en dependent pas.
Le meta-jeu est one-shot et simultane : pas de repetition, pas d’engagement, pas d’ordre de passage. L’exercice 3 ouvre le sequentiel.
Chacun ne reecrit que sa table : le redesign institutionnel croise (reecrire la table de l’autre, ou une table commune) est hors perimetre – c’est une strate encore au-dessus.
Les dettes de verification du chantier (passage 576 vers 144, tore a 37 trous, references arXiv non ouvertes firsthand) restent RAPPORTEES et ne sont pas utilisees ici : ce notebook ne derive que du substrat exact de GT-3b, lui-meme exhaustivement verifie.
4. Le parcours complet — du jeu nommé au coût de la méta-action
Versant d’intégration du chantier #12207, absorbé ici en seconde partie. Les versants précédents ont livré chacun une pièce : GT-20 construit et vérifie des chemins minimaux entre chambres, GT-3b identifie les murs où vivent les égalités, la Partie I ci-dessus (marches 1-3) a tarifé les swaps comme des actions que l’agent paie. Cette seconde partie referme la boucle : il réunit les trois pièces dans un seul parcours, celui que le chantier demande de bout en bout –
un lecteur peut, dans un notebook exécuté : partir d’un jeu nommé, atteindre un autre jeu nommé par un chemin qu’il n’a pas écrit lui-même, voir le mur qu’il traverse à chaque pas, et lire le coût de la méta-action qui l’y a mené.
Plan
Le chemin que le lecteur n’écrit pas : constructeur BFS et vérificateur indépendant (patron GT-20)
Voir les murs : chaque pas traverse un jeu à égalité codimension 1, dont on exhibe les deux faces
Le coût des méta-actions : tarifs par côté puis par niveau – et un résultat inattendu, mesuré
Le parcours complet, bout en bout : le bloc intégral, puis sa re-vérification indépendante
Exercices
La question
La théorie des jeux ordinaire décrit des agents dans des règles. La strate méta demande ce qui se passe quand déplacer le jeu est une action payée : quel trajet, quels murs franchis, à quel prix, et payé par qui. Sur l’univers fini de Robinson-Gofforth, chaque pièce de cette question est calculable – ce notebook les calcule ensemble, sur des jeux portant un nom (Dilemme, Poule, Cerf), pour que le résultat se lise comme un récit et pas comme une coordonnée.
4.0. Le substrat étendu, hérité de GT-3b / GT-20
Un jeu = deux tables de rangs, une par joueur, chacune un 4-uplet ordonnant strictement les quatre cases (haut-gauche, haut-droite, bas-gauche, bas-droite) – rang 4 = meilleur. Les swaps échangent les cases portant deux rangs adjacents k et k+1, de part et d’autre : R1, R2, R3 (côté Ligne) et C1, C2, C3 (côté Colonne). Ce sont les six générateurs de l’espace ; chaque traversée d’un swap franchit un mur du monde de Bruns-Kimmich – c’est l’objet de la section 2. Toute la mécanique est reprise telle quelle des trois versants, sans modification : ce notebook est un consommateur du substrat, pas une refonte.
# Substrat GT-3b / GT-3e / GT-20, repris tel quel -- pur stdlibfrom itertools import productfrom collections import Counter, dequeimport heapqdef swap_valeurs_adjacentes(t, k):"""Échange les cases portant les rangs k et k+1 (traversée de la facette k<->k+1).""" pk, pk1 = t.index(k), t.index(k +1) l =list(t); l[pk], l[pk1] = l[pk1], l[pk];returntuple(l)def swap_jeu(jeu, cote, k):"""Applique le swap de niveau k sur LA table du joueur désigné (méta-action unilatérale).""" row, col = jeuif cote =="ligne":return (swap_valeurs_adjacentes(row, k), col)return (row, swap_valeurs_adjacentes(col, k))stricts =sorted({tuple(p) for p in product(range(1, 5), repeat=4) iflen(set(p)) ==4})chambres = [(r, c) for r in stricts for c in stricts]print("Tables strictes par joueur :", len(stricts), "| chambres (jeux stricts) :", len(chambres))print("Générateurs : R1 R2 R3 (Ligne) x C1 C2 C3 (Colonne)")# Les cinq bornes canoniques (convention GT-21 / GT-3b / GT-20)PD = ((3, 1, 4, 2), (3, 4, 1, 2)) # T>R>P>SPOULE = ((3, 2, 4, 1), (3, 4, 2, 1)) # T>R>S>PCERF = ((4, 1, 3, 2), (4, 3, 1, 2)) # R>T>P>SIDENTITE = ((1, 2, 3, 4), (1, 2, 3, 4))RENVERSE = ((4, 3, 2, 1), (4, 3, 2, 1))for nom, j in [("PD (Dilemme)", PD), ("POULE (Poule)", POULE), ("CERF (Cerf)", CERF), ("IDENTITE", IDENTITE), ("RENVERSE", RENVERSE)]: ok = j in chambresprint(f" {nom:16s} Ligne {j[0]} | Colonne {j[1]} | chambre stricte : {ok}")
Vingt-quatre tables par joueur, donc 24 x 24 = 576 chambres – l’univers de Robinson-Gofforth classique, déjà dérivé par GT-20 et non récité ici. Les cinq bornes canoniques y vivent : le Dilemme du Prisonnier (défection mutuelle stable), la Poule (le jeu du poulet), la Chasse au Cerf (coordination sur le meilleur monde commun), et les deux extrêmes structurants Identité et Renversement.
Deux précisions de vocabulaire pour la suite. Un pas relie deux chambres adjacentes : il modifie exactement une table, celle du joueur qui agit, par exactement un swap de niveau k. Et une méta-action (GT-3e) est ce pas considéré comme payé : réécrire une préférence déclarée coûte quelque chose, en échelons de rang – c’est la section 3 qui fixe le barème.
5. Le chemin que le lecteur n’écrit pas
Le premier impératif du parcours : le chemin ne doit pas être écrit à la main. Si le lecteur choisissait lui-même ses swaps, le notebook ne montrerait rien – il redirait ses propres intuitions. Le constructeur ci-dessous produit donc le chemin par BFS avec remontée des parents, exactement selon le patron de GT-20 : le graphe des 576 chambres est exploré depuis le départ, et la chaîne est reconstruite en remontant de l’arrivée.
Le second impératif est la séparation constructeur / vérificateur (loi II de la série) : celui qui produit le témoin n’est pas celui qui le juge. La cellule suivante héberge un vérificateur qui ne réutilise rien du constructeur – il re-dérive les distances par sa propre recherche exhaustive.
# === Section 1.1 : construire_chemin -- le constructeur de témoin (patron GT-20) ===def construire_chemin(depart, arrivee):"""Produit une suite (jeu_avant, jeu_apres) de départ à arrivée par BFS + remontée des parents.""" parent = {depart: None} q = deque([depart])while q: u = q.popleft()if u == arrivee:breakfor cote in ("ligne", "colonne"):for k in (1, 2, 3): v = swap_jeu(u, cote, k)if v notin parent: parent[v] = u q.append(v)if arrivee notin parent:returnNone chaine = [] cur = arriveewhile parent[cur] isnotNone: chaine.append((parent[cur], cur)) cur = parent[cur]return chaine[::-1]def nommer_pas(pred, cur):"""Nomme le pas élémentaire entre deux jeux consécutifs (côté, niveaux échangés)."""for cote, ti in (("ligne", 0), ("colonne", 1)):if pred[ti] != cur[ti]:for k in (1, 2, 3):if swap_jeu(pred, cote, k) == cur: nom_cote ="R"if cote =="ligne"else"C"returnf"{nom_cote}{k} (côté {cote}, niveaux {k} <-> {k +1})"return"?"chemin_pd_cerf = construire_chemin(PD, CERF)print("Chemin construit : Dilemme du Prisonnier -> Chasse au Cerf")print(f" départ PD : Ligne {PD[0]} | Colonne {PD[1]}")for pred, cur in chemin_pd_cerf:print(f" -- {nommer_pas(pred, cur)} --> Ligne {cur[0]} | Colonne {cur[1]}")print(f"Longueur du chemin produit : {len(chemin_pd_cerf)} pas")
Chemin construit : Dilemme du Prisonnier -> Chasse au Cerf
départ PD : Ligne (3, 1, 4, 2) | Colonne (3, 4, 1, 2)
-- R3 (côté ligne, niveaux 3 <-> 4) --> Ligne (4, 1, 3, 2) | Colonne (3, 4, 1, 2)
-- C3 (côté colonne, niveaux 3 <-> 4) --> Ligne (4, 1, 3, 2) | Colonne (4, 3, 1, 2)
Longueur du chemin produit : 2 pas
Lecture : deux révisions du sommet
Le chemin construit a 2 pas – et chacun réécrit le sommet des préférences d’un joueur (niveaux 3 <-> 4). Ce n’est pas un choix du constructeur, c’est la géométrie : pour passer du Dilemme au Cerf, Ligne doit échanger ses deux meilleures cases (sa table (3, 1, 4, 2) devient (4, 1, 3, 2)) et Colonne doit faire de même. En langage des noms : dans le Dilemme, la défection est le sommet de chacun ; dans le Cerf, c’est la coordination. Aucun détour par les bas niveaux ne raccourcit ce trajet – il faut toucher au sommet, deux fois.
On notera l’asymétrie apparente des tables du Cerf ((4, 1, 3, 2) et (4, 3, 1, 2)) : les deux joueurs y placent le même sommet mais ne classent pas pareil les deux pires cases – le Cerf n’exige pas d’êtres identiques, seulement de préférer le monde commun.
# === Section 1.2 : verifier_chemin -- le vérificateur indépendant (patron GT-20) ===def bfs_complet(depart):"""Distances exhaustives depuis depart (la preuve de minimalité, recalculée par le vérificateur).""" dist = {depart: 0} q = deque([depart])while q: u = q.popleft()for cote in ("ligne", "colonne"):for k in (1, 2, 3): v = swap_jeu(u, cote, k)if v notin dist: dist[v] = dist[u] +1 q.append(v)return distdef pas_elementaire_valide(pred, cur):"""True ssi cur s'obtient de pred par exactement un swap élémentaire."""return cur in [swap_jeu(pred, cote, k) for cote in ("ligne", "colonne") for k in (1, 2, 3)]def verifier_chemin(depart, arrivee, chaine):"""Verdict indépendant : re-dérive TOUT, ne réutilise rien du constructeur."""if chaine isNoneorlen(chaine) ==0:return"INVALIDE : chemin vide"if chaine[0][0] != depart or chaine[-1][1] != arrivee:return"INVALIDE : les extrémités ne sont pas celles annoncées"for pred, cur in chaine:ifnot pas_elementaire_valide(pred, cur):returnf"INVALIDE : pas non élémentaire vers {cur}" d_reel = bfs_complet(depart)[arrivee]iflen(chaine) != d_reel:returnf"VALIDE mais NON MINIMAL : {len(chaine)} pas, distance réelle {d_reel}"returnf"VALIDE + MINIMAL : {len(chaine)} pas = distance exhaustive d(G,H)"print("1. Le chemin du constructeur (PD -> CERF) :")print(" ", verifier_chemin(PD, CERF, chemin_pd_cerf))voisin_cerf = swap_jeu(CERF, "ligne", 1)chemin_detour = chemin_pd_cerf + [(CERF, voisin_cerf), (voisin_cerf, CERF)]print("2. Le même chemin rallongé d'un aller-retour :")print(" ", verifier_chemin(PD, CERF, chemin_detour))print("3. Un 'chemin' en un pas vers un jeu non adjacent :")print(" ", verifier_chemin(PD, POULE, [(PD, POULE)]))
1. Le chemin du constructeur (PD -> CERF) :
VALIDE + MINIMAL : 2 pas = distance exhaustive d(G,H)
2. Le même chemin rallongé d'un aller-retour :
VALIDE mais NON MINIMAL : 4 pas, distance réelle 2
3. Un 'chemin' en un pas vers un jeu non adjacent :
INVALIDE : pas non élémentaire vers ((3, 2, 4, 1), (3, 4, 2, 1))
Lecture : trois verdicts, une séparation
Le vérificateur rend trois verdicts distincts sur trois cas construits pour eux : VALIDE + MINIMAL pour le témoin du constructeur, NON MINIMAL pour le même chemin artificiellement rallongé d’un aller-retour, et INVALIDE pour un saut direct PD -> POULE (deux jeux que rien ne relie en un pas). La minimalité n’est pas une impression : elle est re-dérivée par recherche exhaustive depuis le départ, indépendamment de la façon dont le constructeur a trouvé son chemin.
C’est la loi II de la série, appliquée au parcours : le constructeur (BFS + parents) produit un témoin, le vérificateur (re-dérivation complète) le juge. À la section 4, ce couple sera étendu aux murs et aux coûts : le vérificateur final re-vérifiera tout ce que le parcours affiche.
6. Voir les murs
Entre deux chambres adjacentes – avant et après un swap de niveau k – il y a un mur : un jeu à rangs où les deux niveaux k et k+1 sont tombés ex æquo. C’est la lecture Bruns-Kimmich reprise de GT-3b : le monde complet des jeux à rangs compte 5625 points, dont 576 chambres (aucune égalité) et des murs de codimension 1 (exactement une paire ex æquo). Traverser un swap, c’est littéralement passer à travers le mur qui sépare les deux chambres.
Deux opérations réciproques le formalisent : fusionner les niveaux k et k+1 d’une chambre donne le mur qu’un pas de niveau k traverse ; briser le tie d’un mur redonne les deux chambres qu’il sépare. La propriété qui fait tenir la section : briser le tie du mur d’un pas doit redonner exactement les deux tables extrémités de ce pas. Elle est vérifiée globalement ci-dessous, sur les 24 tables et les 3 niveaux.
# === Section 2.1 : le mur d'un pas -- fusion de niveaux et brisure de tie (GT-3b) ===def mur_du_niveau(t, k):"""L'ordre faible (codim 1) où les niveaux k et k+1 de t sont ex æquo : le mur du pas de niveau k.""" l = [x -1if x > k else x for x in t] # les niveaux au-dessus descendent d'un cran l[t.index(k)] = k l[t.index(k +1)] = k # les deux niveaux fusionnent en kreturntuple(l)def briser_le_tie(t):"""Les deux chambres strictes que le mur t sépare (réciproque exacte de mur_du_niveau).""" v =min(x for x in t if t.count(x) >1) # la valeur ex æquo (mur simple : unique) i, j = [p for p inrange(4) if t[p] == v] inc = [x +1if x > v else x for x in t] # les niveaux au-dessus remontent d'un cran A =list(inc); A[i], A[j] = v, v +1 B =list(inc); B[i], B[j] = v +1, vreturntuple(A), tuple(B)# Propriété clé, vérifiée GLOBALEMENT : briser le tie du mur d'un pas redonne ses extrémitésprop =all(sorted(briser_le_tie(mur_du_niveau(t, k))) ==sorted([t, swap_valeurs_adjacentes(t, k)])for t in stricts for k in (1, 2, 3))print("Propriété mur <-> paire de chambres, vérifiée sur 24 tables x 3 niveaux :", prop)murs_par_joueur = {mur_du_niveau(t, k) for t in stricts for k in (1, 2, 3)}print("Murs simples distincts par joueur :", len(murs_par_joueur), "(GT-3b mesurait 36)")t, k = PD[0], 3print(f"Exemple : table Ligne du Dilemme {t} | pas de niveau 3")print(f" mur : {mur_du_niveau(t, 3)} (codim {4-len(set(mur_du_niveau(t, 3)))})")print(f" faces du mur : {briser_le_tie(mur_du_niveau(t, 3))[0]}, {briser_le_tie(mur_du_niveau(t, 3))[1]}")print(f" extrémités : {t} et {swap_valeurs_adjacentes(t, 3)}")
Propriété mur <-> paire de chambres, vérifiée sur 24 tables x 3 niveaux : True
Murs simples distincts par joueur : 36 (GT-3b mesurait 36)
Exemple : table Ligne du Dilemme (3, 1, 4, 2) | pas de niveau 3
mur : (3, 1, 3, 2) (codim 1)
faces du mur : (3, 1, 4, 2), (4, 1, 3, 2)
extrémités : (3, 1, 4, 2) et (4, 1, 3, 2)
Lecture : le mur a deux faces, et ce sont les extrémités du pas
La propriété est vérifiée sur les 72 combinaisons (24 tables x 3 niveaux) : briser le tie du mur redonne exactement la chambre de départ et la chambre d’arrivée du pas. Le mur n’est donc pas une métaphore décorative – c’est l’objet géométrique entre les deux jeux, celui dont les deux faces sont les extrémités du pas. Sur l’exemple affiché : la table Ligne du Dilemme (3, 1, 4, 2) et sa voisine de niveau 3 (4, 1, 3, 2) – la table du Cerf – sont les deux faces du mur (3, 1, 3, 2), où les cases haut-gauche et bas-gauche sont ex æquo au sommet.
Le comptage rejoint GT-3b : 36 murs simples par joueur (24 chambres x 3 niveaux, chaque mur compté deux fois car chaque mur touche exactement 2 chambres). Chaque niveau de préférence a ses murs ; franchir un mur de niveau 3, c’est réécrire un sommet – le lien avec le coût de la section suivante est direct.
# === Section 2.2 : les murs du chemin PD -> CERF, pas à pas ===print("Le chemin PD -> CERF, lu mur par mur :")for n, (pred, cur) inenumerate(chemin_pd_cerf, 1): cote, ti = ("ligne", 0) if pred[0] != cur[0] else ("colonne", 1) k = [kk for kk in (1, 2, 3) if swap_jeu(pred, cote, kk) == cur][0] mur = mur_du_niveau(pred[ti], k) faces = briser_le_tie(mur) confirmee =sorted(faces) ==sorted([pred[ti], cur[ti]])print(f" pas {n} : {nommer_pas(pred, cur)}")print(f" table {cote} : {pred[ti]} -> {cur[ti]}")print(f" mur traversé : {mur} (codim {4-len(set(mur))}) | faces : {faces[0]}, {faces[1]}")print(f" les faces sont les extrémités du pas : {confirmee}")
Le chemin PD -> CERF, lu mur par mur :
pas 1 : R3 (côté ligne, niveaux 3 <-> 4)
table ligne : (3, 1, 4, 2) -> (4, 1, 3, 2)
mur traversé : (3, 1, 3, 2) (codim 1) | faces : (3, 1, 4, 2), (4, 1, 3, 2)
les faces sont les extrémités du pas : True
pas 2 : C3 (côté colonne, niveaux 3 <-> 4)
table colonne : (3, 4, 1, 2) -> (4, 3, 1, 2)
mur traversé : (3, 3, 1, 2) (codim 1) | faces : (3, 4, 1, 2), (4, 3, 1, 2)
les faces sont les extrémités du pas : True
Lecture : deux murs, un par joueur
Le chemin PD -> CERF traverse exactement deux murs, un par joueur, tous deux de niveau 3. Le mur de Ligne, (3, 1, 3, 2), est le jeu où Ligne est indifférent entre défection et coopération (ses cases haut-gauche et bas-gauche ex æquo au sommet) tandis que la table de Colonne est encore celle du Dilemme. Le mur de Colonne, (3, 3, 1, 2), est le miroir : Colonne indifférent au sommet, Ligne déjà converti au Cerf.
C’est le contenu concret du critère d’intégration : le chemin n’est plus une suite de coordonnées, c’est une suite de situations intermédiaires lisibles – chacune est un jeu où un joueur exactement est suspendu entre ses deux anciens idéaux. La ligne confirmee : True de chaque pas est la garantie que le mur exhibé est bien celui-là et pas un voisin.
7. Le coût des méta-actions
Chaque pas du chemin est une méta-action au sens de GT-3e : un joueur réécrit ses préférences déclarées, et cette réécriture est payée en échelons de rang – l’unité honnête d’un monde purement ordinal (payer 1, c’est renoncer à un échelon pour atteindre le niveau visé). Reste à fixer le barème. Deux lectures naturelles :
par côté : Ligne paie c_L par swap, Colonne c_C – le tarif différencié, par exemple la réécriture de la table Colonne (l’“institution”) plus cher que celle de Ligne ;
par niveau : réécrire le bas de sa liste coûte 1, le milieu 2, le sommet 3 – réviser ce qu’on préfère entre tout est la révision la plus profonde.
La question qui semble s’imposer : le chemin le plus court en nombre de pas est-il le moins cher ? L’intuition crie non – un détour par des niveaux bon marché pourrait battre le trajet direct. La mesure, elle, répond autrement.
# === Section 3.1 : tarifs par côté -- le coût est géométrique ===def dist_tables(t0):"""Distances du graphe des 24 tables d'UN joueur (générateurs : les 3 swaps de niveau).""" d = {t0: 0}; q = deque([t0])while q: u = q.popleft()for k in (1, 2, 3): v = swap_valeurs_adjacentes(u, k)if v notin d: d[v] = d[u] +1; q.append(v)return dNOMS = [("PD", PD), ("POULE", POULE), ("CERF", CERF), ("IDENTITE", IDENTITE), ("RENVERSE", RENVERSE)]c_L, c_C =1, 3print(f"Tarifs par côté : Ligne {c_L}, Colonne {c_C}")print(f"{'paire':>22s} | d_L | d_C | longueur | coût | Ligne paie | Colonne paie")print("-"*78)for (n1, j1), (n2, j2) in [(a, b) for i, a inenumerate(NOMS) for b in NOMS[i +1:]]: dL = dist_tables(j1[0])[j2[0]] dC = dist_tables(j1[1])[j2[1]]print(f"{n1:>10s} -> {n2:<10s} | {dL} | {dC} | {dL + dC} | {c_L*dL + c_C*dC:>2d} | {c_L*dL} | {c_C*dC}")
Les colonnes d_L et d_C sont les distances dans le graphe des 24 tables d’un seul joueur – chacune recalculée par BFS indépendant. Et le tableau montre la structure : la longueur du chemin minimal est exactement d_L + d_C, et son coût exactement c_L·d_L + c_C·d_C. Ce n’est pas un accident d’échantillon, c’est un théorème de trois lignes :
chaque pas modifie exactement une table ; la table de Ligne doit passer de PD[0] à CERF[0], ce qui exige au moins d_L pas côté Ligne ; symétriquement au moins d_C côté Colonne. Tout chemin a donc au moins d_L + d_C pas et coûte au moins c_L·d_L + c_C·d_C – et le chemin BFS atteint exactement ces deux bornes.
La conséquence est le résultat central de cette section : le coût du trajet minimal n’est pas négociable. Aucun choix de chemin – aucune ruse, aucun détour – ne change la facture : qui paie quoi est fixé par la géométrie des deux tables, avant tout parcours. Pour aller du Dilemme au Cerf à ces tarifs, Ligne paie c_L et Colonne c_C – quel que soit le chemin minimal emprunté.
# === Section 3.2 : tarifs par niveau -- la mesure répond à l'intuition ===W = {1: 1, 2: 2, 3: 3} # bas 1, milieu 2, sommet 3def cout_chemin(chaine, w=W): total =0for pred, cur in chaine: cote, ti = ("ligne", 0) if pred[0] != cur[0] else ("colonne", 1) k = [kk for kk in (1, 2, 3) if swap_jeu(pred, cote, kk) == cur][0] total += w[k]return totaldef chemin_min_cout(depart, arrivee, w=W):"""Dijkstra : le chemin de coût minimal sous les tarifs w (pas forcément le plus court).""" dist = {depart: 0}; parent = {depart: None}; pq = [(0, depart)]while pq: d, u = heapq.heappop(pq)if u == arrivee: breakif d > dist.get(u, 10**9): continuefor cote in ("ligne", "colonne"):for k in (1, 2, 3): v = swap_jeu(u, cote, k)if d + w[k] < dist.get(v, 10**9): dist[v] = d + w[k]; parent[v] = u; heapq.heappush(pq, (d + w[k], v)) chaine = []; cur = arriveewhile parent[cur] isnotNone: chaine.append((parent[cur], cur)); cur = parent[cur]return chaine[::-1]print(f"Tarifs par niveau : {W} | le plus court chemin est-il le moins cher ?")ecarts =0for (n1, j1), (n2, j2) in [(a, b) for i, a inenumerate(NOMS) for b in NOMS[i +1:]]: bfs = construire_chemin(j1, j2) dij = chemin_min_cout(j1, j2) cb, cd = cout_chemin(bfs), cout_chemin(dij) ecart = cb - cd ecarts += (ecart >0)print(f" {n1:>8s} -> {n2:<8s} : BFS {len(bfs)} pas à {cb} | Dijkstra {len(dij)} pas à {cd} | écart {ecart}")print(f"Paires nommées où un détour battrait le plus court chemin : {ecarts} / 10")import randomrandom.seed(0)ech = [(random.choice(chambres), random.choice(chambres)) for _ inrange(300)]disc =sum(1for a, b in ech if cout_chemin(chemin_min_cout(a, b)) < cout_chemin(construire_chemin(a, b)))print(f"Échantillon de 300 paires quelconques de chambres : {disc} détour(s) gagnant(s)")# La non-unicité du témoin : deux chemins distincts, la même facturebfs_pi = construire_chemin(PD, IDENTITE)dij_pi = chemin_min_cout(PD, IDENTITE)print()print("PD -> IDENTITE : le BFS et le Dijkstra produisent deux chemins distincts")print(" BFS :", [nommer_pas(p, c) for p, c in bfs_pi][:4], "... coût", cout_chemin(bfs_pi))print(" Dijkstra :", [nommer_pas(p, c) for p, c in dij_pi][:4], "... coût", cout_chemin(dij_pi))
Tarifs par niveau : {1: 1, 2: 2, 3: 3} | le plus court chemin est-il le moins cher ?
PD -> POULE : BFS 2 pas à 2 | Dijkstra 2 pas à 2 | écart 0
PD -> CERF : BFS 2 pas à 6 | Dijkstra 2 pas à 6 | écart 0
PD -> IDENTITE : BFS 7 pas à 14 | Dijkstra 7 pas à 14 | écart 0
PD -> RENVERSE : BFS 5 pas à 10 | Dijkstra 5 pas à 10 | écart 0
POULE -> CERF : BFS 4 pas à 8 | Dijkstra 4 pas à 8 | écart 0
POULE -> IDENTITE : BFS 9 pas à 16 | Dijkstra 9 pas à 16 | écart 0
POULE -> RENVERSE : BFS 3 pas à 8 | Dijkstra 3 pas à 8 | écart 0
CERF -> IDENTITE : BFS 9 pas à 17 | Dijkstra 9 pas à 17 | écart 0
CERF -> RENVERSE : BFS 3 pas à 4 | Dijkstra 3 pas à 4 | écart 0
IDENTITE -> RENVERSE : BFS 12 pas à 20 | Dijkstra 12 pas à 20 | écart 0
Paires nommées où un détour battrait le plus court chemin : 0 / 10
Échantillon de 300 paires quelconques de chambres : 0 détour(s) gagnant(s)
PD -> IDENTITE : le BFS et le Dijkstra produisent deux chemins distincts
BFS : ['R2 (côté ligne, niveaux 2 <-> 3)', 'R1 (côté ligne, niveaux 1 <-> 2)', 'R3 (côté ligne, niveaux 3 <-> 4)', 'C2 (côté colonne, niveaux 2 <-> 3)'] ... coût 14
Dijkstra : ['R2 (côté ligne, niveaux 2 <-> 3)', 'R1 (côté ligne, niveaux 1 <-> 2)', 'C2 (côté colonne, niveaux 2 <-> 3)', 'C1 (côté colonne, niveaux 1 <-> 2)'] ... coût 14
Lecture : le plus court est aussi le moins cher – mesuré, pas prouvé
L’intuition du détour économique est réfutée par la mesure : sur les 10 paires nommées et sur un échantillon de 300 paires quelconques de chambres, aucun chemin de coût minimal n’est plus long que le plus court chemin. Sous ces tarifs, la longueur minimale et le coût minimal sont atteints simultanément – le Dijkstra et le BFS rendent la même facture. Ce fait est mesuré, pas démontré : la section 3.1 le prouve pour les tarifs par côté (théorème des bornes d_L, d_C), mais pour les tarifs par niveau, aucun argument court ne clôt la question – c’est dit tel quel, et l’exercice 2 la rouvre.
Le dernier affichage montre l’autre face de la structure : pour PD -> IDENTITE, BFS et Dijkstra produisent deux chemins distincts de même coût. Le témoin n’est pas unique – plusieurs routes mènent au même prix – mais la facture, elle, ne varie pas. Réuni au théorème de la section 3.1, le paysage économique du parcours tient en une phrase : le prix du voyage est un invariant géométrique ; le chemin, lui, est un choix parmi des égalités.
8. Le parcours complet, bout en bout
Les trois pièces sont prêtes : le constructeur de chemin (section 1), les murs (section 2), le barème (section 3). La fonction ci-dessous les assemble dans un seul bloc – celui que le critère d’intégration du chantier demande : un jeu nommé, un chemin construit et non écrit à la main, chaque mur traversé exhibé avec ses deux faces, chaque méta-action affichée avec son coût et son payeur, et la facture finale par joueur.
Le barème est celui de la section 3.2 (bas 1, milieu 2, sommet 3), le même pour les deux joueurs – la profondeur de la révision fait le prix, pas l’identité de qui révise.
# === Section 4.1 : parcours_complet -- le bloc du critère d'intégration ===def parcours_complet(depart, arrivee, w, nom_dep, nom_arr):"""Le parcours intégral : chemin construit + murs traversés + coûts, affichés d'un seul bloc.""" chaine = construire_chemin(depart, arrivee)print(f"PARCOURS COMPLET : {nom_dep} -> {nom_arr}")print(f" barème par niveau : {w} (le sommet coûte le plus cher)")print(f" {nom_dep} : Ligne {depart[0]} | Colonne {depart[1]}") cumul =0 par_joueur = {"ligne": 0, "colonne": 0}for n, (pred, cur) inenumerate(chaine, 1): cote, ti = ("ligne", 0) if pred[0] != cur[0] else ("colonne", 1) k = [kk for kk in (1, 2, 3) if swap_jeu(pred, cote, kk) == cur][0] mur = mur_du_niveau(pred[ti], k) cout = w[k] cumul += cout par_joueur[cote] += coutprint(f" pas {n} : {nommer_pas(pred, cur)}")print(f" jeu {pred[0]}|{pred[1]} -> {cur[0]}|{cur[1]}")print(f" mur traversé : table {cote}{mur} (codim 1), faces {briser_le_tie(mur)[0]}, {briser_le_tie(mur)[1]}")print(f" coût {cout} payé par {cote} | cumul {cumul}")print(f" {nom_arr} : Ligne {arrivee[0]} | Colonne {arrivee[1]}")print(f" ARRIVÉE : {len(chaine)} pas | facture totale {cumul} (Ligne {par_joueur['ligne']}, Colonne {par_joueur['colonne']})")return chainechemin_final = parcours_complet(PD, CERF, W, "Dilemme (PD)", "Chasse au Cerf (CERF)")
PARCOURS COMPLET : Dilemme (PD) -> Chasse au Cerf (CERF)
barème par niveau : {1: 1, 2: 2, 3: 3} (le sommet coûte le plus cher)
Dilemme (PD) : Ligne (3, 1, 4, 2) | Colonne (3, 4, 1, 2)
pas 1 : R3 (côté ligne, niveaux 3 <-> 4)
jeu (3, 1, 4, 2)|(3, 4, 1, 2) -> (4, 1, 3, 2)|(3, 4, 1, 2)
mur traversé : table ligne (3, 1, 3, 2) (codim 1), faces (3, 1, 4, 2), (4, 1, 3, 2)
coût 3 payé par ligne | cumul 3
pas 2 : C3 (côté colonne, niveaux 3 <-> 4)
jeu (4, 1, 3, 2)|(3, 4, 1, 2) -> (4, 1, 3, 2)|(4, 3, 1, 2)
mur traversé : table colonne (3, 3, 1, 2) (codim 1), faces (3, 4, 1, 2), (4, 3, 1, 2)
coût 3 payé par colonne | cumul 6
Chasse au Cerf (CERF) : Ligne (4, 1, 3, 2) | Colonne (4, 3, 1, 2)
ARRIVÉE : 2 pas | facture totale 6 (Ligne 3, Colonne 3)
Lecture : le récit complet, lisible d’un bloc
Le bloc répond terme à terme au critère. Le jeu de départ porte un nom (Dilemme) et l’arrivée aussi (Chasse au Cerf). Le chemin est construit par BFS – le lecteur ne l’a pas écrit. Chaque pas affiche son mur : la table à égalité exactement entre les deux jeux, avec ses deux faces, qui sont les extrémités du pas. Et chaque pas affiche son coût : deux révisions de sommet à 3 échelons chacune, payées séparément – Ligne paie 3, Colonne paie 3, facture totale 6.
Le récit économique se lit maintenant mot à mot : sortir du Dilemme vers le Cerf ne coûte aucun bas niveau – il ne coûte que des sommets. Personne n’a besoin de réviser ses pires cases ; chacun doit seulement admettre que sa meilleure case change de nature. C’est précisément le contenu de GT-3e sur ce couple (l’indifference exacte de la fuite solitaire), désormais visible comme géométrie tarifée : les deux murs franchis sont tous deux des murs de sommet.
# === Section 4.2 : verifier_parcours -- la re-vérification indépendante, étendue ===def verifier_parcours(depart, arrivee, chaine, w, nom_dep, nom_arr):"""Re-dérive TOUT ce que parcours_complet affiche : extrémités, pas, murs, coûts, minimalités."""if chaine isNoneorlen(chaine) ==0or chaine[0][0] != depart or chaine[-1][1] != arrivee:return"INVALIDE : les extrémités ne sont pas celles annoncées" cumul =0for pred, cur in chaine: cote, ti = ("ligne", 0) if pred[0] != cur[0] else ("colonne", 1) k = [kk for kk in (1, 2, 3) if swap_jeu(pred, cote, kk) == cur]ifnot k:returnf"INVALIDE : pas non élémentaire vers {cur}" k = k[0]ifsorted(briser_le_tie(mur_du_niveau(pred[ti], k))) !=sorted([pred[ti], cur[ti]]):returnf"INVALIDE : le mur exhibé ne sépare pas {pred} et {cur}" cumul += w[k] d_long = bfs_complet(depart)[arrivee]iflen(chaine) != d_long:returnf"INVALIDE : {len(chaine)} pas, distance réelle {d_long}" opt = chemin_min_cout(depart, arrivee, w) d_cout = cout_chemin(opt, w)if cumul != d_cout:returnf"VALIDE mais NON MINIMAL EN COÛT : {cumul} payés, optimum {d_cout}"return (f"TEMOIN CONSTRUIT ET VÉRIFIÉ INDÉPENDAMMENT : {nom_dep} -> {nom_arr} | "f"{len(chaine)} pas = distance exhaustive | facture {cumul} = optimum Dijkstra | "f"chaque mur confirmé par ses faces")print(verifier_parcours(PD, CERF, chemin_final, W, "Dilemme (PD)", "Chasse au Cerf (CERF)"))voisin_cerf = swap_jeu(CERF, "ligne", 1)print()print("Contre-épreuve -- le même parcours rallongé d'un aller-retour gratuit pour les yeux :")print(" ", verifier_parcours(PD, CERF, chemin_final + [(CERF, voisin_cerf), (voisin_cerf, CERF)], W, "PD", "CERF"))
TEMOIN CONSTRUIT ET VÉRIFIÉ INDÉPENDAMMENT : Dilemme (PD) -> Chasse au Cerf (CERF) | 2 pas = distance exhaustive | facture 6 = optimum Dijkstra | chaque mur confirmé par ses faces
Contre-épreuve -- le même parcours rallongé d'un aller-retour gratuit pour les yeux :
INVALIDE : 4 pas, distance réelle 2
Lecture : le témoin construit et vérifié indépendamment
Le vérificateur étendu ne se contente pas des extrémités et de l’élémentarité : il re-dérive chaque mur (brisure de tie recalculée, faces comparées aux extrémités affichées), chaque coût (barème réappliqué pas à pas), la minimalité en longueur (BFS exhaustif) et la minimalité en coût (Dijkstra indépendant) – et rend son verdict en une ligne. La contre-épreuve finale montre qu’un chemin rallongé d’un aller-retour, invisible aux yeux s’il était bien imprimé, est attrapé par la distance réelle.
La terminologie est celle du steer #12205 : un chemin produit par un constructeur et accepté par un vérificateur séparé est un témoin construit et vérifié indépendamment – pas une preuve au sens formel, mais un objet dont chaque affirmation affichée a été re-dérivée par un code qui ne partage rien avec celui qui l’a produite. C’est le standard de la série depuis GT-20, étendu ici aux murs et aux coûts.
Exercice 1 – le sweep vu par Colonne
La marche 2 a fait migrer Ligne. Refaites le meme sweep du cout cote Colonne (ses swaps C1-C3, son gain au sorti de la dynamique). Le compte de migrants a c=1 est-il le meme que cote Ligne (16 %) ? Si oui, dites pourquoi la symetrie l’impose ; sinon, exhibez un jeu qui les distingue.
# Exercice 1 : sweep du cout cote Colonne# TODO etudiant : reconstruire best_by_dist_col (BFS sur la table Colonne, payoff cote 1)# puis le tableau c=0..3 : %migrer, distance optimale moyenne, fuient-un-cycleresultat_ex1 =None# TODO etudiant : {c: (pct_migrer, dist_moyenne, fuites)}print("Exercice 1 : a completer (sweep cote Colonne)")
Exercice 1 : a completer (sweep cote Colonne)
Exercice 2 – deplacer la selection d’equilibre
Les 72 jeux a deux equilibres purs sont les coordinations. En partant de l’un d’eux, montrez un cas ou un seul swap de Ligne change l’equilibre selectionne par la dynamique (la case d’arrivee change), et un cas ou aucun swap ne la change. Combien des 72 jeux sont du premier type ? (Le comptage exhaustif est possible : 72 jeux x 24 tables.)
# Exercice 2 : la selection d'equilibre sous meta-actions# TODO etudiant : pour chaque jeu a 2 NE purs, comparer l'arrivee de br_dyn pour la table# courante et pour les 23 autres tables ; compter ceux ou l'arrivee peut changerresultat_ex2 =None# TODO etudiant : (nb_jeux_selection_deplacable, exemple)print("Exercice 2 : a completer (selection deplacable)")
Exercice 2 : a completer (selection deplacable)
Exercice 3 – le meta-jeu sequentiel
La marche 3 est simultanee : les 4 echecs de coordination dur proviennent de l’instabilite de l’accord. Rendez le meta-jeu sequentiel : Ligne annonce et joue sa meta-action, Colonne voit le jeu deplace puis choisit la sienne. Proposez le protocole (qui observe quoi, ordre des couts), recalculez les equilibres (backward induction sur les 4x4 sous-jeux), et dites combien des 4 echecs durs se referment. Verdict attendu en une phrase : le sequentiel suffit-il a stabiliser l’accord ?
# Exercice 3 : equilibres parfaits du meta-jeu sequentiel# TODO etudiant : pour chacun des 4 jeux en echec dur, construire l'arbre# (Ligne : 4 actions ; Colonne observe ; 4 actions) et resoudre par inductionresultat_ex3 =None# TODO etudiant : {jeu: equilibre_sequentiel, referme: bool}print("Exercice 3 : a completer (meta-jeu sequentiel)")
Exercice 3 : a completer (meta-jeu sequentiel)
Exercice 4 — Le mur du pas, dans l’autre sens
La section 2 allait du pas vers le mur ; cet exercice remonte le chemin inverse. On donne deux chambres adjacentes quelconques (pred, cur) et on demande de retrouver le mur traversé : identifier le côté modifié et le niveau k (le pas), construire le mur par fusion de niveaux, puis vérifier que briser_le_tie(mur) redonne exactement {pred, cur} — la propriété de la section 2.
Test attendu sur pred_ex -> cur_ex : côté colonne, niveau 1, mur (2, 3, 1, 1).
# Exercice 1 -- le mur du pas, dans l'autre sens# On donne deux chambres adjacentes quelconques (pred, cur). Retrouver le mur traversé :# 1. identifier le côté modifié et le niveau k (le pas) ;# 2. construire le mur par fusion de niveaux ;# 3. vérifier que briser_le_tie(mur) redonne exactement {pred, cur} -- la propriété de la section 2.# Test attendu sur pred_ex -> cur_ex : côté colonne, niveau 1, mur (2, 3, 1, 1).def mur_du_pas(pred, cur):"""Retourne (cote, k, mur) du pas pred -> cur, ou None si non adjacent."""pass# TODO étudiant : identifier cote et k, puis mur_du_niveaureturnNonepred_ex = ((2, 1, 4, 3), (3, 4, 1, 2))cur_ex = ((2, 1, 4, 3), (3, 4, 2, 1))print("Exercice 1 à compléter : mur_du_pas", pred_ex, "->", cur_ex)
Exercice 5 — Chercher le contre-exemple au fait mesuré
La section 3.2 a mesuré (10 paires nommées + 300 paires quelconques) que le chemin le plus court est aussi le moins cher sous W = {1: 1, 2: 2, 3: 3}. Le fait est-il robuste à un autre barème ? Choisir un autre tarif (p.ex. W2 = {1: 1, 2: 1, 3: 3} ou {1: 3, 2: 1, 3: 1}, le sommet bon marché), refaire le balayage — pour chaque paire nommée et un échantillon de 300 paires, comparer cout_chemin(construire_chemin) et cout_chemin(chemin_min_cout) — et rapporter : un barème où un détour gagne existe-t-il ?
Indice : commencer par le barème sommet bon marché — que devient l’écart moyen ?
# Exercice 2 -- chercher le contre-exemple au fait mesuré# La section 3.2 a MESURÉ (10 paires nommées + 300 paires quelconques) que le chemin le plus court# est aussi le moins cher sous W = {1: 1, 2: 2, 3: 3}. Est-ce robuste à un autre barème ?# 1. choisir un autre tarif, p.ex. W2 = {1: 1, 2: 1, 3: 3} ou {1: 3, 2: 1, 3: 1} (sommet bon marché !) ;# 2. re-faire le balayage : pour chaque paire nommée et un échantillon de 300 paires,# comparer cout_chemin(construire_chemin) et cout_chemin(chemin_min_cout) ;# 3. rapporter : un barème où un détour gagne existe-t-il ?# Indice : commencer par le barème sommet bon marché -- que devient l'écart moyen ?W2 = {1: 1, 2: 2, 3: 3} # TODO étudiant : essayer d'autres barèmesprint("Exercice 2 à compléter : balayage sous barème", W2)
Exercice 2 à compléter : balayage sous barème {1: 1, 2: 2, 3: 3}
Exercice 6 — Capstone : POULE -> RENVERSE, le parcours complet
Faire courir parcours_complet de la Poule au Renversement sous W = {1: 1, 2: 2, 3: 3}, puis passer le résultat à verifier_parcours. Questions de lecture : combien de pas, et répartis comment entre les deux joueurs ? Quels niveaux sont révisés — le trajet touche-t-il le sommet de qui que ce soit ? La facture par joueur : qui paie le plus, et pourquoi la géométrie l’impose-t-elle (cf 3.1) ?
# Exercice 3 -- capstone : POULE -> RENVERSE, le parcours complet# Faire courir parcours_complet de la Poule au Renversement sous W = {1: 1, 2: 2, 3: 3},# puis passer le résultat à verifier_parcours. Questions de lecture :# 1. combien de pas, et répartis comment entre les deux joueurs ?# 2. quels niveaux sont révisés -- le trajet touche-t-il le sommet de qui que ce soit ?# 3. la facture par joueur : qui paie le plus, et pourquoi la géométrie l'impose-t-elle (cf 3.1) ?print("Exercice 3 à compléter : parcours POULE -> RENVERSE + vérification")
Le critère d’intégration du chantier est tenu, bout en bout et sur un seul écran : un jeu nommé (le Dilemme), un chemin non écrit à la main (BFS + parents, vérifié), des murs visibles (chaque pas exhibe la table à égalité entre les deux jeux, ses deux faces confirmées), des coûts lus (deux révisions de sommet, 3 échelons chacune, payées séparément) – et l’arrivée porte un nom (la Chasse au Cerf).
Deux résultats structurent le parcours au-delà de l’assemblage. Le théorème des bornes (3.1) : le coût du trajet minimal se décompose en c_L·d_L + c_C·d_C où d_L, d_C sont des distances de tables individuelles – la facture est un invariant géométrique, hors de portée de tout marchandage sur le choix du chemin. Et le fait mesuré (3.2) : sous tarification par niveau, le plus court chemin est aussi le moins cher, sur toutes les paires testées – l’intuition du détour économique ne survit pas à la mesure.
Pour aller plus loin : l’arbitrage migrer ou rester (à quel prix un agent accepte-t-il de payer ces 6 échelons ?) est le sujet de la Partie I (marche 2, sections 2-3 ci-dessus) ; la théorie des murs et des chambres, celui de GT-3b ; la séparation constructeur / vérificateur, celle de GT-20.
Conclusion générale : trois marches, puis un parcours
Les trois marches mesurent le passage de la strate 6 a la strate 7 sur un univers fini ou tout se verifie :
Marche
Ce que fait l’agent
Chiffre cle
1 – jouer
subit les regles, converge ou cycle
72 jeux injouables sur 576 ; convergence si et seulement si equilibre pur
2 – deplacer
paie des echelons pour reecrire sa table
56 % -> 16 % -> 8 % -> 4 % de migrants quand le cout monte ; le Dilemme exactement indifferent a c=1
3 – se coordonner sur les regles
joue le meta-jeu 4x4
106 jeux ou bouger est necessaire ; le Dilemme s’evade a (3,3) par equilibre symetrique ; 4 echecs de coordination dur
Le resultat qui donne son sens au chantier : changer les regles est une action ordinaire – elle a un prix, un seuil, des equilibres, et meme ses propres echecs de coordination. Le vocabulaire strategique de la strate 6 s’applique sans modification un etage plus haut ; c’est la definition meme d’un franchissement reussi.
Suite du chantier (#12207) : D1 (la grammaire des generateurs manipulables) et D3 (le chemin minimal certifie, cible Lean a terme via #12205) restent a ecrire sur le meme substrat.
GameTheory 3e – Meta-Actions Tarifees, versant D4 du chantier « Les jeux comme objets » (#12207). Substrat, encodages et swaps herites de GT-3b ; jumeaux conceptuels GT-21 (les deux especes de fleches) et GT-20 (l’engagement comme protocole de coordination sur les regles).