Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 113 additions & 10 deletions MyIA.AI.Notebooks/Search/Applications/CSP/App-7b-Wordle-CSharp.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
" The below script needs to be able to find the current output cell; this is an easy method to get it.\r\n",
" </div>\r\n",
" <script type='text/javascript'>\r\n",

" function timeout(ms, promise) {\r\n",
" return new Promise(function (resolve, reject) {\r\n",
" setTimeout(function () {\r\n",
Expand All @@ -56,10 +55,7 @@
" })\r\n",
" }\r\n",
"\r\n",


"\r\n",

"\r\n",
" if (!rootUrl.endsWith('/')) {\r\n",
" rootUrl = `${rootUrl}/`;\r\n",
Expand All @@ -74,7 +70,6 @@
" headers: {\r\n",
" 'Content-Type': 'text/plain'\r\n",
" },\r\n",

" }));\r\n",
"\r\n",
" if (response.status == 200) {\r\n",
Expand All @@ -86,8 +81,6 @@
" }\r\n",
"}\r\n",
"\r\n",


" .then((root) => {\r\n",
" // use probing to find host url and api resources\r\n",
" // load interactive helpers and language services\r\n",
Expand Down Expand Up @@ -136,13 +129,11 @@
" \r\n",
" \r\n",
" require_script.onload = function() {\r\n",

" };\r\n",
"\r\n",
" document.getElementsByTagName('head')[0].appendChild(require_script);\r\n",
"}\r\n",
"else {\r\n",

"}\r\n",
"\r\n",
" </script>\r\n",
Expand Down Expand Up @@ -177,6 +168,13 @@
"Console.WriteLine(\"C# Wordle twin -- marathon #4956. Kernel .net-csharp, 0 NuGet (algorithme from-scratch).\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'imprimé d'ouverture pose le cadre : « C# Wordle twin -- marathon #4956. Kernel .net-csharp, 0 NuGet (algorithme from-scratch) » — un jumeau C# du notebook Python, algorithme entièrement réécrit, sans la moindre dépendance NuGet. La sortie visible se résume à une ligne de flux, mais la cellule transporte aussi le script de découverte du kernel (HTML intégré) qui négocie la connexion .NET Interactive — c'est lui qui rendra possibles les rendus SVG des dernières sections. Le « 0 NuGet » est une contrainte de conception : feedback, filtrage, CSP, entropie — tout est écrit à la main dans ce qui suit."
]
},
{
"cell_type": "markdown",
"id": "4e4eba349a19",
Expand Down Expand Up @@ -272,6 +270,13 @@
"Console.WriteLine($\"Derniers : {string.Join(\", \", WORD_LIST.TakeLast(5))}\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"« Liste de mots : 297 mots de 5 lettres » — et la condition de parité est écrite dans le code : STRICTEMENT la même WORD_LIST que le notebook Python, pour que les entropies mesurées ici et là soient comparables mot pour mot. L'imprimé en montre les bornes : ouverture ABORD, ACIER, ADORE, AGENT… et fermeture VOLER, VOTER, YACHT, ZEBRA, ZONES. La liste vit en cellule (WORD_LIST construite depuis WORD_LIST_SRC) : chaque solveur qui suit la reçoit en argument, et les benchmarks de la fin n'évaluent donc que la stratégie, jamais la donnée. La parité est vérifiable en principe : les entropies imprimées plus bas (LAINE 5.5115 bits) doivent coïncider avec celles du notebook Python — même liste, même H."
]
},
{
"cell_type": "markdown",
"id": "979b81e7b5e8",
Expand Down Expand Up @@ -400,6 +405,13 @@
"}\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'encodage des feedbacks se lit sur les cinq tests : 0 = gris, 1 = jaune, 2 = vert. « CRANE vs CRANE : [GGGGG] 2,2,2,2,2 » (tout vert) ; « AIMER vs TABLE : [Y__Y_] 1,0,0,1,0 » (aucune lettre en bonne position, deux jaunes) ; « TRACE vs CRANE : [_GGYG] 0,2,2,1,2 » (lettres mélangées). Les deux derniers tests codent le cas piège — les lettres répétées : dans « ANNEE vs AIMER : [G__G_] », le second E d'ANNEE reste gris parce que le E de la réponse est déjà consommé par la position 4 (la règle anti-double-comptage du vrai Wordle, portée par le tableau used du code) ; dans « ARBRE vs BRAVE : [YGY_G] », B jaune, R vert, A jaune — mêmes lettres, statuts différents selon la position. Ce codage 0/1/2 est la donnée brute de tout le notebook : les patterns des histogrammes finaux sont des quintuplets de ces trois valeurs."
]
},
{
"cell_type": "markdown",
"id": "81c7814717af",
Expand Down Expand Up @@ -1023,6 +1035,13 @@
"DrawGridAscii(demoGuesses, demoFeedbacks, \"CROIX\", \"Demonstration ASCII\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La démo ASCII déroule une partie contre le secret CROIX : CRANE (V V . . .), COEUR (V J . . J), CRIER (V V J . .), puis CROIX — V V V V V. Quatre essais ; la légende est dans le code : V = bonne position, J = présent ailleurs, . = absent. Le décodage vérifie la mécanique : dans CRANE, C et R sont verts car C et R ouvrent CROIX ; dans COEUR, O est jaune (le O du secret est une position plus loin) et le R final jaune (le R du secret est en position 2) ; dans CRIER, I jaune — le I du secret est en position 4. La colonne du C est verte dès le premier essai et ne bouge plus : chaque ligne hérite des acquis."
]
},
{
"cell_type": "markdown",
"id": "08a64677a19f",
Expand Down Expand Up @@ -1101,6 +1120,13 @@
"Console.WriteLine(\"SimpleFilterSolver defini (filtrage + tirage aleatoire).\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"« SimpleFilterSolver defini (filtrage + tirage aleatoire) » — la brique minimale du notebook : conserver les mots compatibles avec le feedback reçu (le filtre du code), puis tirer au hasard dans ce qui reste. Toute la hiérarchie qui suit garde ce squelette et ne remplace qu'une chose : la manière de choisir le prochain candidat."
]
},
{
"cell_type": "code",
"execution_count": 6,
Expand Down Expand Up @@ -1372,6 +1398,13 @@
" Console.WriteLine($\"Moyenne (parties gagnees) : {guessCounts.Average():F1} tentatives ({wins}/{testWords.Length} gagnees)\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cinq mots secrets, cinq victoires du filtrage simple : 4, 3, 3, 2 et 3 essais — « Moyenne (parties gagnees) : 3.0 tentatives (5/5 gagnees) ». La trajectoire de CRANE montre la fonte des candidats : 297 → 56 (OLIVE, ____G) → 10 (FAUTE, _Y__G) → 3 (ARBRE, YG__G) → CRANE en GGGGG. Les autres suivent la même pente : FLEUR en 3 (PEAGE _Y___ : 297→20, LINER Y__YG : 20→1), MONDE en 3 (CREUX __Y__ : 297→66, TONNE _GG_G : 66→2), VAGUE en 3 (HUILE _Y__G : 297→15, BAGUE _GGGG : 15→1), et PISTE en 2 — REPAS (_YY_Y) ne laisse que 2 candidats. À chaque essai la taille chute d'un ordre de grandeur : la signature du filtrage sans information d'ouverture. Noter la note du code : le PRNG est ré-amorcé de façon déterministe par mot — chaque partie est reproductible isolément."
]
},
{
"cell_type": "markdown",
"id": "560455991f46",
Expand Down Expand Up @@ -1532,6 +1565,13 @@
"Console.WriteLine(\"CSPWordleSolver defini (domaines + MRV par comptage).\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"« CSPWordleSolver defini (domaines + MRV par comptage) » — la version contraintes du solveur : un domaine de lettres par position (le _domains du code), réduit à chaque feedback, et l'heuristique MRV — Minimum Remaining Values — qui instancie d'abord la position au domaine le plus petit. L'anti-thèse du tirage aléatoire : là où le filtrage devine, le CSP déduit."
]
},
{
"cell_type": "code",
"execution_count": 8,
Expand Down Expand Up @@ -1598,6 +1638,13 @@
"Console.WriteLine($\"\\n=> {(wonCsp ? \"Gagne\" : \"Perdu\")} en {guessesCsp.Count} tentative(s)\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Trois tentatives, et la chute est raide : PEINE (___GG) laisse 4 candidats sur 297, CANNE (GY_GG) n'en laisse qu'un — CRANE, GGGGG. La réduction de domaines élimine en un essai ce que le filtrage simple met deux essais à resserrer ; sur cette instance, le MRV conduit la recherche sans jamais deviner. Le candidat d'ouverture PEINE tombe à 4 restants dès le premier feedback : la propagation a fait le travail du premier tour d'entropie, sans calcul de distribution."
]
},
{
"cell_type": "markdown",
"id": "6b2d4366fd74",
Expand Down Expand Up @@ -1681,6 +1728,13 @@
"Console.WriteLine(\"ComputeEntropy / RankByEntropy definis.\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"« ComputeEntropy / RankByEntropy definis. » — l'entropie de Shannon appliquée aux partitions : pour un mot candidat, compter les secrets par pattern de feedback (le dictionnaire patternCounts du code), convertir les comptes en probabilités, sommer −p·log2(p). RankByEntropy n'est que le tri décroissant du résultat : gagne le mot qui découpe la liste en classes les plus fines."
]
},
{
"cell_type": "code",
"execution_count": 10,
Expand Down Expand Up @@ -1793,6 +1847,13 @@
"Console.WriteLine($\"(une seule tentative suffirait si H = {hMax:F1} bits)\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Huit mots passés au banc d'entropie : CRANE domine à 5,445 bits, devant TABLE (5,082), AIMER (5,027), ADORE (4,954), PISTE (4,818), REGLE (4,759), FLEUR (4,368) et EXTRA (4,128). Deux lectures : l'écart interne — 1,3 bit sépare le meilleur du pire de l'échantillon, soit plus que le double d'information sur la question « quel pattern vais-je recevoir ? » — et le plafond imprimé, « Entropie maximale theorique : log2(297) = 8.214 bits », qu'atteindrait une tentative partitionnant la liste en 297 patterns équiprobables. Même le meilleur mot de la liste restera à ~2,8 bits de ce plafond : aucun mot de 5 lettres françaises ne découpe 297 secrets en classes fines."
]
},
{
"cell_type": "markdown",
"id": "4752abea370f",
Expand Down Expand Up @@ -2009,6 +2070,13 @@
"Console.WriteLine($\"Reduction attendue : {WORD_LIST.Count} -> ~{WORD_LIST.Count / Math.Pow(2, bestH):F0} candidats en moyenne\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Le top-10 imprimé : LAINE 5.5115 bits, PAIRE 5.4658, CLAIR 5.4650, CARTE 5.4638, CRANE 5.4451 — puis TRACE, TRAIN, LANCE, PARIE et ANCRE (5.2983). « Meilleur premier mot : LAINE (H = 5.5115 bits) », pour une « Reduction attendue : 297 -> ~7 candidats en moyenne ». Le podium est serré — 0,046 bit entre LAINE et PAIRE, 0,213 bit sur tout le top-10 : le premier mot optimal n'est pas unique, la famille LAINE/PAIRE/CLAIR/CARTE est interchangeable à l'ouverture. La parité Python/C# est le point de la démonstration, rendue en HTML intégré au kernel (zéro NuGet). LAINE réapparaîtra partout dans la suite : ouverture imposée des tests du solveur par entropie, « Meilleur mot » de la distribution de patterns."
]
},
{
"cell_type": "markdown",
"id": "632962a5d7bd",
Expand Down Expand Up @@ -2088,6 +2156,13 @@
"Console.WriteLine(\"EntropySolver defini (max d'information a chaque tour).\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"« EntropySolver defini (max d'information a chaque tour). » — l'instanciation de la stratégie : premier mot pré-calculé (le LAINE du top-10, passé en paramètre precomputedFirst), puis à chaque tour recalcul de l'entropie sur les seuls candidats restants. Quand il n'en reste plus qu'un ou deux, les tests qui suivent le montrent : H tombe à 1.00 puis 0.00, et la réponse est jouée. Les cinq tests de la section suivante l'exécutent : chacun s'ouvre sur LAINE, quel que soit le secret."
]
},
{
"cell_type": "code",
"execution_count": 13,
Expand Down Expand Up @@ -2292,6 +2367,13 @@
" Console.WriteLine($\"Moyenne (parties gagnees) : {countsE.Average():F1} tentatives ({winsE}/{testWords.Length} gagnees)\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"« Moyenne (parties gagnees) : 3.2 tentatives (5/5 gagnees) » — chaque partie s'ouvre sur LAINE (H=5.51) et se solde en 3, 3, 3, 4 puis 3 essais. Les trajectoires complètes : CRANE en 3 (ARENE YG_GG, H=1.00, 2 candidats), FLEUR en 3 (GELER _YY_G, H=2.32, 5 candidats), MONDE en 3 (CONTE _GG_G, H=2.20, 9 candidats puis H=1.00 sur 2), VAGUE en 3 (BARGE _G_YG, H=3.06, 21 candidats), et la plus longue PISTE en 4 : LAINE (__Y_G, 11 candidats) → VITRE (_GY_G) → MIXTE (_G_GG, 2 candidats) → PISTE en GGGGG. L'entropie affichée par essai raconte la convergence : 5.51 → 3.28 → 1.00 → 0.00. Sur ce petit banc de 5 mots, la moyenne (3.2) ne bat pas celle du filtrage sur son propre banc (3.0) — deux bancs différents ; la mesure qui fait foi est celle de 50 mots, section suivante : 2.60 contre 2.80."
]
},
{
"cell_type": "markdown",
"id": "250024cbd60b",
Expand Down Expand Up @@ -2418,6 +2500,13 @@
"Console.WriteLine($\"{\"3. Entropie (deterministe)\",-28}{Avg(cEnt),-20:F2}{(double)wEnt/benchWords.Count,-15:P0}\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Le banc de 50 mots secrets départage les trois solveurs — tous à « 100 % » de réussite : « Entropie (deterministe) 2.60 », « Filtrage simple 2.80 », « CSP (propagation) 2.88 ». L'ordre est celui de l'information d'ouverture : chaque bit d'entropie au premier essai vaut ici ~0,1 essai de moyenne, l'entropie terminant 0,2 essai avant le filtrage et 0,28 avant le CSP. La note du titre — « graine 42 pour les solveurs aleatoires » — fixe les tirages pour que la comparaison soit reproductible. Le CSP (propagation) ferme la marche : déduire coûte plus de calculs par essai que filtrer, et ne rattrape pas une ouverture informée."
]
},
{
"cell_type": "markdown",
"id": "259393b62e57",
Expand Down Expand Up @@ -2526,6 +2615,13 @@
"Console.WriteLine($\" {best} disperse les secrets (information elevee), {worst} les concentre (information faible).\");\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deux ouvertures, deux distributions : « Meilleur mot : LAINE -> 76 patterns distincts, H = 5.512 bits », dont les cinq patterns les plus fréquents restent modestes (33, 21, 18, 16, 11) ; « Mauvais mot : APPEL -> 37 patterns distincts, H = 4.053 bits », concentré sur deux pics (67 et 44). La moyenne parle autant que les extrêmes : 76 patterns sur 297 secrets ≈ 3,9 secrets par pattern pour LAINE, contre ≈ 8 pour les 37 d'APPEL — diviser par deux la taille moyenne des classes, c'est exactement le bit et demi d'écart (5,512 contre 4,053). L'imprimé conclut : « LAINE disperse les secrets (information elevee), APPEL les concentre (information faible). » C'est la lecture probabiliste du top-10 : LAINE premier non par magie, mais parce que ses 76 classes sont les plus équilibrées."
]
},
{
"cell_type": "markdown",
"id": "72f10fcd841e",
Expand Down Expand Up @@ -2618,6 +2714,13 @@
"HistogramSvg(worst, distWorst);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Les deux histogrammes SVG mettent les distributions ci-dessus en image : celle de LAINE s'étale plate (axe jusqu'à 33, barre dominante « 0,0,0,0,2 »), celle d'APPEL culmine à 67 (« 0,0,0,1,0 ») — la concentration visuelle du déficit d'information. Les axes trahissent la même asymétrie : pic au double de hauteur pour moitié moins de patterns distincts. Le tri par fréquence décroissante fait lire d'un coup d'œil ce que le compte de patterns (76 contre 37) mesure : deux figures, un message — la forme de la distribution est elle-même l'information du mot."
]
},
{
"cell_type": "markdown",
"id": "aa2daef17794",
Expand Down Expand Up @@ -2825,4 +2928,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Loading
Loading