Aller au contenu

Fiche d'exercices : Rappels de Première

Introduction

Exercice 1 : Calculatrice personnelle

Créez un programme qui :

  • Demande le nom de l'utilisateur
  • Demande deux nombres
  • Effectue les quatre opérations de base
  • Affiche les résultats avec un message personnalisé

Exemple : "Bonjour Alice ! 5 + 3 = 8"

Facile

Exercice 2 : Convertisseur d'unités

Écrivez un programme qui convertit :

  • Des mètres en pieds et pouces
  • Des kilogrammes en livres
  • Des degrés Celsius en Fahrenheit

Le programme doit demander le type de conversion et la valeur à convertir.

Moyen

Exercice 3 : Validateur de données

Créez un programme qui valide :

  • Un âge (entre 0 et 120)
  • Un email (contient @ et .)
  • Un mot de passe (au moins 8 caractères)
  • Un numéro de téléphone (10 chiffres)

Utilisez des variables booléennes pour stocker les résultats de validation.

Introduction

Exercice 4 : Système de notes

Créez un programme qui :

  • Demande une note sur 20
  • Affiche la mention correspondante
  • Indique si l'élève est admis (≥ 10)
  • Propose de saisir une nouvelle note

Mentions : Très bien (≥16), Bien (≥14), Assez bien (≥12), Passable (≥10)

Facile

Exercice 5 : Tables de multiplication

Écrivez un programme qui :

  • Demande un nombre à l'utilisateur
  • Affiche sa table de multiplication (1 à 10)
  • Utilise une boucle for
  • Formate joliment l'affichage

Bonus : Permettre de choisir la limite (au lieu de 10)

Moyen

Exercice 6 : Jeu de devinette amélioré

Créez un jeu qui :

  • Génère un nombre aléatoire entre 1 et 100
  • Donne des indices (trop grand/petit)
  • Compte le nombre d'essais
  • Propose différents niveaux de difficulté
  • Permet de rejouer
Difficile

Exercice 7 : Motifs géométriques

Créez des programmes qui dessinent :

  • Un triangle d'étoiles de taille variable
  • Un carré creux avec des bordures
  • Un losange centré
  • Une pyramide inversée

La taille doit être paramétrable par l'utilisateur.

Introduction

Exercice 8 : Bibliothèque mathématique

Créez des fonctions pour :

  • aire_cercle(rayon) : calcule l'aire d'un cercle
  • perimetre_rectangle(longueur, largeur)
  • moyenne(a, b, c) : moyenne de trois nombres
  • est_pair(nombre) : retourne True si pair

Testez chaque fonction avec plusieurs exemples.

Facile

Exercice 9 : Générateur de mots de passe

Créez une fonction qui génère un mot de passe :

  • Longueur paramétrable
  • Inclut lettres, chiffres et symboles
  • Option pour exclure les caractères ambigus
  • Fonction de validation de la force
Moyen

Exercice 10 : Calculatrice scientifique

Développez un programme avec des fonctions pour :

  • Opérations de base (+, -, *, /)
  • Puissance et racine carrée
  • Fonctions trigonométriques
  • Menu interactif
  • Historique des calculs
Introduction

Exercice 11 : Gestionnaire de notes

Créez un programme qui :

  • Stocke les notes d'un élève dans une liste
  • Calcule la moyenne
  • Trouve la meilleure et la pire note
  • Compte les notes au-dessus de la moyenne
  • Permet d'ajouter/supprimer des notes
Facile

Exercice 12 : Tri et recherche

Implémentez :

  • Tri par sélection (sans utiliser sort())
  • Recherche linéaire
  • Suppression des doublons
  • Fusion de deux listes triées

Testez avec des listes de nombres et de chaînes.

Moyen

Exercice 13 : Jeu du pendu

Développez un jeu du pendu avec :

  • Liste de mots prédéfinis
  • Affichage du mot avec des tirets
  • Gestion des lettres déjà proposées
  • Compteur d'erreurs avec limite
  • Interface utilisateur claire
Difficile

Exercice 14 : Matrices et opérations

Travaillez avec des matrices (listes de listes) :

  • Addition et soustraction de matrices
  • Multiplication par un scalaire
  • Transposition
  • Vérification de symétrie
  • Calcul du déterminant (2x2)
Projet

Projet 1 : Carnet d'adresses

Développez un carnet d'adresses complet :

  • Ajouter/modifier/supprimer des contacts
  • Recherche par nom ou téléphone
  • Tri alphabétique
  • Sauvegarde dans un fichier texte
  • Interface menu avec options

Compétences : Listes, fonctions, fichiers, validation

Projet

Projet 2 : Jeu de bataille navale

Créez une bataille navale simplifiée :

  • Grille 10x10 avec coordonnées
  • Placement aléatoire des navires
  • Système de tir avec feedback
  • Compteur de coups et de navires coulés
  • Affichage de la grille mise à jour

Compétences : Matrices, boucles, conditions, fonctions

Projet

Projet 3 : Gestionnaire de bibliothèque

Système de gestion pour une petite bibliothèque :

  • Base de données de livres (titre, auteur, ISBN)
  • Système d'emprunt et de retour
  • Recherche multicritères
  • Statistiques (livres les plus empruntés)
  • Gestion des retards

Compétences : Structures de données, algorithmes, validation

Solution : Calculatrice personnelle

nom = input("Quel est votre nom ? ")
nombre1 = float(input("Premier nombre : "))
nombre2 = float(input("Deuxième nombre : "))

addition = nombre1 + nombre2
soustraction = nombre1 - nombre2
multiplication = nombre1 * nombre2
division = nombre1 / nombre2 if nombre2 != 0 else "Division par zéro!"

print(f"Bonjour {nom} !")
print(f"{nombre1} + {nombre2} = {addition}")
print(f"{nombre1} - {nombre2} = {soustraction}")
print(f"{nombre1} × {nombre2} = {multiplication}")
print(f"{nombre1} ÷ {nombre2} = {division}")

Solution : Système de notes

while True:
    note = float(input("Entrez une note sur 20 : "))

    if note >= 16:
        mention = "Très bien"
    elif note >= 14:
        mention = "Bien"
    elif note >= 12:
        mention = "Assez bien"
    elif note >= 10:
        mention = "Passable"
    else:
        mention = "Insuffisant"

    if note >= 10:
        resultat = "Admis"
    else:
        resultat = "Refusé"

    print(f"Note : {note}/20")
    print(f"Mention : {mention}")
    print(f"Résultat : {resultat}")

    continuer = input("Nouvelle note ? (o/n) : ")
    if continuer.lower() != 'o':
        break

Solution : Bibliothèque mathématique

import math

def aire_cercle(rayon):
    return math.pi * rayon ** 2

def perimetre_rectangle(longueur, largeur):
    return 2 * (longueur + largeur)

def moyenne(a, b, c):
    return (a + b + c) / 3

def est_pair(nombre):
    return nombre % 2 == 0

# Tests
print(f"Aire cercle (r=5) : {aire_cercle(5):.2f}")
print(f"Périmètre rectangle (3x4) : {perimetre_rectangle(3, 4)}")
print(f"Moyenne (10,15,20) : {moyenne(10, 15, 20)}")
print(f"8 est pair : {est_pair(8)}")
print(f"7 est pair : {est_pair(7)}")

Solution : Gestionnaire de notes

notes = []

def ajouter_note():
    note = float(input("Nouvelle note : "))
    notes.append(note)
    print(f"Note {note} ajoutée.")

def supprimer_note():
    if notes:
        print(f"Notes actuelles : {notes}")
        index = int(input("Index à supprimer : "))
        if 0 <= index < len(notes):
            note_supprimee = notes.pop(index)
            print(f"Note {note_supprimee} supprimée.")
        else:
            print("Index invalide.")
    else:
        print("Aucune note à supprimer.")

def calculer_statistiques():
    if not notes:
        print("Aucune note enregistrée.")
        return

    moyenne = sum(notes) / len(notes)
    meilleure = max(notes)
    pire = min(notes)
    au_dessus_moyenne = len([n for n in notes if n > moyenne])

    print(f"Moyenne : {moyenne:.2f}")
    print(f"Meilleure note : {meilleure}")
    print(f"Pire note : {pire}")
    print(f"Notes au-dessus de la moyenne : {au_dessus_moyenne}")

# Menu principal
while True:
    print("\n1. Ajouter note")
    print("2. Supprimer note")
    print("3. Statistiques")
    print("4. Quitter")

    choix = input("Votre choix : ")

    if choix == '1':
        ajouter_note()
    elif choix == '2':
        supprimer_note()
    elif choix == '3':
        calculer_statistiques()
    elif choix == '4':
        break
    else:
        print("Choix invalide.")

Solution : Jeu du pendu

import random

def jeu_pendu():
    mots = ["python", "programmation", "ordinateur", "algorithme", 
            "fonction", "variable", "boucle", "condition"]

    mot_secret = random.choice(mots).upper()
    lettres_trouvees = set()
    lettres_essayees = set()
    erreurs = 0
    max_erreurs = 6

    print("=== JEU DU PENDU ===")
    print(f"Le mot contient {len(mot_secret)} lettres.")

    while erreurs < max_erreurs:
        # Affichage du mot avec les lettres trouvées
        mot_affiche = ""
        for lettre in mot_secret:
            if lettre in lettres_trouvees:
                mot_affiche += lettre + " "
            else:
                mot_affiche += "_ "

        print(f"\nMot : {mot_affiche}")
        print(f"Lettres essayées : {', '.join(sorted(lettres_essayees))}")
        print(f"Erreurs : {erreurs}/{max_erreurs}")

        # Vérifier si le mot est trouvé
        if set(mot_secret) <= lettres_trouvees:
            print(f"\n🎉 Félicitations ! Vous avez trouvé le mot : {mot_secret}")
            break

        # Demander une lettre
        lettre = input("Proposez une lettre : ").upper()

        if len(lettre) != 1 or not lettre.isalpha():
            print("Veuillez entrer une seule lettre.")
            continue

        if lettre in lettres_essayees:
            print("Vous avez déjà essayé cette lettre.")
            continue

        lettres_essayees.add(lettre)

        if lettre in mot_secret:
            lettres_trouvees.add(lettre)
            print(f"✓ Bonne lettre !")
        else:
            erreurs += 1
            print(f"✗ Lettre incorrecte.")

    if erreurs >= max_erreurs:
        print(f"\n💀 Perdu ! Le mot était : {mot_secret}")

    rejouer = input("\nVoulez-vous rejouer ? (o/n) : ")
    if rejouer.lower() == 'o':
        jeu_pendu()

# Lancer le jeu
jeu_pendu()

Solution : Matrices et opérations

def creer_matrice(lignes, colonnes, valeur=0):
    """Crée une matrice remplie d'une valeur"""
    return [[valeur for _ in range(colonnes)] for _ in range(lignes)]

def afficher_matrice(matrice):
    """Affiche une matrice de façon lisible"""
    for ligne in matrice:
        print(" ".join(f"{x:4}" for x in ligne))
    print()

def additionner_matrices(mat1, mat2):
    """Addition de deux matrices"""
    if len(mat1) != len(mat2) or len(mat1[0]) != len(mat2[0]):
        return None

    resultat = []
    for i in range(len(mat1)):
        ligne = []
        for j in range(len(mat1[0])):
            ligne.append(mat1[i][j] + mat2[i][j])
        resultat.append(ligne)
    return resultat

def multiplier_par_scalaire(matrice, scalaire):
    """Multiplication par un scalaire"""
    resultat = []
    for ligne in matrice:
        nouvelle_ligne = [element * scalaire for element in ligne]
        resultat.append(nouvelle_ligne)
    return resultat

def transposer(matrice):
    """Transposition d'une matrice"""
    lignes = len(matrice)
    colonnes = len(matrice[0])

    resultat = creer_matrice(colonnes, lignes)
    for i in range(lignes):
        for j in range(colonnes):
            resultat[j][i] = matrice[i][j]
    return resultat

def est_symetrique(matrice):
    """Vérifie si une matrice est symétrique"""
    if len(matrice) != len(matrice[0]):
        return False

    for i in range(len(matrice)):
        for j in range(len(matrice)):
            if matrice[i][j] != matrice[j][i]:
                return False
    return True

def determinant_2x2(matrice):
    """Calcule le déterminant d'une matrice 2x2"""
    if len(matrice) != 2 or len(matrice[0]) != 2:
        return None

    return matrice[0][0] * matrice[1][1] - matrice[0][1] * matrice[1][0]

# Tests
mat1 = [[1, 2], [3, 4]]
mat2 = [[5, 6], [7, 8]]

print("Matrice 1 :")
afficher_matrice(mat1)

print("Matrice 2 :")
afficher_matrice(mat2)

print("Addition :")
afficher_matrice(additionner_matrices(mat1, mat2))

print("Multiplication par 3 :")
afficher_matrice(multiplier_par_scalaire(mat1, 3))

print("Transposée de mat1 :")
afficher_matrice(transposer(mat1))

print(f"mat1 est symétrique : {est_symetrique(mat1)}")
print(f"Déterminant de mat1 : {determinant_2x2(mat1)}")

Solution : Carnet d'adresses

import json
import os

class CarnetAdresses:
    def __init__(self, fichier="contacts.json"):
        self.fichier = fichier
        self.contacts = self.charger_contacts()

    def charger_contacts(self):
        """Charge les contacts depuis le fichier"""
        if os.path.exists(self.fichier):
            try:
                with open(self.fichier, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except:
                return []
        return []

    def sauvegarder_contacts(self):
        """Sauvegarde les contacts dans le fichier"""
        with open(self.fichier, 'w', encoding='utf-8') as f:
            json.dump(self.contacts, f, indent=2, ensure_ascii=False)

    def ajouter_contact(self):
        """Ajoute un nouveau contact"""
        nom = input("Nom : ").strip()
        if not nom:
            print("Le nom est obligatoire.")
            return

        telephone = input("Téléphone : ").strip()
        email = input("Email : ").strip()
        adresse = input("Adresse : ").strip()

        contact = {
            "nom": nom,
            "telephone": telephone,
            "email": email,
            "adresse": adresse
        }

        self.contacts.append(contact)
        self.sauvegarder_contacts()
        print(f"Contact {nom} ajouté avec succès.")

    def afficher_contacts(self):
        """Affiche tous les contacts"""
        if not self.contacts:
            print("Aucun contact enregistré.")
            return

        contacts_tries = sorted(self.contacts, key=lambda x: x['nom'].lower())

        print("\n=== CARNET D'ADRESSES ===")
        for i, contact in enumerate(contacts_tries, 1):
            print(f"{i}. {contact['nom']}")
            if contact['telephone']:
                print(f"   📞 {contact['telephone']}")
            if contact['email']:
                print(f"   📧 {contact['email']}")
            if contact['adresse']:
                print(f"   🏠 {contact['adresse']}")
            print()

    def rechercher_contact(self):
        """Recherche un contact"""
        terme = input("Rechercher (nom ou téléphone) : ").strip().lower()
        if not terme:
            return

        resultats = []
        for contact in self.contacts:
            if (terme in contact['nom'].lower() or 
                terme in contact['telephone']):
                resultats.append(contact)

        if resultats:
            print(f"\n{len(resultats)} résultat(s) trouvé(s) :")
            for contact in resultats:
                print(f"- {contact['nom']} ({contact['telephone']})")
        else:
            print("Aucun contact trouvé.")

    def supprimer_contact(self):
        """Supprime un contact"""
        if not self.contacts:
            print("Aucun contact à supprimer.")
            return

        self.afficher_contacts()
        try:
            index = int(input("Numéro du contact à supprimer : ")) - 1
            if 0 <= index < len(self.contacts):
                contact_supprime = self.contacts.pop(index)
                self.sauvegarder_contacts()
                print(f"Contact {contact_supprime['nom']} supprimé.")
            else:
                print("Numéro invalide.")
        except ValueError:
            print("Veuillez entrer un numéro valide.")

    def menu_principal(self):
        """Menu principal de l'application"""
        while True:
            print("\n=== CARNET D'ADRESSES ===")
            print("1. Ajouter un contact")
            print("2. Afficher tous les contacts")
            print("3. Rechercher un contact")
            print("4. Supprimer un contact")
            print("5. Quitter")

            choix = input("Votre choix : ").strip()

            if choix == '1':
                self.ajouter_contact()
            elif choix == '2':
                self.afficher_contacts()
            elif choix == '3':
                self.rechercher_contact()
            elif choix == '4':
                self.supprimer_contact()
            elif choix == '5':
                print("Au revoir !")
                break
            else:
                print("Choix invalide.")

# Lancement de l'application
if __name__ == "__main__":
    carnet = CarnetAdresses()
    carnet.menu_principal()

Solution : Jeu de bataille navale

import random

class BatailleNavale:
    def __init__(self):
        self.taille = 10
        self.grille = [['~' for _ in range(self.taille)] for _ in range(self.taille)]
        self.navires = []
        self.coups_tires = 0
        self.navires_coules = 0
        self.total_navires = 5

    def placer_navires(self):
        """Place les navires aléatoirement sur la grille"""
        tailles_navires = [5, 4, 3, 3, 2]  # Porte-avions, croiseur, 2 destroyers, sous-marin

        for taille in tailles_navires:
            place = False
            tentatives = 0

            while not place and tentatives < 100:
                # Direction aléatoire (0=horizontal, 1=vertical)
                direction = random.randint(0, 1)

                if direction == 0:  # Horizontal
                    x = random.randint(0, self.taille - taille)
                    y = random.randint(0, self.taille - 1)
                    positions = [(x + i, y) for i in range(taille)]
                else:  # Vertical
                    x = random.randint(0, self.taille - 1)
                    y = random.randint(0, self.taille - taille)
                    positions = [(x, y + i) for i in range(taille)]

                # Vérifier si les positions sont libres
                if all(self.grille[pos[1]][pos[0]] == '~' for pos in positions):
                    # Placer le navire
                    for pos in positions:
                        self.grille[pos[1]][pos[0]] = 'N'

                    self.navires.append({
                        'positions': positions,
                        'touches': set(),
                        'coule': False
                    })
                    place = True

                tentatives += 1

    def afficher_grille(self, montrer_navires=False):
        """Affiche la grille de jeu"""
        print("\n   ", end="")
        for i in range(self.taille):
            print(f"{i:2}", end=" ")
        print()

        for i in range(self.taille):
            print(f"{i:2} ", end="")
            for j in range(self.taille):
                cellule = self.grille[i][j]

                if cellule == 'N' and not montrer_navires:
                    print(" ~", end=" ")
                elif cellule == 'X':
                    print(" X", end=" ")  # Touché
                elif cellule == 'O':
                    print(" O", end=" ")  # Raté
                elif cellule == 'N' and montrer_navires:
                    print(" N", end=" ")  # Navire (mode debug)
                else:
                    print(" ~", end=" ")  # Eau
            print()

    def tirer(self, x, y):
        """Effectue un tir aux coordonnées données"""
        if not (0 <= x < self.taille and 0 <= y < self.taille):
            return "Coordonnées invalides"

        if self.grille[y][x] in ['X', 'O']:
            return "Déjà tiré ici"

        self.coups_tires += 1

        if self.grille[y][x] == 'N':
            self.grille[y][x] = 'X'

            # Vérifier quel navire a été touché
            for navire in self.navires:
                if (x, y) in navire['positions'] and not navire['coule']:
                    navire['touches'].add((x, y))

                    # Vérifier si le navire est coulé
                    if len(navire['touches']) == len(navire['positions']):
                        navire['coule'] = True
                        self.navires_coules += 1
                        return "Coulé !"
                    else:
                        return "Touché !"
        else:
            self.grille[y][x] = 'O'
            return "Raté"

    def jeu_termine(self):
        """Vérifie si tous les navires sont coulés"""
        return self.navires_coules == self.total_navires

    def jouer(self):
        """Boucle principale du jeu"""
        print("=== BATAILLE NAVALE ===")
        print(f"Trouvez et coulez les {self.total_navires} navires !")
        print("Entrez les coordonnées sous la forme 'x y' (ex: 3 5)")

        self.placer_navires()

        while not self.jeu_termine():
            self.afficher_grille()
            print(f"\nCoups tirés : {self.coups_tires}")
            print(f"Navires coulés : {self.navires_coules}/{self.total_navires}")

            try:
                coordonnees = input("\nCoordonnées (x y) : ").split()
                if len(coordonnees) != 2:
                    print("Format invalide. Utilisez 'x y'")
                    continue

                x, y = int(coordonnees[0]), int(coordonnees[1])
                resultat = self.tirer(x, y)
                print(f"Résultat : {resultat}")

            except ValueError:
                print("Veuillez entrer des nombres valides.")
            except KeyboardInterrupt:
                print("\nPartie interrompue.")
                break

        if self.jeu_termine():
            self.afficher_grille(montrer_navires=True)
            print(f"\n🎉 Félicitations ! Vous avez coulé tous les navires en {self.coups_tires} coups !")

# Lancement du jeu
if __name__ == "__main__":
    jeu = BatailleNavale()
    jeu.jouer()

Solution : Gestionnaire de bibliothèque

from datetime import datetime, timedelta
import json
import os

class GestionnaireBibliotheque:
    def __init__(self, fichier_livres="livres.json", fichier_emprunts="emprunts.json"):
        self.fichier_livres = fichier_livres
        self.fichier_emprunts = fichier_emprunts
        self.livres = self.charger_donnees(fichier_livres)
        self.emprunts = self.charger_donnees(fichier_emprunts)
        self.duree_emprunt = 14  # jours

    def charger_donnees(self, fichier):
        """Charge les données depuis un fichier JSON"""
        if os.path.exists(fichier):
            try:
                with open(fichier, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except:
                return []
        return []

    def sauvegarder_donnees(self, donnees, fichier):
        """Sauvegarde les données dans un fichier JSON"""
        with open(fichier, 'w', encoding='utf-8') as f:
            json.dump(donnees, f, indent=2, ensure_ascii=False)

    def ajouter_livre(self):
        """Ajoute un nouveau livre"""
        titre = input("Titre : ").strip()
        auteur = input("Auteur : ").strip()
        isbn = input("ISBN : ").strip()

        if not all([titre, auteur, isbn]):
            print("Tous les champs sont obligatoires.")
            return

        # Vérifier si l'ISBN existe déjà
        if any(livre['isbn'] == isbn for livre in self.livres):
            print("Un livre avec cet ISBN existe déjà.")
            return

        livre = {
            'id': len(self.livres) + 1,
            'titre': titre,
            'auteur': auteur,
            'isbn': isbn,
            'disponible': True,
            'nb_emprunts': 0
        }

        self.livres.append(livre)
        self.sauvegarder_donnees(self.livres, self.fichier_livres)
        print(f"Livre '{titre}' ajouté avec succès.")

    def afficher_livres(self):
        """Affiche tous les livres"""
        if not self.livres:
            print("Aucun livre dans la bibliothèque.")
            return

        print("\n=== CATALOGUE DES LIVRES ===")
        for livre in self.livres:
            statut = "📗 Disponible" if livre['disponible'] else "📕 Emprunté"
            print(f"{livre['id']}. {livre['titre']}")
            print(f"   Auteur: {livre['auteur']}")
            print(f"   ISBN: {livre['isbn']}")
            print(f"   Statut: {statut}")
            print(f"   Emprunts: {livre['nb_emprunts']}")
            print()

    def rechercher_livres(self):
        """Recherche des livres par titre, auteur ou ISBN"""
        terme = input("Rechercher (titre, auteur ou ISBN) : ").strip().lower()
        if not terme:
            return

        resultats = []
        for livre in self.livres:
            if (terme in livre['titre'].lower() or 
                terme in livre['auteur'].lower() or 
                terme in livre['isbn'].lower()):
                resultats.append(livre)

        if resultats:
            print(f"\n{len(resultats)} résultat(s) trouvé(s) :")
            for livre in resultats:
                statut = "Disponible" if livre['disponible'] else "Emprunté"
                print(f"- {livre['titre']} par {livre['auteur']} ({statut})")
        else:
            print("Aucun livre trouvé.")

    def emprunter_livre(self):
        """Emprunte un livre"""
        self.afficher_livres()

        try:
            livre_id = int(input("ID du livre à emprunter : "))
            livre = next((l for l in self.livres if l['id'] == livre_id), None)

            if not livre:
                print("Livre introuvable.")
                return

            if not livre['disponible']:
                print("Ce livre est déjà emprunté.")
                return

            emprunteur = input("Nom de l'emprunteur : ").strip()
            if not emprunteur:
                print("Le nom de l'emprunteur est obligatoire.")
                return

            # Créer l'emprunt
            emprunt = {
                'id': len(self.emprunts) + 1,
                'livre_id': livre_id,
                'emprunteur': emprunteur,
                'date_emprunt': datetime.now().isoformat(),
                'date_retour_prevue': (datetime.now() + timedelta(days=self.duree_emprunt)).isoformat(),
                'date_retour_effective': None
            }

            # Mettre à jour le livre
            livre['disponible'] = False
            livre['nb_emprunts'] += 1

            # Sauvegarder
            self.emprunts.append(emprunt)
            self.sauvegarder_donnees(self.livres, self.fichier_livres)
            self.sauvegarder_donnees(self.emprunts, self.fichier_emprunts)

            print(f"Livre emprunté par {emprunteur}.")
            print(f"Date de retour prévue : {datetime.fromisoformat(emprunt['date_retour_prevue']).strftime('%d/%m/%Y')}")

        except ValueError:
            print("Veuillez entrer un ID valide.")

    def retourner_livre(self):
        """Retourne un livre emprunté"""
        # Afficher les emprunts en cours
        emprunts_actifs = [e for e in self.emprunts if e['date_retour_effective'] is None]

        if not emprunts_actifs:
            print("Aucun emprunt en cours.")
            return

        print("\n=== EMPRUNTS EN COURS ===")
        for emprunt in emprunts_actifs:
            livre = next((l for l in self.livres if l['id'] == emprunt['livre_id']), None)
            if livre:
                date_retour = datetime.fromisoformat(emprunt['date_retour_prevue'])
                retard = (datetime.now() - date_retour).days
                statut_retard = f" (RETARD: {retard} jours)" if retard > 0 else ""

                print(f"{emprunt['id']}. {livre['titre']}")
                print(f"   Emprunteur: {emprunt['emprunteur']}")
                print(f"   Retour prévu: {date_retour.strftime('%d/%m/%Y')}{statut_retard}")
                print()

        try:
            emprunt_id = int(input("ID de l'emprunt à retourner : "))
            emprunt = next((e for e in self.emprunts if e['id'] == emprunt_id), None)

            if not emprunt or emprunt['date_retour_effective']:
                print("Emprunt introuvable ou déjà retourné.")
                return

            # Mettre à jour l'emprunt
            emprunt['date_retour_effective'] = datetime.now().isoformat()

            # Mettre à jour le livre
            livre = next((l for l in self.livres if l['id'] == emprunt['livre_id']), None)
            if livre:
                livre['disponible'] = True

            # Sauvegarder
            self.sauvegarder_donnees(self.livres, self.fichier_livres)
            self.sauvegarder_donnees(self.emprunts, self.fichier_emprunts)

            print("Livre retourné avec succès.")

        except ValueError:
            print("Veuillez entrer un ID valide.")

    def afficher_statistiques(self):
        """Affiche les statistiques de la bibliothèque"""
        total_livres = len(self.livres)
        livres_disponibles = len([l for l in self.livres if l['disponible']])
        livres_empruntes = total_livres - livres_disponibles

        emprunts_actifs = len([e for e in self.emprunts if e['date_retour_effective'] is None])
        total_emprunts = len(self.emprunts)

        # Livres les plus empruntés
        livres_populaires = sorted(self.livres, key=lambda x: x['nb_emprunts'], reverse=True)[:5]

        # Retards
        retards = 0
        for emprunt in self.emprunts:
            if emprunt['date_retour_effective'] is None:
                date_retour = datetime.fromisoformat(emprunt['date_retour_prevue'])
                if datetime.now() > date_retour:
                    retards += 1

        print("\n=== STATISTIQUES ===")
        print(f"Total de livres : {total_livres}")
        print(f"Livres disponibles : {livres_disponibles}")
        print(f"Livres empruntés : {livres_empruntes}")
        print(f"Emprunts actifs : {emprunts_actifs}")
        print(f"Total emprunts historique : {total_emprunts}")
        print(f"Retards : {retards}")

        if livres_populaires:
            print("\nLivres les plus empruntés :")
            for i, livre in enumerate(livres_populaires, 1):
                if livre['nb_emprunts'] > 0:
                    print(f"{i}. {livre['titre']} ({livre['nb_emprunts']} emprunts)")

    def menu_principal(self):
        """Menu principal de l'application"""
        while True:
            print("\n=== GESTIONNAIRE DE BIBLIOTHÈQUE ===")
            print("1. Ajouter un livre")
            print("2. Afficher tous les livres")
            print("3. Rechercher des livres")
            print("4. Emprunter un livre")
            print("5. Retourner un livre")
            print("6. Statistiques")
            print("7. Quitter")

            choix = input("Votre choix : ").strip()

            if choix == '1':
                self.ajouter_livre()
            elif choix == '2':
                self.afficher_livres()
            elif choix == '3':
                self.rechercher_livres()
            elif choix == '4':
                self.emprunter_livre()
            elif choix == '5':
                self.retourner_livre()
            elif choix == '6':
                self.afficher_statistiques()
            elif choix == '7':
                print("Au revoir !")
                break
            else:
                print("Choix invalide.")

# Lancement de l'application
if __name__ == "__main__":
    bibliotheque = GestionnaireBibliotheque()
    bibliotheque.menu_principal()