Étiquette : Python

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 :

Boucle Tutoriels

Différentes utilisations de la boucle bornée for en Python…

Essayons de dompter le tigre… ou plutôt le serpent en l’occurrence. D’ailleurs en parlant d’occurrence, plongeons dans le monde pantagruélique de la programmation et voyons aujourd’hui la boucle for.

A – Mais qu’est ce qu’une boucle, et a fortiori une boucle for ?

Et non ce n’est pas une formule magique, mais presque !

Mais d’abord l’essentiel serait de savoir ce qu’est une boucle en Python puis ensuite de l’adapter à la boucle for (c’est for en chocolat).

Alors comment dire, quand votre petit frère vous énerve tout particulièrement et que vous ne voulez pas rester trop longtemps dans cette géhenne ni faire trop d’escarmouche, alors vous avez une option, non pas la fuite, mais faire tourner en boucle ce personnage qui hante éblouit vos vies.

En lui promettant quelque chose puis en retirant cette promesse, tout en le répétant à de multiples reprises, vous parvenez par la magie du langage, d’objectiver votre conscience, pour qu’ensuite elle l’atteigne au cœur même de sa subjectivité.

Le mal étant fait, profitez de votre œuvre ! Bravo, vous avec compris le concept de boucle et ici c’est une boucle infinie. Super Mince ! Bon, il manque quelque chose pour que la boucle fonctionne vraiment et soit exécutable, c’est à dire pour que ce chérubin fasse ce que vous voulez (sinon il va rester planté là).

Par exemple quand on a peur de vous entendre dire encore une fois que les personnes âgées n’utilisant pas leurs appareils mobiles à bon escient devraient être emprisonnées à vie sur l’île de la Pêche au Sirop, on va dire : « Avant de nous dire cette frivolité tu es priés d’exercer une rotation dans le sens trigonométrique, de ta langue en faisant 7 tours ».

Ainsi vous connaissez déjà les boucles, et ici en plus c’est une boucle for, trop for ! (elle était facile j’avoue)

Donc, vous voyez le cerveau est surprenant de plasticité, vous savez dorénavant quelque chose que vous ne pensiez pas savoir, alors que vous n’avez rien appris de nouveau.

Mais alors que veut dire le mot for, que nos congénères anglicans apprécient tant ?

Selon toute vraisemblance, la traduction la plus logique serait pour, ou bien sur.

Rentrons maintenant que nous appréhendons ces points importants dans le cœur du sujet, et je peux vous dire qu’on est pas sorti du sable.

Alors, il faut savoir qu’en Python (et nous ne parlerons que du Python même si certains éléments seront présents chez d’autres espèces, sélection naturelle oblige), la boule for est une boucle conditionnelle, c’est-à-dire que comme son nom l’indique, elle est soumise à une condition. C’est pourtant clair non ?

Il faut qu’une condition sine qua none se présente pour que la boucle arrête de boucler et que votre frère vous écoute donc on fera attention à cela pour que tout se passe bien.

Pour qu’elle s’arrête il faut aussi un début. Il peut être indiqué explicitement, ou implicitement avec le sens du code.

A savoir que for ne peut pas marcher tout seul comme un grand, il doit obligatoirement être associé à un parent majeur comme une variable ou certaines fonctions.

B – Les différents usages de la boucle for

1. L’association boucle for et fonction range()

Commençons par le plus classique des points, l’appairage avec la fameuse fonction range() !

Donc vous avez un code tel qu’il suit

for i in range(debut, fin, pas):
    # je fais des choses tralalalala

On retrouve donc la structure clé ; l’instruction for, et ensuite un bazar sans nom avec la fonction range().

Décryptons tout cela en partant de la droite.

Alors range() c’est tout bête, c’est juste une fonction qui va aller de la valeur entière donnée de départ à la valeur d’arrivée en répétant cette tâche un nombre certain de fois. Et les pas seront l’écart entre deux valeurs données.

Et en fait i sera une variable temporaire de la boucle automatiquement créée par Python pour récupérer la valeur de la fonction range car sinon elle se perd.

Ainsi si l’on résume, on va parcourir toutes les valeurs entre 2 entiers avec un pas donné !

Ainsi, résumons :

for i in range(42, 50, 2):
	print(i)

Donnera :

42
44
46
48

Car oui il manquait un détail, on ne prend pas la valeur finale, mais la valeur strictement inférieure comme dernière valeur.

Par conséquent, cette boucle peut s’écrire de différentes manières en fonction du niveau de raffinement voulu :

Mode auto :

for i in range(fin):
	# laziza

Ce mode a par défaut un pas de 1 et un début à 0.

Et ici :

for i in range(debut, fin):
	# laziza

Pareil que tout à l’heure sauf qu’on donne explicitement le début.

Et donc tout cela, c’est bien magique, mais voyons un cas concret pour mieux comprendre l’utilisation.

Imaginons que j’ai une liste de matières préférées, classée par ordre de préférence décroissant ; c’est-à-dire que la matière préférée sera en premier, etc…

J’ai donc une liste comme suit :

matieres_preferees = ["NSI", "Maths Expertes", "Maths", "Physique-Chimie", "Philo", "Ma LV2", "Enseignement Scientifique", "SES"] 

Je vais donc dire au grand jour l’ordre de mes matières :

for id_matiere in range(len(matieres_preferees)):
    print("La matiere", matieres_preferees[id_matiere], "est au rang", id_matiere + 1, "dans mon ordre de préférence")
    #ou bien :  print("La matiere {} est au rang {} dans mon ordre de préférence",.format(matieres_preferees[id_matiere], id_matiere + 1) 

En fait ici la fonction len, très utile, donne la longueur de la liste, on parcourt donc la liste de son id 0 (son premier élément), jusqu’à l’id juste inférieur à la longueur de la liste, soit l’id du dernier élément de la liste.

Pour l’affichage, on rajoute un 1 à l’id de la matière, pour donner le rang, car sinon le premier élément aurait le rang 0, peu parlant en langage franchouillard.

Ce code va donc rendre :

La matiere NSI est au rang 1 dans mon ordre de préférence
La matiere Maths Expertes est au rang 2 dans mon ordre de préférence
La matiere Maths est au rang 3 dans mon ordre de préférence
La matiere Physique-Chimie est au rang 4 dans mon ordre de préférence
La matiere Philo est au rang 5 dans mon ordre de préférence
La matiere Ma LV2 est au rang 6 dans mon ordre de préférence
La matiere Enseignement Scientifique est au rang 7 dans mon ordre de préférence
La matiere SES est au rang 8 dans mon ordre de préférence

Et voila vous savez parcourir des nombres à l’aide de for et de range !

2. Le parcours d’éléments autonome (sans valeur chiffrée)

Maintenant, reprenons l’exemple précédant, et affinons le un peu pour le rendre plus élégant.

On va s’avouer quand même que l’écriture de tout à l’heure pour dire l’ordre de nos matières préférées était un peu lourd, redondant, et inutile. Il faut en effet passer par la valeur chiffrée de l’id de l’élément sur la liste pour ensuite arriver à l’élément en lui même. Or, on a vu en philosophie que le fait de traduire sa pensée subjective vers quelque chose d’objectif, comme le langage ou ici la valeur chiffrée, trahissait notre pensée. Donc ce n’est pas bon car cela ne reflète pas exactement ce qu’on veut faire, il faut donc essayer de rester dans notre idée, sans devoir passer par un élément externe pour y arriver.

Or, c’est magique, Python a parfaitement compris cela et nous propose une solution quasiment optimale pour cela ; on parcourt la liste mais sans dire d’autres éléments en utilisant la structure suivante :

for elem in groupe_d_elem:
  	#tralalilala

L’écriture est très simple, et même enfantine, parce que littéralement on dit « Pour chaque élément dans le groupe d’éléments »

A savoir que la variable plus haut nommée elem sera automatiquement créée et mise à jour par Python pour chaque valeur ; rien à faire 😉 !

Donc revenons à notre exemple, cela va donc faire comme suit :

for matiere in matieres_preferees:
  	print("La matiere est :", matiere)

Ce qui rend donc après exécution :

La matiere est : NSI
La matiere est : Maths Expertes
La matiere est : Maths
La matiere est : Physique-Chimie
La matiere est : Philo
La matiere est : Ma LV2
La matiere est : Enseignement Scientifique
La matiere est : SES

On conviendra que c’est bien pratique a écrire et ce de façon simple mais, problème, on a perdu la numérotation des matières pour savoir l’ordre.

C’est là qu’arrive une fonction encore une fois bien pratique de Python ; enumerate.

3. Enumerate, notre sauveur

On reprend donc exactement ce qu’on a écrit tout à l’heure avec le parcours simple sans le range en rajoutant quelques petits détails :

for rang_matiere, matiere in enumerate(matieres_preferees, start=1):
  	print("La matiere est :", matiere, "au rang de preference :", rang_matiere)

Et là, magie, on retrouve notre rang tant manqué !!

La matiere est : NSI au rang de preference : 1
La matiere est : Maths Expertes au rang de preference : 2
La matiere est : Maths au rang de preference : 3
La matiere est : Physique-Chimie au rang de preference : 4
La matiere est : Philo au rang de preference : 5
La matiere est : Ma LV2 au rang de preference : 6
La matiere est : Enseignement Scientifique au rang de preference : 7
La matiere est : SES au rang de preference : 8

En fait ici Python va créer 2 variables ; la variable matière vu comme tout à l’heure et une variable rang_matière, qu’il incrémente à chaque fois de 1 au fur et à mesure du passage à la matière suivante. On lui indique même un bonne valeur de départ pour être certain qu’il parte du bon nombre comme vu tout à l’heure.

Donc si on résume, pour faire ce que l’on a fait c’est-à-dire afficher le rang et la matière deux options s’offrent à nous ; la méthode « Pattes d’Ours » grossière, brutale et peu élégante avec for et la fonction range, ou bien celle avec enumerate, ou la valeur donnée et l’objet sont tous deux présents dans des variables et donc plus facilement utilisables (pour des vérification par exemple avec des tests conditionnels).

En parlant de tests conditionnels d’ailleurs, on peut sortir de la boucle for au bout d’un certain moment si on veut en utilisant break.

Prenons ici notre même exemple et imaginons qu’on veut juste afficher le top 3 des matières, cela fait donc :

for rang_matiere, matiere in enumerate(matieres_preferees, start=1):
    if rang_matiere > 3:
        break
    else:
        print("La matière", matiere, "est au rang de preference :", rang_matiere)

Cela rend donc après exécution :

La matière NSI est au rang de preference : 1
La matière Maths Expertes est au rang de preference : 2
La matière Maths est au rang de preference : 3

Magique non ?

Bien sur, l’inverse est aussi possible avec continue, c’est-à-dire que même si quelque chose est vérifié, on ne sort pas de la boucle, on continue encore.

Passons maintenant à un autre côté magique de Python, une petite astuce sur les chaînes de caractères ; en effet pour lui, ces dernières sont comme des listes, on peut donc les parcourir de la même manière.

Ainsi la phrase :

phrase = "D'apres une etude de l'INSEE la totalite des gens ayant fait NSI en Premiere et en Terminale vivraient heureux et plus longtemps que les autres"

Donnerait ceci en faisant un petit booster pour n’afficher que les mots :

for id_lettre, lettre in enumerate(phrase):
    if lettre == " ":
        pass
    else:
        print(lettre, end="")
        if id_lettre == len(phrase) - 1 or phrase[id_lettre + 1] == " ":
          print("\n")

Cela donnerait donc :

D'apres

une

etude

de

l'INSEE

la

totalite

des

gens

ayant

fait

NSI

en

Premiere

et

en

Terminale

vivraient

heureux

et

plus

longtemps

que

les

autres

On remarquera ici encore quelques petites astuces sur les boucles for. Il y a tout d’abord le pass, qui permet de sauter une répétition si jamais il se passe quelque chose (ici s’il y a un espace), mais aussi les vérifications d’id de la phrase pour savoir si c’est la fin d’un mot ou non.

4. Un petit plus, l’usage de yield associé à la boucle for

yield en Python est un petit peu particulier. Pour le définir, on pourrait le qualifier comme un return, mais qui s’incrémenterait de 1 tout seul à chaque appel. Comme pour return, il ne peut donc être utilisé que dans une fonction.

Voyons un exemple pour mieux l’appréhender :

def nombre_ex():
    for i in range(40,43):
        print("Nouvelle valeur !")
        yield i

On associe donc à yield, la valeur de i comprise entre 40 et 42.

Si on appelle juste la fonction telle qu’elle dans un terminal Python, on obtient un retour étrange :

>>> nombre_ex()
<generator object nombre_ex at 0x100e2a490>

Etrange, la fonction n’est pas perçue comme une fonction, mais comme un générateur, et elle n’est pas exécutée ! C’est tout simplement parce que yield à la place de return joue la place d’un générateur, vu qu’il s’incrémente d’une valeur.

On peut donc utiliser la boucle for sur ce générateur :

temp_iter = nombre_ex()
for elem in temp_iter:
  print(elem)

Ce qui nous fait donc :

Nouvelle valeur !
40
Nouvelle valeur !
41
Nouvelle valeur !
42

C – L’instanciation d’un itération

1. L’itération d’un objet

Pour les adeptes de la POO (Programmation Orientée Objet), n’oublions pas qu’il existe une méthode, pour pouvoir rajouter à votre classe la possibilité d’être itérative selon votre volonté !

Il s’agit de la méthode __iter__ ; comme indiqué dans son nom, elle permet à votre classe de devenir … itérative !

Mais comment concrètement cela marche ?

Voyons déjà sa structure de base et son explication à l’aide d’un exemple neutre.

class maClasse:
    def __init__(self):
        #J'initie ma classe
    #[...]
    def __iter__(self): #autres arguments si voulu
        #Je retourne mon objet itératif
    def __next__(self):
        if condition is verifie:
      		#Je passe à l'élément suivant dans mon itération en suivant certaines conditions
        else:
            raise StopIteration #on stoppe l'itération si la condition n'est pas vérifiée
        
temp = maClasse("""Tartampion""")
for elem in temp:
    #Réaliser une action

Comme vous le voyez, comme pour les méthodes __init__, ou bien __str__ (qui permet d’utiliser la fonction print()), les méthode __iter__ et __next__ sont implicites. Sachez que l’on reconnait ces méthodes dites implicites mais qui sont bien utiles par la présence de doubles underscores (_) avant et après le terme de la méthode.

Ici __iter__ et __next__ sont au cœur même du concept d’itération pour Python. Et c’est ici qu’il faut comprendre quelque chose d’important dans le fonctionnement de for (que je n’exprime que maintenant supposant que si vous êtes ici, c’est que vous maîtrisez mieux tous les concepts de programmation de base).

En effet, pour fonctionner, la boucle for appelle à chaque fois la fonction iter(), sur l’objet sur lequel on est. En ajoutant une méthode __iter__ à notre objet, on le rend donc « itératif » et on fait donc fonctionner la fonction iter(), et ainsi la boucle for. Mais pour l’instant cette boucle va toujours afficher le même élément, car on n’a pas expliqué comment aller à l’élément suivant ; c’est ce qui se passe avec la méthode __next__, on explique à Python comment dans une boucle il pourra aller à l’élément suivant !

Donc voilà, on a pu créer un comportement itératif pour notre classe !

Mais attention, pour pouvoir le faire, il faut bien comprendre et assimiler le fonctionnement de cette dernière, en connaissant bien les conditions de passage d’un élément à l’autre ; en prenant en quelque sorte du recul sur notre objet, pour essayer de se le représenter virtuellement, au risque d’avoir quelques surprises !

2. Des objets itératifs dans des packages

Bien évidemment, vous vous en doutez, s’il est possible de réaliser l’itération dans ses propres classes, de grands packages de classes en sont dotés, comme par exemple le package os.

Ce package présente, comme son nom l’indique, tout ce qui est en lien avec l’OS (ou système d’exploitation en français). Or dans un ordinateur, il y a quelque chose qu’on peut avoir besoin dans un programme Python, qui est présent en nombre et souvent très mal rangé. Et oui, il s’agit des documents présents dans des « directories » (ou dossiers), que la boucle for permet donc aisément de parcourir, comme suit avec ce petit exemple très simple :

import os

for filename in os.listdir("un_dossier/mon_directory/"):
    print(filename)
    if filename.endswith(".py"):
        print("Ohhh un fichier Python !!")

Comme vous le voyez, on a itéré sur les fichiers de notre directory à l’aide de cette classe (qui fonctionne moyennement sur Linux, attention), et cela est faisable sur toute classe sur laquelle le développeur en a laissé la possibilité.

Conclusion

Et voilà, vous êtes enfin arrivés au terme de ce tuto plutôt didactif, votre calvaire et enfin terminé !

Dans tout les cas j’espère que celui-ci vous aura été aussi utile à vous qu’à moi, me permettant de découvrir des talents cachés de la boucle for que je ne vais cesser d’utiliser.

La programmation et l’algorithmique prennent aujourd’hui une place toujours plus prépondérante dans nos vies, et cela ne va cesser de s’accroître. Maîtriser ces concepts de base autour de la programmation peut vous permettre de mieux appréhender ce nouveau domaine en pleine croissance, et ainsi de vous rendre compte qu’il est beaucoup plus éloigné des geeks solitaires à lunettes que vous pensiez, mais plutôt le fruit de logique, d’expériences, d’idées, et de savoir ; se rapprochant en fait d’une logique algorithmique ; mathématique.

Maintenant la boucle for n’aura (presque) plus de secrets pour vous en Python !

Tutoriels

Utiliser Tkinter pour donner des interfaces à vos scripts…

Dans ce tutoriel, vous allez apprendre pas à pas les bases de tkinter en Python : créer une fenêtre, y ajouter du texte, des boutons, et autres, mais aussi l’organiser à l’aide de boites et de grilles.

Qu’est ce que Tkinter ?

Tkinter (ou ToolKit Interface) est une library Python permettant de donner une interface graphique à vos scripts Python et ainsi les rendre plus agréables à utiliser, qu’ils s’agisse de simulateurs, de programmes de calcul ou de mini jeux textuels par exemple.

Il est important de noter que Tkinter n’est pas disponible sur la Numworks ou autre calculatrice utilisant Python, mais est par contre utilisable sous Windows, Linux macOS et même sur Android en utilisant Pydroid

Installer Tkinter

Tkinter est inclus avec Python 3 sous Windows, Linux ou macOS, vous n’aurez donc normalement pas à vous soucier de devoir l’installer en plus.

Au début de votre script, vous aurez simplement à taper les lignes suivantes afin d’importer la bibliothèque tkinter :

from tkinter import *
import tkinter as tk

Maintenant que l’on a spécifié que l’on allait utiliser cette bibliothèque, on peut commencer à mettre en place notre fenêtre.

Création de la fenêtre

C’est dans cette fenêtre que tous les éléments vont apparaître.

Pour commencer, nous allons créer une fenêtre. Pour cela une ligne suffit:

fenetre = Tk()

Lorsque l’on exécute le script rien ne se passe: il faut spécifier au script que l’on veut faire apparaître la fenêtre. Pour cela, entrer la ligne suivante à la fin de votre script.

fenetre.mainloop()

Il est important que cette ligne reste la dernière de votre programme, car tout ce qui sera écrit après cette ligne n’apparaîtra pas dans la fenêtre

On obtient alors normalement le résultat suivant :

Maintenant que l’on a créé cette fenêtre, on peut la personnaliser: lui donner un nom, des dimensions, une couleur de fond etc…

fenetre.title("Ma permière fenêtre Tkinter!")
fenetre.geometry("1080x720")
fenetre.config(bg="#d1fdff")

Les dimensions seront écrites sous la forme "largeurxhauteur"

La couleur peut être spécifiée de deux manières différentes:

  • Avec le nom de la couleur en anglais (ex. bg="grey")
  • En utilisant sa notation en héxadécimal, comme dans l’exemple donné ci-dessus. Il est possible de trouver des couleurs en héxadécimal à l’aide de cet outil

On obtient alors ceci :

Il est également possible de personnaliser l’icône de la fenêtre.

Pour cela, il vous faudra une image au format .ico . On peut convertir des images au format .png en .ico à l’aide d’un convertisseur en ligne

Une fois que vous avez votre icône, placez la dans le même dossier que votre script, et entrez la ligne suivante (en remplaçant "logo.ico" par "lenomdevotreicone.ico")

fenetre.iconbitmap("logo.ico")

Il est aussi possible de définir la taille minimale de la fenêtre:

fenetre.minsize(500,500)

On a donc appris à personnaliser notre fenêtre tkinter… mais elle reste tout de même bien vide. Nous allons donc voir comment ajouter des éléments à cette fenêtre.

Ajouter du texte

Pour ajouter du texte sur notre interface, on va utiliser une instance nommée Label

titre = Label(fenetre, text="Voici un exemple de Label en police consolas et en taille 20", font=("Consolas",20), bg="#d1fdff", fg="black")
soustitre = Label(fenetre, text="Voici un exemple de Label en police Helvetica et en taille 15", font=("Helvetica",15), bg="#d1fdff", fg="black")

On remarque que l’on peut personnaliser différents aspects du Label:

  • La boîte dans laquelle il se trouve (cette notion sera abordée un peu plus loin)
  • Le texte qu’il renferme ( text )
  • Sa police et taille d’écriture ( font )
  • Son arrière plan ( bg pour background )
  • Sa couleur de police ( fg pour foreground )

Si l’on exécute le script, on s’aperçoit que le texte ne s’affiche pas: là aussi, il faut spécifier qu’il faut l’afficher à l’aide de l’instruction .pack(). Il sera nécessaire d’appeler tout nouvel élément que l’on créé de cette manière.

titre.pack()
soustitre.pack()

Les différents éléments que vous allez incorporer à votre fenêtre apparaîtront dans l’ordre dans lequel vous allez les appeler à l’aide de .pack() , ici, titre sera donc au dessus de soustitre.

Il est possible de donner une marge à notre texte en utilisant pady et padx. Par exemple, l’instruction suivante donne une marge de 45 des deux côtés en hauteur

titre.pack(pady=45)

On peut aussi spécifier au texte de prendre toute la place possible en utilisant expand comme ceci:

titre.pack(expand=YES)

Ajouter des boutons

Maintenant que l’on sait faire apparaître du texte sur notre fenêtre, on va s’intéresser aux boutons. Leur mise en place est très similaire à celle des Labels. Cependant, on peut leur ajouter d’autres arguments

  • command qui spécifiera la fonction à éxécuter en cas de clic sur le bouton
  • width et height qui permettent de modifier respectivement sa largeur et sa longueur. Si ils ne sont pas spécifiés, la taille du bouton sera celle nécessaire pour que son texte s’affiche
nb_clicks=0
def compteur():
    global nb_clicks
    nb_clicks += 1
    print(nb_clicks)

bouton1 = Button(fenetre, text="Je suis un bouton", font=("Consolas",15), bg="white", fg="black", command=compteur, width=10, height=3)

Ici, on a donc créé un Bouton d’une largeur de 10 et d’un hauteur de 3 qui incrémente un compteur à chaque clic et qui donne la valeur du compteur dans la console

Mais ce que l’on chercherait à faire, ce serait de faire apparaître le texte directement dans la fenêtre.

On créé alors un nouveau Label qui va afficher la valeur de nb_clicks.

nb_clicks=0

textecompteur = Label(fenetre, text="nombre de clicks : %s" %(nb_clicks), font=("Helvetica",15), bg="#d1fdff", fg="black")
#ici, dans la chaîne de caractères, le "%s" sera remplacé par la valeur de la variable placée dans le tuple situé après le % soit nb_clicks

def compteur():
    global nb_clicks
    nb_clicks += 1
    print(nb_clicks)

bouton1 = Button(fenetre, text="Je suis un bouton", font=("Consolas",15), bg="white", fg="black", command=compteur, width=50, height=3)

On se rend alors compte que même si la valeur est incrémentée, le label ne s’actualise pas dans la fenêtre. On va alors ajouter une ligne dans la fonction compteur qui va mettre à jour ce Label à chaque clic du bouton à l’aide de l’instruction .config()

def compteur():
    global nb_clicks
    nb_clicks += 1
    textecompteur.config(text="nombre de clicks : %s" %(nb_clicks))

Voici ce que ça donne:

Cependant, si la fonction à exécuter nécessite des arguments, les choses vont se passer un peu différemment.

Dans cet exemple, on va créer deux boutons, qui vont exécuter la même fonction en cas de clic, mais avec des arguments différents.

Il est nécessaire pour cela d’importer la fonction partial de la library functools.

from functools import partial

On créé d’abord notre fonction. Ici, elle prend en argument une couleur et applique cette couleur au Label titre en cas d’appel de la fonction:

def changer_couleur_titre(couleur):
    titre.config(fg=couleur)

Ensuite, on créé les boutons avec pour commande partial( nomdelafonction, argument )

bouton2 = Button(fenetre, text="titre rouge", font=("Consolas",15), bg="white", fg="black", command=partial(changer_couleur_titre,"red"))
bouton3 = Button(fenetre, text="titre bleu", font=("Consolas",15), bg="white", fg="black", command=partial(changer_couleur_titre,"blue"))

Lorsque l’on va cliquer sur bouton2 le titre va devenir rouge, et sur le bouton3 il deviendra bleu

Ajouter un champ pour entrer du texte

Pour certains programmes, et en particulier les simulateurs, il est nécessaire d’entrer des données manuellement, et c’est là que les Entry entrent en jeu.

Là aussi, le processus est assez similaire aux Labels, la seule différence est que text est remplacé par textvariable qui sera donc la variable que l’on va modifier lorsque l’on écrira dans ce champ.

A l’instar des boutons, il est aussi possible de modifier leur taille à l’aide de width et height

Ici, la création de la variable est un peu différente car il doit s’agir d’une variable tkinter. On procède alors de la manière suivante:

#création de la variable
variable = tk.StringVar()
variable.set("Vous pouvez me modifier")

#création du champ
entree = Entry(fenetre, textvariable=variable, font=("Helvetica",15), bg="#d1fdff", fg="black", width=30)

Il faudra utiliser la même méthode à chaque fois que l’on voudra créer des variables pour des chaînes de caractères que l’on voudra afficher dans une interface tkinter

Il est possible aussi possible d’appeler cette variable (en utilisant .get() car il s’agit d’une variable tkinter) pour modifier du texte par exemple:

def changer_titre():
    titre.config(text=variable.get())

bouton4 = Button(fenetre, text="Cliquez pour actualiser", font=("Helvetica",15), bg="#d1fdff", fg="black", command = changer_titre)

Ici, à chaque clic du bouton, le texte du titre sera actualisé pour devenir celui du champ:

Maintenant que l’on sait incorporer différents éléments à notre fenêtre, on va s’intéresser à comment les organiser

Utiliser les boites

Les boites, ou Frames, sont très utiles lorsque l’on utilise tkinter puisqu’elles permettent de regrouper des éléments entre eux et ainsi créer différents menus entre lesquels on peut naviguer.

Il est nécessaire de les définir avant les éléments qui vont en faire partie, c’est pourquoi il est conseillé de les définir juste en dessous de la configuration de la fenêtre.

On créé une boite de la manière suivante, en spécifiant la boite dans laquelle elle est placée, et la couleur de son arrière plan

boitelabels = Frame(fenetre, bg="#d1fdff")

Ici, on a spécifié que la boite apparaîtrait dans la fenêtre principale, mais il est aussi possible de faire apparaître des boites dans des boites.

Comme tout autre élément, si l’on veut qu’une boite apparaisse, il ne faudra pas oublier de l’appeler avec un .pack(). Si un élément est appelé ainsi mais pas sa boîte, il n’apparaîtra pas, et si jamais il n’est pas appelé et que sa boite si, il n’apparaîtra pas non plus

Pour qu’un élément apparaisse dans une boîte, il suffit de remplacer le fenetre que l’on utilisait jusqu’à présent par le nom de la boite:

titre = Label(boitelabels, text="Voici un exemple de Label en police consolas et en taille 20", font=("Consolas",20), bg="#d1fdff", fg="black")
soustitre = Label(boitelabels, text="Voici un exemple de Label en police Helvetica et en taille 15", font=("Helvetica",15), bg="#d1fdff", fg="black")

Nous allons maintenant essayer de créer un menu nous permettant de naviguer entre tout ce que l’on a créé jusqu’à présent.

On créé alors différentes boites pour nos différents éléments. Dans cet exemple

  • boitelabels contiendra les éléments servi à la démonstration de ce que sont les Labels
  • boitecompteur contiendra le compteur créé un peu plus tôt
  • boitecouleurs contiendra les boutons permettant de modifier la couleur du titre
  • boiteentree contiendra les éléments ayant servi à la démonstration de l’utilité des Entries
  • boitemenu contiendra les boutons permettant d’afficher les différentes boites. Elle va être la seule à apparaître lorsque l’on lancera le script et permettra d’afficher les autres
boitecompteur = Frame(fenetre, bg="#d1fdff")
boitecouleurs = Frame(fenetre, bg="#d1fdff")
boiteentree = Frame(fenetre, bg="#d1fdff")
boitemenu = Frame(fenetre, bg="#d1fdff")

En on n’oublie pas de modifier la boite de chaque élément pour qu’il n’apparaisse plus dans la fenêtre principale mais dans la boite dans laquelle on veut qu’il apparaisse

#exemple: ici on place le label textecompteur dans la boitecompteur:
textecompteur = Label(boitecompteur, text="nombre de clicks : %s" %(nb_clicks), font=("Helvetica",15), bg="#d1fdff", fg="black")

On va aussi créer des boutons dans boitemenu qui, lorqu’ils seront cliqués appelleront une autre boite avec un .pack() et feront disparaître le menu à l’aide de .pack_forget(). On va aussi mettre en place des boutons de retour au menu dans chacune des autres boites qui vont, eux, faire disparaître leur boite et apparaître le menu en cas de clic;

def afficher_labels():
    boitelabels.pack(expand=YES)
    boitemenu.pack_forget()

def afficher_compteur():
    boitecompteur.pack(expand=YES)
    boitemenu.pack_forget()
    
def afficher_couleurs():
    boitecouleurs.pack(expand=YES)
    boitemenu.pack_forget()
    
def afficher_entree():
    boiteentree.pack(expand=YES)
    boitemenu.pack_forget()
    
def retour_menu():
    boitemenu.pack(expand=YES)
    boitelabels.pack_forget()
    boitecompteur.pack_forget()
    boitecouleurs.pack_forget()
    boiteentree.pack_forget()
    
b_labels = Button(boitemenu, text="Afficher les Labels", font=("Consolas",15), bg="white", fg="black", command=afficher_labels, width=75)
b_compteur = Button(boitemenu, text="Afficher le compteur", font=("Consolas",15), bg="white", fg="black", command=afficher_compteur, width=75)    
b_couleurs = Button(boitemenu, text="Afficher les boutons pour changer la couleur du titre", font=("Consolas",15), bg="white", fg="black", command=afficher_couleurs, width=75)    
b_entree = Button(boitemenu, text="Afficher le champ pour changer le titre", font=("Consolas",15), bg="white", fg="black", command=afficher_entree, width=75)
b_retour1 = Button(boitelabels, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
b_retour2 = Button(boitecompteur, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
b_retour3 = Button(boitecouleurs, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
b_retour4 = Button(boiteentree, text="retourner au menu", font=("Consolas",15), bg="white", fg="black", command=retour_menu, width=35)
Le Menu

Tout ça est bien beau, mais pour l’instant on ne sait qu’afficher différents éléments les uns au dessous des autres, mais pas les uns à côté des autres. C’est là que les grilles vont intervenir

Utiliser les grilles

Pour faire une grille, on va remplacer les .pack() que l’on utilisait par des .grid(). Il faut savoir que si l’on utilise une grille pour un élément, tous les éléments présents dans la même boite devront eux aussi utiliser cette même grille.

Comme on voudrait seulement placer deux des boutons côte à côte, on va les placer dans une sous boite de la boite du menu:

#création de la sous boite
sousboitemenu = Frame(boitemenu, bg="#d1fdff")

Et on modifie la boite des deux boutons concernés, ainsi que leurs dimensions pour qu’ils soient alignés avec les autres. (On ajoute aussi un \n dans le texte du second bouton pour qu’il s’affiche sur deux lignes)

b_compteur = Button(sousboitemenu, text="Afficher le compteur", font=("Consolas",15), bg="white", fg="black", command=afficher_compteur, width=36, height=2)    
b_couleurs = Button(sousboitemenu, text="Afficher les boutons \npour changer la couleur du titre", font=("Consolas",15), bg="white", fg="black", command=afficher_couleurs, width=36, height=2) 

Il est temps maintenant de placer les boutons en grille à l’aide de .grid().

Cette méthode accepte plusieurs arguments:

  • row qui définit la ligne dans laquelle l’élément va être placé
  • column qui définit sa colonne
  • sticky qui définit de quel côté de la fenêtre l’élément doit se rapprocher ( W, E, S ou N pour les quatre points cardinaux)
  • padx et pady (optionnels) qui définissent les marges en x et en y
b_compteur.grid(row=1, column=0, sticky=W, pady=5, padx=5)
b_couleurs.grid(row=1, column=1, sticky=W, pady=5, padx=5)

On obtient alors le résultat suivant:

Conclusion

Voilà, vous possédez maintenant les bases nécessaires pour pouvoir créer vos propres interfaces graphiques pour vos scripts Python !

Si jamais certains éléments ne sont pas assez clairs, vous pouvez toujours télécharger le script créé pour ce tutoriel afin de le tester ou de lire son code.

NumApps

Le jeu 2048, sur ta NumWorks !

2048 est un jeu vidéo de type puzzle conçu en mars 2014 par le développeur indépendant italien Gabriele Cirulli et publié en ligne sous licence libre. La version présenté ici est le portage du jeu, réalisé par Eric Schrafstetter et légèrement modifiée par nos soins pour un rendu sublimé sur la NumWorks.

Portage du jeu, en python, sur NumWorks

Eric Schrafstetter est le premier a voir porté ce jeu sur la calculatrice NumWorks, ce jeu nécessite une version Epsilon 13 ou supérieur installé sur la calculatrice. En effet, la version 13 de l’OS de cette calculatrice à permis le développement de jeux car il était désormais possible de récupérer les touches du clavier saisies.

Publié le premier avril 2020, la première version de ce programme a été suivie par une version améliorée proposée le 30 juillet 2021. Nous avons découvert ce jeu fin décembre 2021, et avons décidé de l’améliorer afin de proposer une expérience joueur améliorée.

La version 2 de Eric Schrafstetter

La version modifié par nos soins

Modifications réalisées par nos soins

  1. La grille a été centrée et la bordure épaisse réduite, on conserve l’effet « ombre » sans écraser la grille.
  2. Le score a été déplacé, il est moins visible pendant la partie, l’interface présente une symétrie visuelle et la modification du score n’attire pas l’œil.
  3. Les micros-lags ont été supprimés. Dans la version d’origine, la grille était entièrement effacée puis reconstruite ce qui générait un affichage haché avec un effet de rafraichissement pas agréable. Le jeu est du coup plus fluide, plus agréable à utiliser.
  4. Une détection de la fin de partie a été implémentée, si la grille est remplie et que 4 touches sont pressées sans que rien ne se passe, on signale au joueur que la partie est terminée.

Une vidéo de présentation du codage

Si le codage de ce jeu vous intéresse, la vidéo de son auteur est incontournable. 😍

Commandes

Le jeu se joue simplement avec les touches fléchées de la calculatrice.

Télécharger

Des liens vers les version 1 et 2 du jeu, celle de Eric Schrafstetter sont au début de cet article. Nous vous proposons de tester la version modifiée par nos soins :

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!).