Catégorie : Projets

Projets

Le projet Colors en python sur NumWorks

Colors est un utilitaire développé en python pour la NumWorks. Il permet d’obtenir facilement les formats de couleur RGB et hexadécimal d’une couleur via les valeurs Red, Green, et Blue, qui sont à configurer.

Développement

Étant en train de réfléchir au fonctionnment d’un futur projet (paint.py 🤔🤫). J’avais besoin de fournir un utilitaire facultatif en complément de ce projet pour ne pas l’alourdir. Voici comment est né Colors.

Je commence donc le développement tranquillement, et en un petit week-end, le projet Colors est terminé.

Colors v1

De multiples problèmes m’ont fait perdre beaucoup de temps. Je voulais, à l’origine, afficher une large palette de couleurs, puis grâce à un curseur et des flèches, déplacer le curseur sur un pixel de la palette et nous renvoyer les informations sur la couleur selectionné, c’était palette.py. Problème : j’avais besoin au minimum de 255 pixels en hauteur, et 255 pixels en largeur. Mais l’écran de la NumWorks fait 222 pixels en hauteur. Il m’étais donc impossible d’effectuer la palette précise que je pensais avec une telle taille d’écran.

Parallèlement, en me balandant sur le net afin de comprendre le fonctionnement du format RGB et hexadécimal par rapport aux couleurs, je fus tombé sur un système de sélection de couleur grâce à des barres coulissantes, et j’ai adoré !

Je me suis dit que ce serait parfait pour Palette (qui deviendra Colors). J’ai donc commencé à développer un système reproduisant le fonctionnement d’une barre coulissante. Et comme je suis sympa, je vous mets à disposition la fonction documentée si vous voulez en intégrer vous même dans vos programmes ! 😎

Ayant fini, je demande ce qu’en pense cent20. Grave erreur…

Bon bah mon projet attendra, l’écriture du cahier des charges de Colors v2 est en cours.

La nouvelle fonctionnalité majeure (il n’y en a de nombreuses autres pas forcément visibles) de cette seconde version est l’ajout des teintes :

Colors v2

La v2 plus ou moins terminée, une v3 est en réflexion…

Colors, qui devait être à la base un petit projet abouti en 2 jours, deviendra un gros projet de 2 semaines (pas à temps plein🙄).

Cette troisième et dernière version améliore grandement le rafraichissement des données (en limitant le clignotement), l’interface graphique est également légèrement revu. Cette version supporte également le pavé numérique. Et oui, on fait pas les choses à moitié ! 😏

Colors !

À toi de colorer !

Cet utilitaire est disponible sur https://nsi.xyz/colors

Projets

Factors Game en python sur NumWorks

Factors Game est à l’origine un jeu indépendant jouable en ligne, donc directement depuis un site web. Ceci dit, les Numworks ne peuvent pas encore se connecter à internet… Mais pas de panique, nous nous sommes occupés de l’adaptation du jeu sur calculatrice ! On est sacrément sympa nous…
Arriveras tu à atteindre le niveau 404 ?

Développement

02/04
🧐 Bon, maintenant que t’as fini ton nyan_cat, résolu deux de mes énigmes, corrigé les bugs sur laboHelp, aidé au développement de pixChoice. Ça te dit de faire un jeu sur la calculatrice ?
🤓 Pourquoi pas !

Voici le jeu qui était demandé : Factors Game – The 🐐 of Fun Math Games (mnito.github.io)

🧐 On va afficher les nombres dans des tuiles de 42 pixels par 42 pixels

(2 jours plus tard)

🤓 J’ai fini le système de mouvement, et le système de génération aléatoire de la grille. Il y a juste le front qui est à revoir…


🧐 Bon, pas très UX les déplacements du haut vers le bas comme sur le jeu original avec la résolution de l’écran des calculatrices. On va les faire de gauche à droite du coup, tu dois réadapter le système de mouvement.
🤓 … D’accord

Parallèlement le codage des nombres a été intégré, il est d’Eric Schrafstetter, il est explicité sur cette vidéo.

Ce codage, celui du jeu 2048, dont nous avons amélioré le rendu a été réutilisé pour écrire F A C T O R S en haut à gauche de l’écran !

Petit récap’, mouvements OK, grille OK, maintenant il faut faire le système de calcul entre le curseur et les cases de la grille (vous savez, les divisons, les additions, tout ça tout ça. Ça se fait pas par magie malheureusement).

Aussitôt commencé, aussitôt terminé ! Moi qui redoutait cette phase du développement, ça avait été plus facile que prévu. Plus qu’à faire le système de détection de fin du niveau. Fastoche ! Si le curseur est égal à 1, ou que le curseur est positionné dans la dernière colonne, niveau terminé ! Et on relance la génération de la grille sans oublier d’incrémenter le niveau et calculer la moyenne.

Voilà. En quelques jours, le jeu est théoriquement jouable.

🤓 Plus que le menu, et le système de sauvegarde. Mais le jeu est jouable !
🧐 Ok, pour le menu, on va faire ça, ça, mais il faut ça s’il y a ça, ça ça doit donc s’activer et du coup réactiver ça si ça a été désactivé seulement si ça avait été activé et puis…
🤓 Et si on faisait un cahier des charges ? 😅
🧐 Je m’en occupe.

🤓 Le menu est terminé, il faut sauvegarder le score de chaque niveau ?
🧐 Oui, on va utiliser un dictionnaire.
🤓 Larousse ou Le Petit Robert ?

Menu

🧐Le dictionnaire sert à rien, on aurait pu faire ça avec un tableau, mais laissons le dictionnaire ! 😂

🤓 Bon, je taffe sur une transition entre les niveaux, car les coups de sleep(3), ça va 2 secondes… (3 !)

🧐 Parfait !

Et voilà, en une bonne semaine, le jeu est terminé, il manque plus que le système de sauvegarde, enfin c’est ce que je pensais…

🧐 Bon, ton code est illisible on ne comprend rien. Tu me redéveloppes toute la fonction principale en respectant ces contraintes :

  • Le moins de if imbriqués possibles
  • Chaque if doit donner lieu à 2,3 traitements maximum
  • 61 lignes pour une fonction c’est trop
  • Tout abus de booléen est à éviter

🤓 Mais ça nécessite de redévelopper quasiment toutes les fonctions. Donc tout le jeu…
🧐 Oui mais sinon on comprend rien.
🤓 Oui mais le jeu marche.
🧐 Oui mais on comprend rien.
🤓 J’ai compris…

Vous vous comprenez hein rassurez moi. À moins que j’ai un problème.

def factors(): # Ceci est l'ancien code incompréhensible !
    global cursor, pos, level_current, retry
    gui()
    menu()
    level_in_progress, in_menu, re_menu, last_key, end, go, input_await = False, False, False, None, False, False, False
    while not keydown(5):
        if not level_in_progress:
            if not in_menu:
                menu(0)
                start_level(1)
                cursor, pos, end, input_await = level_current, [51 + 44 * 1, 13 + 44 * 1], False, False
                aff(level_current, pos[0] + 44 // 2 + center(level_current)[0],
                    pos[1] + 44 // 2 + center(level_current)[1], black_color)
                level_in_progress = True
            if not re_menu and in_menu:
                menu(1)
                start_level(0)
                re_menu = True
        if keydown(1) and pos[1] >= 51 and last_key != 1:
            if not in_menu:
                move(0, -1)
            if in_menu and level_current != level_max:
                level_current += 1
                re_menu = False
            last_key = 1
        if keydown(2) and pos[1] <= 51 + 44 * 2 and last_key != 2:
            if not in_menu:
                move(0, 1)
            if in_menu and level_current != 1:
                level_current -= 1
                re_menu = False
            last_key = 2
        if keydown(3) and last_key != 3:
            if not in_menu:
                move(1, 0)
            if in_menu:
                level_in_progress, in_menu, re_menu = False, False, False
            if input_await:
                go = True
                fill_rect(pos[0] + 42, pos[1] + 42, 0 - 220, 0 - 176, (255, 255, 255))
            last_key = 3
        if keydown(0) and last_key != 0:
            if pos[0] != 51 + 44 or input_await:
                level_in_progress = False
                retry = True
                if input_await:
                    fill_rect(pos[0] + 42, pos[1] + 42, 0 - 176, 0 - 176, (255, 255, 255))
            if not in_menu and pos[0] == 51 + 44:
                level_in_progress, in_menu = False, True
            last_key = 0
        if not (keydown(0) or keydown(1) or keydown(2) or keydown(3)):
            last_key = None
        if not (pos[0] != 51 + 44 * 5 and (cursor != 1 or pos[0] == 51 + 44)):
            end = True
        if end:
            if not input_await:
                level_transition(cursor)
                input_await = True
            if go:
                next_level()
                level_in_progress, retry, input_await, go = False, False, False, False

Finalement, presque tout le jeu a donc été redéveloppé pour améliorer la lisibilité des fonctions python.
Vous pouvez donc désormais comprendre le code ! Sauf le système de sauvegarde, ça vous vous débrouillez ! 🤫

🧐 Oui enfin le système de sauvegarde est codé pour être incompréhensible, c’est une fonctionnalité pour éviter qu’un joueur lambda ne génère trop facilement une clé de sauvegarde.

🤓 À noter, le code a été compressé pour passer de 10.2 ko à 8.42 ko, et désormais il ne respecte plus PEP 8 – Style Guide for Python Code (EnFr)

Fonctionnement

La plupart des différents événements sont lancés grâce à la liste pos[x, y]. Cette liste correspond aux coordonnées x, et y du curseur :

La position par défaut du curseur est donc [0, 1] ! Oui, l’axe vertical est inversé. 🙄

Ainsi, si pos vaut [-1, y], on lance le menu, si pos[4, 0], on termine le niveau, etc. L’affichage sur l’écran est géré par la fonction d_p(), qui va convertir pos en coordonnées réelles (celles de l’écran), car évidemment, les coordonnées de pos sont virtuelles ! Le (0 ; 0) de l’écran lui se situe tout en haut à gauche !

Fonctionnalités délaissées

Beaucoup de fonctionnalités ont été retirés pour réduire au plus la taille du fichier ainsi que la mémoire utilisée ou pour d’autres raisons.
Un histogramme dynamique avec le score des 20 derniers niveaux a été par exemple testé mais abandonné, le système de chargement d’une sauvegarde sur la couche graphique est lui aussi retiré, remplacé par une fonctionnalité équivalente mais exploitant le console python.

À toi de jouer !

Ce jeu est disponible sur https://nsi.xyz/factors

Nous te mettons au défi d’atteindre le niveau 404. Mais y arriveras tu ? 😱

Projets

Binnerto ©, un système de gestion de bases de…

Il y a quelque mois de cela, nous vous avions laissé bien pantois. En effet, sur le site Binnerto©, un site très qualitatif de produits geek, il était jusque-là impossible de commander le moindre petit produit, et nous en sommes grandement désolés.

Mais aujourd’hui est un jour nouveau, nous venons de sortir du sable, enfin !

Vous allez désormais (dans un temps prochain (soumis à condition et à l’acceptation du crédit par votre banque)) pouvoir acheter (dans la limite des stocks disponibles et des vendeurs disponibles pour réaliser les commandes) tous les produits vendus par notre site dès maintenant grâce à un SGBDR !

Quelques petits rappels utiles et informations nécessaires à la compréhension

Vous vous demandez bien qu’est-ce qu’un SGBDR et à quoi cela peut bien servir pour commander votre produit préféré (et fort bien utile). Et bien pour une fois cette acronyme est en français (ou DBRMS dans la langue de Shakespeare), il signifie littéralement Système de Gestion de Bases de Données Relationnelles. Euuuuh quèsaco ? En fait c’est un logiciel qui permet de mettre en relation différents fichiers contenant de nombreuses données. Voyons à quoi cela ressemble dans notre cas, cela apportera plus de clarté.

Imaginons que vous vous appeliez Antonin Kroyable et que vous ayez commandé une Souris révolutionnaire pour votre tout nouvel appareil à la poire le 14 février 2022. Et bien le SGBDR ; nommons le affectueusement S&Co pour la suite pour plus de simplicité et de proximité, permet de lier trois fichiers pour les unifier. Dans un fichier vous trouverez vos informations personnelles, ce sera le fichier « client », dans un second, vous aurez toutes les informations sur cette souris, c’est-à-dire dans le fichier « produit ». Et pour la commande que vous avez faites ? Et bien elle se trouve dans le fichier « commande » évidemment. Tout le travail du S&Co et donc de lier ces trois fichiers pour avoir les informations les plus utiles pour chaque besoin, c’est-à-dire pour vous clients, et pour nous gestionnaires du site et responsables des stocks et livraisons produits, sans avoir des fichiers de 3 Km de long avec toutes les informations, dont certaines inutiles à certains mais utiles à d’autres.

Rentrons maintenant dans le cœur de la bête pour mieux l’appréhender.

Il faut savoir que ce S&Co, étant user-friendly, a une interface utilisateur (ou UI en anglais) graphique, permettant une meilleure expérience utilisateur (ou UX encore une fois en anglais), en limitant de perdre ce dernier, qui n’a pas forcément les connaissances nécessaires pour affronter la peur d’un terminal sombre et peu ragoûtant. Il est au moins rassuré, mais cela nous complexifie bien la tâche, comme nous allons le voir.

Dans cette quête de la rose-amère de la perfection du design, ce S&Co ne fera que pâle figure car étant codé extrêmement en Python avec comme interface graphique Tkinter, les possibilités de design sont amoindries comparé à d’autres langages mieux prévus pour les interfaces graphiques. Nonobstant, vous avez quand-même un produit a minima fini.

Il y aura donc, vous l’aurez compris deux parties majeures pour appréhender ce S&Co, la partie graphique, en Tkinter, qui n’est que la rustine de ce qui fait tourner la machine, ce qui est à l’intérieur, c’est-à-dire les fonctions mères pour mettre en relation SQL (le langage qui permet de parler aux tables de données ; le charmeur de données), et Python.

Le vif du sujet SQL-Python

Sans plus de divagations, voyons d’où tout est parti : SQLite. En effet, il est impossible de discuter directement avec les données en Python, ou alors cela serait très complexe.

Pour plus de facilité, on utilise à la place un autre langage, le SQL (qui est d’une facilitée incroyable en comparaison). Mais pour lier un programme Python avec du SQL il faut une bibliothèque, et c’est la que SQLite vient nous sauver ! En effet on peut rentrer des commandes SQL dans une fonction Python et avoir tous les bénéfices du SQL en ayant les retours en Python pour être réutilisés, c’est presque magique !

Réalisation des trois bases de données dans un fichier CSV

Pour commencer avec des données à chaque démarrage sans partir de rien, nous avons créé 3 fichiers CSV (où les éléments sont séparés par des virgules) que l’on récupère ensuite pour l’importer (on verra cela plus tard). Donc, comme on l’a dit plus tôt, 3 tables seront liées, il y aura donc 3 fichiers CSV qui contiendront quelques éléments pour chaque table (environ une dizaine).

Tout d’abord, il y a la table PRODUITS, dans le fichiers produits.csv. Dans cette table on présente comme suit les principales caractéristiques du produits :

id_produitnom_produitprixstock
6Binnerto en acier0.001

Toutes les données sont présentées comme cela sauf, que d’une colonne à l’autre, c’est un virgule qui les sépare. L’id_produit est un identifiant unique permettant à coup sur de retrouver le produit sans risque de se tromper (par exemple si 2 produits ont un nom identique).

Il en est de même pour la table CLIENTS sur le fichier clients.csv :

id_clientnomprenomadresse_mailadressedate_naissancedate_inscriptionvip
8KroyableAntoninantonin.kroyable@lol.mdr13 chemin des Rippes, Salle-à-Sac10-03-200711-11-2011True

De la même manière, id_client est l’identifiant unique qui correspond au client.

Passons maintenant à la dernière table, la plus importante, la table COMMANDES, dans le fichier commandes.py :

id_commandeid_client_commandeid_produitdate_commandedate_livraisonenvoyequantiteprix_total
308614-02-202219-02-2022True30.00

Encore une fois, id_commande est l’identifiant unique de la commande mais, il y a une petite subtilité encore. En effet, on va lier les deux autres tables à celle-ci avec 2 attributs : id_client_commande et id_produit, qui correspondent respectivement à la ligne de chaque table. Ainsi, avec une moindre écriture, on arrive à avoir toutes les informations pertinentes sur une seule ligne !

CRUD, la clé de voûte du projet

CRUD, de l’anglais Create, Read, Update, Delete, a été notre expression clé durant ce projet, notre guide suprême, car pour un S&Co, cela les 4 points les plus importants. Il faut pouvoir Créer des tables et des éléments dans ces tables, les Lire, les Mettre-à-Jour, mais aussi les supprimer. Voyons comment cela a pu être réalisé en Python/SQL.

1. Create

Deux choses sont à créer dans ce projet, les tables, que ce soit PRODUITS, CLIENTS ou encore COMMANDES, mais aussi les éléments qui composent ces tables, les lignes si vous vous rappelez de nos tableaux de tout-à-l’heure.

Pour créer une table, rien de plus simple comme on le voit ici avec la création de la table PRODUITS :

def create_table_produits():
    curseur.execute("CREATE TABLE IF NOT EXISTS PRODUITS(id_produit INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, nom_produit TEXT, prix NUMERIC, stock INTEGER)")
    connection.commit() #On envoie la modif dans la base de données

Le SQL est ce qui est dans la méthode .execute()

Cette fonction permet donc de créer la table PRODUITS, en vérifiant bien sûr qu’elle n’existe pas pour éviter les erreurs. Si vous vous rappelez de l’id_produit de tout à l’heure, qui permet de retrouver avec certitude un produit, et bien en SQL, c’est une PRIMARY KEY, soit une clé primaire ; une sorte de numéro d’identité du produit (qui est donc unique).

Mais on peut aussi avoir besoin de créer un enregistrement, comme suit :

def add_produits(produits_ajout):
    curseur.executemany("INSERT INTO PRODUITS(nom_produit, prix, stock) VALUES (?,?,?)", produits_ajout)
    make()

En vérifiant bien sûr que le tuple en entrée soit composé de 3 éléments.

2. Read

Pour lire, ça se corse, on peut vouloir lire tout ou partie, il a donc fallu faire des choix en réfléchissant à chaque fois au besoin le plus utile de lecture.

Ainsi on peut tout lire comme ici :

def select_all_produits():
    curseur.execute("SELECT * FROM PRODUITS")
    return [("id_produit", "nom_produit", "prix", "stock")] + curseur.fetchall()

On retourne en plus les noms des attributs afin de pouvoir les afficher comme il faut dans le tableau en Tkinter

Mais aussi comme cela :

def select_all_commandes():
    curseur.execute("SELECT nom, prenom, nom_produit, date_commande, date_livraison, envoye, quantite FROM COMMANDES AS co INNER JOIN PRODUITS AS p ON co.id_produit = p.id_produit LEFT JOIN CLIENTS AS cl ON co.id_client_commande = cl.id_client")
    return [("nom", "prenom", "nom_produit", "date_commande", "date_livraison", "envoye", "quantite")] + curseur.fetchall()

Ici, on fait une double jointure pour afficher à la place de l’id_client et de l’id_produit, le nom du client et du produit pour plus de clarté.

En jouant avec le SQL, il y a aussi des fonctions plus spécifiques comme celle qui suit permettant d’afficher toutes les commandes d’un même client :

def toutes_commandes_clients(id_client):
    curseur.execute("SELECT nom, prenom, nom_produit, date_commande, date_livraison, envoye, quantite FROM COMMANDES AS co INNER JOIN PRODUITS AS p ON co.id_produit = p.id_produit LEFT JOIN CLIENTS AS cl ON co.id_client_commande = cl.id_client WHERE id_client_commande=?",(id_client,))
    return [("nom", "prenom", "nom_produit", "date_commande", "date_livraison", "envoye", "quantite")] + curseur.fetchall()

3. Update

Pour mettre à jour, nous nous sommes demandé ce que nous aurions le plus besoin de mettre à jour dans chaque enregistrement de chaque table, et pour cela nous avons pour chaque table créé une fonction update qui change uniquement l’élément qui a le plus besoin d’être changé :

def update_commandes(id_commande, envoye: bool):
    curseur.execute("UPDATE COMMANDES SET envoye=? WHERE id_commande=?", (envoye, id_commande))
    make()

On change ici l’état de la commande avec un Booléen (Vrai/Faux), car c’est la commande que l’on est le plus amenés à réaliser.

4. Delete

Pour la suppression, afin d’être sûr de ne pas faire d’erreurs, cette dernière se base uniquement sur l’id unique de chaque enregistrement dans une table.

Ainsi :

def delete_clients(id_client):
    curseur.execute("SELECT * FROM COMMANDES WHERE id_client_commande=?",(id_client,))
    temp = curseur.fetchall()
    if str(temp) == "[]":
        curseur.execute("DELETE FROM CLIENTS WHERE id_client=?", (id_client,))
    make()

Ici, on supprime un produit en fonction de son id_produit, tout en vérifiant qu’il n’est pas utilisé dans la table commande, sinon cela pourrait poser problème.

Un affichage graphique : Tkinter

CComme dit plutôt, il faut bien interagir avec toutes ces bases de données si complexes (qu’on appellera BDD), et ça avec une interface graphique en Tkinter. Après avoir réfléchi à plusieurs croquis de ce que pourrait être l’UI (dont on vous ne donnera pas d’exemple pour ne pas nous faire voler nos maquettes), on a réussi à trouver l’UI la plus simple pour nos futurs employés, qui devront se servir de cette interface pour gérer les commandes, pendant que nous serons à Ibiza en train d’empocher un maximum d’argent.

Tous les produits de chez Binnerto en un clic

N’oublions pas le maître-mot de tout ce que l’on peut produire : LA FACILITÉ ! Il est donc inutile lorsqu’on veut sélectionner un produit d’écrire chaque lettre de ce dernier (sans oublier les potentielles erreurs de frappes ou maladresses). Nous avons donc opter pour une liste des produits qu’il suffit de sélectionner. Avec Tkinter, l’outil se nomme « Combobox » et c’est bien pratique.

from tkinter import *
from tkinter import ttk

def action(recup):
    #on appelle la fonction qui lie Python et SQL pour interagir avec la TABLE

listeproduits = ["Parpaing externe", "Clé USHellB", "Clavier Pasteur STL", "Voiture à Hydrogène", "CPU Nucléaire Leif", "Bouton d'allumage automatique", "Binnerto en acier", "Souris révolutionnaire"]
choix_prod = Label(racine, text = "Enlever Stock :")
choix_prod.place(x=300, y=130)
aff_prod = ttk.Combobox(racine, state = "readonly", values = listeproduits)
aff_prod.bind("<<ComboboxSelected>>", action)
aff_prod.place(x=265, y=170)

Comme une bonne nouvelle n’arrive jamais seule, certes l’outil « Combobox » est parfait pour ce que nous voulons faire, mais il n’est pas inclus lors de l’importation de Tkinter *. Il faut donc le rajouter à part, au péril d’un peu plus de mémoire. 

C’est bien beau de pouvoir sélectionner un produit dans une liste, mais il faut bien le récupérer pour pouvoir l’exploiter un peu. Heureusement, la méthode « .get() » permet de récupérer le choix fait par l’utilisateur, et à l’aide de la fonction ci-dessus qui affiche ce choix, on est sûr d’avoir bien récupéré le produit. Maintenant que l’on peut sélectionner un produit, il ne reste plus qu’à décider s’il faut le retirer du stock en cas de commande, ou l’ajouter en cas de fabrication (même si ce dernier cas n’arrive jamais 🤫).

Un accès aux commandes plus pratique

Nous suivons avec soin chaque commande pour la meilleure satisfaction de nos clients. C’est pour cela qu’il faut savoir à l’aide du numéro de commande : les produits qu’elle contient, le nom et prénom du client, le jour de l’envoi, si le colis a été reçu, etc…

Il faut donc qu’à l’aide de « id_commande », on trouve toutes ces informations, et cela, de façon la plus lisible possible… et pourquoi pas un tableau ?! Mais il faut d’abord récupérer le numéro d’une commande saisit dans un input :

def num_commande(event):
	n_fen = Toplevel(racine)
    t = Tableau(n_fen, select_client_specifique(int(entree.get()))) #on ouvre une nouvelle fenêtre avec le tableau représentant les données

def valide(test):
    return test.isdigit()

com_liv = Label(racine, text = "Afficher la commande d'un client (rentrez son ID)")
com_liv.place(x=900, y=130)
choix_com = racine.register(valide)
value = StringVar(racine)
entree = Entry(racine, textvariable = value, width = 30, validate = 'key', validatecommand = (choix_com,'%S'))
entree.place(x=865, y=170)
entree.bind("<Return>", num_commande)

Comme pour les produits, on récupère le numéro de la commande à l’aide de la méthode « .get() », mais cette fois-ci, uniquement lorsque nous validerons en appuyant sur la touche « Entrée » du clavier. Mais imaginons que lorsque notre employé utilise cette interface, il s’endorme sur le clavier ! Malheur ! Il faut redoubler de sécurité. C’est pour cela que dans cet input, on peut seulement y renseigner des chiffres, et cela, grâce à la fonction « valide » qui vérifie à chaque saisie si celui la est un chiffre.

Le Tkinter a pu grandement avancer grâce au tutoriel d’Aël D. sur Tkinter

Vous trouverez ci-dessous le projet, qui est en constante évolution :

Projets

Système de gestion de base de données relationnelle sur…

Venez découvrir tout au long de ce compte rendu comment nous avons créé un programme en python permettant à l’utilisateur d’interagir avec la base de donnée que nous avons créé.

Dans un premier, on a importé toutes les bibliothèques nécessaires à la création du programme.

from tkinter import *
from tkinter import ttk
import tkinter as tk
import sqlite3
import pandas
import os

La bibliothèque « tkinter » va servir à créer l’interface graphique ( vous pouvez en apprendre plus grâce à ce tutoriel, créé par Aël, élève en Spé NSI en classe de 1ère ).

La bibliothèque sqlite3 permet d’interagir avec une base de donnée.

La bibliothèque pandas permet de récupérer des données dans un ficher csv.

Et enfin la bibliothèque os permet de supprimer des fichiers dans le cadre de ce programme.

I. Création de la page d’accueil

Nous avons d’abord réfléchi à l’interface graphique que nous souhaitions réaliser.

Il nous fallait d’abord une page d’accueil, avec quatre boutons permettant d’effectuer les quatre fonctions nécessaires à l’utilisation d’une base de données : créer la base, lire la base, modifier la base, et enfin supprimer la base.

Le code ci-dessous permet de créer la page d’accueil, contenant un titre et 4 boutons.

window = Tk() # page d'accueil avec les 4 boutons

window.title("Gérer une base de donnée sur les jeux vidéos")
window.geometry("1280x1024")
window.maxsize(1280,1024)
window.config(bg="#7EBDC2")
window.iconbitmap("manette.ico")

titre = Label(window, text="4 boutons pour intéragir avec la base de données", font=("Consolas",20), bg="#d1fdff", fg="black")
titre.pack(pady=45)

boite = Frame(window, bg="#7EBDC2")
boite.pack(pady=200)

boutonC = Button(boite, text="CREATE", font=("Consolas",15), bg="white", fg="black", command=create, width=10, height=3)
boutonC.grid(row = 1, column = 0, padx = 15, pady=15)

boutonR = Button(boite, text="READ", font=("Consolas",15), bg="white", fg="black", command=read, width=10, height=3)
boutonR.grid(row = 1, column = 1, padx = 15, pady=15)

boutonU = Button(boite, text="UPDATE", font=("Consolas",15), bg="white", fg="black", command=update, width=10, height=3)
boutonU.grid(row = 3, column = 0, padx = 15, pady=15)

boutonD = Button(boite, text="DROP\n DATABASE", font=("Consolas",15), bg="white", fg="black", command=delete, width=10, height=3)
boutonD.grid(row = 3, column = 1, padx = 15, pady=15)

window.mainloop()

Visuel graphique :

II. Comment utiliser nos fonctions

Lier une interface graphique à des fonctions nécessite l’emploi de boutons.

Il nous fallait donc créer 4 fonctions permettant d’interagir avec la base de données.

A. La fonction create()

Nos fichiers à exploiter sont au format .csv. Avec la bibliothèque pandas, nous avons stockés les fichiers csv dans des variables pour pouvoir les exploiter.

ed = pandas.read_csv("relation_editeur.csv")
jv = pandas.read_csv("base_principale.csv")

Puis nous avons dû créer un fichier au format .db (Data Base).

connection = sqlite3.connect('base.db')
curseur = connection.cursor()

Nous avons créé les deux relations en indiquant les attributs et leur type.

curseur.execute("CREATE TABLE IF NOT EXISTS ED(Id_Editeur INTEGER, Editeur TEXT, PRIMARY KEY(Id_Editeur AUTOINCREMENT))")
curseur.execute("CREATE TABLE IF NOT EXISTS JV(Id_Jeu INTEGER, Jeu TEXT, Annee INTEGER, Prix FLOAT, Plateforme TEXT, Id_Editeur INTEGER, PRIMARY KEY(Id_Jeu AUTOINCREMENT), FOREIGN KEY(Id_Editeur) REFERENCES ED(Id_Editeur))")

Et enfin, nous avons créé deux boucles « for » pour ajouter les enregistrements des fichiers .csv dans la base de données automatiquement.

for i in range(len(ed)):
    data = [ed.loc[i, "Editeur"]]
    curseur.execute("INSERT INTO ED(Editeur) VALUES (?)", data)
    
for i in range(len(jv)):
    data = []
    data2 = list(jv.loc[i, ["Jeu", "Annee", "Prix", "Plateforme", "Id_Editeur" ]])
    data2[1] = int(data2[1])
    data2[4] = int(data2[4])
    data.append(tuple(data2))
    curseur.executemany("INSERT INTO JV(Jeu, Annee, Prix, Plateforme, Id_Editeur) VALUES (?, ?, ?, ?, ?)", data)

Pour finir, nous avons mis chacun de ces éléments dans une seule fonction : create(), appelée par le premier bouton de l’accueil. Nous avons décidé d’afficher une phrase pour indiquer à l’utilisateur que la base de données a bien été créé. Un simple Label nous a suffit (allez voir le tutoriel d’Aël pour plus de détails).

B. La fonction drop_database()

En SQL, la commande « DROP DATABASE » permet de supprimer la base de données. En python, il est impossible d’utiliser cette commande. On a donc importé le module os qui permet dans ce cas de supprimer un fichier.

Ainsi, la structure de la fonction drop_database() serait :

os.remove("base.db")

Cependant, si le fichier n’existe pas, tenter de le supprimer affichera, dans l’IDE python, un message d’erreur. Pour éviter ce problème, nous avons créé une variable globale « test » qui prend la valeur 0 si la base n’existe pas, et 1 si la base existe. Nous avons donc testé avec un « if » la valeur de « test », et le fichier ne sera supprimé que si « test » est égal à 1 et un message de confirmation s’affichera, sinon un message apparaît sur la fenêtre tkinter pour indiquer que la base n’existe pas, ce qui nous donne la fonction suivante.

def drop_database(): # supprime le fichier de la base de donnée s'il existe
    global test
    global message
    global texte
    
    if test == 1:
        os.remove("base.db")
        test -= 1
        supmes()
        message = "La base de donnée a bien été suprimée"
        texte = Label(window, text=message, font=("Helvetica",15), bg="#d1fdff", fg="black")
        texte.pack()
    elif test == 0:
        supmes()
        message = "La base de donnée n'existe actuellement pas"
        texte = Label(window, text=message, font=("Helvetica",15), bg="#d1fdff", fg="black")
        texte.pack()

C. La fonction read()

Pour la santé mentale de tout lecteur, nous ne montrerons pas ici l’intégralité de notre fonction (sinon, le fichier entier est téléchargeable en bas de page).

En SQL, pour sélectionner des éléments d’une base de données, on utilise la commande « SELECT Attribut_1, Attribut_2, … FROM DB ». En python, avec la bibliothèque sqlite3, la structure est exactement la même. Or il faut récupérer ce que contient le curseur et le stocker dans une variable, pour ensuite l’afficher. Par exemple, pour afficher tous les attributs d’une relation dans la console de l’IDE Python, on utilise :

curseur.execute("SELECT * FROM RELATION")
lecture = curseur.fetchall()#récupère les données obtenues par la ligne 1 en 'str'
print(lecture)

Lorsque l’on clique sur le bouton « read » de la page d’accueil, une nouvelle fenêtre tkinter apparaît. Sur cette fenêtre, on peut choisir de lire la relation principale (relation Jeux Vidéo), la relation secondaire (relation Editeurs) ou de retourner au menu, soit à l’accueil.

Le bouton « Retour Menu » supprime simplement la fenêtre, avec la commande tkinter :

window_r.destroy()

La lecture de la relation Editeurs est assez simple. En effet, la relation Editeurs ne contient que deux attributs : la clé primaire « Id_Editeur » et le nom de l’éditeur, « Editeur ». On a donc choisi de montrer par défaut toute la relation dans la même fenêtre que celle contenant les boutons de lecture, et donc supprimer les boutons. Nous avons donc dû globaliser nos boutons, afin de les supprimer ainsi :

global bouton
bouton.pack_forget()

Enfin, il a fallu sélectionner tous les éléments et les afficher dans un Label.

curseur.execute("SELECT * FROM ED")#fonction simplifiée
affichage = curseur.fetchall()
affiche = Label(text=affichage)#plus de détails sur le tutoriel
affiche.pack()

Pour finir, il nous a fallu afficher le contenu de la relation principale selon les demandes de l’utilisateur.

Pour cela, nous avons créé, grâce à des recherches supplémentaires sur internet, des checkbutton (des cases à cocher), qui peuvent être sélectionnés ou non pour choisir si l’on affiche l’attribut.

De plus, nous avons permis à l’utilisateur de choisir de spécifier sa demande avec la commande SQL « WHERE Attribut = Valeur ». Cela nous permet donc de faire une sélection très précise d’éléments à afficher.

D. La fonction update()

La dernière fonction à créer !

Modifier la base de données implique 3 actions : ajouter un enregistrement, supprimer un enregistrement, et modifier une valeur d’un des attributs de l’enregistrement.

Nous avons décidé qu’il était impossible, grâce à l’interface, d’ajouter directement un éditeur dans la relation « Editeur ». Ainsi lors de l’ajout ou de la modification d’un enregistrement de la relation Jeux Vidéo, si l’éditeur renseigné n’existe pas dans la relation correspondante, il y est créé. Cet ajout se fait simplement :

#Selon les spécificités de la bibliothèque sqlite3, avec "Editeur" renseigné par l'utilisateur
curseur.execute("SELECT Id_Editeur FROM ED WHERE Editeur = ?",(Editeur,))
temp = curseur.fetchall()
if temp == [] : #si l'éditeur n'existe pas
  curseur.execute("INSERT INTO JV (Editeur) VALUES (?)",(Editeur,))
  curseur.execute("SELECT Id_Editeur FROM ED WHERE Editeur = ?",(Editeur,))
  id_editeur = curseur.fetchall()#après ajout dans ED, on récupère l'Identifiant pour l'ajout dans JV

Pour la fonction ajouter(), nous avons créé 5 entrées correspondant aux valeurs du jeu à ajouter à la relation, et un bouton pour confirmer l’ajout.

Tant que le bouton de confirmation n’est pas cliqué, l’enregistrement n’existe pas dans la base de données.

Lorsque l’utilisateur confirme son ajout, une fonction ajouter_bis() est lancée, exécutant simplement :

valeur_1 = entree_1.get()
valeur_2 = entree_2.get()
valeur_3 = entree_3.get()
valeur_4 = entree_4.get()
#Id_Editeur récupéré par comme précédemment
data = (valeur_1, valeur_2, valeur_3, valeur_4, Id_Editeur)
curseur.execute("INSERT INTO JV (Jeu, Annee, Prix, Plateforme, Id_Editeur) VALUES (?,?,?,?,?)",data)

Ensuite, nous avons créé la fonction modifier(). Nous avons créé deux boutons permettant de modifier soit la relation Jeux Vidéo, soit la relation Editeurs.

Pour modifier la relation Editeur, on peut choisir l’éditeur à modifier soit par son Id_Editeur, soit par son nom actuel grâce à un bouton déroulant. Nous avons donc créé ce bouton déroulant et deux entrées correspondant d’abord au nouveau nom de l’éditeur, et ensuite à l’editeur à modifier. La commande SQL qui s’exécute est la suivante :

if bouton_attribut.get() == "Editeur"	
  curseur.execute("UPDATE ED SET Editeur = ? WHERE Editeur = ?",(entree_1.get(),entree_2.get()))
else :
  curseur.execute("UPDATE ED SET Editeur = ? WHERE Id_Editeur = ?",(entree_1.get(),entree_2.get())  

Si l’éditeur indiqué par son Id_Editeur ou son nom existe, alors l’enregistrement est modifié et un message de confirmation est affiché. Sinon il est indiqué que l’éditeur n’existe pas.

La fonction pour modifier un enregistrement de la relation principale est, dans les grandes lignes la même, mais l’utilisateur doit choisir l’attribut à modifier, avec un bouton déroulant. Ainsi :

curseur.execute("UPDATE ED SET Attribut_1 = ? WHERE Attribut_2 = ?",(entree_1.get(),entree_2.get())

« Attribut_1 » et « Attribut_2 » sont des valeurs fixes, testés par les boutons déroulants (demandant de nombreux tests « if » et « elif »)

Pour finir, il fallait pouvoir supprimer des éléments de la relation principale (la relation secondaire Editeurs ne peut que être lue ou modifiée). Deux attributs permettent d’identifier un jeu : son identifiant unique, et son nom (qui peut ne pas être unique). Pourtant nous avons choisi qu’il serait possible de supprimer un jeu grâce à son nom pour un utilisateur qui ne connaîtrait pas l’identifiant du jeu. Si le jeu indiqué par son Id_Jeu ou son nom existe, alors l’enregistrement est modifié et un message de confirmation est affiché. Sinon il est indiqué que le jeu n’existe pas.

Par la suite, quelque soit le choix de l’utilisateur, le fonction est le même : une entrée apparaît, dans laquelle il est possible de renseigner soit le jeu, soit l’id du jeu. Si le jeu indiqué par son Id_Jeu ou son nom existe, alors l’enregistrement est supprimé et un message de confirmation est affiché. Sinon il est indiqué que le jeu n’existe pas.

Nous avons donc créé les quatre fonctions pour interagir avec une base de données, soit la création, la lecture, la modification et la suppression de la base.

III. Conclusion

Ce compte-rendu touche maintenant à sa fin. N’hésitez pas à aller voir le tutoriel de Aël pour en apprendre plus sur la bibliothèque tkinter. Le fichier zip contenant tous les éléments nécessaires au bon fonctionnement du programme est disponible ci-dessous.

Projets

Agence de voyage et base de données

Une base de données sur des destinations paradisiaques tropicales. Consulter et réserver un voyage en exploitant une base de données dans python. L’interface utilisateur est gérée par tkinter

1 – Organiser les idées

Tout bon projet commence par un plan, afin de pouvoir poser et organiser l’idée de ce que l’on veut créer. Nous avons donc commencé par dessiner la base de notre projet :

2 – Création de nos tables

Une fois l’idée posée, il est temps de passer au programme !

Avant toute chose nous avons créé nos 4 tables et leurs attributs pour y ajouter après nos fichiers .csv créés à l’avance :

#création des tables
curseur.execute("CREATE TABLE IF NOT EXISTS DESTINATION (Id_Destination INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Nom TEXT, Continent TEXT, Temp_Moy INT, Note INT)")
curseur.execute("CREATE TABLE IF NOT EXISTS CLIENT(Id_Client INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Prenom TEXT, Nom TEXT, Lieu_depart TEXT)")
curseur.execute("CREATE TABLE IF NOT EXISTS VOL(Id_Vol INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Id_Destination INT, Lieu_Depart TEXT, Date_Loc_Depart DATE, Heure_Loc_Depart TIME, Temps_Vol TIME, Prix INT, FOREIGN KEY (Id_Destination) REFERENCES DESTINATION(Id_Destination), FOREIGN KEY (Lieu_Depart) REFERENCES CLIENT(Id_Client))")
curseur.execute("CREATE TABLE IF NOT EXISTS RESERVATION(Id_Reservation INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, Id_Client INT, Id_Destination INT, Id_Vol INT, Num_Siege INT,FOREIGN KEY (Id_Client) REFERENCES CLIENT(Id_Client), FOREIGN KEY (Id_Destination) REFERENCES DESTINATION(Id_Destination), FOREIGN KEY (Id_Vol) REFERENCES VOL(Id_Vol))")
connection.commit()

Une fois les tables créées ont les a rempli avec les enregistrements contenus dans les fichiers .csv grâce au module panda:

#ajout des enregistrements .csv
pandas.read_csv("Client.csv").to_sql("CLIENT", connection, if_exists='replace', index=False)
pandas.read_csv("Destination.csv").to_sql("DESTINATION", connection, if_exists='replace', index=False)
pandas.read_csv("Vol.csv").to_sql("VOL", connection, if_exists='replace', index=False)
pandas.read_csv("Reservation.csv").to_sql("RESERVATION", connection, if_exists='replace', index=False)
connection.commit()

3 – La fenêtre d’accueil

A partir de là a commencé la création de la page d’accueil (dans les règles de l’art !)

Ici on définit l’aspect de notre fenêtre d’accueil, qui servira de modèle par la suite aux autres fenêtres.

#création de la fenêtre principale
window = Tk()
window.title("Agence de voyage : Partir un Jour sans Retour")
window.geometry("1080x720")
window.minsize(1080,775)
window.iconbitmap("island.ico")
window.config(background = "#B5D7F1")

Dans cette fenêtre on ajoute également 3 boutons, “destinations”, “réserver” et “supprimer son compte client” où les grandes actions s’effectuent.

4 – Fenêtre des destinations

Notre bouton destination mène donc à une fenêtre où les clients pourront découvrir les destinations proposées.

Les destinations sont d’abord triées par continent et rangées dans les boîtes correspondantes.

Ouvertes, ces boîtes affichent les destinations possibles du continent, rangées en tableau grâce à Treeview.

    tableau_europe = ttk.Treeview(boite_europe, columns=('Nom', 'Températures Moyennes', 'Note'), show = 'headings')
    tableau_europe.heading('Nom', text='Nom')
    tableau_europe.heading('Températures Moyennes', text='Températures Moyennes')
    tableau_europe.heading('Note', text='Note')
    
    for row in curseur.execute("SELECT Nom, Temp_Moy, Note FROM DESTINATION WHERE Continent = 'Europe' ORDER BY Nom"):
        tableau_europe.insert('', tk.END, values = row)
   
    tableau_europe.grid(row=0, column=0, sticky='nsew')

Nous avons ensuite utilisé Style() afin de personnaliser nos tableaux avec tous les attributs servant à les modifier comme sur l’image ci-dessous :

    #Style tableau europe
    
    tableau_europe = ttk.Style()
    #Prendre un theme
    tableau_europe.theme_use("clam")
    #configurer les couleurs du treeview
    tableau_europe.configure("Treeview",
        background = "#FEF3EA",
        foreground = "#206093",
        rowheight = 30,
        fieldbackground = "#FEF3EA"             
        )
    #Changer la couleur sélectionné par la souris
    tableau_europe.map('Treeview',
        background=[('selected', '#FFEE93')])

Ainsi qu’un bouton retour afin de fermer la boîte ouverte et de nouveau afficher les autres.

5 – Fenêtre réservation

Pour réserver un vol, il faut pouvoir choisir sa destination et s’inscrire dans la table Client.

Le plus simple a été de relier notre menu déroulant à la base de données. 

choix_destination = curseur.execute("SELECT Nom FROM DESTINATION")
    option_destination = OptionMenu(reservation, variable_destination, *choix_destination)
    option_destination.config(width=35, font=('Consolas', 12))

C’est au niveau du bouton de validation où cela s’est légèrement corsé.

Pas de soucis pour récupérer les textes entrés par l’utilisateur, ni son identifiant, qui constitueront son enregistrement dans la table Client.

        temp_client = (variable_prenom.get(), variable_nom.get())
        curseur.execute("INSERT INTO CLIENT(Prenom, Nom) VALUES (?,?)", temp_client)
        
        curseur.execute("SELECT MAX(Id_Client) FROM CLIENT")
        tempclient = curseur.fetchone()

C’est la récupération de l’identifiant de la destination qui aura posé le souci majeur puisque la donnée récupérée du menu déroulant se présentait comme un tuple sans en être un, enfin rien d’insurmontable un rien de bricolage suffit à régler le souci !

        temp_tempdestination = variable_destination.get()
        temp_bricodestination = (temp_tempdestination[2:-3],)
        curseur.execute("SELECT Id_Destination FROM DESTINATION WHERE Nom = ?", temp_bricodestination)
        temp_destination = curseur.fetchone()

On récupère également l’identifiant du vol

curseur.execute("SELECT Id_Vol FROM VOL WHERE Id_Destination = ?", temp_destination)
        temp_vol = curseur.fetchone()

Et une fois tous les attributs nécessaires à un enregistrement dans la relation réservation récupérés, on peut les y ajouter :

temp_reservation = (tempclient[0], temp_destination[0], temp_vol[0], randint(1, 600))
        curseur.execute("INSERT INTO RESERVATION(Id_Client, Id_Destination, Id_Vol, Num_Siege) VALUES (?,?,?,?)", temp_reservation)
        connection.commit()

6 – Fenêtre suppression de client

 Enfin si un client souhaite supprimer son compte nous avons la fenêtre “supprimer”.

Elle est composée d’une entrée utilisateur, où le client pourra renseigner son identifiant et d’un bouton “Valider” qui exécutera la suppression.

    def valider_suppression():
        temp_supprimer = variable_idclient.get()
        curseur.execute("DELETE FROM CLIENT WHERE Id_Client = ?", temp_supprimer)

Interface Tkinter réalisée avec l’aide du tutoriel d’Aël D.

Voici notre code compressé au format 7zip :

Projets

Jeu du pendu codé en Python

Vous êtes seul et ne savez pas quoi faire? Nous vous proposons un jeux du pendu: affrontez l’ordinateur!

Notre projet consiste à créer un programme en python sur un jeux traditionnel d’école: le jeux du pendu. 

Introduction

L’ordinateur va choisir un mot parmi une liste prédéfinie et inconnue de vous, joueur. Vous devez alors essayer de retrouver le mot en proposant différentes lettres. Vous avez le droit a 8 erreurs, et pas une de plus. Une fois toutes les tentatives épuisées, le bonhomme est pendu: vous avez perdu! Mais si vous trouvez le mot avant d’avoir épuisé toutes vos chances, alors c’est gagné!!

Lorsque vous avez trouvé la bonne lettre, un message s’affiche pour vous informer que votre proposition est juste. Le mot qui était vide se complète alors par la bonne lettre. Ainsi il sera plus facile de deviner quel est le mot.

Le code en entier

from random import randint,choice

def initialisation(): 
    liste_mots=["chocolat", "tentacule", "dictionnaire", "magnifique", "saucisson", "xylophone", "serveur", "houx", "clavier"]
    choix = choice(liste_mots) 
    myst = ""
    for i in choix:
        myst += "_"
    return (choix,myst)

def verification(lettre,mot): 
    if lettre in mot:
        return True
    else:
        return False

def mot_complet(mot):
    if "_" not in mot:
        return True
    else:
        return False

def complete_mot(lettre,mot,myst):
    for i in range(len(mot)):
        if mot[i] == lettre
            temp = list(myst) 
            temp[i] = lettre 
            myst = "".join(temp)
    return myst 

def dessin_pendu(n):
    dessin = ""
    if n == 1:
        dessin = "\n" * 7 + " =============="
    elif n == 2:
        dessin = "\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 3:
        dessin = "==============\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 4:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 5:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 6:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 7:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 8:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||        / \ \n" + " /||\n" + " =============="
    return dessin
  
def jeu():
    tentatives = 8
    mot_choisi,mot_cache = initialisation()
    while (tentatives > 0) and (not mot_complet(mot_cache)):
        print("\nNombre de tentatives restantes :\t", tentatives, "\n")
        print(" ".join(mot_cache),"\n\n")
        lettre_choisie = input("Choisir une lettre (en minuscule) :\t")
        if verification(lettre_choisie,mot_choisi):
            mot_cache = complete_mot(lettre_choisie, mot_choisi, mot_cache)
            print("\nLa lettre se trouve bien dans le mot\n")
        else:
            tentatives -= 1
            print("\nLa lettre ne se trouve pas dans le mot...\n\n", dessin_pendu(8 - tentatives),"\n\n")
    if tentatives == 0:
        print(" ".join(mot_cache))
        print("\nLe mot était :\n")
        print(" ".join(mot_choisi))
        print("\nVous avez malheureusement échoué ... \n")
    if "_" not in mot_cache:
        print(" ".join(mot_cache),"\n\n")
        print("\nBravo, vous avez gagné !\n")
    input("\nVeuillez taper sur la touche Entrer pour recommencer\n\n")
    menu()    
        
def menu():
    print("\n-----------BIENVENUE SUR LE JEU DU PENDU-----------\n")
    print("-------------------BONNE CHANCE--------------------\n\n")
    print("C'EST PARTI! \n")
    jeu()

menu()

Nous avons choisi la bibliothèque random pour avoir accès à choice qui nous permet donc d’avoir un programme capable de faire un choix de manière complètement aléatoire.

Les appels aux différentes fonctions

Le programme ci dessus se résume en une ligne;

menu()

Celle ci sert à faire appel à la fonction menu, qui elle même va faire appel à d’autres fonctions. Ainsi le programme peut s’exécuter. Par exemple, on remarque que la fonction menu fait appel à la fonction jeu, qui elle même, fait appel à d’autres fonctions (à voir dans le code ci-dessous)

def menu():
    #cette fonction présente le jeu
    jeu() #renvoie à la fonction "jeu"
    
def jeu():
  #cette fonction est le squelette du programme. elle permet de relier les nombreuses fonctions de manière a pouvoir jouer
    mot_choisi,mot_cache = initialisation() #renvoie à la fonction initialisation
    while (tentatives > 0) and (not mot_complet(mot_cache)): #tant que le nb de tentatives est > 0 et que --> renvoie à la fonction mot complet  --> alors...
        
        """ 
        etc...
        """

Les grandes lignes

Pour pouvoir jouer au jeu du pendu, nous avons besoin d’avoir une liste de mots prédéfinie dans laquelle, un mot au hasard, va être choisi. Le mot, qui aura été sélectionné, apparaitra sous forme de tirets (le nombre de tirets correspondant au nombre de lettres dans le mot).

def initialisation():
    liste_mots=["chocolat", "tentacule", "dictionnaire", "magnifique", "saucisson", "xylophone", "serveur", "houx", "clavier"]
    choix = choice(liste_mots) 
    myst = "" 
    for i in choix:
        myst += "_"
    return (choix,myst)

Une fois le mot apparu sous la forme de tirets, il faut que lorsqu’une lettre est découverte par le joueur, elle prenne la place du tiret auquel elle correspond (complete_mot). Pour cela, le mot va être transformé en une liste et pourra donc être parcouru par le programme. Mais tout d’abord il faut vérifier que la lettre se trouve bien dans le mot (verification).

def verification(lettre,mot): 
    if lettre in mot:
        return True
    else:
        return False

def complete_mot(lettre,mot,myst):  
    for i in range(len(mot)):
        if mot[i] == lettre
            temp = list(myst) # Transforme le mot en une liste temp
            temp[i] = lettre # Remplace le tiret underscore par la lettre
            myst = "".join(temp) # Re-combine les éléments de temp pour en faire à nouveau un mot
    return myst # Renvoi le mot caché, complété avec la (ou les) lettre(s)

Dans ce jeu, nous avons fait le choix de représenter un bonhomme pendu, comme dans le jeu classique d’école. Pour cela, nous avons programmé une fonction permettant d’afficher une partie du dessin du pendu, au fur et à mesure que le nombre de chances diminue, . Au début il n’y a donc pas de dessin; dessin = "" . Plus le pendu apparait, plus les chances de trouver le bon mot diminuent.

def dessin_pendu(n):
    dessin = ""
    if n == 1:
        dessin = "\n" * 7 + " =============="
    elif n == 2:
        dessin = "\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 3:
        dessin = "==============\n" + "  ||/\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 4:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 5:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||\n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 6:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 7:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||\n" + " /||\n" + " =============="
    elif n == 8:
        dessin = "============Δ=\n" + "  ||/        |\n" + "  ||         O\n" + "  ||        /|\ \n" + "  ||         |\n" + "  ||        / \ \n" + " /||\n" + " =============="
    return dessin
dessin final (tentative = 0, le joueur a perdu)

Pour terminer, nous avons donc crée une fonction qui, comme dit plus haut, structure notre programme. Elle lie toutes les autres fonctions. Le nombre de tentatives y est rentré, et le programme se lance.

Tant que le nombre de tentatives est supérieur à 0 et que le mot n’est pas complet, le script s’exécute. Si la lettre choisie par le joueur est dans le mot, alors le (ou les dans certains cas) tiret correspondant à la lettre sera remplacé par celle-ci. Sinon, le nombre de tentatives diminue de 1 et le dessin du pendu correspondant au nombre de tentatives apparait.

Pour gagné, il suffit donc de vérifier s’il reste ou non des tirets dans le mot (mot_complet). Lorsqu’il n’y en a plus et que tentative>0, alors c’est gagné. Et lorsque le nombre de tentative = 0, alors le mot apparait en entier, c’est perdu.

def jeu():
    tentatives = 8
    mot_choisi,mot_cache = initialisation()
    while (tentatives > 0) and (not mot_complet(mot_cache)):
        print("\nNombre de tentatives restantes :\t", tentatives, "\n")
        print(" ".join(mot_cache),"\n\n") # Pour un affichage montrant bien le nombre de lettres à trouver en séparant chaque tiret underscore
        lettre_choisie = input("Choisir une lettre (en minuscule) :\t")
        if verification(lettre_choisie,mot_choisi):
            mot_cache = complete_mot(lettre_choisie, mot_choisi, mot_cache)
            print("\nLa lettre se trouve bien dans le mot\n")
        else:
            tentatives -= 1
            print("\nLa lettre ne se trouve pas dans le mot...\n\n", dessin_pendu(8 - tentatives),"\n\n")
    if tentatives == 0:
        print(" ".join(mot_cache))
        print("\nLe mot était :\n")
        print(" ".join(mot_choisi))
        print("\nVous avez malheureusement échoué ... \n")
    if "_" not in mot_cache:
        print(" ".join(mot_cache),"\n\n")
        print("\nBravo, vous avez gagné !\n")
    input("\nVeuillez taper sur la touche Entrer pour recommencer\n\n")
    menu()    
    
    
def mot_complet(mot): 
    if "_" not in mot:
        return True
    else:
        return False    

Une fois la partie finie, il ne reste plus qu’à cliquer sur la touche Entrer et le programme nous renvoie au menu de base. Une nouvelle partie peut commencer.

A vous de jouer

Si contre, vous trouverez le lien pour télécharger le programme et faire des parties dignes de ce nom. Quelques explications ont été rajoutées dans ce code pour, si vous le souhaitez, réussir à comprendre plus en détail comment il fonctionne.

Maintenant, à vous de jouer!

Projets

Simulateur de transmission du covid-19

Vérifiez que vous respectez bien le protocole sanitaire avec ce simulateur. Il vous permettra de vous donner une idée sur votre civisme, votre exposition au coronavirus en espace clos ainsi qu’à visualiser les variables qui entrent en jeu dans ce processus de transmission.

Introduction

Ce projet a été réalisé par Iliess L., Luca B. et Romain B., élèves en première NSI en 2021/2022 au lycée Louis Pasteur.

Depuis près de deux ans, nous sommes en pleine pandémie. Cette pandémie a engendré un très grand nombre de morts. Pour éviter que d’autres gens ne meurent à cause du Covid-19 et sensibiliser, nous avons décidé de créer un simulateur de propagation de Covid-19 assez fiable. Malgré que cette histoire de virus relève de la biologie, sa transmission, elle, relève de la physique : la propagation d’aérosols (mini-gouttelettes en suspension dans l’air éjectées par une personne) ainsi une équation a pu être élaborée.

Ce programme permet de calculer le pourcentage de chance que quelqu’un se fasse contaminer par le Covid-19 dans un espace clos et plus précisément dans une salle de classe (d’environ 125 m³). Le résultat dépend du comportement de l’utilisateur ainsi chaque personne testant ce simulateur se sent un minimum concerné.

Nous demandons donc à l’utilisateur différentes informations sur la conduite d’un groupe de personnes dans un espace fermé notamment s’ils ouvrent beaucoup les fenêtres, si le port du masque est bien respecté, le temps passé dans la salle, le nombre de personne dans la salle et le débit d’air inspiré/expiré.

Le code

Le programme est composé de 5 fonctions et de 3 lignes de code. Les fonctions sont toutes dans le même genre, seuls les « prints », les variables et les valeurs changent.

Analysons une fonction :

La première partie de la fonction concerne les différents cas possibles.

def port_du_masque(): #savoir comment le masque est porté en classe, c'est une proportion
    print("1 - Dans votre classe, tout le monde porte bien le masque sur le nez et tout le temps.")
    print("2 - Il y en a quelques uns qui le mettent sous le nez.")
    print("3 - Tout le monde le porte tout le temps sous le nez.")
    print("4 - Personne ne le porte !")
    print()

Ensuite, nous utilisons une instruction conditionnelle pour permettre à l’utilisateur de choisir parmi les choix proposés et ainsi y associer une valeur correspondante au choix.

    choix = int(input("Veuillez selectionner votre choix:\t"))
    if choix == 1:
        F = 0.1
    elif choix == 2:
        F =  0.2
    elif choix == 3:
        F = 0.80
    elif choix == 4:
        F = 1

Et enfin une petite sécurité qui permet de vous renvoyez au début de la fonction si la réponse de l’utilisateur ne convient pas aux propositions.

    else:
        print()
        print("Votre choix n'est pas valide, veuillez taper 1, 2 ou 3 s'il vous plait.")
        print()
        port_du_masque()
    return F

4 des 5 fonctions sont exécutées dans une fonction globale. Cette fonction fait le plus gros du travail. Elle exécute toutes les autres fonctions, fait le calcul final de probabilité et compare le résultat de l’utilisateur aux normes de l’Etat.

Voici le calcul utilisé pour calculer la probabilité d’infection d’une personne.

print (" Dans votre classe, il y a",(nb_personne*t*Q**2*f**2*quantum)/(L*volume), "% de chance que quelqu'un soit infecté.")#calcul de la probabilité

Problèmes rencontrés

Le premier problème qu’on a pu rencontrer est le suivant : rendre une variable locale globale. La variable calculée dans la fonction était perdue si on ne la sauvegardait pas dans une nouvelle variable globale.

On a trouvé deux moyens de contrer ce problème :

Le premier est d’utiliser la fonction native « global ».

def temps_expo():#savoir le temps passé en classe en heure
    global t
    print("Combien de temps en heures passez-vous en classe en moyenne ? ")
    print()
    print("1 - entre 0 et 3 heures.")
    print("2 - entre 3 et 6 heures.")
    print("3 - entre 6 et 9 heures.")
    print()
    choix = int(input("Veuillez selectionner votre choix:\t"))
    if choix == 1:
        T = 2
    elif choix == 2:
        T = 5
    elif choix == 3:
        T = 8
    else:#permet de renvoyer au début de la fonction si le choix ne convient pas
        print()
        print("Votre choix n'est pas valide, veuillez taper 1, 2 ou 3 s'il vous plait.")
        print()
        temps_expo()
     t = T

Et le deuxième est d’exécuter la fonction directement dans la fonction globale et en même temps, assigner la valeur que la fonction renvoie à une nouvelle variable.

def proba_totale(): #probabilité d'infection
    print("------------------------------------------------------")
    print("|                       COVID                        |")
    print("------------------------------------------------------")
    t = temps_expo()

Le deuxième problème était : le « return » de la fonction qui ne marchait pas.

Pour résoudre ce problème, nous avons tout simplement décidé de remplacer le « return » par un « print ».

	print (" Dans votre classe, il y a",(nb_personne*t*Q**2*f**2*quantum)/(L*volume), "% de chance que quelqu'un soit infecté.")#calcul de la probabilité
    # return (nb_personne*t*Q**2*f**2*quantum)/(L*volume)

Conclusion

Ce simulateur permet de calculer la probabilité d’infection d’une personne dans un espace fermé par conséquent il peut permettre de faire réagir certaines personnes qui ne respectent pas les règles mises en place par le gouvernement : ce programme a aussi un objectif de sensibilisation.

En bas de la page, vous pourrez télécharger le programme.

Crédits

Toutes les informations et la formule utilisées proviennent de la chaine youtube Scienceetonnante tenu par David Louapre, un physicien et la vidéo d’où nous avons tiré toutes nos informations est inspirée des recherches de physiciens du MIT basé aux Etats-Unis : Bazant M. Z., & Bush J. W..

Projets

Machine à sous en python

Vous connaissez le casino non ? Impossible de gagner vous me direz… pour ce projet, ce n’est pas le cas, en effet nous vous garantissions une machine à sous 100% gagnante pour n… pour vous !

Vous possédez 10 000 euros, et vous pouvez misez autant d’argent que vous voulez… si vous gagnez, vous multipliez votre mise par 10 !!!!

Au moins une victoire est GARANTIE, puise que vous pouvez nous faire des crédits à l’ infini*

*bonne chance pour rembourser après

Le fonctionnement du jeu est très simple, on vous demande la somme miser, le jeu se lance, et si 3 symboles identiques s’alignent, vous gagnez (par exemple : ♢♢♢ ou ♠♠♠). Vous pouvez choisir de partir quand vous voulez , et si vous n’avez plus d’argent, vous pouvez demander des crédits pour jouer encore, ceux ci seront a rembourser. En choisissant de miser 0 vous pourrez rembourser vos dette ou choisir d’arrêter les frais.

Fonction mise et jeu :

# mise
def mise_et_jeu():
    porte_monnaie = 10000
    while porte_monnaie > 0:
        print("\nil vous reste :", porte_monnaie, "\n")
        mise=int(input("Choissisez la somme a miser : "))
        if mise > porte_monnaie:
            print("on ne peut pas jouer avec de l'argent imaginaire....\n")
        elif mise == 0:
            print("votre porte monnaie vaut", porte_monnaie)
            fin_2()
            break

        else:
            porte_monnaie -= mise
       
            # Partie jeu
            tirage_cumul = 1
            for i in range(3):          
                tirage_unitaire = choice([2,3,5,7])
                tirage_cumul = tirage_cumul  * tirage_unitaire
            
                if tirage_unitaire == 2:
                    print("♡", end=" ")
                    sleep(pause)
                elif tirage_unitaire == 3:
                    print("♢", end=" ")
                    sleep(pause)
                elif tirage_unitaire == 5:
                    print("♣", end=" ")
                    sleep(pause)
                elif tirage_unitaire == 7:
                    print("♠", end=" ")
                    sleep(pause)
            
           # récompense et affichage du résultat
            if tirage_cumul in [8,27,125,343]:
                porte_monnaie += 10*mise
                print("\nBravo !!!!!!, vous avez gagné dix fois votre mise")
            elif porte_monnaie > 0:
                print("\nVous avez perdu, essayez encore une fois")
            else:
                print("\nVous avez perdu")
    else:
        if porte_monnaie == 0:
            fin()

La fonction mise et jeu est la plus importante de notre script, elle permet a notre script de fonctionner. C’est cette fonction qui demande la mise au joueur et qui tire 3 symboles au hasard, déterminant ainsi la victoire ou la défaite.

Les différentes fins :

    
#fin
def fin():
    # porte monnaie
    global porte_monnaie
    print("Vous êtes à sec")
    sleep (2*pause)
    print("vous pouvez faire des crédits d'un montatnt de 10 000 €, celui-ci", 
          " sera à rembourser dans un an au plus tard")
    sleep(pause)
        
    # demande de prêt
    pret = int(input("Pour faire un crédit taper 1 :"))
    if pret == 1:
        porte_monnaie += 10000
        credit()
    else:
        fin_3()
#fin alternative
def fin_2():
    choix = int(input("Si vous voulez quitter taper 1\n"
                       "\nSi vous voulez rejouer taper 2\n"
                       "\nSi vous voulez rembourser votre dette taper 3\n"
                       "\nVotre réponse:"))
    if choix == 1:
        global porte_monnaie
        print("Merci d'avoir joué, il vous reste:", porte_monnaie, "€")
    elif choix == 2:
        mise_et_jeu()
    elif choix == 3:
        global dette
        if dette > 0:
            remboursement()
        else:
            print("Vous n'avez aucune dette à rembourser")
            mise_et_jeu()
    else:
        print ("Votre choix ne correspond pas\n""Merci d'avoir joué")
    
#fin alternative
def fin_3():          
        global dette
        if dette > 0:
            print("Votre dette s'élève à:", dette, "€",
                  "\nelle sera à rembourser dans un an au plus tard\n")
            print("Merci d'avoir joué et au revoir")
        else:
            print("Merci d'avoir joué et au revoir")

(ainsi que les définitions remboursement et crédit nécessaire aux différentes fins du jeu)

#credit
def credit():
    global dette
    dette += 10000
    print ("Votre dette s'élève à:", dette, "€")
    mise_et_jeu()

#remboursement
def remboursement():
    global dette
    global porte_monnaie
    argent = int(input("Rentrer la somme à rembourser:"))
    if argent > porte_monnaie:
        print("on ne peut pas payer avec de l'argent imaginaire")
    elif argent < dette:
        porte_monnaie -= argent
        dette -= argent
        print("Il vous reste ", dette, "à rembourser et vous avez",
              porte_monnaie, "€")
    elif argent == dette:
        porte_monnaie -= argent
        dette -= argent
        print("Vous n'avez plus de dette à rembourser et vous avez",
              porte_monnaie, "€")
        fin_2()

Les difficultés rencontrées :

  • nous n’avions pas mis de fonctions dans un premiers temps, et il a été dur de les créer pour chaque parties du script et de les relier entres elles
  • Nous ne connaissions pas la fonction global quand nous avons fais le script et par conséquent il était impossible pour nous d’utiliser la même variable dans plusieurs fonctions.

Pour tester notre machine a sous :

Projets

Hexapawn, ou une IA évolutive simple en Python

Comment ça on peut coder des IA qui apprennent en Python en 1ère ? C’est pas censé être un truc ultra pointu ? Eh bien en fait c’est relativement facile à faire pour des jeux aux règles simples comme l’Hexapawn.

Un Hexapawn, quésaco

Il s’agit d’un jeu d’échec miniature, qui se joue sur un plateau de jeu constitué de 9 cases (3×3), et dans lequel chaque joueur possède 3 pions – d’où le nom: hexa, six et pawn, pion en anglais.

Les règles du jeu sont simples: les pions se déplacent comme des pions d’échec:

  • Ils ne se déplacent que d’une case à la fois et uniquement vers l’avant
  • Ils ne peuvent manger qu’en diagonale, là aussi dans un rayon d’une case

Le but est de réussir à amener l’un de ses pions de l’autre côté du plateau ou d’éliminer la totalité des pions adverses.

Dans le cas ou aucun pion ne peut plus bouger, il y a égalité

Ce projet a été inspiré par une vidéo de Vsauce2, une chaîne présentant divers jeux ayant lien avec les mathématiques ou la logique.

Particularités du jeu

Comme les règles de l’Hexapawn sont très simples, il suffit simplement d’empêcher l’ordi de refaire des mouvements qui l’ont amené à perdre afin de le faire s’améliorer au fur et a mesure qu’il joue.

La principale difficulté dans la réalisation de ce projet a été de devoir prendre en compte toutes les possibilités de jeu, ce qui rend au final le script assez long.

En bonus, on s’est dit que ce serait sympa d’ajouter un menu afin de personnaliser les couleurs de l’interface

Commencer le projet : les Libraries nécessaires

Ce jeu a été entièrement codé en Python, et en Python, la première chose à faire lorsqu’on commence quelque chose est d’importer les Libraries (programmes externes) nécessaires pour sa réalisation.

#les différentes libraries nécessaires
from tkinter import *
import tkinter as tk
from functools import partial
from random import *
import webbrowser

Les différentes libraries que nous avons choisi d’utiliser sont les suivantes :

  • tkinter (abrégé de ToolKit Interface) qui permet de créer une interface graphique,
  • la fonction partial de la library functools, qui va nous être utile lors de la création des différents boutons,
  • random, qui va permettre d’ajouter de l’aléatoire dans certaines actions,
  • webbrowser, qui va permettre d’ouvrir des pages web.

Maintenant que l’on a importé tous les modules nécessaires, on va pouvoir commencer à coder le projet en lui-même.

Configuration de la fenêtre

Comme l’on utilise tkinter afin de pouvoir donner une interface graphique à notre jeu, on va commencer par configurer notre fenêtre (taille, titre, couleur de fond etc…)

couleur = "#424242"

#mise en place de la fenêtre
window = Tk()
window.title("♟ Hexapawn ♟")
window.geometry("1080x720")
window.config(bg=couleur)

Ici on a donc une fenêtre de couleur grise, dont le titre est « ♟ Hexapawn ♟ » et dont la taille est de 1080 x 720 pixels

Création des variables

Une fois la fenêtre créée, on configure les variables qui seront utilisées par le script (le nombre de points de chacun, l’état des cases -vides au début-, les différentes variables à afficher…)

#définition de certaines variables
couleur_texte = "#666666"
couleur_selection = "#cccccc"
couleur_ordi = "#000000"
couleur_joueur = "#ffffff"
tour = 0
pions_joueur = 3
pions_ordi = 3
pions_bloques = 0
resultat = ""
coups_perdants = []
coups_gagnants = []
jeu = ""
a1 = ""
a2 = ""
a3 = ""
b1 = ""
b2 = ""
b3 = ""
c1 = ""
c2 = ""
c3 = ""
ordi_gagne = False
joueur_gagne = False
egalite = False
points_ordi = 0
points_joueur = 0

Tkinter ne peut pas afficher des variables classiques dans les fenêtres, c’est pourquoi il est nécessaire de créer des variables spéciales que l’on déclare comme ceci :

#définition des variables qui seront affichées dans l'interface tkinter
a1_display = tk.StringVar()
a2_display = tk.StringVar()
a3_display = tk.StringVar()
b1_display = tk.StringVar()
b2_display = tk.StringVar()
b3_display = tk.StringVar()
c1_display = tk.StringVar()
c2_display = tk.StringVar()
c3_display = tk.StringVar()
resultat_display = tk.StringVar()
score_ordi_display = tk.IntVar()
score_joueur_display = tk.IntVar()
valeur = tk.StringVar()
contour_texte = tk.StringVar()
contour_texte.set('''
⎟
⎟
⎟
⎟
⎟
⎟
⎟
⎟
⎟
⎟
⎟
⎟
''')

Comme la majorité de ces variables tkinter sont associées à des variables classiques, nous avons décidé d’ajouter « _display » à la fin de leur nom pour pouvoir plus facilement les différencier.

Par exemple, la variable a1 sera associée à la variable tkinter a1_display qui sera actualisée dans le script à chaque fois que a1 change de valeur

Configuration des boites

Maintenant on crée les différentes boites. Elles permettront d’afficher les différents éléments qu’elles contiennent quand elles seront appelées (la boite menu, règle etc…) ou de ne plus les afficher lorsqu’elles seront oubliées.

#mise en place des différentes boites
boite_titre = Frame(window, bg=couleur)
boite_menu = Frame(window, bg=couleur)
boite_regles = Frame(window, bg=couleur)
boite_jeu = Frame(window, bg=couleur)
boite_ordi = Frame(window, bg=couleur)
boite_resultat = Frame(window, bg=couleur)
boite_couleurs = Frame(window, bg=couleur)
boite_couleurs2 = Frame(window, bg=couleur)

Mise en place des Labels

Afin d’avoir une interface claire, il est important d’ajouter des éléments textuels: titre, règles du jeu etc.

Dans tkinter, les éléments textuels sont nommés labels. On les déclare comme ceci:

#mise en place des différents éléments textuels
titre = Label(boite_titre, text =
              '''
 .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .----------------.  .-----------------.
| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |
| |  ____  ____  | || |  _________   | || |  ____  ____  | || |      __      | || |   ______     | || |      __      | || | _____  _____ | || | ____  _____  | |
| | |_   ||   _| | || | |_   ___  |  | || | |_  _||_  _| | || |     /  \     | || |  |_   __ \   | || |     /  \     | || ||_   _||_   _|| || ||_   \|_   _| | |
| |   | |__| |   | || |   | |_  \_|  | || |   \ \  / /   | || |    / /\ \    | || |    | |__) |  | || |    / /\ \    | || |  | | /\ | |  | || |  |   \ | |   | |
| |   |  __  |   | || |   |  _|  _   | || |    > `' <    | || |   / ____ \   | || |    |  ___/   | || |   / ____ \   | || |  | |/  \| |  | || |  | |\ \| |   | |
| |  _| |  | |_  | || |  _| |___/ |  | || |  _/ /'`\ \_  | || | _/ /    \ \_ | || |   _| |_      | || | _/ /    \ \_ | || |  |   /\   |  | || | _| |_\   |_  | |
| | |____||____| | || | |_________|  | || | |____||____| | || ||____|  |____|| || |  |_____|     | || ||____|  |____|| || |  |__/  \__|  | || ||_____|\____| | |
| |              | || |              | || |              | || |              | || |              | || |              | || |              | || |              | |
| '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |
 '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'  '----------------'

''', font=("consolas",7), bg = couleur, fg = couleur_texte)

sep = Label(boite_titre, text = "."*200, font=("consolas",7), bg = couleur, fg = couleur_texte)
regles_label = Label(boite_regles, text =
                     '''
Regles du jeu:

Vous possédez 3 pions, qui peuvent se déplacer comme des pions aux échecs:

-ils ne peuvent que se déplacer vers l'avant
-ils ne peuvent se déplacer que d'une case à la fois
-ils ne peuvent "manger" un pion adverse qu'en diagonale

2 conditions de victoire possibles:

-l'adversaire ne possède plus aucun pion
-vous parvenez à amener un pion à l'autre bout du plateau

Pour déplacer un pion, cliquez sur lui puis sur la case vers laquelle vous
voulez qu'il aille.
Lorsque vous avez joué, cliquez sur le bouton sous l'échéquier

'''
, font=("consolas",15), bg = couleur, fg = couleur_texte)
resultat_Label = Label(boite_resultat, text = "", font=("consolas",15), bg = couleur, fg = couleur_texte)
scores = Label(boite_resultat, text = "%s à %s" %(score_joueur_display.get(),score_ordi_display.get()), font=("consolas",15), bg = couleur, fg = couleur_texte)
retour_selection = Label(boite_couleurs, text = "", font=("consolas",10), bg = couleur, fg = couleur_texte)
contour1 = Label(boite_menu, text = contour_texte.get(), font=("consolas",7), bg = couleur, fg = couleur_texte)
contour2 = Label(boite_menu, text = contour_texte.get(), font=("consolas",7), bg = couleur, fg = couleur_texte)
contour3 = Label(boite_menu, text = contour_texte.get(), font=("consolas",7), bg = couleur, fg = couleur_texte)
contour4 = Label(boite_menu, text = contour_texte.get(), font=("consolas",7), bg = couleur, fg = couleur_texte)
contour5 = Label(boite_menu, text = contour_texte.get(), font=("consolas",7), bg = couleur, fg = couleur_texte)
contour6 = Label(boite_menu, text = contour_texte.get(), font=("consolas",7), bg = couleur, fg = couleur_texte)

Pour le titre, nous avons décidé de l’écrire en ASCII à l’aide de ce générateur

Chaque Label possède plusieurs attributs: la boîte dans lequel il va apparaître, le texte qu’il contient, la police et la taille d’écriture, la couleur de fond et enfin la couleur de l’écriture.

Comme vous pouvez le constater, certains de ces Labels font appel à des variables tkinter qui nous avons précédemment définies comme celui-ci par exemple:

scores = Label(boite_resultat, text = "%s à %s" %(score_joueur_display.get(),score_ordi_display.get()), font=("consolas",15), bg = couleur, fg = couleur_texte)

Pour appeler ces variables, il suffit d’écrire leur nom suivi de .get()

Maintenant que l’on a défini les éléments texte, on va pouvoir s’attaquer aux différentes fonctions du script.

Premières fonctions: fonctions de navigation

Comme l’on a décidé d’utiliser une ineterface graphique pour ce projet, la plupart des fonctions seront appelées à l’aide de boutons.

#commande permettant d'afficher les règles du jeu
def command_regles():
    boite_menu.pack_forget()
    boite_regles.pack(pady = 25)

Cette fonction par exemple permettra d’afficher les règles du jeu. Il suffit d’ajouter .pack() à la fin du nom d’une boite pour la faire apparaître, et .pack_forget() pour la faire disparaître.

Ainsi, lorsque cette fonction sera appelée, les éléments de la boite_menu disparaîtront pour laisser place à ceux de la boite_regles

Cette fonction sera associée à un bouton de la manière suivante :

b_regles = Button(boite_menu, text = "Règles du jeu", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = command_regles)

On créé un nouvel élément nommé b_regles: un bouton (Button) à qui on donne différents attributs: une boite, un texte, une police et une taille, une couleur de fond, une couleur de texte, et surtout une fonction (l’attribut command) qui sera appelée en cas de clic.

Lorsque l’on cliquera sur ce bouton, la fonction command_regles sera exécutée, ce qui aura pour effet d’afficher les règles du jeu.

On procède de même avec les autres fonctions qui vont nous permettre de naviguer dans notre fenêtre :

#commande pour retourner au menu depuis les règles
def command_retour_regles():
    boite_menu.pack(pady = 25)
    boite_regles.pack_forget()

#commande pour afficher le menu de personnalisation des couleurs
def command_personnaliser():
    boite_menu.pack_forget()
    boite_couleurs.pack(pady = 25)
    boite_couleurs2.pack()

#commande pour retourner au menu principal depuis celui de personnalisation des couleurs
def command_retour_personnaliser():
    boite_menu.pack(pady = 25)
    boite_couleurs.pack_forget()
    boite_couleurs2.pack_forget()

Ainsi qu’avec leurs boutons respectifs

b_retour_regles = Button(boite_regles, text = "Retour", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = command_retour_regles)
b_personnaliser = Button(boite_menu, text = "Personnaliser", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = command_personnaliser)
b_retour_personnaliser = Button(boite_couleurs2, text = "Retour", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = command_retour_personnaliser)

Personnalisation des couleurs

Grâce au menu de personnalisation des couleurs, les utilisateurs du script pourront choisir les couleurs qui leur plait ainsi créant des mélanges uniques.

Ce menu est constitué de plusieurs boutons :

b_couleur1 = Button(boite_couleurs2, text = "Changer la couleur principale ", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = partial(changer_couleur,0))
b_couleur2 = Button(boite_couleurs2, text = "Changer la couleur secondaire ", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = partial(changer_couleur,1))
b_couleur3 = Button(boite_couleurs2, text = "Changer la couleur de selection", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = partial(changer_couleur,2))
b_couleur_joueur = Button(boite_couleurs2, text = " Changer la couleur du joueur ", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = partial(changer_couleur,3))
b_couleur_ordi = Button(boite_couleurs2, text = " Changer la couleur de l'ordi ", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = partial(changer_couleur,4))
b_personnaliser = Button(boite_menu, text = "Personnaliser", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = command_personnaliser)

couleur_entry = Entry(boite_couleurs, text = "#", textvariable=valeur, font=("Helvetica",15) , bg="white", fg="black")
b_ok = Button(boite_couleurs, text = "Cliquez pour appliquer", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = rien)

C’est ici que la fonction partial que nous avons importée va nous être utile: en effet, tous les boutons de ce menu vont exécuter la même fonction, mais avec un paramètre différent, ce qui nous permet de n’écrire qu’une seule fonction au lieu de 5 différentes.

def changer_couleur(test):
    b_ok.config(command = partial(appliquer,test))
    if test == 0:
        retour_selection.config(text = "couleur principale")
    elif test == 1:
        retour_selection.config(text = "couleur secondaire")
    elif test == 2:
        retour_selection.config(text = "couleur de sélection")
    elif test == 3:
        retour_selection.config(text = "couleur du joueur")
    elif test == 4:
        retour_selection.config(text = "couleur de l'ordi")

def appliquer(test):
    global couleur, couleur_texte, couleur_selection, couleur_joueur, couleur_ordi
    if test == 0:
        couleur = str(valeur.get())
    elif test == 1:
        couleur_texte = str(valeur.get())
    elif test == 2:
        couleur_selection = str(valeur.get())
    elif test == 3:
        couleur_joueur = str(valeur.get())
    elif test == 4:
        couleur_ordi = str(valeur.get())
    window.config(bg = couleur)
    titre.config(bg = couleur, fg = couleur_texte)
    sep.config(bg = couleur, fg = couleur_texte)
    boite_titre.config(bg = couleur)
    boite_menu.config(bg = couleur)
    boite_regles.config(bg = couleur)
    boite_jeu.config(bg = couleur)
    boite_ordi.config(bg = couleur)
    boite_resultat.config(bg = couleur)
    boite_couleurs.config(bg = couleur)
    boite_couleurs2.config(bg = couleur)
    b_a1.config(bg = couleur_texte)
    b_a2.config(bg = couleur)
    b_a3.config(bg = couleur_texte)
    b_b1.config(bg = couleur)
    b_b2.config(bg = couleur_texte)
    b_b3.config(bg = couleur)
    b_c1.config(bg = couleur_texte)
    b_c2.config(bg = couleur)
    b_c3.config(bg = couleur_texte)
    b_jouer.config(bg = couleur_texte, fg = couleur)
    b_regles.config(bg = couleur_texte, fg = couleur)
    b_retour_regles.config(bg = couleur_texte, fg = couleur)
    b_personnaliser.config(bg = couleur_texte, fg = couleur)
    b_retour_personnaliser.config(bg = couleur_texte, fg = couleur)
    b_couleur1.config(bg = couleur_texte, fg = couleur)
    b_couleur2.config(bg = couleur_texte, fg = couleur)
    b_couleur3.config(bg = couleur_texte, fg = couleur)
    b_couleur_ordi.config(bg = couleur_texte, fg = couleur)
    b_couleur_joueur.config(bg = couleur_texte, fg = couleur)
    b_rejouer.config(bg = couleur_texte, fg = couleur)
    b_ordi.config(bg = couleur_texte, fg = couleur)
    b_trouver_hex.config(bg = couleur_texte, fg = couleur)
    b_ok.config(bg = couleur_texte, fg = couleur)
    regles_label.config(bg = couleur, fg = couleur_texte)
    b_retour_jeu.config(bg = couleur_texte, fg = couleur)
    scores.config(bg = couleur, fg = couleur_texte)
    resultat_Label.config(bg = couleur, fg = couleur_texte)
    retour_selection.config(bg = couleur, fg = couleur_texte)
    contour1.config(bg = couleur, fg = couleur_texte)
    contour2.config(bg = couleur, fg = couleur_texte)
    contour3.config(bg = couleur, fg = couleur_texte)
    contour4.config(bg = couleur, fg = couleur_texte)
    contour5.config(bg = couleur, fg = couleur_texte)
    contour6.config(bg = couleur, fg = couleur_texte)

Lorsque le bouton « Cliquez pour appliquer » est cliqué la fonction appliquer est exécutée avec un paramètre différent en fonction du bouton sélectionné auparavant.

La fonction change la couleur demandée puis rafraîchit tous les éléments différents afin que le changement de couleur apparaisse.

Il est à noter que le script ne comprend que certains noms de couleur en anglais ou les couleurs données en hexadécimal

Ce menu comporte aussi un bouton permettant d’ouvrir une page web avec un panneau pour sélectionner la couleur de son choix et avoir son code en hexadécimal :

b_trouver_hex = Button(boite_couleurs2, text = "    Trouver des couleurs     ", font = ("consolas", 15), bg = couleur_texte, fg = couleur, command = pick_hex)

L’ouverture de la page depuis le script est faite grâce a la Library webbrowser

def pick_hex():
    webbrowser.open_new("https://www.google.com/search?client=opera-gx&q=color+picker&sourceid=opera&ie=UTF-8&oe=UTF-8")

Mise en place du plateau de jeu

Après avoir lu les règles du jeu et modifié les couleurs si nécessaire on peut enfin jouer.

En cliquant sur le bouton jouer on exécute la commande suivante :

def command_jouer():
    boite_resultat.pack_forget()
    boite_jeu.pack(pady = 25)
    boite_ordi.pack(pady = 25)
    boite_menu.pack_forget()
    global a1 , a2 , a3 , b1 , b2 , b3 , c1 , c2 , c3 , pions_ordi, pions_joueur, pions_bloques, ordi_gagne, egalite, joueur_gagne, tour, couleur_ordi, couleur_joueur, jeu
    jeu = ""
    a1 , a2 , a3 , b1 , b2 , b3 , c1 , c2 , c3 = "♟" , "♟" , "♟" , "  " , "  " , "  " , "♙" , "♙" , "♙"
    a1_display.set(a1)
    a2_display.set(a2)
    a3_display.set(a3)
    b1_display.set(b1)
    b2_display.set(b2)
    b3_display.set(b3)
    c1_display.set(c1)
    c2_display.set(c2)
    c3_display.set(c3)
    b_a1.config(text = a1_display.get(), fg = couleur_ordi)
    b_a2.config(text = a2_display.get(), fg = couleur_ordi )
    b_a3.config(text = a3_display.get(), fg = couleur_ordi )
    b_b1.config(text = b1_display.get() )
    b_b2.config(text = b2_display.get() )
    b_b3.config(text = b3_display.get() )
    b_c1.config(text = c1_display.get(), fg = couleur_joueur )
    b_c2.config(text = c2_display.get(), fg = couleur_joueur )
    b_c3.config(text = c3_display.get(), fg = couleur_joueur )
    pions_ordi = 3
    pions_joueur = 3
    pions_bloques = 0
    joueur_gagne = False
    ordi_gagne = False
    egalite = False
    tour = 0
    tester_cases_selectionnables()

Elle permet de mettre en place le plateau de jeu en faisant apparaître les boites boite_jeu et boite_ordi et en faisant disparaître les autres.

Ensuite les pions sont placés sur le plateau et le compteur de tours, et ceux des pions sont mis à leurs valurs initiales

Enfin, le script teste les cases à partir desquelles l’on peut bouger nos pions à l’aide de la fonction tester_cases_selectionnables().

Les possibilités de déplacement

La fonction « tester_cases_selectionnables() » teste les cases sur lesquels se trouve les pions du joueur et permet au joueur de cliquer le bouton si jamais un de ses pions y est présent.

def tester_cases_selectionnables():
    if c1 == "♙":
        b_c1.config(command = partial(selected,"c1"), fg = couleur_joueur )

    if c2 == "♙":
        b_c2.config(command = partial(selected,"c2"), fg = couleur_joueur  )

    if c3 == "♙":
        b_c3.config(command = partial(selected,"c3"), fg = couleur_joueur  )

    if b1 == "♙":
        b_b1.config(command = partial(selected,"b1"), fg = couleur_joueur  )

    if b2 == "♙":
        b_b2.config(command = partial(selected,"b2"), fg = couleur_joueur  )

    if b3 == "♙":
        b_b3.config(command = partial(selected,"b3"), fg = couleur_joueur  )

Ensuite, selon la case sélectionnée le script étudie les mouvements possibles et modifie les commandes éxécutées en cas de clic sur la case en conséquence :

def selected(case):
    global a1, a2, a3, b1, b2, b3, c1, c2, c3
    global tour

    if tour == 0 :

        if case == "c1":
            b_b1.config(command = partial(jouer_vers,"c1","b1"))
            b_b2.config( command = rien)
            b_b3.config(command = rien)
            b_c1.config(fg=couleur_selection)
            b_c2.config(fg=couleur_joueur)
            b_c3.config(fg=couleur_joueur)
            b_b2.config(command = rien)
            b_b3.config(command = rien)

        elif case == "c2":
            b_b2.config(command = partial(jouer_vers,"c2","b2"))
            b_c2.config(fg=couleur_selection)
            b_c1.config(fg=couleur_joueur)
            b_c3.config(fg=couleur_joueur)
            b_b1.config(command = rien)
            b_b3.config(command = rien)
#et ainsi de suite avec les autres cases
    elif tour >= 2:

        if case == "c1":
            reset_commandes()
            tester_cases_selectionnables()
            b_c1.config(fg=couleur_selection)
            if b1 == "  ":
                b_b1.config(command = partial(jouer_vers,"c1","b1"))
            if b2 == "♟":
                b_b2.config(command = partial(jouer_vers,"c1","b2"))

        elif case == "c2":
            reset_commandes()
            tester_cases_selectionnables()
            b_c2.config(fg=couleur_selection)
            if b2 == "  ":
                b_b2.config(command = partial(jouer_vers,"c2","b2"))
            if b1 == "♟":
                b_b1.config(command = partial(jouer_vers,"c2","b1"))
            if b3 == "♟":
                b_b3.config(command = partial(jouer_vers,"c2","b3"))
#etc...

Après avoir choisi le pion qu’il souhaitait déplacer, le joueur va cliquer sur la case vers laquelle il veut que le pion se déplace.

La fonction jouer_vers va alors être exécutée avec en paramètres la case de départ et la case d’arrivée du pion.

La fonction va rendre vide la case de départ du pion et va mettre un pion blanc dans la case d’arrivée

def jouer_vers(case1, case2):
    global tour, jeu
    global a1, a2, a3, b1, b2, b3, c1, c2, c3
    global pions_ordi, ordi_gagne

    if tour / 2 == tour // 2 and not ordi_gagne:

        if case1 == "c1":
            c1 = "  "
            c1_display.set(c1)
            b_c1.config(text = c1_display.get())

        elif case1 == "c2":
            c2 = "  "
            c2_display.set(c2)
            b_c2.config(text = c2_display.get())
#etc...
        if case2 == "b1":
            if b1 == "♟":
                pions_ordi -= 1
            b1 = "♙"
            b1_display.set(b1)
            b_b1.config(text = b1_display.get(), fg = couleur_joueur)
            jeu += case2

        elif case2 == "b2":
            if b2 == "♟":
                pions_ordi -= 1
            b2 = "♙"
            b2_display.set(b2)
            b_b2.config(text = b2_display.get(), fg = couleur_joueur)
            jeu += case2
#etc...

Si le joueur a réussi à amener l’un de ses pions de l’autre côté, il gagne un point et le résultat de la partie est affiché avec les scores.

Sinon, c’est à l’ordi de jouer.

        elif case2 == "a3":
            a3 = "♙"
            a3_display.set(a3)
            b_a3.config(text = a3_display.get(), fg = couleur_joueur)
            joueur_gagne = True
            coups_perdants.append(jeu)

        tour += 1

    if pions_joueur == 0:
        ordi_gagne = True

    if ordi_gagne :
        boite_jeu.pack_forget()
        resultat = "Défaite"
        resultat_display.set(resultat)
        resultat_Label.config(text = resultat_display.get())
        boite_resultat.pack()

    else:
        pass

L’IA

Les mouvements possible de l’ordinateur sont étudiés par la fonction « ordi() ».

#début de la fonction ordi()
def ordi():
    global tour
    global a1, a2, a3, b1, b2, b3, c1, c2, c3
    global pions_joueur, points_joueur, ordi_gagne, points_ordi, pions_ordi, joueur_gagne, pions_bloques, resultat, egalite, coups_gagnants, coups_perdants, jeu, couleur_ordi

Elle étudie tout d’abord si il y a un vainqueur ou une égalité ( grâce à la fonction « tester_tie() » expliquée un peu plus plus loin ) .

    if pions_ordi == 0:
        joueur_gagne = True
    if pions_joueur == 0:
        ordi_gagne = True
    tester_tie()


    if pions_bloques == pions_ordi:
        egalite = Tr

Si il n’y a pas de vainqueur ou d’égalité et que c’est au tour de l’ordi de jouer, le script analyse les mouvements possibles de l’ordinateur. si il s’aperçoit qu’un mouvement l’a amené à perdre dans une configuration similaire, il ne l’inclura pas dans la liste des mouvements possibles.

Si, au contraire un des mouvements l’a fait gagner précédemment, il le mettra dans une liste spéciale

 elif tour/2 != tour//2 and not joueur_gagne and not ordi_gagne :
            mvt_possibles = []
            mvt_gagnants = []
            if a1 == "♟":
                if b1 == "  " and jeu + "a1b1" not in coups_perdants :
                    mvt_possibles.append("a1b1")
                    if jeu + "a1b1" in coups_gagnants:
                        mvt_gagnants.append("a1b1")

                if b2 == "♙" and jeu + "a1b2" not in coups_perdants:
                    mvt_possibles.append("a1b2")
                    if jeu + "a1b2" in coups_gagnants:
                        mvt_gagnants.append("a1b2")


            if a2 == "♟":
                if b1 == "♙" and jeu + "a2b1" not in coups_perdants :
                    mvt_possibles.append("a2b1")
                    if jeu + "a2b1" in coups_gagnants:
                        mvt_gagnants.append("a2b1")

                if b2 == "  " and jeu + "a2b2" not in coups_perdants:
                    mvt_possibles.append("a2b2")
                    if jeu + "a2b2" in coups_gagnants:
                        mvt_gagnants.append("a2b2")

                if b3 == "♙" and jeu + "a2b3" not in coups_perdants:
                    mvt_possibles.append("a2b3")
                    if jeu + "a2b3" in coups_gagnants:
                        mvt_gagnants.append("a2b3")
#et ainsi de suite avec toutes les cases...

Ensuite, la fonction teste si des mouvements possibles peuvent amener l’ordi à gagner à coup sûr. Si c’est le cas, un mouvement aléatoire est pioché parmi la liste mvt_gagnants.

Sinon, l’ordi choisit un mouvement aléatoire parmi les mouvements possibles.

            if mvt_gagnants != []:
                mvt_ordi = mvt_gagnants[randint(0,len(mvt_gagnants)-1)]
            else:
                mvt_ordi = mvt_possibles[randint(0,len(mvt_possibles)-1)]

Le mouvement choisi est ensuite « joué »

            if mvt_ordi == "a1b1":
                a1 = "  "
                a1_display.set(a1)
                b_a1.config(text = a1_display.get())
                b1 = "♟"
                b1_display.set(b1)
                b_b1.config(text = b1_display.get(), fg = couleur_ordi)

            elif mvt_ordi == "a1b2":
                a1 = "  "
                a1_display.set(a1)
                b_a1.config(text = a1_display.get())
                b2 = "♟"
                b2_display.set(b2)
                b_b2.config(text = b2_display.get(), fg = couleur_ordi)
                pions_joueur -= 1
#etc...
            elif mvt_ordi == "b1c1":
                b1 = "  "
                b1_display.set(b1)
                b_b1.config(text = b1_display.get())
                c1 = "♟"
                c1_display.set(c1)
                b_c1.config(text = c1_display.get(), fg = couleur_ordi)
                ordi_gagne = True

Le coup joué est ensuite ajouté à la variable jeu.

Si le mouvement amène l’ordi à prendre un pion, le nombre de pions du joueur est décrémenté de 1

Si l’ordi a amené l’un de ses pions de l’autre côté, la variable ordi_gagne devient « vraie » (True)

            jeu += mvt_ordi

Si l’ordi a gagné, c’est à dire si ordi_gagne est True, son score est incrémenté, la variable « jeu » est ajouté à la liste des coups gagnants et le score ainsi que le résultat de la partie sont affichés

            if ordi_gagne:
                coups_gagnants.append(jeu)
                reset_commandes()
                boite_ordi.pack_forget()
                points_ordi += 1
                resultat = "Défaite"
                resultat_display.set(resultat)
                resultat_Label.config(text = resultat_display.get())
                score_ordi_display.set(points_ordi)
                score_joueur_display.set(points_joueur)
                scores.config(text = "%s à %s" %(score_joueur_display.get(), score_ordi_display.get()) )
                boite_resultat.pack()

Ensuite, on passe au tour suivant.

Si le joueur avait déjà gagné au début du tour (l’ordi n’a donc pas joué) ou s’il y a égalité le score est incrémenté en conséquence et l’issue de la partie est affichée

Si aucune de ces conditions n’est vérifiées, c’est au tour du joueur de jouer.

            tour +=1
            reset_commandes()
            tester_cases_selectionnables()
            tester_tie()




    if pions_bloques == pions_ordi:
        egalite = True


    if egalite:
        reset_commandes()
        boite_ordi.pack_forget()
        resultat = "Egalité"
        resultat_display.set(resultat)
        resultat_Label.config(text = resultat_display.get())
        boite_resultat.pack()

    elif joueur_gagne:
        coups_perdants.append(jeu)
        reset_commandes()
        boite_ordi.pack_forget()
        points_joueur += 1
        resultat = "Victoire"
        resultat_display.set(resultat)
        resultat_Label.config(text = resultat_display.get())
        score_ordi_display.set(points_ordi)
        score_joueur_display.set(points_joueur)
        scores.config(text = "%s à %s" %(score_joueur_display.get(), score_ordi_display.get()) )
        boite_resultat.pack()

    else:
        pass

Gagnant, perdant ou ex æquo

Plisieurs conditions peuvent mettre fin à la partie

  • Le joueur ou l’ordi amène l’un de ses pions de l’autre côté du plateau
  • le joueur ou l’ordi ne possèdent plus de pions
  • Tous les pions sont bloqués

Les deux premières conditions sont facilement vérifiables et sont déjà testées directement dans les fonctions ordi() et jouer_vers()

On teste donc s’il y a égalité à l’aide de la fonction tester_tie(), appelée à l’interieur de la fonction ordi() comme vu précédemment.

Cette fonction teste tout simplement pour chaque case si le pion présent n’a plus de possibilités de mouvement, et ajoute au compteur de pions bloqués 1 si jamais le pion ne peut plus bouger.

def tester_tie():
    global a1, a2, a3, b1, b2, b3, c1, c2, c3, pions_bloques
    pions_bloques = 0
    if a1 == "♟":
        if b1 == "♙":
            if b2 == "♟" or b2 == "  ":
                pions_bloques += 1
            else:
                pass
        else:
            pass

    if a2 == "♟":
        if b2 == "♙":
            if b1 == "♟" or b1 == "  ":
                if b3 == "♟" or b3 == "  ":
                    pions_bloques += 1
                else:
                    pass
            else:
                pass
        else:
            pass

    if a3 == "♟":
        if b3 == "♙":
            if b2 == "♟" or b2 == "  ":
                pions_bloques += 1
            else:
                pass
        else:
            pass

    if b1 == "♟":
        if c1 == "♙":
            if c2 == "♟" or c2 == "  ":
                pions_bloques += 1
            else:
                pass
        else:
            pass


    if b2 == "♟":
        if c2 == "♙":
            if c1 == "♟" or c1 == "  ":
                if c3 == "♟" or c3 == "  ":
                    pions_bloques += 1
                else:
                    pass
            else:
                pass
        else:
            pass


    if b3 == "♟":
        if c3 == "♙":
            if c2 == "♟" or c2 == "  ":
                pions_bloques += 1
            else:
                pass
        else:
            pass

Si jamais tous les pions de l’ordi sont bloqués il y a égalité

if pions_bloques == pions_ordi:
        egalite = True

if egalite:
        reset_commandes()
        boite_ordi.pack_forget()
        resultat = "Egalité"
        resultat_display.set(resultat)
        resultat_Label.config(text = resultat_display.get())
        boite_resultat.pack()

L’affichage

Maintenant que toutes les fonction sont définies, il faut afficher tous les différents éléments de la fenêtre:

titre.pack()
sep.pack()
contour1.grid(row = 0, column = 1 , sticky = W, padx = 100, pady = 0)
contour2.grid(row = 1, column = 1 , sticky = W, padx = 100, pady = 0)
contour3.grid(row = 2, column = 1 , sticky = W, padx = 100, pady = 0)
b_jouer.grid(row = 0, column = 2 , sticky = W, pady = 0)
b_regles.grid(row = 1, column = 2 , sticky = W, pady = 0)
b_personnaliser.grid(row = 2, column = 2 , sticky = W, pady = 0)
contour4.grid(row = 0, column = 3 , sticky = W, padx = 100, pady = 0)
contour5.grid(row = 1, column = 3 , sticky = W, padx = 100, pady = 0)
contour6.grid(row = 2, column = 3 , sticky = W, padx = 100, pady = 0)
regles_label.pack()
b_retour_regles.pack()
b_a1.grid(row = 0, column = 0, sticky = W)
b_a2.grid(row = 0, column = 1, sticky = W)
b_a3.grid(row = 0, column = 2, sticky = W)
b_b1.grid(row = 1, column = 0, sticky = W)
b_b2.grid(row = 1, column = 1, sticky = W)
b_b3.grid(row = 1, column = 2, sticky = W)
b_c1.grid(row = 2, column = 0, sticky = W)
b_c2.grid(row = 2, column = 1, sticky = W)
b_c3.grid(row = 2, column = 2, sticky = W)
b_ordi.pack(pady = 15)
b_retour_jeu.pack(pady = 15)
resultat_Label.pack(pady = 15)
scores.pack(pady = 15)
b_rejouer.pack(pady = 15)
boite_titre.pack()
boite_menu.pack(pady = 25)
retour_selection.pack(pady = 10)
couleur_entry.pack(pady = 15)
b_ok.pack(pady = 10)
b_couleur1.grid(row = 0, column = 0, sticky = W, pady = 5, padx = 5)
b_couleur2.grid(row = 0, column = 1, sticky = W, pady = 5, padx = 5)
b_couleur3.grid(row = 0, column = 2, sticky = W, pady = 5, padx = 5)
b_couleur_joueur.grid(row = 1, column = 0, sticky = W, pady = 5, padx = 5)
b_couleur_ordi.grid(row = 1, column = 1, sticky = W, pady = 5, padx = 5)
b_trouver_hex.grid(row = 1, column = 2, sticky = N, pady = 5, padx = 5)
b_retour_personnaliser.grid(row = 2, column = 1, sticky = N, pady = 10, padx = 5)

Et surtout ne pas oublier d’ouvrir la fenêtre en cas d’exéction du script

window.mainloop()

Captures d’écran

Si vous souhaitez vous mesurer à cette IA et vérifier qu’elle apprend bel et bien de ses erreurs, vous pourrez télécharger le script du projet (défi : dépasser un score de 17!).

Projets

Le jeu du plus ou moins en python

Le principe du jeu est de trouver le nombre de l’ordinateur en un certains nombres de tentatives. Si le joueur ne trouve pas ce nombre en ce certains nombres de tentatives, la partie prend fin mais aussi lorsque le joueur trouve le nombre généré aléatoirement par l’ordinateur.

Introduction

Ce jeu a été réalisé par un élève en première et spécialité NSI.

Jouez au jeu du plus ou moins sur ordinateur ou sur un émulateur qui soit compatible avec le module tkinter !

Dans le cadre de mon projet, le jeu a été codé en python sous 2 formes différentes :

La première qui permet au joueur de jouer directement via la console d’exécution de son application :

La seconde qui permet au joueur de jouer dans un terminal appart soit dans une nouvelle fenêtre :

Évolution

Jeu dans la console d’exécution

  • L’importation des modules et variables

Le module randint est un module aléatoire donne accès à diverses fonctions utiles et l’une d’elles est capable de générer des nombres aléatoires. Elle s’écrit de cette façon : randint(start, end)

J’ai crée 5 variables nombres : La 1ère définit notre nombre d’essais maximum, la 2nde notre nombre d’essais au départ, la 3ème le nombre du joueur, la 4ème et la 5ème sont liée car ici nous faisons appel a la variable nombre_max_ordi dans la fonction randit().

from random import randint

essais_max= 5  # nombre d'essais maximum
essais = 1   # nombre essais
nombre_joueur = 0   # nombre du joueur au départ
nombre_max_ordi = 25  # nombre maximum généré par l'ordinateur
nombre_ordi = randint(1,nombre_max_ordi)   # nombre choisi par l'ordinateur
  • Affichage

La fonction print permet d’afficher a l’écran du texte.

print("L'ordinateur a choisi un nombre entre 1 et",nombre_max_ordi, ".")
print("Vous devez le trouver en moins de 5 tentatives !")
  • Boucle while

En anglais  » while  » signifie « Tant que ». Pour créer une boucle , il faut donc utiliser ce mot clé suivi d’une indication qui dit quand la boucle s’arrête.

Ici notre boucle ne s’arrêtera pas tant que quand le nombre de l’ordinateur ne sera pas égal au nombre du joueur et que son nombre d’essais soit inférieur ou égal au nombre d’essais maximum.

if, elif et else sont des conditions incrémenté dans notre boucle while.

if signifie « si ». Ici, elle marchera si le nombre du joueur est inférieur a celui de l’ordinateur.

elif est contraction de « else » et « if », qu’on pourrait traduire par « sinon ». Ici elle marchera si le nombre du joueur est supérieur a celui de l’ordinateur.

else signifie « autre ». Elle marchera si les conditions if et elif ne sont pas respectés. Entre autre le joueur aura gagné.

while nombre_ordi  != nombre_joueur and essais <= essais_max:
    print("vous êtes au", essais, "essai.")
    nombre_joueur = int(input("Choisissez un nombre :"))
    if nombre_joueur < nombre_ordi:
        print("Le nombre que vous avez choisi est trop petit")
    elif nombre_joueur > nombre_ordi:
        print("Le nombre que vous avez choisi est trop grand")
    else:
        print("Félicitations ! Vous avez trouvé le nombre de l'ordinateur ","en",essais,"essai(s)")
    essais += 1
  • Condition if

Si le joueur dépasse le nombre d’essais maximum et que si son nombre n’est pas le même que celui de l’ordinateur alors il aura perdu.

if essais>essais_max and nombre_joueur != nombre_ordi :
    print("Désolé, vous avez utilisé tous vos essais.")
    print("Vous êtes vraiment nul, réessayer jusqu'a que vous gagnez!")
    print("L'ordinateur avais choisi le nombre",nombre_ordi,".")

Jeu avec interface graphique

  • L’importation des modules et variables

Tkinter (de l’anglais Tool kit interface) est la bibliothèque graphique libre d’origine pour le langage Python, permettant la création d’interfaces graphiques.

Pour créer mon interface graphique, j’ai d’abord créer une fenêtre dont j’ai préciser sa dimension dans laquelle on y retrouva le titre, puis la couleur de fond (qui sera ici du bleu cyan), ainsi que le texte de présentation.

from random import randint
from tkinter import *

fenetre = Tk()#creation fenetre
fenetre.title("Devine le nombre")#titre
fenetre.config(bg = "#87AEAB")# couleur de fond
fenetre.geometry("1080x720") # dimension
texte = Label (fenetre, text = "Bienvenue sur le jeu du + OU -")# texte
texte.pack()

Du coté des variables une seule chose a changé : leurs valeurs ( je vous expliquerai pourquoi plus tard).

essais_max= 4 # nombre d'essais maximum
essais = 1   # nombre essais
nombre_joueur = 0   # nombre proposé par le joueur
nombre_max_ordi = 9  # nombre maximum généré par l'ordinateur
nombre_ordi = randint(0,nombre_max_ordi)   # nombre choisi par l'ordinateur
  • Affichage

Le widget Label est un widget Tkinter standard utilisé pour afficher un texte ou une image à l’écran. Ici il va afficher du texte.

La fonction str() convertit les valeurs en forme de chaîne afin qu’elles puissent être combinées avec d’autres chaînes. Elle est utilisé dans mon programme car on ne peut écrire un texte avec des valeurs autres que des lettres alors que ma variable est un chiffre.

La méthode .pack place les widgets les uns en dessous des autres et ajuste la taille de la fenêtre. On verra plus bas que l’on peut passer des arguments à cette méthode pour placer les widgets différemment (en haut, à droite, à gauche).

Pour résumer si la méthode .pack() n’est pas utilisé a chaque fin de variables, celles-ci ne s’afficheront pas lors de l’exécution du programme.

texte2 = Label (fenetre, text = "L'ordinateur a choisi un nombre entre 1 et " +str(nombre_max_ordi))
texte2.pack()

Label (fenetre, text = "Tu as 4 essais pour le trouver ").pack()
Label (fenetre, text = "A toi de jouer !").pack()
  • Le cerveau du programme

La fonction chekInput représente la boucle principale, c’est elle qui contient les conditions de notre programme.

En Python, le mot clé global permet de modifier la variable en dehors de la portée courante. Il est utilisé pour créer une variable globale et apporter des modifications à la variable dans un contexte local ( donc ici dans notre fonction).

Puisque c’est une boucle qui tourne à toutes les actions du joueur, c’est l’endroit idéal pour mettre à jour les infos du joueur, comme le nombre d’essais.

Cette fonction ne change pas vraiment de celle du 1er programme appart le texte.

def checkInput(nombre_joueur):
    global essais, essais_max, nombre_ordi
    Label (fenetre, text = "Vous avez choisi " + str(nombre_joueur) + " :").pack()

    print(essais , essais_max)
    if nombre_ordi  != nombre_joueur and essais <= essais_max:
        Label (fenetre, text = "vous êtes au " + str(essais) + " essai.").pack()
        essais += 1
        nombre_joueur = int(nombre_joueur)
        if nombre_joueur < nombre_ordi:
            Label (fenetre, text = "Le nombre que vous avez choisi est trop petit").pack()
        elif nombre_joueur > nombre_ordi:
            Label (fenetre, text = "Le nombre que vous avez choisi est trop grand").pack()
        
    elif nombre_ordi == nombre_joueur:
        Label (fenetre, text = "Bien joué, le chiffre était bien : " + str(nombre_ordi)).pack()
    elif  essais>essais_max and nombre_joueur != nombre_ordi :
        Label (fenetre, text = "Désolé, vous avez utilisé tous vos essais. Le chiffre était " + str(nombre_ordi)).pack()
  • Problème rencontré

Le moment est venu de vous expliquer pourquoi les valeurs de mes variables ont changé.

Tout simplement puisque j’ai crée des boutons de 0 à 9 et je n’avais pas la place d’en mettre plus sinon on aurait pas vu la fin de notre programme.

Voici une photo pour mieux comprendre.

Si il y avait eu plus de bouton le texte qui apparait après chaque clic sur un bouton aurait dépassé de ma fenêtre.

  • Interaction homme/machine

Voici les 10 boutons allant de 0 a 9.

Pour lier un événement à un widget, on utilise la méthode bind() sur ce widget.

Le premier argument, ‘<Button-1>’, est un «descripteur de séquence» qui indique à tkinter que lorsque le bouton gauche de la souris est pressé, il faut qu’il appelle la fonction  chekInput fourni comme deuxième argument.

Une fonction lambda est une petite fonction anonyme qui peut prendre n’importe quel nombre d’arguments, mais ne peut avoir qu’une seule expression.

Ici elle va prendre le chiffre qui lui est associé et le joué dans la fonction chekInput.

btn0 = Label (fenetre, text = str(0))
btn0.bind("<Button-1>", lambda e: checkInput(0))
btn0.pack()

btn1 = Label (fenetre, text = str(1))
btn1.bind("<Button-1>", lambda e: checkInput(1))
btn1.pack()

btn2 = Label (fenetre, text = str(2))
btn2.bind("<Button-1>", lambda e: checkInput(2))
btn2.pack()

btn3 = Label (fenetre, text = str(3))
btn3.bind("<Button-1>", lambda e: checkInput(3))
btn3.pack()

btn4 = Label (fenetre, text = str(4))
btn4.bind("<Button-1>", lambda e: checkInput(4))
btn4.pack()

btn5 = Label (fenetre, text = str(5))
btn5.bind("<Button-1>", lambda e: checkInput(5))
btn5.pack()

btn6 = Label (fenetre, text = str(6))
btn6.bind("<Button-1>", lambda e: checkInput(6))
btn6.pack()

btn7 = Label (fenetre, text = str(7))
btn7.bind("<Button-1>", lambda e: checkInput(7))
btn7.pack()

btn8 = Label (fenetre, text = str(8))
btn8.bind("<Button-1>", lambda e: checkInput(8))
btn8.pack()

btn9 = Label (fenetre, text = str(9))
btn9.bind("<Button-1>", lambda e: checkInput(9))
btn9.pack()
  • Fin de la fenêtre

La méthode mainloop() permet d’afficher la fenêtre qui vient d’être créée. Pour ceux qui ne l’avais pas deviné, elle marque la fin de notre programme.

fenetre.mainloop()

Anecdote

A la base j’étais dans un groupe de 3 personnes et ont voulais créer un space invaders mais ont s’est vite rendu compte de la difficulté alors ont s’est orienté sur le jeu du morpion. Puis je décida de quitter le groupe et de me mettre en solo car ils ne travaillait pas et c’est ainsi que j’ai codé le jeu du plus ou moins.

Conclusion

Pour pouvoir jouer à mes des versions du jeu, téléchargez le code par l’intermédiaire du lien ci-dessous. Pour la 1ère version il vous faut écrire le nombre dans la console d’exécution et pour la 2nde il vous suffit juste de cliquer sur les boutons.
Amusez-vous bien !

Téléchargement