GameTheory-02c : Traveler’s Dilemma en C# — le twin qui re-exerce le raisonnement

Navigation : << 02c-Travelers-Dilemma | 02-NormalForm (palier parent) | 02b - Définitions Lean | Série GameTheory

Kernel : .NET (C#) — jumeau .NET Interactive de la série GameTheory.

Rôle dans l’Epic #12208 (temps 2) : ce notebook est le twin C# du notebook de distillation GameTheory-02c (Traveler’s Dilemma, Basu 1994, livré par #14937) — même accrétion 02c, seul le suffixe de langage distingue les jumeaux (convention twins, #15486). La famille 02 devient ainsi tri-moteur : 02/02-Part2 en Python, 02b en Lean 4, 02c bimoteur Python/C#. Le critère de promotion de l’Epic (§5.4 : « a survécu à au moins une variante — c’est-à-dire qu’on a essayé de le casser et qu’on sait ce qui s’est passé ») dit ce qu’on attend de ce twin : re-dériver le paiement, la meilleure réponse, l’élimination itérée vers (2,2), le seuil \(r^*=1\) et le contre-claim de fragilité dans un second moteur, et rapporter honnêtement toute divergence. Le précédent 13b/13c avait cassé le matériau en trois endroits ; la barre est là.

Le récit complet (l’accident de compagnie aérienne, la lecture de Basu, le paradoxe de la rationalité) vit dans 02c — ce twin le cite et n’en duplique que le strict nécessaire à la lecture autonome des cellules.

Le jeu et la question posée

Deux voyageurs réclament indépendamment une valeur entre 2 et 100 dollars pour un bagage identique perdu. L’arbitre paie min(x, y) aux deux, +r au moins-disant, −r au plus-disant (r = 2 dans la version canonique). L’élimination itérée des stratégies faiblement dominées descend de 100 jusqu’à l’équilibre (2, 2) — unique pour \(r > 1\) ; à \(r = 1\) les équilibres se multiplient (§2, §4) — alors que les humains jouent ~100. Le matériau à re-exercer :

Claim de 02c Où le twin le re-dérive
\(u_1\) par morceaux (min ± r) §1
meilleure réponse \(\max(C_{min}, y-1)\) §2
élimination itérée (faible) → survivant unique [2] à \(r>1\) §3
seuil de dissolution \(r^* = 1\) §4
contre-claim : (2,2) fragile dès \(\varepsilon > 0\) §5

Source primaire : Basu (1994), The Traveler’s Dilemma: Paradoxes of Rationality in Game Theory, American Economic Review 84(2), 391-395 (JSTOR 2117865) — lue firsthand pour la distillation 02c ; ce twin hérite de cette lecture et re-exécute le matériel, il ne la refait pas.

1. La fonction de paiement : l’objet formel, en C

La règle de l’arbitre, identique à celle de 02c :

\[ u_1(x, y) = \begin{cases} \min(x,y) + r & \text{si } x < y \\[2pt] \min(x,y) - r & \text{si } x > y \\[2pt] x & \text{si } x = y \end{cases} \]

Le moteur est une classe statique (Travelers02c), les paramètres r et cMax suivent la signature Python (\(u_1(x, y, r, \bar c)\)) pour que la comparaison cellule à cellule soit littérale. Deux conventions C# sont fixées ici et payées tout du long : le formatage des doubles passe par CultureInfo.InvariantCulture (sinon une machine fr-FR imprime 92,5 avec une virgule et les lectures deviennent ambiguës), et tout argmax itère x croissant en ne retenant que le strictement meilleur — l’équivalent exact du max() Python, qui rend le plus petit x en cas d’égalité.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

public static class Travelers02c
{
    public const int CMin = 2;
    public const int CMax = 100;

    // u1(x, y) : paiement du joueur Ligne. Signature parallele a la version
    // Python u1(x, y, r, c_max) -- cMax reste dans la signature pour la
    // comparaison litterale, le corps n'en a pas besoin (bornes non testees,
    // comme dans 02c).
    public static double U1(int x, int y, double r = 2.0, int cMax = CMax)
    {
        int m = Math.Min(x, y);
        if (x < y) return m + r;
        if (x > y) return m - r;
        return m;
    }

    // Paiement du joueur Colonne (symetrie).
    public static double U2(int x, int y, double r = 2.0, int cMax = CMax) => U1(y, x, r, cMax);
}

// Formatage invariant : sur une machine fr-FR, 92.5 s'imprime "92,5" sans ceci.
public static string F(double v) =>
    v.ToString("0.###", CultureInfo.InvariantCulture);

Console.WriteLine("u1(90, 100, r=2) = " + F(Travelers02c.U1(90, 100)) + "    (90 < 100 -> min=90 + bonus 2)");
Console.WriteLine("u1(100, 90, r=2) = " + F(Travelers02c.U1(100, 90)) + "    (100 > 90 -> min=90 - penalite 2)");
Console.WriteLine("u1(90, 90, r=2)  = " + F(Travelers02c.U1(90, 90)) + "    (egalite -> 90)");
u1(90, 100, r=2) = 92    (90 < 100 -> min=90 + bonus 2)
u1(100, 90, r=2) = 88    (100 > 90 -> min=90 - penalite 2)
u1(90, 90, r=2)  = 90    (egalite -> 90)

Lecture du résultat

Trois impressions, trois cas de la définition par morceaux : sous-coter rapporte le minimum majoré du bonus (92), sur-coter le minimum minoré de la pénalité (88), l’égalité paie le montant commun (90). Divergence avec 02c : aucune — les valeurs sont byte-identiques aux sorties committées de 02c. La dissymétrie (baisser est au moins aussi bon dès que l’autre est en dessous) est le moteur que les sections suivantes exploitent.

2. La meilleure réponse : se tenir juste en dessous de l’autre

Ce n’est pas cosmétique : à \(r = 1\) précisément, l’égalité \(u_1(y, y) = u_1(y-1, y) = y\) vaut pour tout \(y > 2\) — la meilleure réponse devient un ensemble \(\{y-1, y\}\) et le choix du représentant change la réponse affichée (sonde ci-dessous).

static (int X, double V) BestResponse(int y, double r = 2.0, int cMax = Travelers02c.CMax)
{
    // Parite avec max() Python : x croissant, remplacement seulement si
    // strictement meilleur -> le plus petit x gagne les egalites.
    int xStar = Travelers02c.CMin;
    double vStar = double.NegativeInfinity;
    for (int x = Travelers02c.CMin; x <= cMax; x++)
    {
        double v = Travelers02c.U1(x, y, r, cMax);
        if (v > vStar) { vStar = v; xStar = x; }
    }
    return (xStar, vStar);
}

foreach (var y in new[] { 100, 2, 55, 3 })
{
    var (xs, vs) = BestResponse(y);
    Console.WriteLine($"y={y,3} -> x* = {xs}  (valeur {F(vs)})");
}

// Cas limite r = 1 : la meilleure reponse est un ENSEMBLE {y-1, y} ;
// l'argmax strict n'en montre qu'un representant (le plus petit, par convention).
Console.WriteLine();
Console.WriteLine("Cas limite r = 1 : sous-coter a y-1 et rester a y rapportent tous deux y");
foreach (var yy in new[] { 100, 55, 3 })
{
    double uUnder = Travelers02c.U1(yy - 1, yy, 1.0), uStay = Travelers02c.U1(yy, yy, 1.0);
    Console.WriteLine($"y={yy,3} r=1 : u1(y-1,y) = {F(uUnder)} = u1(y,y) = {F(uStay)} -> BR(y) = {{{yy - 1}, {yy}}}");
}
Console.WriteLine("BestResponse(y, r=1) n'affiche que y-1 (convention) ; a r=1 chaque profil (y,y) est un equilibre de Nash : (2,2) n'y est pas unique");
y=100 -> x* = 99  (valeur 101)
y=  2 -> x* = 2  (valeur 2)
y= 55 -> x* = 54  (valeur 56)
y=  3 -> x* = 2  (valeur 4)

Cas limite r = 1 : sous-coter a y-1 et rester a y rapportent tous deux y
y=100 r=1 : u1(y-1,y) = 100 = u1(y,y) = 100 -> BR(y) = {99, 100}
y= 55 r=1 : u1(y-1,y) = 55 = u1(y,y) = 55 -> BR(y) = {54, 55}
y=  3 r=1 : u1(y-1,y) = 3 = u1(y,y) = 3 -> BR(y) = {2, 3}
BestResponse(y, r=1) n'affiche que y-1 (convention) ; a r=1 chaque profil (y,y) est un equilibre de Nash : (2,2) n'y est pas unique

Lecture du résultat

Quatre sondes, quatre confirmations du claim de 02c : la meilleure réponse est \(\max(C_{min},\ y-1)\) — (99, 101) face à 100, (54, 56) face à 55, et contre 3 la réponse est (2, 4) : descendre sous le plancher étant impossible, on s’y plaque (\(2 + r = 4\)). Face au plancher lui-même (\(y=2\)), l’égalité (2, 2) est forcée. Les valeurs sont identiques aux sorties Python de 02c. La sonde \(r=1\) ajoute ce que 02c formulait de travers : la meilleure réponse y est un ensemble \(\{y-1, y\}\), chaque profil diagonal \((y,y)\) est un équilibre de Nash, et l’argmax du code n’en rend qu’un représentant — l’unicité de \((2,2)\) vaut pour \(r > 1\), pas pour \(r \ge 1\) (défaut hérité de 02c, cf. #15461).

3. L’élimination itérée des stratégies faiblement dominées

Le raisonnement de 02c, re-dérivé — et son correctif : dans le jeu réduit aux stratégies restantes, la plus haute réclamation \(k\) est faiblement dominée par \(k-1\) : au moins autant partout, strictement plus quelque part. Elle n’est jamais strictement dominée — pour tout adversaire \(y < k-1\), \(k-1\) et \(k\) rapportent toutes deux \(y - r\) (égalité structurelle) ; les situations où \(k\) était le moins-disant (un adversaire au-dessus) ont déjà été éliminées, et c’est ce qui laisse la stricte inégalité quelque part. Conséquence de théorie : l’élimination itérée de dominations faibles n’est pas indépendante de l’ordre — la cascade « plus haute d’abord » est une convention (celle de 02c), pas une garantie. En cascade : 100 dominée par 99, puis 99 par 98… jusqu’au plancher.

static (List<int> Survivors, List<int> ElimOrder) IterativelyDominated(double r = 2.0, int cMax = Travelers02c.CMax)
{
    bool WeaklyDominatedByPrev(int x, List<int> remaining)
    {
        if (!remaining.Contains(x - 1)) return false;
        // Domination FAIBLE : >= partout ET > quelque part sur l'ensemble restant.
        // Jamais stricte dans ce jeu : face a y < x-1, x-1 et x rapportent y - r.
        bool weakly   = remaining.All(y => Travelers02c.U1(x - 1, y, r, cMax) >= Travelers02c.U1(x, y, r, cMax));
        bool strictly = remaining.Any(y => Travelers02c.U1(x - 1, y, r, cMax) > Travelers02c.U1(x, y, r, cMax));
        return weakly && strictly;
    }

    var remaining = Enumerable.Range(Travelers02c.CMin, cMax - Travelers02c.CMin + 1).ToList();
    var elim = new List<int>();
    while (true)
    {
        var candidates = remaining.Where(x => x > Travelers02c.CMin && WeaklyDominatedByPrev(x, remaining)).ToList();
        if (candidates.Count == 0) break;
        int top = candidates.Max();   // on elimine la plus haute d'abord
        remaining.Remove(top);
        elim.Add(top);
    }
    return (remaining, elim);
}

var (survivors, elimOrder) = IterativelyDominated();
Console.WriteLine("Strategies eliminees (du haut vers le bas), extrait : [" + string.Join(", ", elimOrder.Take(6)) + "] ...");
Console.WriteLine("Strategies survivantes : [" + string.Join(", ", survivors) + "]  (a r = 2 > 1 : equilibre unique au plancher)");
Console.WriteLine();
// Verification : a l'equilibre (2,2), aucun joueur ne veut bouger seul
Console.WriteLine("u1(2,2) = " + F(Travelers02c.U1(2, 2)) + " | devier a 3 pour Ligne   : " + F(Travelers02c.U1(3, 2)) + " -> pas profitable");
Console.WriteLine("u1(2,2) = " + F(Travelers02c.U1(2, 2)) + " | devier a 3 pour Colonne : " + F(Travelers02c.U2(2, 3)) + " -> pas profitable");
Strategies eliminees (du haut vers le bas), extrait : [100, 99, 98, 97, 96, 95] ...
Strategies survivantes : [2]  (a r = 2 > 1 : equilibre unique au plancher)

u1(2,2) = 2 | devier a 3 pour Ligne   : 0 -> pas profitable
u1(2,2) = 2 | devier a 3 pour Colonne : 0 -> pas profitable

Lecture du résultat

L’ordre d’élimination démarre à [100, 99, 98, 97, 96, 95] et le survivant est [2], seul. À (2,2) chaque joueur touche 2 $ ; dévier seul à 3 rapporte \(\min(2,3) - r = 0\) — strictement pire, aucun côté. Le survivant [2] à \(r = 2\) survit au changement de moteur — mais le claim de 02c tel que formulé (« strictement dominées », « unique équilibre dès \(r \ge 1\) ») ne survit pas à la re-dérivation : la procédure est une élimination faible ordre-dépendante (§3), et l’unicité de \((2,2)\) vaut pour \(r > 1\) seulement. C’est le défaut hérité que cette version corrige ; il est tracké pour 02c par #15461. Reste le point incertain de la ré-implémentation : la cascade dépend de l’ordre « plus haute d’abord » et de la double condition — le second moteur ne dévie pas.

4. La sensibilité au bonus : où naît le paradoxe

Trois régimes, séparés nettement. Face à un adversaire qui réclame \(c\), sous-coter à \(c-1\) rapporte \((c-1)+r\) contre \(c\) à l’égalité, soit un gain de \(r-1\), indépendant de \(c\). \(r > 1\) : le sous-cotage est strictement profitable, la meilleure réponse est unique (\(y-1\)), l’équilibre pur est unique au plancher. \(r = 1\) : le gain est nul — égalité de meilleures réponses \(\{y-1, y\}\), chaque \((y,y)\) est un équilibre de Nash (§2), et l’élimination ne roule que par domination faible avec la convention d’ordre du §3. \(r < 1\) : le sous-cotage est strictement déficitaire, la cascade ne démarre pas. On mesure les deux choses ensemble : le gain de sous-cotage \(u_1(c-1,c) - u_1(c,c)\), et le fait que l’élimination (§3, même code, \(r\) propagé) atteigne ou non le plancher.

static double UndercutGain(int c, double r = 2.0) =>
    Travelers02c.U1(c - 1, c, r) - Travelers02c.U1(c, c, r);

static bool EliminationReachesFloor(double r = 2.0, int cMax = Travelers02c.CMax)
{
    bool WeaklyDominatedByPrev(int x, List<int> remaining)
    {
        if (!remaining.Contains(x - 1)) return false;
        // Domination FAIBLE : >= partout ET > quelque part (cf. §3).
        bool weakly   = remaining.All(y => Travelers02c.U1(x - 1, y, r, cMax) >= Travelers02c.U1(x, y, r, cMax));
        bool strictly = remaining.Any(y => Travelers02c.U1(x - 1, y, r, cMax) > Travelers02c.U1(x, y, r, cMax));
        return weakly && strictly;
    }

    var remaining = Enumerable.Range(Travelers02c.CMin, cMax - Travelers02c.CMin + 1).ToList();
    while (true)
    {
        var candidates = remaining.Where(x => x > Travelers02c.CMin && WeaklyDominatedByPrev(x, remaining)).ToList();
        if (candidates.Count == 0) break;
        remaining.Remove(candidates.Max());
    }
    return remaining.Count == 1 && remaining[0] == Travelers02c.CMin;
}

double[] rs = { 0.5, 1.0, 1.5, 2.0, 5.0, 10.0 };
Console.WriteLine("  r  |  gain de sous-cotage (r-1)  |  la spirale atteint le plancher ?");
foreach (var r in rs)
{
    double g = UndercutGain(50, r);
    bool ok = EliminationReachesFloor(r);
    Console.WriteLine($"{r.ToString("0.0", CultureInfo.InvariantCulture),4} | {g.ToString("0.0", CultureInfo.InvariantCulture),14} | {(ok ? "OUI" : "NON")}");
}
  r  |  gain de sous-cotage (r-1)  |  la spirale atteint le plancher ?
 0.5 |           -0.5 | NON
 1.0 |            0.0 | OUI
 1.5 |            0.5 | OUI
 2.0 |            1.0 | OUI
 5.0 |            4.0 | OUI
10.0 |            9.0 | OUI

Lecture du résultat

La table reproduit celle de 02c ligne à ligne. Deux lectures, dont une fine :

  • \(r < 1\) : le gain de sous-cotage est négatif (−0,5) — sous-coter coûte, la cascade ne démarre pas, l’élimination s’arrête haut (NON). Le paradoxe est vivant : la théorie ne dit plus rien d’utile, et les humains jouent ~100.
  • \(r > 1\) : la spirale descend jusqu’au plancher (OUI), le sous-cotage rapporte \(r - 1 > 0\), l’équilibre pur est unique en \((2,2)\) — le paradoxe se dissout dans la prédiction. * \(r = 1\) : la spirale atteint encore le plancher (OUI), mais la prédiction se brouille — meilleures réponses en ensemble \(\{y-1, y\}\), chaque \((y,y)\) est un équilibre de Nash (§2) : le plancher n’est plus le seul. Le seuil de la spirale est \(r^* = 1\) ; le seuil de l’unicité est \(r > 1\) strictement.

Le cas \(r = 1\) exactement est le point subtil que le twin isole mieux qu’une lecture rapide : le gain de sous-cotage y vaut 0,0 — sous-coter ne rapporte rien — et pourtant l’élimination atteint le plancher. La raison est dans la double condition du §3 : à \(r=1\), \(k-1\) est faiblement meilleur partout et strictement meilleur quelque part (face à un adversaire au-dessus de \(k-1\), \(k\) perd le bonus), ce qui suffit à éliminer. Autrement dit : à \(r = 1\) la spirale ne rapporte rien, mais elle roule quand même. C’est la version formelle du fait expérimental de Basu — la gravité du paradoxe est une fonction de \(r\), continue à travers le seuil.

5. Le contre-claim : (2,2) est réel, mais ce n’est pas une prédiction comportementale

L’énoncé « l’équilibre est (2,2) » est vrai ; ce qu’il n’implique pas, c’est que des joueurs humains y arrivent. Le contre-claim de 02c se re-exécute : si l’adversaire « grimpe » (réclame haut) avec probabilité \(\varepsilon\), uniformément sur \([\bar c / 2, \bar c]\), le reste jouant 2, la meilleure réponse du joueur prudent se recalcule contre cette distribution.

static double ExpectedU1GivenMix(int myX, IEnumerable<(int Y, double P)> opponentDist,
                              double r = 2.0, int cMax = Travelers02c.CMax) =>
    opponentDist.Sum(tp => tp.P * Travelers02c.U1(myX, tp.Y, r, cMax));

static int AggregateBE(IEnumerable<(int Y, double P)> opponentDist,
                      double r = 2.0, int cMax = Travelers02c.CMax)
{
    // Meme convention d'argmax que BestResponse : plus petit x gagne les egalites.
    int xBe = Travelers02c.CMin;
    double best = double.NegativeInfinity;
    for (int x = Travelers02c.CMin; x <= cMax; x++)
    {
        double v = ExpectedU1GivenMix(x, opponentDist, r, cMax);
        if (v > best) { best = v; xBe = x; }
    }
    return xBe;
}

foreach (var eps in new[] { 0.0, 0.05, 0.2, 0.5 })
{
    var dist = new List<(int Y, double P)> { (Travelers02c.CMin, 1 - eps) };
    for (int y = Travelers02c.CMax / 2; y <= Travelers02c.CMax; y++)
        dist.Add((y, eps / (Travelers02c.CMax / 2 + 1)));
    int xBe = AggregateBE(dist);
    Console.WriteLine($"eps={eps.ToString("F2", CultureInfo.InvariantCulture),4} :: meilleure reponse de Ligne = {xBe,3}");
}
eps=0.00 :: meilleure reponse de Ligne =   2
eps=0.05 :: meilleure reponse de Ligne =  96
eps=0.20 :: meilleure reponse de Ligne =  96
eps=0.50 :: meilleure reponse de Ligne =  96

Verdict du contre-claim, et verdict du twin

Contre-claim confirmé par le second moteur : dès \(\varepsilon = 0{,}05\) — cinq pour cent de chance que l’autre grimpe — la meilleure réponse saute de 2 à 96, et y reste pour \(\varepsilon = 0{,}2\) puis \(0{,}5\). L’équilibre (2,2) exige la confiance absolue dans la rationalité de l’autre ; c’est un équilibre fragile, pas un attracteur. Les trois valeurs (2, 96, 96, 96) sont identiques aux sorties Python de 02c.

Verdict du twin (maturation §5.4 de l’Epic) : défaut hérité trouvé, et corrigé. La première version de ce twin avait recopié deux formulations de 02c — « élimination itérée de stratégies strictement dominées » (c’est une domination faible : égalité structurelle face à \(y < k-1\)) et « unique équilibre \((2,2)\) dès \(r \ge 1\) » (à \(r = 1\), \(\mathrm{BR}(y) = \{y-1, y\}\) et chaque \((y,y)\) est un équilibre de Nash) ; la review au head 27bc6831 les a nommées, et cette version corrige les deux dans le twin. La correction pour 02c est trackée par #15461. La parité numérique des sorties avec 02c reste exacte — paiement, argmax, élimination, seuil \(r^*\), saut à 96 — mais c’est précisément la leçon de maturation : la parité des sorties ne vaut pas parité de validité des claims. Le twin a fait ce que 13b/13c avait fait avant lui : casser là où ça devait casser.

6. Exercices (3) — à compléter par l’étudiant

Mêmes exercices que 02c, transposés : des stubs C.1 qui s’exécutent sans erreur (le C# retourne null là où Python retournait None), le raisonnement restant à écrire. Contenu réel à produire, jamais une valeur fabriquée.

// Exercice 1 : Transformer le jeu en jeu de coordination
// Objectif : montrer que le Dilemme des Voyageurs est ANTAGONISTE par nature.
// Question : pour quelles valeurs de r la gestion (x=50, y=50) est-elle meilleure
//            que (x=2, y=2) pour les DEUX joueurs a la fois ? Repondez en code.
//
// NB pedagogique : ce qui suit est un VRAI stub C.1 -- la fonction renvoie null
// tant que l'etudiant n'a pas ecrit le corps. Lire la sortie : le verdict sera
// null (imprime "None") jusqu'a implementation.

static bool? CoordinationPossible(double r = 2.0, int cMax = Travelers02c.CMax)
{
    // --- TODO etudiant : implementer la logique ci-dessous ---
    // Indice : comparer U1(CMin, CMin, r) (paiement au plancher) a U1(x, x, r)
    // pour x dans [CMin+1, cMax]. Si UN x rapporte strictement plus aux DEUX
    // joueurs que le plancher, retourner true ; sinon false.
    Console.WriteLine("Exercice a completer : implementer CoordinationPossible(r).");
    return null;  // a remplacer par l'implementation de l'etudiant
}

bool? result1 = CoordinationPossible();
Console.WriteLine("u1(2,2)     = " + F(Travelers02c.U1(2, 2)) + "    ([2,2] = paiement du plancher)");
Console.WriteLine("u1(100,100) = " + F(Travelers02c.U1(100, 100)) + "  ([100,100] = egalite au plafond)");
Console.WriteLine("coordination_possible() -> " + (result1?.ToString() ?? "None") + "  (None tant que l'etudiant n'a pas implemente)");
Exercice a completer : implementer CoordinationPossible(r).
u1(2,2)     = 2    ([2,2] = paiement du plancher)
u1(100,100) = 100  ([100,100] = egalite au plafond)
coordination_possible() -> None  (None tant que l'etudiant n'a pas implemente)

Exercice 2 — le seuil de dissolution du paradoxe

L’égalité au plafond \((\bar c, \bar c)\) rapporte-t-elle strictement plus que l’égalité au plancher \((2, 2)\), quel que soit \(r\) ? Comparez \(u_1(\bar c, \bar c)\) et \(u_1(2, 2)\), puis dites si le verdict dépend de \(r\) — en justifiant depuis la définition de \(u_1\) sur la diagonale \(x = y\).

// Exercice 2 : le seuil de dissolution du paradoxe
// Question : est-ce que l'egalite au plafond (100,100) bat toujours l'egalite au
// plancher (2,2), quel que soit r ? Repondez en code (VRAI stub C.1 : la
// fonction renvoie null tant que l'etudiant ne l'a pas implementee).

static bool? PlafondBatPlancher(double r = 2.0, int cMax = Travelers02c.CMax)
{
    // --- TODO etudiant : implementer ---
    // Indice : comparer U1(cMax, cMax, r) et U1(CMin, CMin, r). Si la premiere
    // est strictement plus grande, retourner true ; sinon false.
    Console.WriteLine("Exercice a completer : implementer PlafondBatPlancher(r).");
    return null;  // a remplacer par l'implementation de l'etudiant
}

foreach (var r in new[] { 0.5, 2.0, 10.0, 50.0 })
{
    Console.WriteLine($"r={r.ToString("0.0", CultureInfo.InvariantCulture),5} | plafond(100,100)={F(Travelers02c.U1(100, 100, r)),6} | " +
                      $"plancher(2,2)={F(Travelers02c.U1(2, 2, r)),4} | plafond bat ? " +
                      (PlafondBatPlancher(r)?.ToString() ?? "None"));
}
Exercice a completer : implementer PlafondBatPlancher(r).
r=  0.5 | plafond(100,100)=   100 | plancher(2,2)=   2 | plafond bat ? None
Exercice a completer : implementer PlafondBatPlancher(r).
r=  2.0 | plafond(100,100)=   100 | plancher(2,2)=   2 | plafond bat ? None
Exercice a completer : implementer PlafondBatPlancher(r).
r= 10.0 | plafond(100,100)=   100 | plancher(2,2)=   2 | plafond bat ? None
Exercice a completer : implementer PlafondBatPlancher(r).
r= 50.0 | plafond(100,100)=   100 | plancher(2,2)=   2 | plafond bat ? None

Exercice 3 — le coût de l’optimisme

Reprenez le calcul du §5 et trouvez la plus petite valeur de \(\varepsilon\) pour laquelle la meilleure réponse du joueur prudent devient strictement supérieure au plancher — le « seuil de bascule » de la confiance. Indice : la table du §5 saute déjà à \(\varepsilon = 0{,}05\) ; le seuil est plus petit encore (~0,03).

// Exercice 3 : seuil de bascule de la confiance
// Objectif : determiner la plus petite valeur de eps pour laquelle la meilleure
// reponse stricte depasse le plancher CMin (VRAI stub C.1 : renvoie null tant
// que l'etudiant ne l'a pas implemente).

static double? SeuilBascule(int cMax = Travelers02c.CMax, double r = 2.0)
{
    // --- TODO etudiant : implementer ---
    // Indice : construire la distribution mixte ((CMin, 1-eps) + uniforme sur
    // [cMax/2, cMax]) pour plusieurs valeurs de eps ; appeler AggregateBE(dist,
    // r, cMax) ; retourner le plus petit eps pour lequel le resultat depasse
    // strictement CMin.
    Console.WriteLine("Exercice a completer : implementer SeuilBascule(cMax, r).");
    return null;  // a remplacer par l'implementation de l'etudiant
}

double? s = SeuilBascule();
Console.WriteLine("Seuil de bascule eps* = " + (s?.ToString(CultureInfo.InvariantCulture) ?? "None") +
                  "  (None tant que l'etudiant n'a pas implemente)");
Console.WriteLine("Indice pour l'etudiant : voir fonction AggregateBE au paragraphe 5 ; le seuil attendu est ~0.03.");
Exercice a completer : implementer SeuilBascule(cMax, r).
Seuil de bascule eps* = None  (None tant que l'etudiant n'a pas implemente)
Indice pour l'etudiant : voir fonction AggregateBE au paragraphe 5 ; le seuil attendu est ~0.03.

À retenir

  • L’objet formel : jeu à stratégies bornées \([2, \bar c]\), paiement \(\min(x,y)\) ± \(r\) selon le signe de \(x - y\) — re-dérivé en C# sans divergence.
  • Le claim corrigé : l’élimination itérée de stratégies faiblement dominées (jamais strictes : égalité structurelle face à \(y < k-1\) ; ordre-dépendante) laisse le survivant unique \([2]\) et l’équilibre unique \((2,2)\) pour \(r > 1\) ; à \(r = 1\) chaque \((y,y)\) est Nash — défaut hérité de 02c, corrigé ici, tracké là-bas par #15461.
  • Le contre-claim : (2,2) n’est pas une prédiction comportementale. Dès \(\varepsilon \approx 0{,}03\) de probabilité que l’autre grimpe, la meilleure réponse saute vers le haut (à \(\varepsilon = 0{,}05\) : 96).
  • Le seuil : le paradoxe se dissout pour \(r > 1\) ; à \(r = 1\) la spirale roule encore (domination faible partout + stricte quelque part) quoiqu’elle ne rapporte plus rien — la nuance que ce twin isole.
  • Ce que le twin a ajouté (critère de maturation de l’Epic §5.4) : un défaut hérité trouvé — les formulations « strictement dominées » et « unique dès \(r \ge 1\) » de 02c ne passent pas la re-dérivation (#15461) — et deux implicites nommés : la convention de tie-breaking de l’argmax (qui choisit un représentant d’un ensemble à \(r = 1\)) et la distinction « la spirale roule » / « la spirale rapporte ».

Expérience ICT candidate (héritée de 02c) : mesurer en TP le taux de « montée » (réclamation > 50) pour \(r=2\) vs \(r=25\), et confronter à la courbe de bascule de l’exercice 3 — test falsifiable du contre-claim.

Jumelage : ce notebook est le twin C#/.NET Interactive de GameTheory-02c-Travelers-Dilemma-Python (Python, distillation Basu 1994, même accrétion). Palier parent : GameTheory-02-NormalForm-Python · Autre moteur : GameTheory-02b-Lean-Definitions-Lean · Série : GameTheory

Retour au sommet