Un Tic-Tac-Toe en HTML écrit en Python, sans framework, mais avec les pieds.


Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Mea Sam&Maxima Culpa

Tout d’abord, je me dois de vous présenter mes excuses.

Je vous avais laissés miroiter, lors de ma première intervention, des articles avec du code bien dégueulasse afin de décomplexer les plus nuls d’entre vous, mais, quand je me suis penché sur la rédaction de mon troisième article ainsi que sur le script que je vais vous présenter aujourd’hui, j’étais tellement embarrassé de ne pas comprendre moi-même ce que j’avais fait à l’époque que j’ai repoussé cette publication à un jour où j’aurais le temps de décortiquer mon propre sale travail.

Bien entendu, ce jour n’est jamais venu et vous n’avez plus entendu parler de moi.

Quelle misère.
Alors que j’avais sous les yeux un code illisible, d’une inutilité profonde et surtout particulièrement long pour ce qu’il avait à faire, je faisais la fine bouche et vous privais d’une véritable leçon de vie qui pourrait se résumer par “Comment faire n’importe quoi n’importe comment.”

Me voilà donc devant vous aujourd’hui pour tenter de me rattraper.

L’idée géniale.

En 1998, poussé par une intuition qui ne survient sur Terre qu’une fois tous les 69 ans, mon ami Frédéric s’est lancé dans l’écriture informatique d’un Tic-Tac-Toe afin de rendre un hommage appuyé à l’accadémie des neufs. Il a donc pondu, à la main, en HTML, toutes les combinaisons possibles pour pouvoir jouer au jeu.

Chez moi, qui découvrais à peine les homepages sur fond gris et les GIF animés “Work in progress…”, je peux vous assurer que cette initiative eut l’effet d’une bombe. Mon ami était un génie.

Une dizaine d’années plus tard, pour son anniversaire, je me suis dit que j’allais faire la même chose, mais en Python. Compte-tenu de mon niveau et de l’intérêt de la démarche, j’étais convaincu d’arriver à un résultat tout aussi absurde. Je n’ai pas été déçu.

Le résultat.

Avant d’aborder la manière dont je m’y suis pris et d’essuyer, de fait, les ricanements des plus véloces d’entre vous, je vous propose d’admirer le résultat. Car, c’est n’importe quoi, c’est fait n’importe comment, mais ça marche :

Cliquez ici pour être certain de ne pas gagner.

La mise en Œuvre.

Le script s’appuie sur la méthode .format() qui permet de faire des choses émouvantes comme :

>>> toto = "J'ha{1} Orleans pour son cor{0} artis{3} suc{2}ent."
>>> toto.format("bite", "nichon", "anal", "cul")
"J'hanichon Orleans pour son corbite artiscul sucanalent."

À noter que si vous faites…

>>> toto = "J'ha{1} Orleans pour son cor{0} artis{3} suc{2}ent."
>>> titi = ("bite", "nichon", "anal", "cul")
>>> toto.format(titi)

…vous allez récolter un IndexError parce que, même si titi contient 4 éléments, vous ne filez qu’un seul argument à .format() alors qu’il en a besoin de 4. Il faut donc faire…

>>> toto.format(*titi)

… où l’étoile vous dépackagera votre tuple bien comme il faut (mais ne me demandez pas pourquoi).

Armé de cette subtilité, j’ai créé une page HTML avec un petit tableau de 3×3 puis j’ai écrit dans chacune des cellules {0}, {1}, {2}… pour pouvoir les remplacer par le texte

<image src="rond.png" />

si j’avais besoin d’y placer un rond, ou par

<image src="croix.png" />

si j’avais besoin d’y mettre une croix.

Ici, par exemple, je vois bien que j’ai numéroté les cellules en faisant un petit escargot et non pas ligne par ligne, mais je n’ai aucune idée de pourquoi j’ai fait ce choix. J’ai dû vouloir faire une blague. Si ça vous fait rire, c’est qu’elle est réussie. Moi, je suis resté de marbre.

<table >
  <tr>
    <td>{0}</td>
    <td>{1}</td>
    <td>{2}</td>
  </tr>
  <tr>
    <td>{7}</td>
    <td>{8}</td>
    <td>{3}</td>
  </tr>
  <tr>
    <td>{6}</td>
    <td>{5}</td>
    <td>{4}</td>
  </tr>
</table>

Puis, pour exporter chaque page HTML, j’ai écrit cette petite fonction…

  def ecriturePage (namePage, contenuPage):
    with open ('{0}.html'.format(namePage), 'w') as new_file:
        new_file.write (contenuPage)

…qui récupère le nom de la page, son contenu et l’enregistre dans le dossier courant.

Ensuite…

Ensuite, c’est plus compliqué.
De ce que je comprends, j’initialise une liste que je réécris partiellement en fonction des combinaisons recherchées.

C’est un peu flou, mais, ce qui est sûr c’est que je n’ai pas automatisé grand chose, surtout sur la fin…
J’ai rempli des tas de feuilles de brouillon pour trouver les bonnes positions pour les croix et les ronds à chaque étape… et je les ai retranscrites avec des for des if et des else

Pour la dernière étape, je suis sûr qu’il eut été plus rapide pour moi d’écrire les pages HTML directement à la main, mais, j’aurais alors lamentablement échoué dans ma mission et mon ami m’aurait sans nul doute frappé violemment au visage si je lui avais présenté ce résultat médiocre comme cadeau pour son anniversaire.

J’ai donc persévéré.

La récompense.

Vous l’aurez compris, cette décision absurde s’est révélée extrêmement judicieuse.

En effet, concentré que j’étais dans l’obtention de toutes les pages nécessaires pour jouer, je n’ai absolument pas prêté attention à la non-obtention des pages inutiles…

Et je me suis retrouvé avec ce joyau.

J’avais fait écrire une combinaison qui n’était pas possible.
J’avais créé un futur pour le jeu qui, bien qu’existant, ne lui était pas accessible.

MON DIEU QUEL PUR MOMENT DE BONHEUR !!!

Le code

L’archive avec l’index.html, les images, le CSS… est disponible ici.

Pour créer toutes les pages (dont ces bijoux de pages inutiles) il suffit de lancer le fichier GO.py.

Mais pour les plus impatients, voici l’intégralité du code :

# -*- coding: utf-8 -*-
# Ce programme écrit toutes les pages html nécessaires pour jouer au Tic-Tac-Toe 
# En partant du principe que l'ordinateur ne doit pas perdre, donc commence...
 
c = range (10)
rond = '<image src="rond.png" />'
croix = '<image src="croix.png" />'
croixR = '<image src="croixR.png" />'
back = """<a href="index.html"> Pour recommencer, c'est ici... </a>"""
 
def page (grille):
    return('''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" type="text/css" href="style.css" />
  <link rel="icon" type="image/png" href="favicon.png" />
  <title>Y a matière à... l académie des neuf</title>
 </head>  
 
<body> 
 
<div class="grille">
<table >
<tr>
<td>{0}</td>
<td>{1}</td>
<td>{2}</td>
</tr>
<tr>
<td>{7}</td>
<td>{8}</td>
<td>{3}</td>
</tr>
<tr>
<td>{6}</td>
<td>{5}</td>
<td>{4}</td>
</tr>
</table>
</div>
 
<div class="redo">
{9}
</div>
 
 
</body>
 
</html>
'''.format(*grille))
 
def lien (link):
    return ('<a href="{0}.html"><image src="vide.png" /></a>'.format(link))
 
 
def ecriturePage (namePage, contenuPage):
    with open ('{0}.html'.format(namePage), 'w') as new_file:
        new_file.write (contenuPage)
 
def grilleVide (num):
 
    for i in range (8):
        c[i] = lien ("{0}{1}".format(num, i))
        c[8] = croix
        c[9] = ""
 
def grilleFin ():
 
    for i in range (8):
        c[i] = '<image src="vide.png" />'
        c[8] = croix
        c[9] = back
 
 
####### 1er clic
 
for i in range (8):
 
    grilleVide(i)
    c[i] = rond
 
    if i !=1 and i !=5:    
        c[1] = croix        
    else:
        c[7] = croix
 
    ecriturePage(i, page(c))        
 
####### 2eme clic
 
for i in range (8):
 
    if i == 0:
 
        for j in range (8):
 
            if j !=5:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[5] = croixR
                c[8] = croixR   
 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
    if i == 2:
 
        for j in range (8):
 
            if j !=5:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[5] = croixR
                c[8] = croixR 
 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[4] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
    if i == 7:
 
        for j in range (8):
 
            if j !=5:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[5] = croixR
                c[8] = croixR 
 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[2] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
    if i == 3:
 
        for j in range (8):
 
            if j !=5:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[5] = croixR
                c[8] = croixR 
 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[0] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
 
    elif i == 1:
 
        for j in range (8):
 
 
            if j !=3:
                grilleFin()
                c[7] = croixR
                c[i] = rond
                c[j] = rond
                c[3] = croixR
                c[8] = croixR 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[7] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
    elif i == 5:
 
        for j in range (8):
 
            if j !=3:
                grilleFin()
                c[7] = croixR
                c[i] = rond
                c[j] = rond
                c[3] = croixR
                c[8] = croixR 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[7] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
    elif i == 6:
 
        for j in range (8):
 
            if j !=5:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[5] = croixR
                c[8] = croixR 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[4] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
    elif i == 4:
 
        for j in range (8):
 
            if j !=5:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[5] = croixR
                c[8] = croixR 
            else:
                grilleVide("{0}{1}".format(i, j))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croix
 
            ecriturePage("{0}{1}".format(i, j), page(c))
 
 
#######3eme clic                
 
for i in range (8):
 
    if i in (0, 4):
 
        j = 5
 
        for k in range (8):
 
            if k !=2:
                grilleFin()
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croixR
                c[k] = rond
                c[2] = croixR
                c[8] = croixR 
 
            else:
                grilleVide("{0}{1}{2}".format(i, j, k))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croix
                c[k] = rond
                c[3] = croix
 
            ecriturePage("{0}{1}{2}".format(i, j, k), page(c))
 
    if i in (2, 6):
 
        j = 5
 
        for k in range (8):
 
            if k !=0:
                grilleFin()
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[4] = croixR
                c[k] = rond
                c[0] = croixR
                c[8] = croixR 
 
            else:
                grilleVide("{0}{1}{2}".format(i, j, k))
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[4] = croix
                c[k] = rond
                c[7] = croix
 
            ecriturePage("{0}{1}{2}".format(i, j, k), page(c))
 
 
    if i == 7:
 
        j = 5
 
        for k in range (8):
 
            if k in (0, 3, 4):
                grilleFin()
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[2] = croixR
                c[k] = rond
                c[6] = croixR
                c[8] = croixR 
 
            elif k == 6:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[2] = croixR
                c[k] = rond
                c[0] = croixR
 
            ecriturePage("{0}{1}{2}".format(i, j, k), page(c))
 
    if i == 3:
 
        j = 5
 
        for k in range (8):
 
            if k in (2, 6, 7):
                grilleFin()
                c[1] = croix
                c[i] = rond
                c[j] = rond
                c[0] = croixR
                c[k] = rond
                c[4] = croixR
                c[8] = croixR 
 
            elif k == 4:
                grilleFin()
                c[1] = croixR
                c[i] = rond
                c[j] = rond
                c[0] = croixR
                c[k] = rond
                c[2] = croixR
 
            ecriturePage("{0}{1}{2}".format(i, j, k), page(c))
 
 
    if i == 1:
 
        j = 3
 
        for k in range (8):
 
            if k in (0, 4, 5):
                grilleFin()
                c[7] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croixR
                c[k] = rond
                c[2] = croixR
                c[8] = croixR 
 
            elif k == 2:
                grilleFin()
                c[7] = croixR
                c[i] = rond
                c[j] = rond
                c[6] = croixR
                c[k] = rond
                c[0] = croixR
 
            ecriturePage("{0}{1}{2}".format(i, j, k), page(c))
 
    if i == 5:
 
        j = 3
 
        for k in range (8):
 
            if k in (0, 1, 4):
                grilleFin()
                c[7] = croix
                c[i] = rond
                c[j] = rond
                c[6] = croixR
                c[k] = rond
                c[2] = croixR
                c[8] = croixR 
 
            elif k == 2:
                grilleFin()
                c[7] = croixR
                c[i] = rond
                c[j] = rond
                c[6] = croixR
                c[k] = rond
                c[0] = croixR
 
            ecriturePage("{0}{1}{2}".format(i, j, k), page(c))
 
 
######4eme clic
 
for i in range (8):
 
    if i == 0:
 
        grilleFin()
        j = 5
        k = 2
        l = 4
        c[1] = croix
        c[i] = rond
        c[j] = rond
        c[6] = croix
        c[k] = rond
        c[3] = croixR
        c[l] = rond
        c[7] = croixR
        c[8] = croixR
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
        l = 7
        c[3] = croix
        c[l] = rond
        c[7] = croix
        c[8] = croix
        c[l] = rond
        c[4] = croix
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
    if i == 2:
 
        grilleFin()
        j = 5
        k = 0
        l = 3
        c[1] = croix
        c[i] = rond
        c[j] = rond
        c[4] = croix
        c[k] = rond
        c[7] = croix
        c[l] = rond
        c[6] = croix
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
        l = 6
        c[l] = rond
        c[3] = croixR
        c[7] = croixR
        c[8] = croixR
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
 
    if i == 4:
 
        grilleFin()
        j = 5
        k = 2
        l = 7
        c[1] = croix
        c[i] = rond
        c[j] = rond
        c[6] = croix
        c[k] = rond
        c[3] = croix
        c[l] = rond
        c[0] = croix
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
        l = 0
        c[l] = rond
        c[7] = croixR
        c[3] = croixR
        c[8] = croixR
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
    if i == 6:
 
        grilleFin()
        j = 5
        k = 0
        l = 3
        c[1] = croix
        c[i] = rond
        c[j] = rond
        c[4] = croix
        c[k] = rond
        c[7] = croix
        c[l] = rond
        c[2] = croix
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))
 
        l = 2
        c[l] = rond
        c[3] = croixR
        c[7] = croixR
        c[8] = croixR
 
        ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))

Je ne doute pas un seul instant que ces lignes vont vous ouvrir les yeux sur le sens de la Vie et que tout le reste n’aura plus trop d’importance, mais retenez tout de même que la première personne à être concernée par les commentaires dans vos codes, c’est vous. Et que si vous ne voulez pas vous retrouver, comme moi, avec des scripts imbitables, prenez le temps de les commenter (avec des commentaires à jour, bien entendu, sinon, autant écrire en JavaScript :-p )

À bientôt…

10 thoughts on “Un Tic-Tac-Toe en HTML écrit en Python, sans framework, mais avec les pieds.

  • kontre

    Rah mais fuck, pourquoi tu commences par le milieu, c’est impossible de gagner si les deux adversaires jouent bien. C’est frustrannnnt !
    Bon sinon, j’ai bien aimé ta prose !

  • 01ivier Post author

    @Pwet: À ton service… :-p

    @kontre: La victoire réside dans fait d’empêcher la machine de gagner car c’est ainsi qu’on sauvera l’Humanité.

    (Une leçon de Vie, ce script, je vous dis…
    Une leçon de Vie… :-p )

  • Anb

    *Lit le script* Hmmm bah c’est pas si mal…

    # 1er click

    Que ?

    # 2e click

    Non ? …

    # 3e click

    Argh….. le con ! :D C’est vraiment très très très crado, bravo vraiment !

  • Etienne

    Ça me fait penser au Nom de la rose, les moines copistes devant leur pupitre…

  • Etienne

    Faut quand-même noter que ça fonctionne. Le problème est bien cadré, on voit pas où ça pourrait merder.

    Je suis convaincu que les meilleurs programmeurs font ça régulièrement. On sait pas comment aborder tel problème, alors on le prend par le premier bout qui vient et on code pas à pas. Une fois que ça fonctionne, on soupire de soulagement et on essaye de ne plus y penser.

  • PocketTiger

    Tout cela me rappel quelque peut l’article sur la complexité algorithmique

    Ici générer toutes les possibilités une à une est humainement d’une complexité folle, cela dit c’est d’une rapidité à toute épreuves à l’exécution.
    D’un autre coté faire une “IA” rend l’exécution plus lente, mais c’est bien moins complexe à codé.

    Et je ne parle même pas des algo-solveur de puzzle et autres casse-tête, si quelqu’un veux un mal de tête à 2G$ tentez un peut de résoudre Eternity II

    Sinon c’est bien beau. Mais j’ai envie de te dire, que tu a juste effleuré la surface des possibilités. Aurais tu les couilles suffisantes de faire la même chose avec un jeux d’échec ou de dame ? =)

  • kontre

    Je dirais pas qu’une IA est moins complexe à coder, mais plutôt que c’est moins long. Ça peut rester super complexe de coder une IA…

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.