Polar Code 🎭

Command Palette

Search for a command to run...

14
Pièce N°14

Chapitre 14 : Mini-projets

La pluie avait enfin cessé. Sur l'écran cathodique de l'ancien ordinateur de la safehouse, quatre fenêtres de terminal s'ouvraient. Quatre projets. Quatre preuves de concept. Ce n'est pas dans les manuels qu'on devient bon, c'est sur le terrain. Avec des outils qu'on forge soi-même. Voici l'entraînement.

Projet 1 : Calculatrice

Pas une calculatrice ordinaire. Une calculatrice pour des opérations spéciales. Conversions de devises, calculs de trajectoires, ajustements de doses.

# calculatrice_operationnelle.py
"""
Calculatrice pour opérations sur le terrain.
Supporte les conversions et calculs discrets.
"""

def calculatrice():
    """Interface principale de la calculatrice."""
    print("""
╔══════════════════════════════════╗
║   CALCULATRICE OPERATIONNELLE    ║
╠══════════════════════════════════╣
║ 1. Addition                      ║
║ 2. Soustraction                  ║
║ 3. Multiplication                ║
║ 4. Division                      ║
║ 5. Conversion USD/EUR            ║
║ 6. Calcul de dilution            ║
║ 0. Quitter                       ║
╚══════════════════════════════════╝
""")
    
    while True:
        try:
            choix = input("\nOpération > ").strip()
            
            if choix == "0":
                print("Effacement des logs...")
                break
            
            elif choix in {"1", "2", "3", "4"}:
                a = float(input("Premier nombre > "))
                b = float(input("Deuxième nombre > "))
                
                if choix == "1":
                    resultat = a + b
                    symbole = "+"
                elif choix == "2":
                    resultat = a - b
                    symbole = "-"
                elif choix == "3":
                    resultat = a * b
                    symbole = "×"
                elif choix == "4":
                    if b == 0:
                        print("Erreur: Division par zéro.")
                        continue
                    resultat = a / b
                    symbole = "÷"
                
                print(f"Résultat: {a} {symbole} {b} = {resultat}")
            
            elif choix == "5":
                montant = float(input("Montant en USD > "))
                taux = 0.95  # Taux fictif, à remplacer par API
                converti = montant * taux
                print(f"{montant} USD = {converti:.2f} EUR")
            
            elif choix == "6":
                concentre = float(input("Concentration initiale (%) > "))
                volume_final = float(input("Volume final (mL) > "))
                volume_concentre = (concentre * volume_final) / 100
                print(f"Ajouter {volume_concentre:.1f} mL du concentré")
            
            else:
                print("Option non reconnue.")
        
        except ValueError:
            print("Erreur: Entrée invalide.")
        except KeyboardInterrupt:
            print("\nInterruption. Nettoyage...")
            break

if __name__ == "__main__":
    calculatrice()

Projet 2 : Jeu simple (Poker menteur)

Une adaptation. Pas la devinette naïve, ni pierre-feuille-ciseaux. Un jeu de poker menteur contre l'ordinateur. Un exercice de bluff et de détection.

# poker_menteur.py
"""
Jeu de poker menteur simplifié.
Un exercice de détection du mensonge.
"""

import random

class PokerMenteur:
    def __init__(self):
        self.paquets = list(range(1, 14)) * 4  # 52 cartes
        random.shuffle(self.paquets)
        self.main_joueur = []
        self.main_ordi = []
        self.pile = []
        self.distribuer()
    
    def distribuer(self):
        """Distribue 5 cartes à chaque joueur."""
        self.main_joueur = self.paquets[:5]
        self.main_ordi = self.paquets[5:10]
        self.paquets = self.paquets[10:]
        print("\n=== DISTRIBUTION ===")
        print(f"Votre main: {self.main_joueur}")
        print("(L'ordinateur a reçu 5 cartes)")
    
    def tour_joueur(self):
        """Tour du joueur."""
        if not self.main_joueur:
            return False
        
        print(f"\n--- VOTRE TOUR ---")
        print(f"Votre main: {self.main_joueur}")
        print(f"Pile actuelle: {self.pile[-3:] if self.pile else 'Vide'}")
        
        try:
            carte = int(input("Choisissez une carte (nombre 1-13) > "))
            if carte not in self.main_joueur:
                print("Vous ne possédez pas cette carte.")
                return True
            
            annonce = input("Annonce (réelle ou mensonge) > ")
            annonce = int(annonce)
            
            self.main_joueur.remove(carte)
            self.pile.append(carte)
            
            # L'ordinateur décide de croire ou non
            if random.random() < 0.6:  # 60% de chance de vérifier
                print(f"\nL'ordinateur vérifie...")
                if annonce != carte:
                    print("Mensonge détecté! Vous récupérez la pile.")
                    self.main_joueur.extend(self.pile)
                    self.pile = []
                else:
                    print("Annonce véridique. L'ordinateur prend la pile.")
                    self.main_ordi.extend(self.pile)
                    self.pile = []
            else:
                print("L'ordinateur passe.")
        
        except (ValueError, KeyboardInterrupt):
            return False
        
        return True
    
    def tour_ordi(self):
        """Tour de l'ordinateur."""
        if not self.main_ordi:
            return False
        
        carte = random.choice(self.main_ordi)
        self.main_ordi.remove(carte)
        self.pile.append(carte)
        
        # L'ordinateur ment 40% du temps
        if random.random() < 0.4:
            annonce = random.choice([c for c in range(1, 14) if c != carte])
        else:
            annonce = carte
        
        print(f"\n--- TOUR ORDINATEUR ---")
        print(f"L'ordinateur annonce: {annonce}")
        print(f"Pile actuelle: {len(self.pile)} cartes")
        
        choix = input("Vérifier? (o/n) > ").lower()
        if choix == 'o':
            if annonce != carte:
                print("Mensonge! L'ordinateur récupère la pile.")
                self.main_ordi.extend(self.pile)
                self.pile = []
            else:
                print("Vérité! Vous prenez la pile.")
                self.main_joueur.extend(self.pile)
                self.pile = []
        
        return True
    
    def jouer(self):
        """Boucle principale du jeu."""
        print("""
╔══════════════════════════════════╗
║       POKER MENTEUR - Noir       ║
╚══════════════════════════════════╝
Règles: Débarrassez-vous de vos cartes.
        Vous pouvez mentir sur la valeur.
        Si on vous croit, la pile continue.
        Si on vous prend en mensonge, vous récupérez la pile.
""")
        
        while self.main_joueur and self.main_ordi:
            if not self.tour_joueur():
                break
            if not self.tour_ordi():
                break
        
        # Déterminer le gagnant
        if not self.main_joueur:
            print("\n>>> Vous avez gagné. <<<")
        elif not self.main_ordi:
            print("\n>>> L'ordinateur a gagné. <<<")
        else:
            print("\n>>> Partie interrompue. <<<")
        
        print(f"Cartes restantes - Vous: {len(self.main_joueur)}, Ordinateur: {len(self.main_ordi)}")

if __name__ == "__main__":
    jeu = PokerMenteur()
    jeu.jouer()

Projet 3 : Gestion de contacts

Pas un carnet d'adresses banal. Une base de contacts opérationnels, avec niveaux de sécurité et statuts.

# contacts_operationnels.py
"""
Gestionnaire de contacts pour opérations.
Chiffrement basique des informations sensibles.
"""

import json
import hashlib
from datetime import datetime

class GestionnaireContacts:
    def __init__(self, fichier="contacts.json"):
        self.fichier = fichier
        self.contacts = self.charger()
        self.cle_hash = "operation_phantom"  # À remplacer par une vraie clé
    
    def _chiffrer(self, texte):
        """Chiffrement basique (pour démonstration)."""
        return hashlib.sha256(f"{texte}{self.cle_hash}".encode()).hexdigest()[:10]
    
    def charger(self):
        """Charge les contacts depuis le fichier."""
        try:
            with open(self.fichier, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            return {}
    
    def sauvegarder(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 opérationnel."""
        print("\n--- NOUVEAU CONTACT ---")
        
        identifiant = input("Identifiant unique > ").strip()
        if identifiant in self.contacts:
            print("Erreur: Identifiant déjà utilisé.")
            return
        
        nom_code = input("Nom de code > ").strip()
        specialite = input("Spécialité > ").strip()
        niveau_securite = input("Niveau sécurité (1-5) > ").strip()
        
        contact = {
            'nom_code': nom_code,
            'specialite': specialite,
            'niveau_securite': niveau_securite,
            'statut': 'actif',
            'dernier_contact': datetime.now().strftime("%Y-%m-%d"),
            'identifiant_chiffre': self._chiffrer(identifiant)
        }
        
        self.contacts[identifiant] = contact
        self.sauvegarder()
        print(f"Contact '{nom_code}' ajouté.")
    
    def lister_contacts(self, filtre=None):
        """Liste les contacts, éventuellement filtrés."""
        print("\n--- LISTE DES CONTACTS ---")
        
        if not self.contacts:
            print("Aucun contact enregistré.")
            return
        
        for identifiant, infos in self.contacts.items():
            if filtre and infos['statut'] != filtre:
                continue
            
            print(f"""
ID: {identifiant} [{infos['identifiant_chiffre']}]
Nom: {infos['nom_code']}
Spécialité: {infos['specialite']}
Sécurité: {'★' * int(infos['niveau_securite'])}
Statut: {infos['statut'].upper()}
Dernier contact: {infos['dernier_contact']}
""")
    
    def rechercher_contact(self):
        """Recherche un contact par nom ou spécialité."""
        terme = input("Rechercher (nom/spécialité) > ").lower().strip()
        
        print("\n--- RÉSULTATS ---")
        trouve = False
        
        for identifiant, infos in self.contacts.items():
            if terme in infos['nom_code'].lower() or terme in infos['specialite'].lower():
                print(f"{infos['nom_code']} - {infos['specialite']} ({infos['statut']})")
                trouve = True
        
        if not trouve:
            print("Aucun contact correspondant.")
    
    def menu(self):
        """Affiche le menu principal."""
        while True:
            print("""
╔══════════════════════════════════╗
║   GESTIONNAIRE DE CONTACTS       ║
╠══════════════════════════════════╣
║ 1. Ajouter un contact            ║
║ 2. Lister tous les contacts      ║
║ 3. Lister contacts actifs        ║
║ 4. Rechercher un contact         ║
║ 5. Marquer contact inactif       ║
║ 0. Quitter                       ║
╚══════════════════════════════════╝
""")
            
            choix = input("Choix > ").strip()
            
            if choix == "0":
                print("Nettoyage des traces...")
                break
            elif choix == "1":
                self.ajouter_contact()
            elif choix == "2":
                self.lister_contacts()
            elif choix == "3":
                self.lister_contacts(filtre='actif')
            elif choix == "4":
                self.rechercher_contact()
            elif choix == "5":
                identifiant = input("ID du contact à désactiver > ").strip()
                if identifiant in self.contacts:
                    self.contacts[identifiant]['statut'] = 'inactif'
                    self.sauvegarder()
                    print("Contact désactivé.")
            else:
                print("Option invalide.")

if __name__ == "__main__":
    gc = GestionnaireContacts()
    gc.menu()

Projet 4 : Script d'automatisation simple

Nettoyage de logs, surveillance de dossiers, notification discrète.

# automate_surveillance.py
"""
Script d'automatisation pour surveillance.
Vérifie des dossiers, nettoie des logs, envoie des alertes.
"""

import os
import time
import shutil
from datetime import datetime, timedelta

class AutomateSurveillance:
    def __init__(self, dossier_logs="logs", dossier_backup="backup", seuil_jours=7):
        self.dossier_logs = dossier_logs
        self.dossier_backup = dossier_backup
        self.seuil_jours = seuil_jours
        
        # Créer les dossiers s'ils n'existent pas
        os.makedirs(self.dossier_logs, exist_ok=True)
        os.makedirs(self.dossier_backup, exist_ok=True)
    
    def nettoyer_vieux_logs(self):
        """Supprime les logs plus vieux que le seuil."""
        print("[Nettoyage] Recherche de logs obsolètes...")
        maintenant = datetime.now()
        fichiers_supprimes = 0
        
        for fichier in os.listdir(self.dossier_logs):
            chemin = os.path.join(self.dossier_logs, fichier)
            
            if os.path.isfile(chemin):
                date_modif = datetime.fromtimestamp(os.path.getmtime(chemin))
                age = maintenant - date_modif
                
                if age > timedelta(days=self.seuil_jours):
                    try:
                        # Archive avant suppression
                        date_str = date_modif.strftime("%Y%m%d")
                        archive_nom = f"{fichier}_{date_str}.bak"
                        chemin_archive = os.path.join(self.dossier_backup, archive_nom)
                        shutil.copy2(chemin, chemin_archive)
                        
                        # Suppression
                        os.remove(chemin)
                        fichiers_supprimes += 1
                        print(f"  Archivé et supprimé: {fichier}")
                    
                    except Exception as e:
                        print(f"  Erreur avec {fichier}: {e}")
        
        print(f"[Nettoyage] Terminé. {fichiers_supprimes} fichiers traités.")
        return fichiers_supprimes
    
    def verifier_dossier(self, chemin, extensions=None):
        """Vérifie un dossier pour des fichiers récents."""
        if extensions is None:
            extensions = ['.txt', '.log', '.csv']
        
        print(f"[Vérification] Dossier: {chemin}")
        
        if not os.path.exists(chemin):
            print(f"  Dossier inexistant: {chemin}")
            return []
        
        fichiers_recents = []
        limite = datetime.now() - timedelta(hours=24)
        
        for fichier in os.listdir(chemin):
            if any(fichier.endswith(ext) for ext in extensions):
                chemin_complet = os.path.join(chemin, fichier)
                date_modif = datetime.fromtimestamp(os.path.getmtime(chemin_complet))
                
                if date_modif > limite:
                    fichiers_recents.append((fichier, date_modif))
        
        if fichiers_recents:
            print("  Fichiers modifiés dans les dernières 24h:")
            for nom, date in fichiers_recents:
                print(f"    {nom} - {date.strftime('%H:%M')}")
        else:
            print("  Aucune modification récente.")
        
        return fichiers_recents
    
    def generer_rapport(self, details):
        """Génère un rapport de surveillance."""
        date = datetime.now().strftime("%Y-%m-%d_%H-%M")
        rapport_nom = f"rapport_surveillance_{date}.txt"
        chemin = os.path.join(self.dossier_logs, rapport_nom)
        
        with open(chemin, 'w', encoding='utf-8') as f:
            f.write(f"RAPPORT DE SURVEILLANCE - {date}\n")
            f.write("=" * 50 + "\n\n")
            
            for section, contenu in details.items():
                f.write(f"{section.upper()}:\n")
                if isinstance(contenu, list):
                    for ligne in contenu:
                        f.write(f"  - {ligne}\n")
                else:
                    f.write(f"  {contenu}\n")
                f.write("\n")
        
        print(f"[Rapport] Généré: {rapport_nom}")
        return chemin
    
    def executer_cycle(self, dossiers_a_surveiller=None):
        """Exécute un cycle complet de surveillance."""
        if dossiers_a_surveiller is None:
            dossiers_a_surveiller = [".", "data"]
        
        print("\n" + "="*50)
        print(f"Début du cycle: {datetime.now().strftime('%H:%M:%S')}")
        print("="*50)
        
        details = {
            'nettoyage': f"{self.nettoyer_vieux_logs()} fichiers nettoyés",
            'surveillance': []
        }
        
        for dossier in dossiers_a_surveiller:
            recents = self.verifier_dossier(dossier)
            if recents:
                details['surveillance'].append(f"{dossier}: {len(recents)} fichiers récents")
        
        self.generer_rapport(details)
        print(f"Fin du cycle: {datetime.now().strftime('%H:%M:%S')}")
    
    def executer_en_continu(self, intervalle_minutes=60):
        """Exécute la surveillance en continu."""
        print(f"""
╔══════════════════════════════════╗
║    SURVEILLANCE AUTOMATIQUE      ║
╠══════════════════════════════════╣
║ Intervalle: {intervalle_minutes} minutes
║ Logs: {self.dossier_logs}
║ Backup: {self.dossier_backup}
╚══════════════════════════════════╝
Démarrer la surveillance continue...
(Ctrl+C pour arrêter)
""")
        
        try:
            while True:
                self.executer_cycle()
                print(f"\nProchain cycle dans {intervalle_minutes} minutes...")
                time.sleep(intervalle_minutes * 60)
        
        except KeyboardInterrupt:
            print("\n\nSurveillance arrêtée.")
            print("Nettoyage final...")
            self.nettoyer_vieux_logs()

if __name__ == "__main__":
    automate = AutomateSurveillance()
    
    # Mode interactif
    print("Mode d'exécution:")
    print("1. Un cycle unique")
    print("2. Surveillance continue (1h)")
    choix = input("Choix > ").strip()
    
    if choix == "1":
        automate.executer_cycle()
    else:
        automate.executer_en_continu(intervalle_minutes=60)

Quatre fenêtres, quatre scripts en cours d'exécution. La calculatrice convertissait des montants. Le poker menteur bluffait. Le gestionnaire de contacts cryptait des identifiants. L'automate nettoyait des traces.

Sur le terrain, ce ne sont pas les concepts théoriques qui comptent. C'est la capacité à les assembler pour créer des outils utiles. Fonctionnels. Discrets.

Chaque projet est une compétence. Chaque ligne de code, une balle dans le chargeur.

Et quand le moment viendra, vous saurez quoi construire pour survivre.


La théorie forge l'esprit. La pratique forge les armes.