Au programme de SNT, on peut lire parmi les capacités attendues :
Traiter par programme une image pour la transformer en agissant sur les trois composantes de ses points.
Or puisque les points (pixels en fait) d’une photo ont trois composantes, c’est que les couleurs sont placées dans un espace de dimension 3, en prenant
pour abscisse le niveau de rouge (de 0 à 255),
pour ordonnée le niveau de vert (de 0 à 255),
pour hauteur le niveau de bleu (de 0 à 255).
Changer les couleurs d’une photo (par exemple pour la désaturer) peut donc se faire par des transformations de l’espace de dimension 3. Bouger des points (y compris par des rotations dans l’espace) peut se faire avec Css, mais ici, c’est bien dans l’espace des couleurs qu’on va effectuer ces transformations (symétries, rotations, homothéties, projections) et ce sont des couleurs qu’on va changer. Et, comme par ailleurs, le programme de SNT précise aussi :
Au moment de la conception de ce programme, le langage choisi est Python version 3 (ou supérieure).
C’est donc en Python qu’on va programmer ces filtres photo, avec le module PIL :
from PIL import Image
img = Image.open('CamphrierTampon2.jpg')
(largeur,hauteur) = img.size
On constate que les dimensions de la photo forment un couple (largeur,hauteur). Les élèves sont censés retoucher leurs propres photos mais au cas où, il a été choisi cette photo sous licence Creative Commons, extraite de l’article sur Le Tampon :
On remarque qu’il y a une voiture bleue tout à gauche, et, à côté de celle-ci, une voiture rouge. Cela va changer si on inverse les composantes rouge et bleue :
Symétrie
On effectue une symétrie par rapport au plan vert (les composantes rouge et bleue sont égales) en parcourant l’image pixel par pixel, et en lisant les couleurs du pixel, puis en modifiant celui-ci avec (b,g,r) au lieu de (r,g,b) :
for x in range(largeur):
for y in range(hauteur):
(r,g,b) = img.getpixel((x,y))
img.putpixel((x,y),(b,g,r))
Sans surprise, la voiture rouge est devenue bleue, et vice-versa :
Pour voir la photo retouchée, on peut faire
img.show()
mais il faut attendre quelques secondes pour que le script Python ait parcouru tous les pixels de l’image…
Rotations
On tourne autour de l’axe des gris, de 120°, d’abord dans un sens avec img.putpixel((x,y),(g,b,r)) :
puis dans l’autre avec img.putpixel((x,y),(b,r,g)) :
Projections
Pour projeter sur le plan des images sans rouge, on fait img.putpixel((x,y),(0,g,b)), ce qui donne une image sans rouge :
et pour projeter sur le plan sans bleu, on fait img.putpixel((x,y),(r,g,0)), ce qui donne une image sans bleu (ce qui imite certaines formes de daltonisme) :
On peut aussi projeter sur une droite comme celle des gris (r=g=b), avec img.putpixel((x,y),(g,g,g)), ce qui donne une image en niveaux de gris :
Géométrie de l’espace des couleurs
Homothéties
Comme on veut éviter de dépasser 255 comme valeur de rouge, vert ou bleu, on choisit un rapport d’homothétie inférieur à 1, par exemple 1/2. On va donc remplacer chaque composante par sa moitié entière avec img.putpixel((x,y),(r//2,g//2,b//2)) ce qui donne une image plu sombre :
Solarisation
On peut faire des choses plus riches, par exemple en donnant à chaque pixel une teinte en arc-en-ciel, qui évoque une solarisation, avec la formule img.putpixel((x,y),(255-g,255-abs(g-128),g)) :
Géolocalisation
L’arbre photographie ci-dessus se trouve aux coordonnées (-21.281207 , 55.521736) ce qui permet de le voir d’un autre angle sur photos satellite, par exemple sur Géoportail :
ou sur maps :
Le « gigantesque pied de bois » est en fait un ravintsara.
Un des problèmes soumis au camp Zabey et brillament résolu par les lycéennes. Bravo ! Le lien permet de constater que celui qui commence a effectivement une stratégie gagnante…
Une page pour connaître à chaque instant le lieu de passage du soleil au zénith, c’est à dire l’endroit sur terre où l’on peut mesurer la hauteur du soleil à 90° au dessus de l’horizon.
Activités pédagogiques :
Trouver la date et l’heure du prochain et du dernier passage du soleil au zénith de l’observatoire des Makes à La Réunion. Astuce : la date et l’heure peuvent être modifiées avec le formulaire.
Repérer l’ensemble des lieux où le soleil peut un jour passer au zénith. Distinguer en fonction des saisons. Astuce : les lignes de l’équateur et des tropiques sont dessinées sur la carte.
Les deux jeux présentés ici font appel à la motricité fine, et sont donc intéressants à pratiquer dès le CP, voire la GS. Leurs noms vient de ceux de leurs auteurs (Bim est Busser’s nim et Schim est Schilli’s nim). Les deux jeux proviennent de tentatives d’améliorations de Rim, jeu créé à la va-vite par John Conway, et ici décrit (page 131 de On Numbers and Games) :
En fait, le jeu décrit par Conway, tel quel, est relativement sans intérêt, puisqu’il y a une stratégie gagnante simple pour celui qui joue en premier : il n’a qu’à tracer une courbe fermée passant par tous les points et son adversaire est bloqué.
Dans Bim, on ajoute la règle suivante : au premier coup on trace une courbe passant par au moins un point, mais pas par tous les points.
Dans Schim on ne dit rien sur le premier coup, mais la courbe tracée doit obligatoirement passer par 1, 2 ou 3 points qui ne sont pas déjà sur une courbe. Schim est dont une généralisation de Rayles décrit ci-dessus.
Bim
On appelle libre un point qui n’est pas sur une courbe. Voici donc la règle de Bim ;
Le jeu se joue sur une constellation. Le premier trace une courbe passant par au moins un point, et lassant libre au moins un point. Ensuite chaque joueur à son tour, trace une courbe passant par au moins un point libre, et ne croisant ni touchant aucune courbe déjà tracée. Le premier qui ne peut plus jouer a perdu.
Le jeu se joue sur une constellation. Chaque joueur à son tour trace une courbe passant par 1, 2 ou 3 points libres et ne croisant ni touchant aucune courbe déjà tracée. Le premier qui ne peut plus jouer a perdu.
Schim est le jeu octal 0.777.
On convient que dès qu’une courbe en croise une autre (ou elle-même) ou passe par plus de 3 points libres ou par un point non libre, le joueur ayant fait l’erreur a perdu.
Voici quelques exemples de parties de Schim jouées en (fin de) CP :
Exemples d’erreurs
Ces exemples sont utiles pour expliquer la règle du jeu : on demande aux élèves quelles sont les erreurs :
Une courbe passe par plus de 3 points, et deux courbes passent par un même point.
Le petit cercle orange à gauche, passe par un point qui est sur une courbe, ou croise cette courbe.
Une courbe reliant l’unique point libre à un point déjà sur une courbe, a été tracée par erreur, puis barrée, ce qui a caché le fait que le point tout à droite est libre et que le jeu n’était donc pas fini.
La courbe fractale se croise elle-même.
L’hypoténuse du triangle à gauche, n’arrive pas vraiment à éviter un point non libre.
Deux points sont sur deux courbes (en gris).
La courbe jaune passe par 5 points.
Deux courbes marron sont tangentes.
La courbe bleue passe par 4 points.
La courbe fractale s’auto-croise.
Les verts n’ont pas vu qu’il reste un point libre et ont cru perdre alors qu’ils gagnaient.
Une courbe passe deux fois par le même point.
Deux courbes se croisent (et en plus l’une est de trop, les verts n’ayant joué qu’une fois).
Si on ne triche pas, le jeu se termine sur des sommets qui sont tous de degré 2 ; ici il y a même un sommet de degré 4 !
Le théorème de Jordan (toute courbe fermée sans point double sépare le reste en un intérieur et un extérieur) n’est pas une vision naturelle en CP (au début, aucun élève n’a pensé à englober des courbes dans une courbe, par contre la notion de courbe fractale semble être présente, comme on le voit sur certains exemples ci-dessus.
Exemples de parties terminées
Pour analyser des parties, il vaut mieux imposer la couleur bleue à celui qui joue en premier et la couleur rouge à celui qui joue en deuxième, afin de pouvoir expliquer qui a gagné au cas où il y a un nombre pair de courbes. Voici quelques parties remarquables observées en CP :
Les élèves peuvent très bien jouer sur une ardoise, sur laquelle ils adorent d’ailleurs dessiner les points avant de jouer. Mais il vaut mieux les faire jouer juste avant la sonnerie, car le jeu les excite. Comme cela avait déjà été fait avec Sprouts (auquel Bim et Schim ressemblent un peu), on peut même envisager un tournoi au tableau avec Velleda.
On détermine qui joue en premier, avec Pierre-Feuille-Ciseaux : le gagnant à Pierre-Feuille-Ciseaux joue la couleur bleue et commence. Quand le jeu est terminé, il est intéressant de l’examiner avec les élèves et débattre de qui a gagné et pourquoi. S’il y a un nombre pair de courbes, c’est Bleu qui a gagné, sinon c’est celui qui a tracé une courbe de plus que l’autre (sauf s’il y a eu triche comme deux courbes qui se croisent ou une courbe qui passe par plus de 3 points).
Il peut être intéressant aussi de donner des problèmes de Schim du type Rouge joue et gagne en un coup. Et surtout, le jeu faisant travailler les nombres 1, 2 et 3 et la motricité fine, serait probablement intéressant à explorer en cycle 1, ou en début de CP : on voit plus haut sur les exemples, la maîtrise qu’ont certaines élèves en tracé de cercle circonscrit à la main, ainsi qu’un cercle de diamètre donné, il serait intéressant de comparer ces performances avec celles d’élèves plus jeunes, mais aussi avec celles d’élèves plus âgés : l’idée de séparer les points par une courbe n’est apparue spontanément à aucun.e élève de CP.
Des élèves, avides de jouer sur beaucoup de points, ont inventé une variante de Schim où on a aussi le droit de passer par 4 points :
Mais ils n’ont pas pensé à englober des points ou des courbes dans une courbe, d’ailleurs ils voyaient des polygones remplis :
Combien de points ?
Manifestement, le jeu testé en CP avec 7 points au départ, est peu intéressant. Or il se trouve que le nombre de Grundy de ce jeu est 3 qui n’est pas énorme. Un critère de choix de nombre initial de points pour lequel la stratégie gagnante n’est pas évidente est un nombre de Grundy élevé. Par exemple, 6, 10, 11, 14, 16, 33 ou 34 points semblent intéressants :
Ces nombres de Grundy ont été calculés avec ces fonctions Python, utilisant la programmation dynamique :
def mex(tab):
for k in range(len(tab)+1):
if k not in tab:
return k
def decomp(n):
return [(i,n-i) for i in range(n//2+1)]
def grundy(n):
tab = list(range(5))
for k in range(5,n+1):
sommes = set()
for j in range(k-1,k-4,-1):
for t in decomp(j):
sommes.add(tab[t[0]]^tab[t[1]])
tab.append(mex(sommes))
return tab[n]
On constate que pour un nombre raisonnable de points, le nombre de Grundy 9 ne semble pas exister. En fait il faut attendre jusqu’à 418 points pour que le nombre de Grundy soit 9 :
La répartition des nombres de Grundy semble aussi complexe que celle pour ce jeu inventé par un autre Patrick, ce qui montre que Schim est un jeu objectivement intéressant. En plus, il est praticable très jeune, alors pourquoi se priver ?
Dans les programmes de 2025, on aborde les fractions dès le cycle 2, selon la progression suivante :
En CE, on aborde des fractions de grandeurs (en général continues : par exemple pour obtenir les trois quarts d’une pizza, on coupe la pizza en quatre parts égales, puis on en prend trois ; la plupart des enfants savent qu’il est impossible de couper une pizza en quatre parts rigoureusement égales…),
en CM, on passe progressivement des fractions de grandeurs, à des fractions de mesures,
en 6e, on apprend qu’en fait les fractions sont des nombres (quotients d’entiers).
une fraction est le quotient de deux entiers (numérateur et dénominateur)
Le programme, qui s’inspire de (Neagoy 2017), n’aborde cependant pas les ratios, clairement évoqués dans l’ouvrage de référence. L’activité présentée ici, par contre, permet aux élèves de réinventer le concept de ratio. En n’évoquant que des grandeurs discrètes (et donc des mesures entières), elle est clairement hors programme. Cependant elle permet de parler de fractions dès le CE 1 avec simplicité et efficacité, et elle reste basée sur de la manipulation d’objets. Elle est basée sur le modèle de Bernoulli qui n’est n’est pas du tout évoqué dans les programmes, mais qui permet d’aborder aussi la notion de probabilité : chez Bernoulli, la probabilité (lorsqu’on extrait un pion de l’urne) que le pion soit noir, est égale à la proportion de pions noirs dans l’urne, comme on le voit avec cette activité en ligne qui existe depuis plus de 10 ans et a déjà servi comme question flash en CM.
L’activité proposée ici dure environ une heure et est composée de deux parties : au début on distribue des pions de deux couleurs (que les élèves connaissent pour avoir joué à alquerkonane la semaine précédente) et on demande à chaque élève la proportion de pions noirs sur le total de pions ; et dans la seconde partie on demande aux élèves de troquer des pions de manière que la proportion de pions noirs soit une fraction donnée (ici, 1/2).
Première partie : trouver la fraction
Sur l’ardoise, les élèves écrivent la phrase à trous suivante :
Il y a …… pions noirs sur …… pions.
Puis on leur distribue un nombre aléatoire de pions de chaque couleur, et ils doivent compléter la phrase. Par exemple voici la fraction 7/17 :
et la fraction 4/11 :
Au début, beaucoup d’élèves ont tendance à ne compter que les pions blancs au lieu de tous les pions, ce qui aboutit à un ratio (ci-dessus 4:7) au lieu d’une fraction.
Ceci dit, une fois comprise la différence entre ratios et fractions, les élèves continuent à compter les pions blancs, parce qu’il est plus simple (dès le CE 1) d’additionner les nombres de pions noirs et pions blancs, plutôt que compter tous les pions.
Souvent, les élèves placent les pions de manière à pouvoir les comparer :
(fraction 7/12)
(fraction 4/11)
(fraction 5/14)
(fraction 4/11)
(fraction 8/13)
(fraction 4/11)
Simplification de fractions en CE 1
Pour trouver les nombres de pions noirs et de pions blancs, des élèves de CE 1 ont tendance à simplifier l’urne en sous-urnes identiques entre elles. Ce qui révèle des écritures différentes d’une même fraction :
4/12 = 1/3
4/10 = 2/5
3/9 = 1/3
6/12 = 1/2
qui amène à citer un théorème totalement hors programme :
Lorsqu’une urne peut être décomposée en sous-urnes toutes identiques entre elles, la proportion de pions noirs dans l’urne est la même que la proportion de pions noirs dans chacune des sous-urnes.
Cependant, il faut bien faire attention au début de l’énoncé, de peur que plus tard des élèves en déduisent à tort des choses comme 2/3+4/5 = (2+4)/(3+5).
Cette manière de montrer la simplification de fractions, outre le fait qu’elle a été inventée par des élèves de CE 1, présente sur la manière classique l’avantage de ne pas nécessiter d’hypothèse sur la stricte égalité entre mesures continues et de ne parler que de nombres entiers, conformément au programme de cycle 4 de 2026.
Version cours moyen
En CM, on apprend plutôt à compliquer les fractions, qu’à les simplifier. Par exemple pour montrer visuellement que 4/12 = 1/3, on commence par représenter 1/3 (en supposant que les trois rectangles sont rigoureusement superposables) :
et ensuite, on découpe chaque rectangle en 4 parties (rigoureusement, bien sûr, puisque tout un chacun sait que c’est possible) égales :
et, comme on compte 3 carrés cyan sur 12 carrés, la fraction 1/3 est égale à la fraction 4/12.
Version CE 1
Là, on part de 12 graines dont 4 sont noires (on n’a donc effectivement que des entiers) et on arrange les graines pour faire apparaître des motifs identiques, ayant chacun un pion noir sur trois :
Encore une fois, ce modèle
est basé sur des nombres entiers et pas des rectangles,
part de la fraction compliquée 4/12 et va vers une simplification,
est basée sur une vraie manipulation d’objets et non un simple dessin de rectangles.
Ce qui peut expliquer son succès auprès d’élèves de CE 1, qui en sont d’ailleurs les auteurs. Reste à produire des exercices du style simplifier les fractions par un jeu sérieux…
Construire la fraction 1/2
La première version de l’exercice, en ligne, consistait à créer soi-même une proportion imposée par l’énoncé. Pour la version pions, l’énoncé suivant a été écrit au tableau :
La moitié des pions sont noirs.
puis il a été demandé aux élèves de faire en sorte que ce soit vrai, par troc avec l’animateur :
soit on demande des pions en plus,
soit on se débarrasse de certains pions,
et, dans chacun des cas, on précise le nombre et la couleur des pions.
Dans de rares cas, il y avait déjà la bonne proportion de pions, mais même là, les élèves ont essayé de changer la représentation de la fraction 1/2. En fait le but est d’avoir autant de pions de chaque couleur.
En fait, il s’agit d’un exercice de soustraction. Par exemple si j’ai 7 pions noirs sur 17 pions, c’est que j’ai 10 (obtenu par soustraction) pions blancs, donc un trop plein de pions blancs. Alors je peux
me débarrasser des pions blancs superflus (10 pions – 7 pions = 3 pions, ou je les vois par alignement),
ou demander des pions noirs : il en faut 10-7=3.
La seconde méthode, bien que plus compliquée, a été souvent préférée des élèves, aboutissant à des fractions de grand dénominateur et pas forcément égales à 1/2. Par exemple à partir de 7/17 on est arrivé à 12/22 :
Une élève de CE 1, semblant viser d’instinct la fraction 2/3, a régulièrement constaté qu’il y avait trop de pions noirs, et a essayé de compenser cela en ajoutant d’autres pions noirs, ce qui n’a fait qu’aggraver la situation :
Une seule élève (CE 2) a pensé à se débarrasser de presque tous ses pions en n’en laissant qu’un de chaque couleur : la moitié c’est un sur deux donc un pion noir et un pion blanc. D’autres fractions, plus complexes, ont été trouvées :
8/16 :
6/12 :
5/10 :
Avec, souvent, une recherche esthétique impressionnante :
Presque 1/2
En CE 2, il y a eu parfois des difficultés à arriver pile sur 1/2. Voici quelques erreurs constatées :
Dans ces deux derniers cas, il est possible que les élèves en soient restés à la partie précédente, ou qu’ils aient visés d’autres fractions que 1/2 (1/3 ci-dessus). Cependant, il y a eu de réelles difficultés de planification des tâches (la soustraction n’est pas toujours maîtrisée) et la fraction ci-dessus peut aussi provenir de la recherche d’un ratio 1:2 plutôt que celle d’une fraction 1/2.
Conclusion
Cette activité est hors programme, mais elle est axée sur la représentation d’une fraction comme quotient de deux nombres entiers, permet de découvrir instinctivement les notions de ratio et de probabilités, est basée sur un cycle entre manipulation et observation, et peut être traitée dans le cadre de la résolution de problèmes. Il serait donc dommage de ne pas la tester, ne serait-ce qu’en APC.
Voici un petit livret pouvant servir ne serait-ce que pour les dessins :
14h-14h30 Le rendez-vous Jeu d’Alain busser: Retour aux jeux de Nim
14h30-15h00 Retour sur la semaine des Maths: 323 élèves au tournoi d’Alquerkonané,Patrick Schilli, Alain Busser, Julien Bominthe
15h-16h10 Atelier de réflexion/débat: C’est quoi le problème avec les probas? Pan des mathématiques souvent mal aimé des élèves, des étudiants et il faut le dire, de certains enseignants aussi, les probabilités posent des problèmes à beaucoup de monde. D’Alembert ne vous aurait pas jeté la pierre… A l’heure où les probabilité et les statistiques sont partout, diagnostiquer pourquoi elles sont boudées et/ou redoutées sera notre point de départ pour une réflexion pour savoir comment réconcilier les élèves et leurs enseignants avec l’apprentissage (et la beauté!) des probabilités. Marion Le Gonidec
PAUSE
16h30-18h Atelier de réflexion/test: Résolution de problèmes. Présentation et tests de ressources autour de la résolution de problèmes au cycle 3, en lien avec les programmes, Karim Bouasla, Pascal Dorr, Claire Lagarde.
C’est dans le cadre de recherches sur les algèbres de Kac-Moody en physique théorique, que dans les années 1950, P.C. Welter d’une part, Mikio Sato de l’autre, ont inventé le jeu étudié ici. Le jeu se joue sur un plateau similaire à celui de Sowing, avec des cases alignées. Des pions ou des jetons sont posés sur le plateau de jeu, de telle manière qu’il n’y ait jamais plus d’un pion par case.
Un tour de jeu consiste à déplacer un pion vers la gauche, d’au moins une case, et vers une case vide (sinon il y aurait plusieurs pions dans une même case, ce qui est interdit par la règle du jeu). Par exemple à partir de la situation ci-dessus il y a 8 possibilités dont celle-ci :
(le pion le plus dextre est parti dans la troisième case)
Le coup ci-dessus est d’ailleurs un mauvais coup, parce que l’adversaire peut alors jouer à son tour le pion le plus dextre et arriver à cette situation bloquante :
En effet, dès que tous les pions sont tassés à gauche comme ci-dessus, le jeu est terminé, et le premier qui ne peut plus bouger aucun pion a perdu le jeu.
Ce jeu est praticable dès lors qu’on sait
distinguer le nombre 1 des nombres supérieurs à 1 (il est interdit d’avoir plus d’un pion par case),
distinguer la gauche et la droite (on joue vers la gauche),
ce qui vise la fin du CP et les classes suivantes.
Conway consacre à ce jeu un chapitre entier (le 13) de son livre On Numbers And Games :
La stratégie gagnante est en effet basée sur des notions subtiles de théorie des groupes et de physique théorique, et n’est pas très simple à décrire. Ceci dit, le cadre du jeu est lui-même assez simple avec des jetons sur une bande, comme cette figure de Conway :
(on constate que Conway utilise bien des jetons plats, empilables, mais des pions comme ceux du jeu d’échecs sont également possibles, d’autant que ci-dessus il n’y en a que 8, en effet il est matériellement difficile de mettre des pions du jeu d’échecs l’un au-dessus de l’autre)
Le jeu de Welter est impartial, donc on peut chercher une stratégie gagnante sans utiliser l’arsenal théorique de Conway : il suffit de tracer le graphe du jeu et de le colorier pour trouver une stratégie gagnante. Pour cela, il est intéressant de modéliser le jeu (et son plateau) en Python.
Codage du plateau
Le plateau du départ :
est assez naturellement codé par un tableau de booléens :
[True,False,False,True,False,True,True,False]
ou (ce qui revient au même) par un tuple d’entiers :
(1,0,0,1,0,1,1,0)
ou encore (c’est ce qu’on ferait en C ou Rust) par un octet 10010110 soit le nombre entier 150) mais aucune de ces représentations ne permet facilement de coder un tour de jeu. Ceci dit, on constate la présence
d’un variant : la somme des indices des cases où il y a un pion (un tour de jeu consiste à faire décroître l’un de ces indices, donc leur somme),
d’un invariant : le nombre de pions est toujours égal au nombre initial de pions, puisqu’on ne retire ni ne rajoute un pion au cours du jeu (comme disait Lavoisier, rien ne se perd, rien ne se crée, tout se transforme).
Or il y a un moyen de coder cet invariant, non dans une variable, mais comme longueur d’une collection, celle des abscisses des jetons. Finalement le plateau
sera codé par l’expression (0,3,5,6) donnant (dans l’ordre croissant) les abscisses des 4 pions, le pion le plus à gauche ayant pour abscisse 0. C’est cette modélisation qui sera choisie dans la suite, ne serait-ce que parce que le tuple
(0,3,5,6)
occupe 56 octets de mémoire, alors que la chaîne de caractères
|o| | |o| |o|o|
en occupe 64.
Utilitaires
Tout d’abord, on regarde les pions de gauche à droite, sans tenir compte de l’historique de leurs déplacements, donc (0,3,5,2) peut aussi être représenté sous la forme (0,2,3,5) où les abscisses sont triées dans l’ordre croissant. On définit alors une fonction can donnant la forme canonique d’un tuple (ou plateau de jeu) :
def can(p):
return tuple(sorted(list(p)))
(on convertit le tuple en une liste, qui est mutable, on trie cette liste et on convertit le résultat à nouveau en un tuple)
Ainsi, dans le dessin du graphe d’un jeu de Welter, on évitera d’avoir plusieurs sommets correspondant à la même situation de jeu.
Ensuite, on définit une fonction str1, destinée à remplacer str, qui donne une chaîne de caractères plus lisible que le tuple, avec des pions représentés par la lettre o et des bords de cases représentés par des traits verticaux :
def str1(p):
tab = [' ']*(1+max(p))
for x in p:
tab[x] = 'o'
return '|'+'|'.join(tab)+'|'
On constate au passage que les cases vides à droite ne sont plus représentées, en effet elles ne servent plus à rien dans le jeu.
Enfin, un dernier utilitaire pour la suite est la vérification que chaque case ne contient au maximum qu’un seul pion :
def ok(p):
return all(p[i]!=p[j] for i in range(len(p)) for j in range(i))
En effet, dire qu’aucune case ne contient plus d’un pion, c’est vérifier qu’aucune abscisse n’est présente plus d’une fois dans le tuple. A priori, c’était le point faible de cette modélisation (avec un tableau d’entiers on aurait juste eu à écrire all (x in (0,1) for x in tab)) et le fait d’avoir réussi à exprimer la condition en termes d’abscisses est garante de la réussite du modèle.
Règle du jeu
On définit maintenant une fonction tenter qui modifie le plateau (un tuple d’abscisses entières) en respectant la règle du jeu :
def tenter(p,i,j):
if j<p[i]:
q = list(p)
q[i] = j
if ok(q):
return can(q)
La fonction prend en entrée un plateau de jeu p (un tuple d’abscisses de pions), un indice de départ i (p[i] est l’abscisse du pion à bouger) et une abscisse j d’arrivée (là où ira le pion si possible). Si on peut bouger le pion d’indice i, son abscisse sera j, et si on ne peut pas (parce que j est trop grand – trajet vers la droite ce qui est interdit par la règle du jeu – ou parce que cela aboutirait à plus d’un pion sur une case) alors la fonction ne renvoie rien, ou plutôt elle renvoie None. Si le mouvement est possible, la fonction renvoie le nouveau plateau can(q).
La modélisation du jeu de Welter en Python est, à ce stade, terminée. Il reste juste à construire le graphe du jeu.
Graphe du jeu
Fidèle au programme de NSI (Terminale), on choisit de représenter le graphe par listes d’adjacences. Dans un graphe orienté, on dit qu’un sommet est enfant d’un autre s’il y a un arc les joignant, et la liste d’adjacence d’un sommet est la liste de ses enfants. On la calcule par une fonction, en tenant compte du fait que l’indice i ne peut pas dépasser la longueur du tuple, et que l’abscisse j ne peut pas dépasser p[i] :
def enfants(p):
tab = []
for i in range(len(p)):
for j in range(p[i]):
t = tenter(p,i,j)
if t is not None:
tab.append(t)
return tab
Dans la liste tab, on n’a gardé que les positions possibles à partir de p, c’est-à-dire celles pour lesquelles la tentative renvoie autre chose que None.
Pour construire le graphe (un dictionnaire dont les valeurs sont des listes d’adjacence), on utilise le fait que sum(p) est un variant, donc que sa valeur initiale majore le nombre de boucles à parcourir :
def graphe(p):
g = {p: enfants(p)}
for n in range(sum(p)):
gr = {k:v for k,v in g.items()}
for k,v in g.items():
for u in v:
if u not in gr:
gr[u] = enfants(u)
g = gr.copy()
return g
On travaille sur une copie gr du graphe, parce que Python interdit (à juste titre) de modifier un objet mutable en cours de parcours dudit objet. Avec cette fonction on peut construire le graphe d’un jeu (utile pour chercher la stratégie gagnante) :
Pour trouver la stratégie gagnante, il n’y a même pas besoin de calculer tous les nombres de Grundy, mais seulement colorier en vert les sommets de nombre de Grundy nul (et en rouge les autres). Cela revient à l’algorithme suivant :
si un sommet a au moins un enfant vert, alors il est rouge,
si un sommet n’a que des enfants rouges (ou pas d’enfant du tout) alors il est vert.
La stratégie gagnante est alors de systématiquement viser un sommet vert. Mais avec le jeu
C’est à Nicholas de Bruijn que Conway et Gardner attribuent ce jeu. La variante sans le dollar d’argent est peut-être inspirée d’un jeu que Conway attribue à un certain Northcott, mais que Charles Béart décrit comme traditionnel de l’Ouest africain, sous le nom de Tiouk Tiouk. Quoiqu’il en soit, le jeu du dollar d’argent sans le dollar est une variante du jeu de Welter, où il n’est plus permis de sauter par-dessus un jeton, mais seulement de glisser un jeton (et il est toujours interdit d’empiler les jetons, contrairement à la version avec le dollar). Le plateau de jeu est donc le même que pour le jeu de Welter, comme en témoigne cette figure de Conway :
La théorie de ce jeu est plus simple que celle du jeu de Welter : dans la position ci-dessus, on est face à un jeu de Nim dont les tas ont pour hauteurs respectives 0, 2, 4, 5 et 3. Pour ce qui est de la modélisation en Python, la seule différence avec le jeu de Welter est qu’on ne peut pas sauter par-dessus un pion, ce qui s’exprime par une contrainte supplémentaire dans la fonction tenter :
def tenter(p,i,j):
if j<p[i] and (i==0 or j>p[i-1]):
q = list(p)
q[i] = j
if ok(q):
return can(q)
(j doit être plus petit que p[i] parce qu’on va toujours vers la gauche, mais en plus il doit être plus grand que l’abscisse p[i-1] du pion précédent – à condition que celui-ci existe c’est-à-dire que i soit non nul – parce que sinon on a sauté par-dessus un pion, ce qui est autorisé dans le jeu de Welter mais pas celui du dollar d’argent sans le dollar)
Avec cette contrainte supplémentaire, les graphes de jeu ne sont pas les mêmes que pour Welter. Par exemple avec
Les experts du m’raha wa n’tso à Mayotte ne comptent jamais les graines (Tiennot communication privée). Pour le katro de Madagascar, seuls les enfants ont le droit de compter les graines, sinon le seul fait d’avoir touché ne serait-ce qu’une graine, oblige à semer depuis la case où se trouvait cette graine. Toujours à Madagascar (Tiennot communication privée) les marchands de fruits connaissent avec une grande précision le cardinal d’un tas de fruits sans compter ceux-ci. Quand à Mayotte, les champions du m’raha wa n’tso savent avec précision (cette connaissance étant d’importance stratégique) le nombre de graines contenu dans la nyumba (la case de forme carrée) sans jamais compter ces graines.
Comment font-ils ? Une première possibilité est qu’ils aient mémorisé les nombres de graines au cours de la partie (ou alors qu’ils aient compté les coups joués, et les graines des cases peu remplies, et retrouvé le cardinal des nyumbas par déduction), ce qui suppose une mémoire de travail impressionnante. Une seconde est qu’ils savent estimer avec précision un grand nombre entier.
Dans cet article, on se propose d’explorer la manière dont des machines (vous avez dit IA ?) peuvent dénombrer sans compter. On peut dénombrer par pesée, par estimation du volume, voire par le regard.
Premier projet : dénombrement par pesage
Un plateau de jeu de semailles (par exemple, Sowing à 6 cases) pédagogique est un alignement de cases (où on sème des graines), chacune d’entre elles ayant en son fond une jauge de contrainte, permettant de mesurer la masse du tas de graines ; de plus, à côté de chaque case, figure un afficheur LED indiquant le nombre de graines actuellement dans la case. Quelque chose comme
o
oo
o
oo
qui peut aider les jeunes élèves à apprendre à associer les chiffres et les nombres. Voici un exemple de ce qui est attendu :
Un problème qui se pose avec la pesée de graines, est que celles-ci ne sont pas calibrées avec précision. Une mesure effectuée sur les 48 graines d’un jeu de katro indique une masse moyenne de 2,583g et un écart-type (estimé) de 0,414g. Le tracé d’une droite de Henry :
suggère, avec un coefficient de corrélation de carré 0,988 que la modélisation de la masse d’une graine par une variable aléatoire normale de paramètres 2,583g et 0,414g est pertinente dans ce contexte. En supposant qu’il y a indépendance entre les masses des graines, la masse du tas de graines est alors normale de paramètres et et le quotient de cette masse totale par 2,583 suit lui aussi une loi normale, de paramètres et . La probabilité qu’elle soit comprise entre et (c’est-à-dire que son arrondi entier soit correct) est donnée dans le tableau suivant :
1
0,998221949401783
2
0,972874613820933
3
0,928803110724748
4
0,881829754134665
5
0,837749500152768
6
0,797965253549604
7
0,762452482209559
8
0,730777292410847
9
0,702433751636496
10
0,676951054399075
11
0,653921494553211
12
0,633001454164905
On voit que, pour des grands tas (ceux-là même où l’humain est peu performant pour ce qui est de dénombrer), la probabilité de se tromper est d’une chance sur trois, ce qui n’est pas négligeable.
Pour réaliser ce projet, par exemple sur un Sowing pédagogique à 6 cases, il faut
un microcontrôleur type Arduino ou ESP-32,
6 capteurs de type jauge de contrainte, dont les signaux sont multiplexés pour pouvoir n’utiliser qu’un seul convertisseur analogique-numérique du ESP-32 (lequel dispose de 8 canaux tout de même),
6 actuateurs de type affichage LED 7 segments, un (deux s’il risque d’y avoir plus de 9 graines) par case, avec interfaces séries permettant de les programmer en binaire, et chacun branché sur une sortie numérique du ESP-32.
Le module ESP-32 peut être programmé en micropython. Le plateau serait plus pédagogique si les chiffres étaient affichés en calligraphie : ce sont les formes que les enfants doivent apprendre à reconnaître.
Variante avec machine learning
L’initiation au machine learning commence souvent par la régression linéaire (qui est un apprentissage supervisé pour une prédiction) et il se trouve que justement le tableur permet de calculer une droite de régression, et qu’en plus les fonctions affines sont au programme de mathématiques de Seconde.
Le matériel nécessaire est composé de
machines ESP-32 (programmables en micropython ; idéalement une par élève),
jauges de contrainte (à brancher sur une entrée analogique de l’ESP-32),
ordinateurs, sur chacun desquel est installé Thonny et un tableur.
Ci-dessous, à gauche c’est la carte ESP-32, au centre l’amplificateur hx-711, et à droite la jauge de contrainte avec 10 graines de soja noir posées dessus :
Une fois lancé Thonny et branché (par USB) l’ESP-32, il faut changer l’interpréteur Python pour choisir celui de l’ESP-32, pour cela on va dans exécuter :
Là, on choisit la version ESP-32 :
on clique dessus :
puis sur OK, ce qui a pour effet de redémarrer Python, mais en mode ESP-32 :
Ensuite, on déclare un objet de type broche, avec Pin (importé du module machine), comme entrée analogique, Puis on commence l’apprentissage, dont chaque étape consiste à
choisir un entier ,
poser graines sur le plateau,
lire (avec la méthode read de la broche) la masse du tas de graines,
entrer dans la colonne A le résultat de la lecture,
entrer dans la colonne B le nombre .
En recommençant sur plusieurs lignes du tableur des données de ce genre, on réalise l’apprentissage. Il est conseillé de choisir au moins une fois chaque valeur de entre 0 et 12 (compris). La phase suivante consiste à dessiner le nuage de points, puis tracer la doite de régression, avec son équation.
L’utilisation consiste à mettre des graines sur le plateau, lire la tension aux bornes du plateau, puis calculer son image par la fonction affine construite dans l’apprentissage, et arrondir à l’entier le plus proche pour avoir une estimation du nombre de graines. Ensuite on vérifie si l’estimation correspond au nombre réel de graines. En munissant la jauge de contrainte d’un amplificateur hx-711, et à l’aide de la bibliothèque Python permettant de gérer le tout, on peut effectuer des mesures en lançant ce script :
évaluer dans la console l’expression hx.get_value(),
noter le résultat dans une cellule du tableur (colonne 1) et le nombre de graines dans la cellule voisine (colonne 2)
Au bout de quelques mesures notées (phase d’apprentissage supervisé), il est possible de dessiner le nuage de points, puis de demander la droite de régression (donnant n fonction de la mesure) :
Ensuite on peut utiliser le modèle pour une estimation en programmant la fonction affine en Python :
(on arrondit à zéro chiffre après la virgule parce qu’il y a un nombre entier de graines)
Pour utiliser le modèle, on pose des graines sur la balance puis on évalue dans la console l’expression f(hx.get_value()) pour estimer le nombre de graines. On ne s’attend pas à avoir une estimation correcte à chaque fois, par exemple ci-dessous on a mis trois graines pour en estimer 5 :
Second projet : dénombrement par regard
Avec ImageJ
Usuellement, quand on veut estimer le nombre de manifestants dans une manif ou le nombre de globules rouges dans une goutte de sang, on ne le fait ni par pesée ni par comptage. Une application intéressante sur le smartphone serait celle qui afficherait sur l’écran de la webcam des cadres de ce genre :
Avec le logiciel libre ImageJ, on peut compter ces graines :
Les ombres gênent la visibilité, au point que si on essaye d’analyser cette image sans retouche, ImageJ ne compte que les constellations :
Il faut donc d’abord séparer les graines et les ombres. Comme ici les graines sont rougeâtres, on peut effectuer un filtrage passe-bande :
Le rouge ressort bien par rapport à l’ombre grise, et du coup la composante verte de l’image porte des graines plus foncées que le reste :
Ensuite on choisit un seuil au-delà duquel la couleur est suffisamment foncée pour être une graine :
La suite est de la topologie : on demande à ImageJ de compter les composantes connexes (analyse particles) de la partie noire de l’image. Le résultat (pour les « particules » de 400 pixels² à l’infini) est ici :
Area
Mean
1
1423
246.936
2
1474
253.789
3
1154
254.337
4
1415
250.855
5
1670
247.671
6
1209
241.079
7
1029
248.309
8
1520
253.658
9
1450
255.000
ImageJ compte 9 composantes connexes, donc 9 graines dans l’image. Elles peuvent être numérotées :
ou schématisées par des ellipses :
Très bien : l’ordinateur sait compter au moins jusqu’à 9. Sauf que la suite d’opérations précédentes n’est pas automatisable (dans un script), notamment parce que le choix du seuil a été fait manuellement, et il n’est pas facile de déléguer ce choix à un algorithme. Par exemple, avec la nyumba qui ouvre cet article, on voit que seules quelques ombres (et aucune graine, celles-ci sont trop claires) ont été comptées :
Ce projet peut néanmoins être tenté en cours de SNT (thèmes : les objets connectés, et la photographie numérique),
soit directement avec ImageJ,
soit avec le module Python openCV,
soit avec appInventor, du MIT, en programmation par blocs, pour les smartphones Android, ou l’équivalent Apple pour les iPhones.
Avec openCV
Python possède un module opencv permettant de faire du traitement d’images (y compris en vidéo). Avec
import cv2
import numpy as np
on dispose d’une fonction cv2.imread qui permet d’importer une image (comme celle de nyumba déjà vue) dans une variable img, puis de la mettre en noir et blanc et lui appliquer une transformée de Hough circulaire avec
Avec ces paramètres seules 2 graines sur les 12 n’ont pas été détectées :
En effet elles sont partiellement cachées par le bord inférieur du plateau et la partie visible n’a pas une forme circulaire. D’ailleurs cet algorithme (dû à Serge Bays) ne fonctionne pas avec des graines de forme allongée.
Avec un réseau de neurones
Les nombreuses caméras chinoises sont dotées de réseaux de neurones qui ont appris à reconnaître des visages. Sauraient-elles reconnaître (et dénombrer) des graines ? Pour simuler un réseau de neurones, on utilise le module keras de Python. Pour un apprentissage supervisé, a été créé un jeu de données artificiel, composé d’images de 256 pixels comportant des images de graines ne se chevauchant pas trop :
ainsi que le nombre de graines de chaque image (ci-dessus [5, 7, 9, 3, 4, 9, 1, 6, 6, 3]). Après plusieurs essais, ce réseau de neurones a été choisi :
Avec la descente du gradient comme optimiseur, on est déçu par la faible précision 0,5206666588783264 : le réseau prédit correctement le nombre seulement une fois sur deux. Par exemple cette image a été vue comme représentant le nombre 4 :
La distribution (pour cette image) a en effet 4 comme mode :
Le problème vient de ce que ⠌ et ⠡ sont des images différentes (les pixels noirs ne sont pas du tout au même endroit) alors qu’elles représentent le même nombre 2. Il faudrait entraîner un réseau de neurones sur des images qui se ressemblent à translation et rotation près. C’est le cas pour la transformée de Fourier bidimensionnelle. En lançant le réseau de neurones convolutif sur des transformées de Fourier de constellations, on obtient plus de succès, avec une précision de 0,6243333220481873. Le réseau de neurones ne se trompe qu’une fois sur trois, et pas de beaucoup, puisqu’au lieu de
[3, 3, 4, 8, 2, 7, 4, 5, 3, 4]
il trouve
[3, 4, 5, 9, 3, 6, 4, 6, 3, 4]
donc (ici) 6 erreurs sur 10 mais des erreurs d’une seule valeur à chaque fois (par exemple 4 au lieu de 3). Dans ce cas (deuxième valeur), la distribution est presque précise :
Le mode 4 (erroné) dépasse de peu la valeur correcte 3.
Et si on comptait quand même ?
L’algorithme par transformée de Hough ne fait que repérer les graines (des cercles) et les compter. Mais cela peut être utile à un joueur de m’raha wa n’tso parce que, comme lui, la machine compte les graines sans les toucher. Seulement il faut d’abord qu’elle reconnaisse les graines, indépendamment de leur orientation, ce qui va passer par l’apprentissage : la machine doit apprendre à reconnaître une graine. L’affichage de la caméra intelligente après apprentissage pourrait ressembler à ceci :
Cela mènerait à un autre type de TP, portant sur une caméra intelligente (smart cam) où les élèves n’auraient qu’à apprendre à la machine à reconnaître un nouvel objet (une graine) et après cette phase d’apprentissage supervisé, on testerait la machine sur des constellations : la machine compte correctement si elle reconnaît précisément les graines, et
sous-compte si elle ne reconnaît pas certaines graines (elle ne les voit pas bien),
sur-compte en cas de faux positifs (une tache d’ombre prise pour une graine par exemple).
Dans cet article, on va s’intéresser à des jeux impartiaux, dont l’archétype est le jeu de Nim. Dans ce jeu, on dispose de jetons, disposés en tas comme ici (jeu de Nim à deux tas) :
Ci-dessous ce jeu sera noté (3,2) car il y a un tas de 3 jetons et un tas de 2 jetons. Un coup du jeu de Nim consiste à
choisir un tas (par exemple celui de droite),
en retirer autant de jetons qu’on souhaite (par exemple un seul) :
Celui qui prend le dernier jeton gagne le jeu. Ci-dessus ce sera celui qui a joué en premier, parce qu’il dispose maintenant d’une stratégie gagnante (jouer dans un tas ce que son adversaire vient de jouer dans l’autre tas). Mais dans le cas général, on calcule ce qu’on appelle nombre de Grundy du jeu, et ce nombre (un entier résumant le jeu) se calcule récursivement sur le graphe du jeu. Pour construire ce graphe, on assimilera la position
avec la position
car l’emplacement des tas importe peu pour la stratégie gagnante, tout ce qu’il faut connaître, c’est la hauteur de chacun des tas. Les deux positions ci-dessus seront toutes deux notées (2,1) : on convient de mettre dans l’ordre décroissant les hauteurs des tas de jetons. De plus, on ne note pas les tas devenus vides. Avec ces conventions le graphe du jeu est :
Le calcul des nombres de Grundy de chaque sommet donne la valeur 1 pour le sommet qui est en haut. Ce nombre de Grundy étant différent de 0, il y a une stratégie gagnante consistant à viser un sommet dont le nombre de Grundy est nul, et ce sommet est (2,2). L’idée fondamentale de Fraenkel est d’utiliser ce graphe, non pour modéliser le jeu, mais pour jouer, par le biais d’un pion, commun aux deux joueurs, et qu’à chaque tour de jeu on glisse le long d’une flèche, jusqu’à ce que ce ne soit plus possible (parce que le pion est tombé dans un puits en bas), auquel cas le dernier à avoir bougé le pion (le joueur qui a fait tomber le pion dans le puits) a gagné le jeu. Quelques définitions :
un graphe orienté est connexe s’il est d’un seul tenant (on ne peut pas le séparer en deux morceaux sans enlever un sommet ou un arc),
un cycle est un chemin permettant d’aller d’un sommet à lui-même en suivant des flèches,
un graphe n’ayant pas de cycle est un graphe acyclique,
un sommet où n’arrive aucune flèche est une source,
un sommet dont ne part aucune flèche est un puits.
Les jeux de Fraenkel se jouent sur des graphes orientés connexes et acycliques. De tels graphes ont au moins une source et au moins un puits. Pour savoir où mettre le pion au départ, il faut qu’ils n’y ait qu’une source, et le jeu est plus facile à jouer s’il n’y a qu’un puits, comme sur ce graphe d’enfant (dont le nombre de Grundy est 2) :
On énonce alors le
Théorème de Fraenkel : tout jeu impartial est équivalent à un jeu où on déplace un pion sur un graphe orienté connexe acyclique ayant une source et un puits, le pion étant placé au départ sur la source et le gagnant étant celui qui amène le pion dans le puits.
Atelier de création de jeux impartiaux
Un atelier de création de jeux impartiaux revient à un atelier de dessins de graphes orientés, ce qui suggère ce genre de tournoi :
chaque enfant se voit remettre du papier et un crayon, et dessine un graphe orienté,
si le graphe comporte un cycle, il est disqualifié,
s’il comporte plusieurs sources ou plusieurs puits, il est pénalisé,
les graphes restants sont alors classés par nombre de Grundy, et le graphe ayant le plus grand nombre de Grundy est gagnant du tournoi.
La récompense pour le gagnant est l’organisation d’un tournoi de jeu impartial sur son graphe. Un outil d’aide à la conception de graphes orientés est disponible en ligne sur Nirina974. Un bêta-test a permis à une enfant de 8 ans de rapidement produire ce graphe dont le nombre de Grundy est 3 :
Conway et Fraenkel ont classé les interactions possibles entre pions sur un graphe orienté connexe (avec ou sans cycle) :
Les pions peuvent être des bosons, auquel cas il n’y a pas de limite supérieure au nombre de pions présents sur un sommet du graphe.
Les pions peuvent s’exclure mutuellement comme des fermions, ce qui occasionne des blocages ; Conway attribue à un certain Welter la création d’un cas particulier de ce genre de jeu, où le graphe est formé de sommets alignés, chacun étant relié à tous les sommets qui sont à sa gauche.
Les pions peuvent se comporter comme des antiparticules : lorsqu’un pion arrive sur un sommet déjà occupé, les deux pions sont retirés du jeu.
les pions peuvent être de deux couleurs, et avoir un comportement mixte : un pion ne peut pas aller sur un sommet où se trouve un pion de la même couleur, mais peut occuper un sommet où se trouve un pion de l’autre couleur, auquel cas il va le clobber, c’est-à-dire qu’il prend sa place en le retirant du graphe, comme aux échecs.
Dans ce dernier cas, Fraenkel a conçu un jeu impartial :
Ce jeu a été commercialisé dans les années 1970 sous le nom de on the line, puis dans les années 2000 sous le nom de arrows. Dans ce dernier cas, le plateau de jeu était en bois, avec des creux pour les pions, lesquels étaient des billes, transparentes pour un joueur, et métalliques pour l’autre joueur. Sur le site https://mathpickle.com/ (consacré à l’enseignement des maths par la ludologie), des jeux similaires ont été proposés sur des graphes plus simples :
Certains de ces graphes semblent permettre d’introduire la notion de parité (apparemment en cycle 2).
Variation sur un thème de Fraenkel
Revenons un peu en arrière, là où Fraenkel suggère de mettre plusieurs pions sur un graphe orienté connexe acyclique, le mode d’interaction des pions faisant partie de la règle du jeu. La carte des cartes donne envie d’explorer cette variante :
Avant de jouer, on place un pion sur l’unique source de chaque composante connexe d’un graphe orienté acyclique. Un tour de jeu consiste à choisir un des pions qui n’est pas encore bloqué, puis le bouger suivant une flèche. Le premier qui ne peut plus bouger a perdu le jeu.
Les composantes connexes
En vrai, avant de jouer, on construit les composantes connexes du graphe, par exemple en les puisant d’une collection établie lors de l’activité décrite dans la partie atelier de création de jeux impartiaux plus haut, ou comme modèles de jeux impartiaux connus comme par exemple le jeu de Sowing impartial (graphes simplifiables en mettant ensemble tous les puits et en tenant compte des symétries) :
ou alors des graphes orientés connexes acycliques créés sans chercher à modéliser un autre jeu impartial, juste en dessinant le graphe, comme par exemple le graphe d’enfant vu en début d’article, que voici rendu en Python :
les deux joueurs choisissent des graphes orientés connexes acycliques ayant une source et un puits parmi la collection (iles choisissent des cartes dans le jeu de cartes),
puis posent ces cartes sur la carte des cartes (la table : ils construisent le plateau de jeu avant de jouer),
puis posent un pion à chaque départ d’une carte,
et, enfin, jouent : chacun son tour, déplace un des pions en lui faisant suivre une flèche.
Le premier qui ne peut plus bouger (parce que tous les pions sont dans des puits) a perdu.
Application au jeu de Nim
On rappelle que pour le jeu de Nim à deux tas (3,2), le graphe possède 9 sommets :
et on peut simplifier encore : comme la composante connexe de gauche a un nombre de Grundy égal à 3 (c’est un tas de 3 jetons) et celle de droite a un nombre de Grundy égal à 2 (c’est un tas de 2 jetons), le jeu est, d’après les théorèmes de Sprague-Grundy et de Fraenkel, LE jeu de nombre de Grundy 1 (Nim-somme de 3 et 2), c’est-à-dire ce jeu (à seul pion pour le coup) :
Comme il s’agit de jeux à deux joueurs, chaque table de jeu occupe deux joueurs. Mais en plus il y a une table centrale où se trouvent posées des cartes, dont chacune porte un graphe orienté connexe acyclique avec une source et un puits. Les joueurs d’une table de jeu commencent par choisir quelques cartes de la table centrale (en essayant de ne pas frustrer les joueurs d’autres tables) puis les posent sur leur table de jeu, en mettant un pion au départ de chaque carte.
Puis ils jouent au jeu qu’ils ont créé, c’est-à-dire que chacun son tour bouge un pion le long d’une flèche. Lorsque le jeu est terminé (plus aucun pion ne peut bouger, donc celui qui devait jouer a perdu), ils notent les nombres de Grundy des cartes choisies et leur Nim-somme.
Ensuite ils préparent un autre jeu, en choisissant parmi ces opérations :
enlever une carte jugée inintéressante,
ajouter une carte qui semblait manquer,
échanger une carte peu intéressante contre une carte plus intéressante (comme au poker) au niveau de la table centrale.
Après quelques tours de ce métajeu, on compare les jeux les plus intéressants, et les nombres de Grundy obtenus. La version sans nombres de Grundy est plus simple mais fait appel au jugement humain pour ranger les jeux construits par qualité subjective.
Lors du séminaire IREMI du 22 avril 2026
Voici des jeux jugés intéressants par leurs créateurs :
Les nombres de Grundy des 5 termes de cette somme sont :
4
3
3
3
2
et le nombre de Grundy des 5 cartes est 4+3+3+3+2 = 5.
Les nombres de Grundy des 4 cartes sont :
1
0
3
3
dont la nim-somme est 1+0+3+3 = 1.
Les nombres de Grundy sont :
2
4
1
3
Le nombre de Grundy du tout est donc 2+4+1+3 = 4.
Les nombres de Grundy sont :
1
2
3
2
dont la somme est 1+2+3+2 = 2.
Les nombres de Grundy sont :
0
3
1
3
3
dont la somme n’est égale qu’à 0+3+1+3+3 = 2, mais trois des cartes (différentes entre elles) valent 3, donc deux d’entre elles s’annulent mutuellement, sans qu’il soit aisé de voir comment.