<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mbrunot</id>
	<title>Wiki du LAMA (UMR 5127) - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="http://os-vps418.infomaniak.ch:1250/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mbrunot"/>
	<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php/Sp%C3%A9cial:Contributions/Mbrunot"/>
	<updated>2026-05-21T07:43:37Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15638</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15638"/>
		<updated>2024-05-27T23:49:38Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
 - La valeur de grundy de plusieurs tas se trouve être le XOR de toutes les valeurs de Grundy de chaque tas individuel.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram sur une ligne = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&amp;quot;Il a été conjecturé par les auteurs de Winning Ways que tous les jeux octaux seraient ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
Un jeu ultimement périodique est un jeu dans lequel les valeurs des nombres de Grundy deviennent périodiques après un certain point. Cela signifie que, à partir d&#039;une certaine position, les valeurs des nombres de Grundy se répètent de manière régulière avec une certaine période.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + k) &amp;lt;/math&amp;gt; où k est le nombre de règles d&#039;un jeu octal ou simplement le nombre de chiffres suivant le &amp;quot;0.&amp;quot; du jeu.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15636</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15636"/>
		<updated>2024-05-24T14:38:32Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
 - La valeur de grundy de plusieurs tas se trouve être le XOR de toutes les valeurs de Grundy de chaque tas individuel.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram sur une ligne = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&amp;quot;Il a été conjecturé par les auteurs de Winning Ways que tous les jeux octaux seraient ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
Un jeu ultimement périodique est un jeu dans lequel les valeurs des nombres de Grundy deviennent périodiques après un certain point. Cela signifie que, à partir d&#039;une certaine position, les valeurs des nombres de Grundy se répètent de manière régulière avec une certaine période.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + k) &amp;lt;/math&amp;gt; où k est le nombre de règles suivant le &amp;quot;0.&amp;quot; d&#039;un jeu octal.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15635</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15635"/>
		<updated>2024-05-24T14:35:23Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
 - La valeur de grundy de plusieurs tas se trouve être le XOR de toutes les valeurs de Grundy de chaque tas individuel.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram sur une ligne = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&amp;quot;Il a été conjecturé par les auteurs de Winning Ways que tous les jeux octaux seraient ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
Un jeu ultimement périodique est un jeu dans lequel les valeurs des nombres de Grundy deviennent périodiques après un certain point. Cela signifie que, à partir d&#039;une certaine position, les valeurs des nombres de Grundy se répètent de manière régulière avec une certaine période.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + k) &amp;lt;/math&amp;gt; où k est le nombre de règles suivant le 0. d&#039;un jeu octal.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15634</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15634"/>
		<updated>2024-05-24T14:34:15Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
 - La valeur de grundy de plusieurs tas se trouve être le XOR de toutes les valeurs de Grundy de chaque tas individuel.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram sur une ligne = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&amp;quot;Il a été conjecturé par les auteurs de Winning Ways que tous les jeux octaux seraient ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
Un jeu ultimement périodique est un jeu dans lequel les valeurs des nombres de Grundy deviennent périodiques après un certain point. Cela signifie que, à partir d&#039;une certaine position, les valeurs des nombres de Grundy se répètent de manière régulière avec une certaine période.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15633</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15633"/>
		<updated>2024-05-24T14:24:43Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Théorème de Sprague-Grundy */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
 - La valeur de grundy de plusieurs tas se trouve être le XOR de toutes les valeurs de Grundy de chaque tas individuel.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram sur une ligne = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15632</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15632"/>
		<updated>2024-05-24T14:21:48Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Représentation en jeu octal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram sur une ligne = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15631</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15631"/>
		<updated>2024-05-24T14:21:30Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Description */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne(jeu du domino)&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15630</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15630"/>
		<updated>2024-05-24T14:20:11Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Description */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|500px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15629</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15629"/>
		<updated>2024-05-24T14:20:02Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Description */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_domino.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Jeu_domino.png&amp;diff=15628</id>
		<title>Fichier:Jeu domino.png</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Fichier:Jeu_domino.png&amp;diff=15628"/>
		<updated>2024-05-24T14:19:45Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15627</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15627"/>
		<updated>2024-05-24T14:03:32Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Description */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram sur une ligne&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15558</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15558"/>
		<updated>2024-05-21T19:58:27Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Jeu de Impartiaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15555</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15555"/>
		<updated>2024-05-21T19:48:56Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Application */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Notre but est de ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante.&lt;br /&gt;
&lt;br /&gt;
Pour faire ceci il nous faut changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15554</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15554"/>
		<updated>2024-05-21T19:30:51Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Représentation en jeu octal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15553</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15553"/>
		<updated>2024-05-21T19:30:32Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Représentation en jeu octal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15536</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15536"/>
		<updated>2024-05-21T08:55:01Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Bonus jeu octal contre une IA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
Pour finir voici un jeu interactif qui se joue soit seul contre une IA soit à deux joueurs.&lt;br /&gt;
L&#039;IA fera en sorte de toujours jouer le meilleur coup possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15535</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15535"/>
		<updated>2024-05-21T08:53:41Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Méthode rare/fréquentes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
Après avoir calculé un tableau de valeurs de Grundy d&#039;un jeu octal suffisamment grand nous voyons que des valeurs sont très récurrentes et que d&#039;autres ne le sont quasiment jamais.&lt;br /&gt;
&lt;br /&gt;
On a divisé ces valeurs en deux catégories les rares et les fréquentes.&lt;br /&gt;
&lt;br /&gt;
Cette délimitation que nous avons fait à vue, peut s&#039;automatiser par le biais d&#039;un masque binaire.&lt;br /&gt;
=== Masque ===&lt;br /&gt;
Pour savoir si un masque convient à la division rare/fréquentes des valeurs de Grundy d&#039;une liste, on utilise celui-ci pour déterminer les valeurs rares.&lt;br /&gt;
Il faut que tous les indice à 1 d&#039;une valeur de grundy en binaire soient un nombre paires de fois au même coordonnées que les indices à 1 du masque.&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous permettra de déterminer un masque pour un tableau des valeurs de Grundy d&#039;un jeu octal.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Avec ce code nous avons pu ainsi calculer des tableaux de valeurs de grundy allant jusqu&#039;à l&#039;ordre des &amp;lt;math&amp;gt; 10^7 &amp;lt;/math&amp;gt;&lt;br /&gt;
Maintenant le calcul des valeurs n&#039;est plus la source du problème mais le calcul de la période est celui qui ralenti notre code.&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15534</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15534"/>
		<updated>2024-05-21T08:34:26Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode de taille s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n+s) = G(n + s + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité, il nous faut voir si les valeurs de Grundy d&#039;un jeu octal apparaissent périodiques après la dernière occurrence de la prépériode G(s) alors la dernière valeur nécessitant d&#039;être vérifié pour prouver la période à partir d&#039;un instant i est &amp;lt;math&amp;gt; G(2(s+p) + i) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisamment loin pour calculer les jeu octaux : .16, .127, .376&lt;br /&gt;
&lt;br /&gt;
Il nous faut alors changer de méthode de calcul pour les valeurs de Grundy.&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15533</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15533"/>
		<updated>2024-05-21T08:27:14Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n) + s = G(n + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisement loin pour calculer les jeu octaux suivants:&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15532</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15532"/>
		<updated>2024-05-21T08:26:53Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Périodicité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
Par conjecture nous pensons que tous les jeux octaux sont ultimes périodiques.&lt;br /&gt;
&lt;br /&gt;
C&#039;est à dire pour toute position n suffisamment grande sachant qu&#039;une prépériode s peut se manifester, il faut satisfaire &amp;lt;math&amp;gt; G(n) + s = G(n + P) &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour prouver cette périodicité voici le code qui nous a permis de calculer toutes les periodes des jeux octaux se trouvant dans le livre [https://fr.wikipedia.org/wiki/Winning_Ways_for_your_Mathematical_Plays| Winning ways]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notre seul problème ici, est que notre algorithme pour calculer les valeurs de Grundy ne nous permet pas d&#039;aller suffisement loin pour calculer les jeu octaux suivants:&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15531</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15531"/>
		<updated>2024-05-21T08:06:58Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Méthode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux.&lt;br /&gt;
&lt;br /&gt;
Le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15530</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15530"/>
		<updated>2024-05-21T08:04:24Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Méthode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
Par le code suivant on a pu calculer les valeurs de Grundy pour tous les jeux octaux, le code est long mais simple nous faisons le calcul de toutes les zones atteignables depuis notre instant i pour en faire le minimum exclu afin de trouver sa valeur grundy et on continue ainsi pour i+1, etc... &lt;br /&gt;
&lt;br /&gt;
Cependant nous n&#039;avons pu aller dans des calcul de valeurs de Grundy n&#039;allant que jusqu&#039;à des éléments de l&#039;ordre de &amp;lt;math&amp;gt; 10^4 &amp;lt;/math&amp;gt; avant que ça ne devienne trop long pour cette méthode.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15529</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15529"/>
		<updated>2024-05-21T07:59:10Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
Le jeux octaux étant impartiaux la méthode des valeurs de Grundy s&#039;applique à eux.&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str&lt;br /&gt;
    assert all(int(ele) in JEU_VALIDE for ele in nbr_octal), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nbr_octal)):&lt;br /&gt;
        tab_res += [[nbr_octal[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15528</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15528"/>
		<updated>2024-05-21T07:39:59Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def verif_jeu(nbr_octal:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie nombre en str et enleve le 0. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str, &amp;quot;tu dois donner une chaine de caractères en argument&amp;quot;&lt;br /&gt;
    for ele in nbr_octal:&lt;br /&gt;
        ele = int(ele)&lt;br /&gt;
        assert (ele in JEU_VALIDE), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    return nbr_octal&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nb = verif_jeu(nbr_octal)&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nb)):&lt;br /&gt;
        tab_res += [[nb[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bonus jeu octal contre une IA =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from function import *&lt;br /&gt;
BATON = &#039;|&#039;&lt;br /&gt;
&lt;br /&gt;
class Jeu:&lt;br /&gt;
    &lt;br /&gt;
    def __init__(self, t_grille:int, nbr_octal:str, IA:bool=False)-&amp;gt;None:&lt;br /&gt;
        self.tas = [t_grille]&lt;br /&gt;
        self.jeu = nbr_octal&lt;br /&gt;
        self.liste_des_kn_di = tab_kn_dn(nbr_octal)&lt;br /&gt;
        self.IA = IA&lt;br /&gt;
        self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
    &lt;br /&gt;
    def liste_des_coups_pour_un_tas(self, tas)-&amp;gt;list:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Renvoie la liste des coups pour un tas donné sous forme d&#039;un tableau &lt;br /&gt;
        contenant des sous dictionnaires avec le message sur l&#039;information du coup et son id&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = []&lt;br /&gt;
        for jeu in self.liste_des_kn_di:&lt;br /&gt;
            action = liste_action_possibles(tas, jeu[0], jeu[1])#kn = le type d&#039;action et di = la taille sur laquelle l&#039;action s&#039;applique&lt;br /&gt;
            #rajoute les id&lt;br /&gt;
            if action[0]:&lt;br /&gt;
                liste_coups += [f&amp;quot;0.{jeu[1]}&amp;quot;]&lt;br /&gt;
            &lt;br /&gt;
            if action[1]:&lt;br /&gt;
                liste_coups += [f&amp;quot;1.{jeu[1]}&amp;quot;]&lt;br /&gt;
                &lt;br /&gt;
            if action[2]:&lt;br /&gt;
                liste_coups += [f&amp;quot;2.{jeu[1]}&amp;quot;]    &lt;br /&gt;
                &lt;br /&gt;
        return liste_coups&lt;br /&gt;
    &lt;br /&gt;
    def affiche_les_tas(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; affiche tous les tas du jeu &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        for i in range(len(self.tas)):&lt;br /&gt;
            les_batons = BATON*self.tas[i]&lt;br /&gt;
            print(f&amp;quot;tas numéro {i}: {les_batons}&amp;quot;)&lt;br /&gt;
             &lt;br /&gt;
    def retire_tas(self, id_tas:int, id_action:str, choix_IA:list=None)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Retire un/des elements d&#039;un tas seulement si possible,&lt;br /&gt;
        si il y a un reste celui est rajouté en fin de tableau&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
        tas_enleve = self.tas.pop(id_tas) #retire le tas en question&lt;br /&gt;
        type_action = id_action[0]&lt;br /&gt;
        taille_tas_a_enlever = int(id_action[2])&lt;br /&gt;
        &lt;br /&gt;
        #rajoute les restes s&#039;il y en a&lt;br /&gt;
        if type_action == &#039;1&#039;:&lt;br /&gt;
            self.tas.append(tas_enleve-taille_tas_a_enlever)&lt;br /&gt;
            &lt;br /&gt;
        elif type_action == &#039;2&#039;:&lt;br /&gt;
            if choix_IA != None:&lt;br /&gt;
                choix = choix_IA&lt;br /&gt;
            else:&lt;br /&gt;
                separation_possible = liste_des_separations_tas_en_2(tas_enleve,taille_tas_a_enlever)&lt;br /&gt;
                choix = choix_de_la_separation_en_deux(separation_possible)&lt;br /&gt;
            &lt;br /&gt;
            self.tas.append(choix[0])&lt;br /&gt;
            self.tas.append(choix[1])&lt;br /&gt;
    &lt;br /&gt;
    ### INTERFACE ###&lt;br /&gt;
    &lt;br /&gt;
    def choix_du_tas(self)-&amp;gt;int:&lt;br /&gt;
        self.affiche_les_tas&lt;br /&gt;
        ind = fait_choix_id(self.tas, &amp;quot;Quel tas veux-tu enlever : &amp;quot;)&lt;br /&gt;
        return ind&lt;br /&gt;
    &lt;br /&gt;
    def choix_des_batons_a_enlever(self,ind_tas)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; fait la requête pour demander à l&#039;utilisateur de changer de batons &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        liste_coups = self.liste_des_coups_pour_un_tas(self.tas[ind_tas])&lt;br /&gt;
        liste_dico_id = []&lt;br /&gt;
        for id_coup in liste_coups:&lt;br /&gt;
            liste_dico_id += [id_coup]&lt;br /&gt;
            print(f&#039;{message_sur_un_id(id_coup)} pour un id = {id_coup}&#039;)&lt;br /&gt;
        ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        while ind not in liste_dico_id:&lt;br /&gt;
            print(&amp;quot;Tu t&#039;es trompé&amp;quot;)&lt;br /&gt;
            ind = input(&amp;quot;\nL&#039;id que tu veux utiliser : &amp;quot;)&lt;br /&gt;
        self.retire_tas(ind_tas,ind)&lt;br /&gt;
&lt;br /&gt;
    ### IA ###&lt;br /&gt;
    &lt;br /&gt;
    def tour_IA(self):&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Ici l&#039;IA va  essayer de ramener le jeu à xor de 0&lt;br /&gt;
        pour les valeurs de grundy des tas &lt;br /&gt;
        Si ce n&#039;est pas possible elle fait la première action possible&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        grundy_tas = [Grundy(taille, self.jeu) for taille in self.tas]&lt;br /&gt;
        le_xor = xor_des_tas(grundy_tas)&lt;br /&gt;
        coup_choisi = -1&lt;br /&gt;
        separation_en_deux = False&lt;br /&gt;
        &lt;br /&gt;
        if le_xor == 0:&lt;br /&gt;
            for i in  range (len(self.tas)):&lt;br /&gt;
                coups_pour_le_tas = self.liste_des_coups_pour_un_tas(self.tas[i])&lt;br /&gt;
                if coups_pour_le_tas != []:&lt;br /&gt;
                    coup_choisi = coups_pour_le_tas[0] #prend le premier coup parmis ceux possible&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
                    if coup_choisi[0] == &#039;2&#039;:&lt;br /&gt;
                        taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
                        separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup_choisi[2])) #taille du tas initial , la nombre d&#039;ele ment qu&#039;on lui enleve&lt;br /&gt;
                        couple_choisi = separation_possible[0]&lt;br /&gt;
                        separation_en_deux = True&lt;br /&gt;
                    break&lt;br /&gt;
            &lt;br /&gt;
            &lt;br /&gt;
        else:#cas où l&#039;IA peut ramener le jeu à 0 en val de grundy&lt;br /&gt;
            taille_bit = len(bin(max(grundy_tas))[2:])&lt;br /&gt;
            le_xor_bin = mets_nombre_en_bin_sur_x_bits(le_xor,taille_bit)&lt;br /&gt;
            id_du_premier_1 = le_xor_bin.index(&#039;1&#039;)&lt;br /&gt;
            &lt;br /&gt;
            tas_en_bin = mets_en_bin_tous_les_ele_dans_tab(grundy_tas)&lt;br /&gt;
            &lt;br /&gt;
            for i in range(len(tas_en_bin)):&lt;br /&gt;
                if tas_en_bin[i][id_du_premier_1] == &#039;1&#039;:&lt;br /&gt;
                    id_tas_a_changer = i&lt;br /&gt;
            &lt;br /&gt;
            taille_tas_a_changer = self.tas[id_tas_a_changer]&lt;br /&gt;
            nombre_grundy_a_faire = le_xor ^ grundy_tas[id_tas_a_changer] #on doit pouvoir faire l&#039;action qui enmène à cette valeur de grundy&lt;br /&gt;
            liste_coup = self.liste_des_coups_pour_un_tas(taille_tas_a_changer)&lt;br /&gt;
            print(liste_coup)&lt;br /&gt;
            print(f&amp;quot;nombre à faire : {nombre_grundy_a_faire}&amp;quot;)&lt;br /&gt;
            for coup in liste_coup:&lt;br /&gt;
                type_coup = coup[0]&lt;br /&gt;
                &lt;br /&gt;
                if type_coup == &#039;0&#039; and nombre_grundy_a_faire == 0:&lt;br /&gt;
                    print(&#039;0&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                elif type_coup == &#039;1&#039; and nombre_grundy_a_faire == Grundy(taille_tas_a_changer - int(coup[2]), self.jeu):&lt;br /&gt;
                    print(&#039;1&#039;)&lt;br /&gt;
                    coup_choisi = coup&lt;br /&gt;
                    break&lt;br /&gt;
                else:&lt;br /&gt;
                    print(&#039;2&#039;)&lt;br /&gt;
                    separation_possible = liste_des_separations_tas_en_2(taille_tas_a_changer,int(coup[2]))&lt;br /&gt;
                    for couple in separation_possible:&lt;br /&gt;
                        if Grundy(couple[0],self.jeu) ^ Grundy(couple[1],self.jeu) == nombre_grundy_a_faire:&lt;br /&gt;
                            coup_choisi = coup&lt;br /&gt;
                            couple_choisi = couple&lt;br /&gt;
                            separation_en_deux = True&lt;br /&gt;
                            break&lt;br /&gt;
                    if coup_choisi != -1:&lt;br /&gt;
                        break&lt;br /&gt;
        &lt;br /&gt;
        print(f&amp;quot;L&#039;IA a choisi de {message_sur_un_id(coup_choisi)} pour le tas numéro {id_tas_a_changer}&amp;quot;)&lt;br /&gt;
        if separation_en_deux:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi, couple_choisi)&lt;br /&gt;
            print(f&amp;quot;en laissant un tas de {couple_choisi[0]} et de {couple_choisi[1]}&amp;quot;)&lt;br /&gt;
        else:&lt;br /&gt;
            self.retire_tas(id_tas_a_changer, coup_choisi)&lt;br /&gt;
        &lt;br /&gt;
                &lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
    ### Etat d&#039;une partie###&lt;br /&gt;
    def start(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; Lance la partie &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        while not self.est_fini():&lt;br /&gt;
            print(f&amp;quot;\n___Tour de {self.etat_joueur}___\n&amp;quot;)&lt;br /&gt;
            print(&amp;quot;Voici les tas possibles&amp;quot;)&lt;br /&gt;
            self.affiche_les_tas()&lt;br /&gt;
            print()&lt;br /&gt;
            if self.etat_joueur == &#039;IA&#039;:&lt;br /&gt;
                self.tour_IA()&lt;br /&gt;
            else:&lt;br /&gt;
                tas = self.choix_du_tas()&lt;br /&gt;
                print(&amp;quot;\nTu peux&amp;quot;)&lt;br /&gt;
                self.choix_des_batons_a_enlever(tas)&lt;br /&gt;
            self.change_etat_joueur()&lt;br /&gt;
        &lt;br /&gt;
        self.change_etat_joueur()&lt;br /&gt;
        self.affiche_les_tas()&lt;br /&gt;
        print(&#039;\nJEU FINI&#039;)&lt;br /&gt;
        print(f&#039;Bravo {self.etat_joueur} tu as gagné&#039;)&lt;br /&gt;
    &lt;br /&gt;
    def est_fini(self)-&amp;gt;bool:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; verifie si la partie est finie. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = True&lt;br /&gt;
        for tas in self.tas:&lt;br /&gt;
            for jeu in self.liste_des_kn_di:&lt;br /&gt;
                action = liste_action_possibles(tas, jeu[0], jeu[1])&lt;br /&gt;
                if any(flag == True for (flag) in action):#verifie si il y a une action de possible&lt;br /&gt;
                    res = False&lt;br /&gt;
                    break&lt;br /&gt;
        return res&lt;br /&gt;
        &lt;br /&gt;
    ### Gère la gestion du tour du joueur ###&lt;br /&gt;
    def change_etat_joueur(self)-&amp;gt;None:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        if self.etat_joueur == &#039;J1&#039;:&lt;br /&gt;
            if self.IA:&lt;br /&gt;
                self.etat_joueur = &#039;IA&#039;&lt;br /&gt;
            else:&lt;br /&gt;
                self.etat_joueur = &#039;J2&#039;&lt;br /&gt;
        else:&lt;br /&gt;
            self.etat_joueur = &#039;J1&#039;&lt;br /&gt;
      &lt;br /&gt;
        &lt;br /&gt;
######### FONTCION JEU #########     &lt;br /&gt;
&lt;br /&gt;
def fait_choix_id(liste:list,message:str)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; fait une proposition dans un input pour demander à l&#039;utilisateur&lt;br /&gt;
    l&#039;id qu&#039;il choisit dans la liste&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    ind = -1&lt;br /&gt;
    while ind &amp;gt;= len(liste) or ind &amp;lt; 0: &lt;br /&gt;
        ind = input(message)&lt;br /&gt;
        if ind.isdigit():&lt;br /&gt;
            ind = int(ind)&lt;br /&gt;
        else:&lt;br /&gt;
            ind = -1&lt;br /&gt;
    return ind&lt;br /&gt;
&lt;br /&gt;
def xor_des_tas(liste_tas:list)-&amp;gt;int:&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; renvoie le xor de tous les elements d&#039;un tas&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
        res = 0&lt;br /&gt;
        for ele in liste_tas:&lt;br /&gt;
            res ^= ele&lt;br /&gt;
        return res&lt;br /&gt;
    &lt;br /&gt;
def choix_de_la_separation_en_deux(liste_possibilites:list):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; s&#039;applique quand le joueur choisi de separer un tas en deux&lt;br /&gt;
    _affiche la liste des choix de déparation possibles&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    print(&amp;quot;\n Choisi la taille des deux tas que tu veux laisser dans cette liste&amp;quot;)&lt;br /&gt;
    for i in range(len(liste_possibilites)):&lt;br /&gt;
        print(f&amp;quot;id {i} : {liste_possibilites[i]}&amp;quot;)&lt;br /&gt;
    ind = fait_choix_id(liste_possibilites, &amp;quot;Ton choix :&amp;quot;)&lt;br /&gt;
    return liste_possibilites[ind]&lt;br /&gt;
&lt;br /&gt;
def message_sur_un_id(ind:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne le message approprié selon l&#039;id d&#039;une action&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    type_action = ind[0]&lt;br /&gt;
    if type_action == &#039;0&#039;:&lt;br /&gt;
        message = &amp;quot;retirer tout le tas&amp;quot;&lt;br /&gt;
    elif type_action == &#039;1&#039;:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) sur le bord du tas&amp;quot;&lt;br /&gt;
    else:&lt;br /&gt;
        message = f&amp;quot;retirer {ind[2]} bâton(s) en séparant le tas en deux&amp;quot;&lt;br /&gt;
    return message&lt;br /&gt;
&lt;br /&gt;
def mets_en_bin_tous_les_ele_dans_tab(tab:list)-&amp;gt;list:&lt;br /&gt;
    taille_bit = len(bin(max(tab))[2:])&lt;br /&gt;
    res = []&lt;br /&gt;
    for nbr in tab:&lt;br /&gt;
        res.append(mets_nombre_en_bin_sur_x_bits(nbr,taille_bit))&lt;br /&gt;
    return res&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15527</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15527"/>
		<updated>2024-05-21T07:37:56Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Méthode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
JEU_VALIDE = (0,1,2,3,4,5,6,7)&lt;br /&gt;
TAS_0 = (1,3,5,7) #les valeurs laissant 0 tas&lt;br /&gt;
TAS_1 = (2,3,6,7) #les valeurs laissant 1 tas&lt;br /&gt;
TAS_2 = (4,5,6,7) #les valeurs laissant 2 tas&lt;br /&gt;
&lt;br /&gt;
def verif_jeu(nbr_octal:str)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie nombre en str et enleve le 0. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert type(nbr_octal) == str, &amp;quot;tu dois donner une chaine de caractères en argument&amp;quot;&lt;br /&gt;
    for ele in nbr_octal:&lt;br /&gt;
        ele = int(ele)&lt;br /&gt;
        assert (ele in JEU_VALIDE), &amp;quot;ton jeu octal n&#039;est pas conforme&amp;quot;&lt;br /&gt;
    return nbr_octal&lt;br /&gt;
&lt;br /&gt;
def calc_mex(un_set:set)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la plus petite valeur entière qui n&#039;appartient pas au subset. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    mex = 0&lt;br /&gt;
    while (mex in un_set):&lt;br /&gt;
        mex += 1&lt;br /&gt;
    return mex&lt;br /&gt;
&lt;br /&gt;
def tab_kn_dn(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau avec [kn:str,dn:int] pour chaque action d&#039;un jeu octal. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nb = verif_jeu(nbr_octal)&lt;br /&gt;
    tab_res = []&lt;br /&gt;
    &lt;br /&gt;
    for i in range(0, len(nb)):&lt;br /&gt;
        tab_res += [[nb[i], i+1]]&lt;br /&gt;
    &lt;br /&gt;
    return tab_res&lt;br /&gt;
&lt;br /&gt;
def liste_action_possibles(taille_tab:int, kn:str, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de bool sur la possibilité des 3 actions. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    assert taille_tab &amp;gt; 0 , &#039;ton tableau est vide&#039;&lt;br /&gt;
    &lt;br /&gt;
    k_int = int(kn)&lt;br /&gt;
    action = [False,False,False] #tableau des 3 actions est nous dis si elles sont possibles&lt;br /&gt;
    if not dn &amp;gt; taille_tab: #lance les conditions seuleument la taille retiré est convenable&lt;br /&gt;
        if k_int in TAS_0:&lt;br /&gt;
            if dn == taille_tab:&lt;br /&gt;
                action[0] = True&lt;br /&gt;
        if k_int in TAS_1:&lt;br /&gt;
            if dn &amp;lt; taille_tab:&lt;br /&gt;
                action[1] = True&lt;br /&gt;
        if k_int in TAS_2:&lt;br /&gt;
            if dn+1 &amp;lt; taille_tab:&lt;br /&gt;
                action[2] = True&lt;br /&gt;
    &lt;br /&gt;
    return action&lt;br /&gt;
    &lt;br /&gt;
def liste_des_separations_tas_en_2(taille_tab:int, dn:int)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    dans un tableau de set. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
        val1 = i&lt;br /&gt;
        val2 = somme_des_taille_couple-i&lt;br /&gt;
        couple = (val1,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables(jeu_actions:list, tab_grundy:list, instant_i:int):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables pour le coup pour calculer la &lt;br /&gt;
    valeur suivant du tab_grundy&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            liste_separations_possibles = liste_des_separations_tas_en_2(instant_i, actions[1])&lt;br /&gt;
            for tab in liste_separations_possibles:&lt;br /&gt;
                res.add(tab_grundy[tab[0]] ^ tab_grundy[tab[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy(taille_tab:int, nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de valeur de Sprague Grundy pour une taille_tab donné. &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = [0]&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    for i in range(1, taille_tab+1):&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables(jeu_actions, tab_grundy, i)              &lt;br /&gt;
        tab_grundy += [calc_mex(set_valeurs_atteignables)]&lt;br /&gt;
    return tab_grundy&lt;br /&gt;
    &lt;br /&gt;
def Grundy(taille_tab:int , nbr_octal:float):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la valeur de grundy du nombre donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab, nbr_octal)&lt;br /&gt;
    return tab_grundy[taille_tab]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15526</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15526"/>
		<updated>2024-05-21T07:36:35Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Méthode rare/fréquentes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;br /&gt;
&lt;br /&gt;
=== Masque ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def trie_dico_en_tab_croissant(dico:dict)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tableau trié par ordre croissant &lt;br /&gt;
    selon les valeurs des clefs&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    dico_copy = dico.copy()&lt;br /&gt;
    while dico_copy != {}:&lt;br /&gt;
        clef_min = min(dico_copy, key=dico_copy.get)#cherche la clef avec la val minimale&lt;br /&gt;
        res += [[clef_min,dico_copy[clef_min]]]&lt;br /&gt;
        dico_copy.pop(clef_min)&lt;br /&gt;
    return res&lt;br /&gt;
        &lt;br /&gt;
def cherche_id_gap_rare_freq(tab_frequences:list, nbr_echantillon:int)-&amp;gt;int:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie l&#039;id du dernier element rare&lt;br /&gt;
    d&#039;un tableau trie de frequence de valeur &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    freq = 0.1 * nbr_echantillon #frequence choisi pour determiner les valeurs rares&lt;br /&gt;
    for i in range(len(tab_frequences)):&lt;br /&gt;
        if tab_frequences[i] &amp;gt; freq:&lt;br /&gt;
            id_gap = i-1&lt;br /&gt;
            break&lt;br /&gt;
    return id_gap&lt;br /&gt;
&lt;br /&gt;
def cherche_rare_commun(tab_grundy:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; cherche les valeures rare d&#039;un tableau en regardant leurs nombre d&#039;apparitions&lt;br /&gt;
    à partir d&#039;un dico qui a pour clef le nombre et pour valeur son nombre d&#039;apparition&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    nombre_ele = len(tab_grundy)&lt;br /&gt;
    dico_frequence = {}&lt;br /&gt;
    for ele in tab_grundy:&lt;br /&gt;
        dico_frequence[ele] = dico_frequence.get(ele,0) + 1 #creer le dico de toutes les valeurs en clef et en valeurs leurs fréquences&lt;br /&gt;
    &lt;br /&gt;
    tab_trie = trie_dico_en_tab_croissant(dico_frequence)&lt;br /&gt;
    print(tab_trie)&lt;br /&gt;
    tab_clef = [tab[0] for tab in tab_trie]&lt;br /&gt;
    tab_frequences = [tab[1] for tab in tab_trie]&lt;br /&gt;
    id_gap = cherche_id_gap_rare_freq(tab_frequences,nombre_ele)&lt;br /&gt;
    &lt;br /&gt;
    rare = {0}&lt;br /&gt;
    commun = set()&lt;br /&gt;
    &lt;br /&gt;
    for i in range(len(tab_clef)):&lt;br /&gt;
        if i &amp;lt;= id_gap:&lt;br /&gt;
            rare.add(tab_clef[i])&lt;br /&gt;
        else:&lt;br /&gt;
            if tab_clef[i] != 0:&lt;br /&gt;
                commun.add(tab_clef[i])&lt;br /&gt;
            &lt;br /&gt;
    &lt;br /&gt;
    res = [rare,commun]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def indice_des_bits_a_un(masque:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau contenant les indices des bits à 1&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(len(masque)):&lt;br /&gt;
        if masque[i] == &#039;1&#039;:&lt;br /&gt;
            res.append(i)&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def est_rare(ele_bin:str,ind_1_du_masque:list)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; verfie si le nombre est rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    cpt_1 = 0&lt;br /&gt;
    for ind in ind_1_du_masque:&lt;br /&gt;
        if ele_bin[ind] == &#039;1&#039;:#verifie que l&#039;element à l&#039;indice ind est bien 1 à l&#039;indice des 1 sur le masque&lt;br /&gt;
            cpt_1 += 1&lt;br /&gt;
    return cpt_1 % 2 == 0&lt;br /&gt;
&lt;br /&gt;
def rares_dans_masque(masque:str,val_max:int)-&amp;gt;set:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Donne toutes les valeurs rares d&#039;un masque dans un set&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    n_bit = len(masque)&lt;br /&gt;
    ind_des_1 = indice_des_bits_a_un(masque)&lt;br /&gt;
    for nbr in range(val_max+1):&lt;br /&gt;
        nbr_bin = mets_nombre_en_bin_sur_x_bits(nbr,n_bit)&lt;br /&gt;
        if est_rare(nbr_bin,ind_des_1):&lt;br /&gt;
            res.add(nbr)&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def cherche_masque_pour_un_tab_grundy(tab_grundy:list)-&amp;gt;str:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; va chercher un masque de n bit qui convient le mieux pour le tableau de grundy donné&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    tab_grundy = tab_grundy[1:] #enlève la première valeur qui est 0&lt;br /&gt;
    val_max = max(tab_grundy)&lt;br /&gt;
    taille_bit = len(bin(val_max)[2:])&lt;br /&gt;
    masques_possibles = 2**taille_bit&lt;br /&gt;
    tab_rare_commun= cherche_rare_commun(tab_grundy)&lt;br /&gt;
    set_rare_cherche = tab_rare_commun[0]&lt;br /&gt;
    set_frequent = tab_rare_commun[1]&lt;br /&gt;
    res = 0&lt;br /&gt;
    &lt;br /&gt;
    print(&amp;quot;\nset_rare : {}&amp;quot;.format(set_rare_cherche))&lt;br /&gt;
    print(&amp;quot;set_frequent : {}\n&amp;quot;.format(set_frequent))&lt;br /&gt;
    &lt;br /&gt;
    for i in range(1,masques_possibles):&lt;br /&gt;
        masque = mets_nombre_en_bin_sur_x_bits(i,taille_bit)&lt;br /&gt;
        set_rare_masque = rares_dans_masque(masque,val_max)&lt;br /&gt;
        &lt;br /&gt;
        contient_rare = set_rare_cherche.issubset(set_rare_masque)&lt;br /&gt;
        inter = set_frequent.intersection(set_rare_masque)&lt;br /&gt;
        ne_contient_pas_frequent = inter == set() &lt;br /&gt;
        if contient_rare and ne_contient_pas_frequent: #verifie si les rares cherchés sont biens dans le masque et qu&#039;il ne contient pas de valeurs frequentes&lt;br /&gt;
            res = masque&lt;br /&gt;
            break&lt;br /&gt;
    return f&amp;quot;\nle masque qui convient est {res}&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def liste_des_id_des_rares_dans_tab(tab_grundy:list, listes_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau de tous les id des valeurs rares dans le tableau &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    for i in range(1,len(tab_grundy)):&lt;br /&gt;
        if tab_grundy[i] in listes_rares:&lt;br /&gt;
            res + [i]&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def liste_des_separations_tas_en_2_pour_avoir_val_commune(taille_tab:int, dn:int, liste_rares:set)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie tous les couples de possibilités d&#039;une separation en deux d&#039;un tas&lt;br /&gt;
    contenant uniquement une valeur rare&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = []&lt;br /&gt;
    somme_des_taille_couple = taille_tab - dn&lt;br /&gt;
    &lt;br /&gt;
    for val1_rare in liste_rares:&lt;br /&gt;
        val2 = somme_des_taille_couple-val1_rare&lt;br /&gt;
        if not val2 in liste_rares: #verifie que la valeur ne soit pas rares&lt;br /&gt;
            couple = (val1_rare,val2) #crée le couple sous forme de tuple&lt;br /&gt;
        res += [couple]&lt;br /&gt;
    &lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def set_des_valeurs_grundy_atteignables_rares(jeu_actions:list, tab_grundy:list, instant_i:int, liste_rares:set):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un set des valeurs atteignables avec des couples contenant des valeurs rares&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = set()&lt;br /&gt;
    for actions in jeu_actions:&lt;br /&gt;
        les_actions_possibles = liste_action_possibles(instant_i, actions[0], actions[1])&lt;br /&gt;
        if les_actions_possibles[0] == True:&lt;br /&gt;
            res.add(0)&lt;br /&gt;
            &lt;br /&gt;
        if les_actions_possibles[1] == True:&lt;br /&gt;
            res.add(tab_grundy[instant_i - actions[1]])           &lt;br /&gt;
                &lt;br /&gt;
        if les_actions_possibles[2] == True:&lt;br /&gt;
            global liste_separations_possibles_rares_commun&lt;br /&gt;
            liste_separations_possibles_rares_commun = liste_des_separations_tas_en_2_pour_avoir_val_commune(instant_i, actions[1], liste_rares)&lt;br /&gt;
            for couple in liste_separations_possibles_rares_commun:&lt;br /&gt;
                res.add(tab_grundy[couple[0]] ^ tab_grundy[couple[1]])&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def tab_val_grundy_methode_masque(taille_tab_souhaite:int, nbr_octal:str, masque:str)-&amp;gt;list:&lt;br /&gt;
    taille_tab_inital = 10000&lt;br /&gt;
    assert taille_tab_souhaite &amp;gt; taille_tab_inital, f&amp;quot;La taille de tableau recherché doit dépasser {taille_tab_inital}&amp;quot;&lt;br /&gt;
    tab_grundy = tab_val_grundy(taille_tab_inital, nbr_octal)&lt;br /&gt;
    jeu_actions = tab_kn_dn(nbr_octal)&lt;br /&gt;
    listes_rares = rares_dans_masque(masque,max(tab_grundy)) #un set contant toutes les valeurs rares du masque&lt;br /&gt;
    liste_id_rares = liste_des_id_des_rares_dans_tab(tab_grundy, listes_rares)&lt;br /&gt;
&lt;br /&gt;
    for taille in range(taille_tab_inital+1, taille_tab_souhaite+1):#Trouver les valeurs suivantes avec la méthode du masque&lt;br /&gt;
        set_valeurs_atteignables = set_des_valeurs_grundy_atteignables_rares(jeu_actions, tab_grundy, taille, liste_id_rares) &lt;br /&gt;
        mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
        if not mex in listes_rares:#si le mex est commun alors&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
        &lt;br /&gt;
        else: #on va rajouter des valeurs jusqu&#039;à avoir un mex commun sinon on continue jusqu&#039;au bout&lt;br /&gt;
            for actions in jeu_actions:&lt;br /&gt;
                les_actions_possibles = liste_action_possibles(taille, actions[0], actions[1])&lt;br /&gt;
                if les_actions_possibles[2] == True:&lt;br /&gt;
                    somme_des_taille_couple = taille - actions[1]&lt;br /&gt;
                    for i in range(1, (somme_des_taille_couple//2)+1):&lt;br /&gt;
                        val1 = i&lt;br /&gt;
                        val2 = somme_des_taille_couple-i&lt;br /&gt;
                        if all(val1 not in couple for (couple) in liste_separations_possibles_rares_commun):#verifie que la valeur n&#039;a pas déjà été testé&lt;br /&gt;
                            set_valeurs_atteignables.add(tab_grundy[val1] ^ tab_grundy[val2])&lt;br /&gt;
                            mex = calc_mex(set_valeurs_atteignables)&lt;br /&gt;
                            if not mex in listes_rares:&lt;br /&gt;
                                break&lt;br /&gt;
                if not mex in listes_rares:&lt;br /&gt;
                    break&lt;br /&gt;
            tab_grundy += [mex]&lt;br /&gt;
            &lt;br /&gt;
    return tab_grundy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15525</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15525"/>
		<updated>2024-05-21T07:33:37Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Valeurs de Grundy pour les jeux octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def est_periode(p:int,s:int,tab_grundy:list,n:int)-&amp;gt;bool:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; test la périodicité&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    res = True&lt;br /&gt;
    m = n-(p+s)&lt;br /&gt;
    for i in range(m):&lt;br /&gt;
        if s+p+i == len(tab_grundy):&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
        if tab_grundy[s+i] != tab_grundy[s+p+i]:&lt;br /&gt;
            res = False&lt;br /&gt;
            break&lt;br /&gt;
    return res&lt;br /&gt;
&lt;br /&gt;
def test_periodicite(n:int,k:int,tab:list)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie un tableau si on trouve la période sinon renvoie None&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    for p in range(1,n+1):#periode&lt;br /&gt;
        s = n-p&lt;br /&gt;
        if est_periode(p,s,tab,n*2+k):&lt;br /&gt;
            return (s,p,tab[s:s+p])&lt;br /&gt;
    return None&lt;br /&gt;
&lt;br /&gt;
def periodicite(nbr_octal:str)-&amp;gt;list:&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot; renvoie la période , taille de la période, taille de la prépériode d&#039;un tableau avec les valeurs de grundy d&#039;un jeu&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    taille_test = 30000&lt;br /&gt;
    tab_test = tab_val_grundy(taille_test, nbr_octal)[1:taille_test] #tableau où chercher la périodicité&lt;br /&gt;
    k = len(nbr_octal)&lt;br /&gt;
    for n in range(k+2,len(tab_test)+1,2):&lt;br /&gt;
        res = test_periodicite(n,k,tab_test)&lt;br /&gt;
        if res != None:&lt;br /&gt;
            return res &lt;br /&gt;
    return &amp;quot;Periode non trouvé pour .{}&amp;quot;.format(nbr_octal)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15524</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15524"/>
		<updated>2024-05-21T07:30:31Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Valeurs de Grundy pour les jeux octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;br /&gt;
&lt;br /&gt;
== Méthode ==&lt;br /&gt;
&lt;br /&gt;
== Périodicité ==&lt;br /&gt;
&lt;br /&gt;
== Méthode rare/fréquentes ==&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15521</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15521"/>
		<updated>2024-05-21T01:32:23Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy pour les jeux octaux =&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15520</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15520"/>
		<updated>2024-05-21T01:30:58Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Représentation en jeu octal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;br /&gt;
Un jeu &amp;quot;octal&amp;quot; qui pourrait représenter le jeu de Nim serait 0.333... un sauf que ce n&#039;est pas un jeu octal.&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15519</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15519"/>
		<updated>2024-05-21T01:26:54Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Représentation en jeu octal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Cram = 0.07&lt;br /&gt;
&lt;br /&gt;
Néanmoins, le jeu de Nim n&#039;est pas un jeu octal.&lt;br /&gt;
Dans Nim, les mouvements sont extrêmement flexibles : un joueur peut retirer n&#039;importe quel nombre d&#039;objets d&#039;une pile choisie. Cette flexibilité n&#039;est pas présente dans les jeux octaux, où les mouvements sont prédéfinis.&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15518</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15518"/>
		<updated>2024-05-21T01:19:52Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Représentation des jeux en jeux octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation en jeu octal ==&lt;br /&gt;
Comme vous l&#039;avez compris il y a une infinité de jeux octaux.&lt;br /&gt;
&lt;br /&gt;
On retrouve les jeux qu&#039;on a introduit dans ces catégories.&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;br /&gt;
Jeu de Fort Boyard = 0.333 &amp;lt;/br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15517</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15517"/>
		<updated>2024-05-21T01:17:18Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Jeu de Octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeux Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Représentation des jeux en jeux octaux ==&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15516</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15516"/>
		<updated>2024-05-21T01:13:01Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Règles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 (d&#039;où le nom jeu octal) :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15515</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15515"/>
		<updated>2024-05-21T01:07:31Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Règles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;2^0&amp;lt;/math&amp;gt; si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^1&amp;lt;/math&amp;gt; si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* &amp;lt;math&amp;gt;2^2&amp;lt;/math&amp;gt; si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15514</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15514"/>
		<updated>2024-05-21T01:07:03Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Jeu de Octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* 2^0 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2^1 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 2^2 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15513</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15513"/>
		<updated>2024-05-21T01:02:31Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Règles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* 1 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 4 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille supérieure ou égale &#039;&#039;n&#039;&#039;.&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15512</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15512"/>
		<updated>2024-05-21T01:00:20Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Règles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* 1 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 4 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille &#039;&#039;n&#039;&#039; (3=&#039;&#039;&#039;1&#039;&#039;&#039;+2) ou de taille strictement supérieure à &#039;&#039;n&#039;&#039; (3=1+&#039;&#039;&#039;2&#039;&#039;&#039;).&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15511</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15511"/>
		<updated>2024-05-21T00:58:49Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Règles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
&lt;br /&gt;
Notation 0.&#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
&lt;br /&gt;
où chaque chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;k&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
&lt;br /&gt;
* 1 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 4 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille &#039;&#039;n&#039;&#039; (3=&#039;&#039;&#039;1&#039;&#039;&#039;+2) ou de taille strictement supérieure à &#039;&#039;n&#039;&#039; (3=1+&#039;&#039;&#039;2&#039;&#039;&#039;).&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15510</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15510"/>
		<updated>2024-05-21T00:50:33Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Règles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
:&#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; . &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
où chaque chiffre &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
* 1 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 4 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du jeu de Nim, à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille &#039;&#039;n&#039;&#039; (3=&#039;&#039;&#039;1&#039;&#039;&#039;+2) ou de taille strictement supérieure à &#039;&#039;n&#039;&#039; (3=1+&#039;&#039;&#039;2&#039;&#039;&#039;).&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15509</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15509"/>
		<updated>2024-05-21T00:49:57Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Cas à 1 tas */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille  &#039;&#039;n&#039;&#039; (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à  &#039;&#039;n&#039;&#039; sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est  &#039;&#039;n&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
:&#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; . &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
où chaque chiffre &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
* 1 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 4 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du [[jeux de Nim|jeu de Nim]], à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille &#039;&#039;n&#039;&#039; (3=&#039;&#039;&#039;1&#039;&#039;&#039;+2) ou de taille strictement supérieure à &#039;&#039;n&#039;&#039; (3=1+&#039;&#039;&#039;2&#039;&#039;&#039;).&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15508</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15508"/>
		<updated>2024-05-21T00:49:31Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Jeu de Octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;br /&gt;
&lt;br /&gt;
Les règles d&#039;un jeu octal précisent les actions possibles lorsqu&#039;un joueur retire &#039;&#039;n&#039;&#039; objets d&#039;un tas donné, et sont encodées par une suite de chiffres entre 0 et 7 :&lt;br /&gt;
:&#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt; . &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt; &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt; …&lt;br /&gt;
où chaque chiffre &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; indique en combien de tas le joueur doit scinder le tas dans lequel il a retiré &#039;&#039;n&#039;&#039; objets. Le chiffre &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt; est la somme de :&lt;br /&gt;
* 1 si le joueur peut laisser 0 tas, 0 sinon;&lt;br /&gt;
* 2 si le joueur peut laisser 1 tas, 0 sinon;&lt;br /&gt;
* 4 si le joueur peut laisser 2 tas, 0 sinon&lt;br /&gt;
&lt;br /&gt;
Par exemple, si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=0, les joueurs n&#039;ont pas le droit de retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas. Si &#039;&#039;d&#039;&#039;&amp;lt;sub&amp;gt;&#039;&#039;n&#039;&#039;&amp;lt;/sub&amp;gt;=3, on retrouve la règle classique du [[jeux de Nim|jeu de Nim]], à savoir que le joueur peut retirer &#039;&#039;n&#039;&#039; objets d&#039;un tas de taille &#039;&#039;n&#039;&#039; (3=&#039;&#039;&#039;1&#039;&#039;&#039;+2) ou de taille strictement supérieure à &#039;&#039;n&#039;&#039; (3=1+&#039;&#039;&#039;2&#039;&#039;&#039;).&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15507</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15507"/>
		<updated>2024-05-21T00:44:39Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Jeu de Octaux */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux =&lt;br /&gt;
Les jeux octaux sont aussi des jeux impartiaux, assez proches du jeu de Nim. Ils se jouent aussi avec des tas d’objet. Leurs caractéristiques sont que les coups autorisés sont définis à l’aide d’un système numérique de 0 à 7 d’où le nom jeu octal. &lt;br /&gt;
&lt;br /&gt;
== Règles ==&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15506</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15506"/>
		<updated>2024-05-21T00:43:15Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Jeu de Octaux=&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15505</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15505"/>
		<updated>2024-05-21T00:42:18Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Application */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; On a choisi de retirer 1 bâton au tas de 24 pour avoir une valeur de Grundy totale de 0. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; L&#039;adversaire est dans une position perdante, quoiqu’il fasse il ne peut pas passer en position gagnante. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Il nous reste plus qu&#039;à continuer ainsi pour s&#039;assurer de gagner. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15503</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15503"/>
		<updated>2024-05-21T00:38:41Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Application */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
Imaginons que nous soyons les premiers joueurs à jouer.&lt;br /&gt;
&lt;br /&gt;
Nous nous trouvons dans une position gagnante car la valeur de Grundy du Jeu est de &amp;lt;math&amp;gt; 15 \neq 0 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Pour pouvoir ramener celui-ci à une valeur de 0 pour que notre adversaire soit dans une position perdante, il nous suffit de changer un tas où la valeur de grundy de celui-ci possède un bit à 1 au même indice que le bit à 1 avec le plus grand exposant de 2 de la valeur de Grundy du jeu.&lt;br /&gt;
&lt;br /&gt;
Dans notre cas le premier bit à 1 de 15 est celui qui est au 3eme exposant de 2.&lt;br /&gt;
Le nombre qu’on doit changer est 24. Pour savoir à quelle valeur on doit le baisser il suffit de faire le XOR de 24 et de la valeur de Grundy du jeu qui est 15. On obtient &amp;lt;math&amp;gt; 24 \oplus 15 = 23 &amp;lt;/math&amp;gt;. &lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15497</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15497"/>
		<updated>2024-05-21T00:12:28Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Application */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;br /&gt;
Mettons à l’œuvre nos connaissances afin de gagner une partie dans le jeu de Nim de l&#039;exemple précédent.&lt;br /&gt;
&lt;br /&gt;
I&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15496</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15496"/>
		<updated>2024-05-21T00:09:49Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Valeurs de Grundy */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Application ==&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15495</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15495"/>
		<updated>2024-05-21T00:06:35Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Cas à plusieurs tas */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
[[Fichier:Exemple_jeu_de_nim.png|300px]]&lt;br /&gt;
&lt;br /&gt;
Ce jeu a pour valeur de Grundy :&lt;br /&gt;
&amp;lt;math&amp;gt; 17 \oplus 6 \oplus 24 \Leftrightarrow 10001\oplus 00110\oplus 11000 \Leftrightarrow 15 &amp;lt;/math&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
	<entry>
		<id>http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15494</id>
		<title>Calcul des valeurs de Grundy pour des jeux octaux</title>
		<link rel="alternate" type="text/html" href="http://os-vps418.infomaniak.ch:1250/mediawiki/index.php?title=Calcul_des_valeurs_de_Grundy_pour_des_jeux_octaux&amp;diff=15494"/>
		<updated>2024-05-21T00:00:32Z</updated>

		<summary type="html">&lt;p&gt;Mbrunot : /* Calcul des valeurs pour le jeu de Nim */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Étudiant : Mathieu BRUNOT&lt;br /&gt;
&lt;br /&gt;
Tuteur : Valenti Gledel&lt;br /&gt;
&lt;br /&gt;
= Jeu de Impartiaux =&lt;br /&gt;
&lt;br /&gt;
== Description ==&lt;br /&gt;
&lt;br /&gt;
Un jeu impartial est un jeu à 2 joueurs où les joueurs jouent à tour de rôle et leurs mouvements possibles ne dépendent pas du joueur qui joue, il n&#039;y a donc pas de hasard ni d&#039;égalité dans une partie.&lt;br /&gt;
&lt;br /&gt;
Le perdant étant celui qui ne peut plus jouer, aussi appelé version normale.&lt;br /&gt;
&lt;br /&gt;
En voici des exemples:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Cram&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Les joueurs mettent des dominos sur une grille. Le joueur qui met le dernier domino gagne.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_cram.png|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu de Nim&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Il se joue avec un ou plusieurs tas d&#039;objets exemple des allumettes.&amp;lt;/br&amp;gt;&lt;br /&gt;
Chacun des joueurs joue à tour de rôle en choisissant un tas et en retirant autant d’allumettes que souhaité dans le tas,&lt;br /&gt;
pour que son adversaire n’ait plus la possibilité de jouer, c’est ainsi que le jeu prend fin.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Jeu_de_nim.jpg|200px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;&#039;Jeu des bâtonnets Fort Boyard&#039;&#039;&#039;&amp;lt;/br&amp;gt;&lt;br /&gt;
Ce jeu consiste à n’avoir que le droit de retirer de 1 à 3 bâtonnets à chaque tour. Hormis la différence est que le joueur perdant est celui qui prend le dernier bâtonnet.&lt;br /&gt;
Cette condition de victoire est appelé version misère.&lt;br /&gt;
&amp;lt;/br&amp;gt;&lt;br /&gt;
[[Fichier:Batonnets-fort-boyard.jpg|300px]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Stratégie Gagnante ==&lt;br /&gt;
&lt;br /&gt;
En effet, l’ensemble des jeux impartiaux sont résolubles à l’aide d’outils théorique afin de connaitre l&#039;action à faire pour être le joueur en position gagnante.&lt;br /&gt;
&lt;br /&gt;
Prenons le jeu de Fort Boyard mais cette fois-ci en version normale.&lt;br /&gt;
Dans celui-ci la stratégie gagnante est de laisser un tas à l&#039;adversaire qui serait un multiple de quatre.&lt;br /&gt;
* Pour le trouver il suffit de regarder les positions perdantes dans chaque cas:&lt;br /&gt;
*# Si le nombre de bâtonnets est n=1, le joueur courant gagne car il peut enlever le dernier jeton et laisser l&#039;adversaire avec un tas vide.&lt;br /&gt;
*# Si n=2, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=3, le joueur courant gagne car il peut retirer les derniers bâtonnets.&lt;br /&gt;
*# Si n=4, le joueur courant perd car il va forcément laisser son adversaire dans une position gagnante.&lt;br /&gt;
*# Plus généralement, toute position où &amp;lt;math&amp;gt;n \equiv 1,2,ou 3\pmod 4&amp;lt;/math&amp;gt;est une position gagnante, et toute position où &amp;lt;math&amp;gt;n \equiv 0\pmod 4&amp;lt;/math&amp;gt; est une position perdante.&lt;br /&gt;
&lt;br /&gt;
Ainsi on pourrait se demander la stratégie gagnant d&#039;autres jeux tel que le jeu de Nim ou le jeu de Cram&lt;br /&gt;
&lt;br /&gt;
= Valeurs de Grundy =&lt;br /&gt;
===Théorème de Sprague-Grundy ===&lt;br /&gt;
&lt;br /&gt;
Le théorème de Sprague-Grundy nous permet de trouver la stratégie gagnante d’un jeu impartial fini en version normale (le perdant est le joueur ne pouvant plus jouer).&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy ou d’un tas se calcule récursivement par ses règles :&lt;br /&gt;
&lt;br /&gt;
 - Un tas vide a pour valeur de Grundy 0.&lt;br /&gt;
 - Le nombre Grundy d’une position non nulle est le plus petit entier positif ou nul qui n’est pas dans la liste des valeurs de Grundy des positions que l’on peut atteindre à partir de notre position.&lt;br /&gt;
&lt;br /&gt;
Une position perdante d’un jeu celle qui donne une valeur de Grundy de 0.&lt;br /&gt;
&lt;br /&gt;
== Calcul des valeurs pour le jeu de Nim ==&lt;br /&gt;
&lt;br /&gt;
=== Cas à 1 tas ===&lt;br /&gt;
&lt;br /&gt;
La valeur de Grundy du jeu de Nim classique d’un tas de taille n (un entier) est celle du nombre de bâtonnets dans ce tas.&lt;br /&gt;
&lt;br /&gt;
On l’explique car toutes les tailles de tas strictement inférieures à n sont atteignables à partir de celui-ci. Alors le minimum exclu(mex) des valeurs atteignables est n.&lt;br /&gt;
&lt;br /&gt;
On appelle ce nombre précis dans le cas du jeu de Nim, un nimber.&lt;br /&gt;
&lt;br /&gt;
Donc par exemple dans un tas de taille 9 du jeu Nim, on aurait une valeur de Grundy de 9, ceci est simple à comprendre.&lt;br /&gt;
&lt;br /&gt;
=== Cas à plusieurs tas ===&lt;br /&gt;
&lt;br /&gt;
Dans le cas de plusieurs tas, la valeur de Grundy de tous les jeux est le XOR de toutes les valeurs de Grundy de chaque tas.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
&lt;br /&gt;
Voici un jeu de Nim composé de trois tas de bâtonnets:&lt;br /&gt;
Exemple_jeu_de_nim.png&lt;/div&gt;</summary>
		<author><name>Mbrunot</name></author>
	</entry>
</feed>