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.