Compare commits

..
6 Commits
10 changed files with 2926 additions and 60 deletions
+1 -1
View File
@@ -93,7 +93,7 @@ def somme(a, b):
Les IDE comme Thonny ou VSCode offrent des outils de débugging intégrés qui permettent de définir des points d'arrêt, d'exécuter le code pas à pas et d'inspecter les valeurs des variables. Les IDE comme Thonny ou VSCodium offrent des outils de débugging intégrés qui permettent de définir des points d'arrêt, d'exécuter le code pas à pas et d'inspecter les valeurs des variables.
+25 -25
View File
@@ -12,44 +12,41 @@ Les projets constituent une part essentielle de l'enseignement de la NSI. Ils pe
Les projets sont aussi l'occasion de découvrir que l'informatique permet de **modéliser et simuler** des phénomènes issus de domaines variés : biologie, physique, mathématiques, sciences sociales... Les projets sont aussi l'occasion de découvrir que l'informatique permet de **modéliser et simuler** des phénomènes issus de domaines variés : biologie, physique, mathématiques, sciences sociales...
## Rythme
Chaque projet est découpé en phases dans son fichier `JALONS.md`, sur une **progression d'environ 10 séances, de septembre à février**. Une séance par semaine, en groupe, avec une fiche de suivi à compléter à chaque fois.
## Projets proposés ## Projets proposés
### Simulations et automates cellulaires | Projet | Ce qu'on y fait | Notions mobilisées | Base de code fournie |
|--------|-----------------|--------------------|----------------------|
| [Le Jeu de la Vie](jeu_de_la_vie/) | L'automate cellulaire de Conway : quatre règles, et des structures qui émergent | Grilles 2D, fonctions pures, POO | non |
| [Labyrinthe](labyrinthe/) | Générer un labyrinthe parfait, puis le résoudre automatiquement | Graphes, arbres couvrants, DFS et BFS, récursivité | oui, `src/maze.py` |
| [Wa-Tor](wator/) | Simuler un monde torique où des requins chassent des thons | Grilles 2D, simulation, POO | non |
| [Mastermind et algorithmes génétiques](mastermind/) | Faire évoluer une population de combinaisons jusqu'à trouver le code secret | POO, paradigme fonctionnel, optimisation | oui, `src/algo_gen.py` et `src/combinaison.py` |
| Projet | Description | Le Jeu de la Vie et le Labyrinthe sont les deux plus accessibles pour démarrer. Le Labyrinthe est celui qui recoupe le plus directement le programme de Terminale, entre le chapitre Graphes et le chapitre Récursivité.
|--------|-------------|
| **Le Jeu de la Vie** | Automate cellulaire de Conway, exploration de l'émergence et des structures auto-organisées |
| **Wa-Tor** | Simulation proie-prédateur (poissons et requins) dans un monde torique |
| **Feu de forêt** | Modélisation de la propagation d'un incendie, étude des seuils de percolation |
### Génération procédurale et algorithmes > Le Mastermind utilise l'**héritage de classes**, qui ne figure pas au programme de Terminale. Le projet reste faisable, mais il demande d'accepter une notion vue nulle part ailleurs dans l'année.
| Projet | Description | D'autres projets seront ajoutés au fil de l'année.
|--------|-------------|
| **Algogen** | Génération algorithmique d'images, de textures ou de terrains |
| **Labyrinthe** | Génération et résolution de labyrinthes (DFS, Kruskal, A*) |
| **L-Systems** | Génération de fractales et de plantes virtuelles par réécriture |
### Intelligence artificielle et jeux
| Projet | Description |
|--------|-------------|
| **Morpion / Puissance 4** | Implémentation d'une IA avec l'algorithme Minimax |
| **2048** | Clone du jeu avec IA basée sur des heuristiques |
| **Pathfinding** | Visualisation des algorithmes de recherche de chemin |
## Structure d'un projet ## Structure d'un projet
Chaque projet contient généralement : Chaque dossier de projet contient :
``` ```
nom_du_projet/ nom_du_projet/
├── README.md # Présentation et consignes ├── README.md # Présentation, contexte, notions mobilisées
├── src/ # Code source ├── JALONS.md # Découpage en phases, séance par séance
── tests/ # Tests unitaires ── SUIVI.md # Fiche de suivi à compléter par le groupe
└── docs/ # Documentation complémentaire
``` ```
Le code de départ commun à plusieurs projets se trouve dans `src/`.
Si deux groupes travaillent sur le même projet, chacun part de sa propre copie de
[`SUIVI_TEMPLATE.md`](SUIVI_TEMPLATE.md) plutôt que du `SUIVI.md` du dossier.
## Évaluation ## Évaluation
Les projets sont évalués selon plusieurs critères : Les projets sont évalués selon plusieurs critères :
@@ -58,3 +55,6 @@ Les projets sont évalués selon plusieurs critères :
- **Qualité du code** : lisibilité, modularité, documentation - **Qualité du code** : lisibilité, modularité, documentation
- **Démarche** : analyse du problème, choix techniques justifiés - **Démarche** : analyse du problème, choix techniques justifiés
- **Présentation** : capacité à expliquer son travail - **Présentation** : capacité à expliquer son travail
---
*Lycée Charlotte Perriand, NSI Terminale, Licence CC BY-SA 4.0*
+3 -2
View File
@@ -11,9 +11,10 @@ Les fichiers `.ipynb` peuvent être ouverts avec :
| Outil | Installation | | Outil | Installation |
|-------|--------------| |-------|--------------|
| **[Basthon](https://basthon.fr)** | En ligne, sans installation | | **[Notebook du cours](https://notebook.florianmathieu.prof)** | En ligne, sans installation. Menu `Fichier` puis `Ouvrir...` |
| **[Basthon](https://basthon.fr)** | Le même outil, sur son site public |
| **VSCodium** | Extension Python. C'est l'éditeur installé au lycée |
| **Jupyter Notebook** | `pip install notebook` puis `jupyter notebook` | | **Jupyter Notebook** | `pip install notebook` puis `jupyter notebook` |
| **VS Code** | Extension Python |
## Chapitres ## Chapitres
+568
View File
@@ -0,0 +1,568 @@
# Corrigé du TP Z-Event
---
## Partie 1 : Le compteur (variables et types)
### 1.1. État de la cagnotte
**Question 1.1** :
```python
pourcentage = round(collecte / objectif * 100, 2)
print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à 73.5 %
```
**Question 1.2** :
```python
reste = objectif - collecte
print(f"Il manque {reste} euros") # Il manque 662549.25 euros
```
### 1.2. Le piège du compteur
**Question 1.3** :
```python
don_1 = "25"
don_2 = "40"
total = don_1 + don_2
print(total) # affiche 2540 : sur deux chaînes, + est une concaténation
# Correction : convertir les chaînes en nombres avant d'additionner
total = int(don_1) + int(don_2)
print(total) # 65
```
## Partie 2 : L'annonce des dons (fonctions)
### 2.1. Formater un don
**Question 2.1** :
```python
def formater_don(pseudo, montant):
return f"{pseudo} vient de donner {montant} euros"
# Test
print(formater_don("Nyx_42", 5.0)) # Nyx_42 vient de donner 5.0 euros
print(formater_don("Marion_NSI", 100.0)) # Marion_NSI vient de donner 100.0 euros
```
### 2.2. Repérer les gros dons
**Question 2.2** :
```python
def est_gros_don(montant, seuil=50):
return montant >= seuil
# Test
print(est_gros_don(120.0)) # True
print(est_gros_don(50.0)) # True
print(est_gros_don(20.0)) # False
print(est_gros_don(20.0, 10)) # True
```
### 2.3. Les paliers
**Question 2.3** :
```python
def palier(total):
if total < 500000:
return "Départ"
elif total < 1000000:
return "Palier 1"
elif total < 2000000:
return "Palier 2"
else:
return "Objectif"
# Test
print(palier(120000)) # Départ
print(palier(750000)) # Palier 1
print(palier(1837450.75)) # Palier 2
print(palier(2500000)) # Objectif
```
### 2.4. Le message complet
**Question 2.4** :
```python
def annonce_regie(pseudo, montant):
message = formater_don(pseudo, montant)
if est_gros_don(montant):
message = message + " [GROS DON]"
return message
# Test
print(annonce_regie("Poulpe3000", 2.0)) # Poulpe3000 vient de donner 2.0 euros
print(annonce_regie("Anonyme", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON]
```
## Partie 3 : L'historique des dons (boucles et listes)
### 3.1. Le total collecté
**Question 3.1** :
```python
def total_dons(liste_dons):
total = 0
for don in liste_dons:
total = total + don["montant"]
return total
# Test
print(total_dons(dons)) # 1449.5
print(total_dons([])) # 0
```
### 3.2. Le don le plus élevé
**Question 3.2** :
```python
def plus_gros_don(liste_dons):
record = liste_dons[0] # on part du premier don, surtout pas de 0
for don in liste_dons:
if don["montant"] > record["montant"]:
record = don
return record
# Test
record = plus_gros_don(dons)
print(record["pseudo"], record["montant"]) # Anonyme 500.0
```
### 3.3. Compter et filtrer
**Question 3.3** :
```python
def compter_gros_dons(liste_dons, seuil=50):
compteur = 0
for don in liste_dons:
if est_gros_don(don["montant"], seuil):
compteur = compteur + 1
return compteur
# Test
print(compter_gros_dons(dons)) # 7
print(compter_gros_dons(dons, 100)) # 4
```
**Question 3.4** :
```python
def donateurs(liste_dons):
pseudos = []
for don in liste_dons:
if don["pseudo"] not in pseudos:
pseudos.append(don["pseudo"])
return pseudos
# Test
print(donateurs(dons))
# ['Nyx_42', 'TitiLeBg', 'Camille_R', 'Poulpe3000', 'Anonyme', 'Sam_du_59', 'Lulu_2007', 'Marion_NSI', 'Kevin_B']
```
### 3.4. Le bandeau des derniers dons
**Question 3.5** :
```python
def derniers_dons(liste_dons, n):
return liste_dons[-n:]
# Test
for don in derniers_dons(dons, 3):
print(don["heure"], don["pseudo"])
# 21:39 Anonyme
# 21:48 Poulpe3000
# 21:59 Marion_NSI
```
## Partie 4 : Le classement des chaînes (dictionnaires)
### 4.1. La cagnotte de chaque chaîne
**Question 4.1** :
```python
def cagnottes(liste_dons):
totaux = {}
for don in liste_dons:
streamer = don["streamer"]
if streamer in totaux:
totaux[streamer] = totaux[streamer] + don["montant"]
else:
totaux[streamer] = don["montant"]
return totaux
# Test
totaux = cagnottes(dons)
print(totaux["Antoine Daniel"]) # 115.0
print(totaux["Joueur du Grenier"]) # 140.0
print(len(totaux)) # 13
```
### 4.2. La chaîne en tête
**Question 4.2** :
```python
def meilleure_chaine(totaux):
meilleur = None
for streamer in totaux:
if meilleur is None or totaux[streamer] > totaux[meilleur]:
meilleur = streamer
return meilleur
# Test
print(meilleure_chaine(cagnottes(dons))) # MisterMV
```
### 4.3. Le détail des dons
**Question 4.3** :
```python
def dons_par_chaine(liste_dons):
detail = {}
for don in liste_dons:
streamer = don["streamer"]
if streamer not in detail:
detail[streamer] = []
detail[streamer].append(don["montant"])
return detail
# Test
detail = dons_par_chaine(dons)
print(detail["MisterMV"]) # [20.0, 12.0, 500.0]
print(detail["Baghera Jones"]) # [15.5, 5.0, 35.0]
```
### 4.4. Le podium
**Question 4.4** :
```python
def podium(totaux):
classement = sorted(totaux, key=lambda streamer: totaux[streamer], reverse=True)
return classement[:3]
# Test
print(podium(cagnottes(dons)))
# ['MisterMV', 'Domingo', 'Ultia']
```
## Partie 5 : Le planning et les raids (synthèse)
### 5.1. Qui est à l'antenne
**Question 5.1** :
```python
def a_l_antenne(planning, heure):
for streamer, debut, fin in planning:
if debut < fin:
if debut <= heure < fin:
return streamer
else:
# créneau à cheval sur minuit
if heure >= debut or heure < fin:
return streamer
return None
# Test
print(a_l_antenne(planning, 19)) # Antoine Daniel
print(a_l_antenne(planning, 22)) # Joueur du Grenier
print(a_l_antenne(planning, 1)) # Baghera Jones
print(a_l_antenne(planning, 10)) # None
```
### 5.2. Suivre la chaîne des raids
**Question 5.2** :
```python
def suivre_raids(raids, depart):
parcours = [depart]
courant = depart
while raids.get(courant): # .get évite l'erreur si la clé est absente
courant = raids[courant][0]
parcours.append(courant)
return parcours
# Test
print(suivre_raids(raids, "Antoine Daniel"))
# ['Antoine Daniel', 'MisterMV', 'Joueur du Grenier', 'Baghera Jones', 'Ultia']
print(suivre_raids(raids, "Horty"))
# ['Horty', 'Sylvain Levy']
print(suivre_raids(raids, "Ultia"))
# ['Ultia']
```
---
## Code complet fonctionnel
```python
# Toutes ces données sont inventées pour l'exercice.
objectif = 2500000
collecte = 1837450.75
dons = [
{"pseudo": "Nyx_42", "streamer": "Antoine Daniel", "montant": 5.0, "heure": "18:04"},
{"pseudo": "TitiLeBg", "streamer": "MisterMV", "montant": 20.0, "heure": "18:11"},
{"pseudo": "Camille_R", "streamer": "Joueur du Grenier", "montant": 50.0, "heure": "18:23"},
{"pseudo": "Nyx_42", "streamer": "Baghera Jones", "montant": 15.5, "heure": "18:37"},
{"pseudo": "Poulpe3000", "streamer": "Horty", "montant": 2.0, "heure": "18:41"},
{"pseudo": "Anonyme", "streamer": "Ultia", "montant": 120.0, "heure": "18:52"},
{"pseudo": "Sam_du_59", "streamer": "Antoine Daniel", "montant": 10.0, "heure": "19:03"},
{"pseudo": "Camille_R", "streamer": "Sylvain Levy", "montant": 30.0, "heure": "19:14"},
{"pseudo": "Lulu_2007", "streamer": "ZeratoR", "montant": 8.5, "heure": "19:20"},
{"pseudo": "TitiLeBg", "streamer": "Etoiles", "montant": 45.0, "heure": "19:33"},
{"pseudo": "Marion_NSI", "streamer": "Joueur du Grenier", "montant": 75.0, "heure": "19:47"},
{"pseudo": "Poulpe3000", "streamer": "MisterMV", "montant": 12.0, "heure": "19:55"},
{"pseudo": "Anonyme", "streamer": "Domingo", "montant": 250.0, "heure": "20:08"},
{"pseudo": "Kevin_B", "streamer": "Baghera Jones", "montant": 5.0, "heure": "20:16"},
{"pseudo": "Nyx_42", "streamer": "Maghla", "montant": 60.0, "heure": "20:29"},
{"pseudo": "Sam_du_59", "streamer": "Horty", "montant": 3.5, "heure": "20:38"},
{"pseudo": "Marion_NSI", "streamer": "Antoine Daniel", "montant": 100.0, "heure": "20:44"},
{"pseudo": "Lulu_2007", "streamer": "Alphacast", "montant": 18.0, "heure": "20:57"},
{"pseudo": "Camille_R", "streamer": "Ultia", "montant": 40.0, "heure": "21:05"},
{"pseudo": "Kevin_B", "streamer": "Ponce", "montant": 22.5, "heure": "21:12"},
{"pseudo": "TitiLeBg", "streamer": "Joueur du Grenier", "montant": 15.0, "heure": "21:26"},
{"pseudo": "Anonyme", "streamer": "MisterMV", "montant": 500.0, "heure": "21:39"},
{"pseudo": "Poulpe3000", "streamer": "Sylvain Levy", "montant": 7.5, "heure": "21:48"},
{"pseudo": "Marion_NSI", "streamer": "Baghera Jones", "montant": 35.0, "heure": "21:59"},
]
planning = [
("Antoine Daniel", 18, 20),
("MisterMV", 20, 22),
("Joueur du Grenier", 22, 24),
("Baghera Jones", 0, 3),
("Ultia", 3, 6),
]
raids = {
"Antoine Daniel": ["MisterMV"],
"MisterMV": ["Joueur du Grenier", "Horty"],
"Joueur du Grenier": ["Baghera Jones"],
"Baghera Jones": ["Ultia"],
"Ultia": [],
"Horty": ["Sylvain Levy"],
"Sylvain Levy": [],
}
pourcentage = round(collecte / objectif * 100, 2)
print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à 73.5 %
reste = objectif - collecte
print(f"Il manque {reste} euros") # Il manque 662549.25 euros
don_1 = "25"
don_2 = "40"
total = don_1 + don_2
print(total) # affiche 2540 : sur deux chaînes, + est une concaténation
# Correction : convertir les chaînes en nombres avant d'additionner
total = int(don_1) + int(don_2)
print(total) # 65
def formater_don(pseudo, montant):
return f"{pseudo} vient de donner {montant} euros"
# Test
print(formater_don("Nyx_42", 5.0)) # Nyx_42 vient de donner 5.0 euros
print(formater_don("Marion_NSI", 100.0)) # Marion_NSI vient de donner 100.0 euros
def est_gros_don(montant, seuil=50):
return montant >= seuil
# Test
print(est_gros_don(120.0)) # True
print(est_gros_don(50.0)) # True
print(est_gros_don(20.0)) # False
print(est_gros_don(20.0, 10)) # True
def palier(total):
if total < 500000:
return "Départ"
elif total < 1000000:
return "Palier 1"
elif total < 2000000:
return "Palier 2"
else:
return "Objectif"
# Test
print(palier(120000)) # Départ
print(palier(750000)) # Palier 1
print(palier(1837450.75)) # Palier 2
print(palier(2500000)) # Objectif
def annonce_regie(pseudo, montant):
message = formater_don(pseudo, montant)
if est_gros_don(montant):
message = message + " [GROS DON]"
return message
# Test
print(annonce_regie("Poulpe3000", 2.0)) # Poulpe3000 vient de donner 2.0 euros
print(annonce_regie("Anonyme", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON]
def total_dons(liste_dons):
total = 0
for don in liste_dons:
total = total + don["montant"]
return total
# Test
print(total_dons(dons)) # 1449.5
print(total_dons([])) # 0
def plus_gros_don(liste_dons):
record = liste_dons[0] # on part du premier don, surtout pas de 0
for don in liste_dons:
if don["montant"] > record["montant"]:
record = don
return record
# Test
record = plus_gros_don(dons)
print(record["pseudo"], record["montant"]) # Anonyme 500.0
def compter_gros_dons(liste_dons, seuil=50):
compteur = 0
for don in liste_dons:
if est_gros_don(don["montant"], seuil):
compteur = compteur + 1
return compteur
# Test
print(compter_gros_dons(dons)) # 7
print(compter_gros_dons(dons, 100)) # 4
def donateurs(liste_dons):
pseudos = []
for don in liste_dons:
if don["pseudo"] not in pseudos:
pseudos.append(don["pseudo"])
return pseudos
# Test
print(donateurs(dons))
# ['Nyx_42', 'TitiLeBg', 'Camille_R', 'Poulpe3000', 'Anonyme', 'Sam_du_59', 'Lulu_2007', 'Marion_NSI', 'Kevin_B']
def derniers_dons(liste_dons, n):
return liste_dons[-n:]
# Test
for don in derniers_dons(dons, 3):
print(don["heure"], don["pseudo"])
# 21:39 Anonyme
# 21:48 Poulpe3000
# 21:59 Marion_NSI
def cagnottes(liste_dons):
totaux = {}
for don in liste_dons:
streamer = don["streamer"]
if streamer in totaux:
totaux[streamer] = totaux[streamer] + don["montant"]
else:
totaux[streamer] = don["montant"]
return totaux
# Test
totaux = cagnottes(dons)
print(totaux["Antoine Daniel"]) # 115.0
print(totaux["Joueur du Grenier"]) # 140.0
print(len(totaux)) # 13
def meilleure_chaine(totaux):
meilleur = None
for streamer in totaux:
if meilleur is None or totaux[streamer] > totaux[meilleur]:
meilleur = streamer
return meilleur
# Test
print(meilleure_chaine(cagnottes(dons))) # MisterMV
def dons_par_chaine(liste_dons):
detail = {}
for don in liste_dons:
streamer = don["streamer"]
if streamer not in detail:
detail[streamer] = []
detail[streamer].append(don["montant"])
return detail
# Test
detail = dons_par_chaine(dons)
print(detail["MisterMV"]) # [20.0, 12.0, 500.0]
print(detail["Baghera Jones"]) # [15.5, 5.0, 35.0]
def podium(totaux):
classement = sorted(totaux, key=lambda streamer: totaux[streamer], reverse=True)
return classement[:3]
# Test
print(podium(cagnottes(dons)))
# ['MisterMV', 'Domingo', 'Ultia']
def a_l_antenne(planning, heure):
for streamer, debut, fin in planning:
if debut < fin:
if debut <= heure < fin:
return streamer
else:
# créneau à cheval sur minuit
if heure >= debut or heure < fin:
return streamer
return None
# Test
print(a_l_antenne(planning, 19)) # Antoine Daniel
print(a_l_antenne(planning, 22)) # Joueur du Grenier
print(a_l_antenne(planning, 1)) # Baghera Jones
print(a_l_antenne(planning, 10)) # None
def suivre_raids(raids, depart):
parcours = [depart]
courant = depart
while raids.get(courant): # .get évite l'erreur si la clé est absente
courant = raids[courant][0]
parcours.append(courant)
return parcours
# Test
print(suivre_raids(raids, "Antoine Daniel"))
# ['Antoine Daniel', 'MisterMV', 'Joueur du Grenier', 'Baghera Jones', 'Ultia']
print(suivre_raids(raids, "Horty"))
# ['Horty', 'Sylvain Levy']
print(suivre_raids(raids, "Ultia"))
# ['Ultia']
```
---
Auteur : Florian Mathieu
Licence CC BY-SA
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>.
+65 -10
View File
@@ -1,7 +1,55 @@
# Révisions de rentrée : PERRIAND-7 # Révisions de rentrée : notes de séance
Activités de reprise pour la rentrée de Terminale, après la coupure estivale. Deux activités de reprise pour la rentrée de Terminale, après la coupure estivale.
Fil rouge unique : le réveil d'une station orbitale après deux mois d'hibernation. Elles couvrent les mêmes notions et sont interchangeables : Z-Event (marathon caritatif de
streaming) et PERRIAND-7 (station orbitale en avarie).
## Z-Event
| Fichier | Pour qui | Publié sur hub-docs |
|---|---|---|
| `TP_ZEvent.md` | Élèves | oui |
| `TP_ZEvent.ipynb` | Élèves | seulement si ajouté à `NOTEBOOK_ALLOWLIST` |
| `Corrige_TP_ZEvent.md` | Professeur | **oui par défaut** (c'est un `.md`), voir ci-dessous |
| `_generateur_zevent.py` | Professeur | non (préfixe `_`) |
Écrit au gabarit de tes TP : `## Contexte`, `## Objectifs pédagogiques`, `## Partie N`,
`**Question N.M**`, sorties attendues en commentaire à droite des `print`, `## Barème
indicatif` sur 20, auteur et licence. Cinq parties, dix-huit questions, environ deux heures.
**Le contexte factuel est volontairement prudent.** Le TP ne dit rien de l'édition en cours :
ni montant, ni association, ni participants réels. Seuls figurent des éléments stables
(l'événement existe depuis 2016, créé par ZeratoR et Dach). Toutes les données manipulées
sont inventées, et un encadré le dit explicitement aux élèves dès le contexte. Les streamers
cités ne servent que de noms dans un jeu de données fictif.
**Deux décisions de publication à prendre** avant de pousser :
- `Corrige_TP_ZEvent.md` sera publié automatiquement sur hub-docs, comme tous tes autres
`Corrige_TP_*.md`. Le `robots.txt` le désindexe des moteurs de recherche, mais il reste
accessible depuis la navigation du site. Si tu ne veux pas ça pour demain, il faut soit
l'ajouter temporairement à `EXCLUDE_FILES`, soit ne pas le pousser tout de suite.
- `TP_ZEvent.ipynb` ne sera **pas** publié tant qu'il n'est pas dans
`NOTEBOOK_ALLOWLIST["terminale"]`. Sans ça, les élèves n'auront que la version Markdown
en ligne, et le choix de format annoncé dans le README n'existera pas.
### Points de vigilance
- **Question 1.3** : `"25" + "40"` donne `"2540"`. Leur faire exécuter avant d'expliquer.
- **Question 3.2** : l'initialisation de l'accumulateur avec `liste[0]` et non `0` est le
point qui coince le plus. C'est la question 2 de la synthèse.
- **Question 5.1** : le créneau de Baghera Jones est à cheval sur minuit, donc `debut > fin`.
C'est volontaire, et c'est le seul endroit du TP qui demande de distinguer deux cas.
- **Question 5.2** : parcours d'une liste d'adjacence. La question 4 de la synthèse pose le
problème du cycle sans demander de le résoudre, c'est le pont vers le chapitre Graphes.
### Barème
Réparti sur 20 dans l'énoncé. Les parties 1 à 3 pèsent 13 points : un élève qui a décroché
pendant l'été peut valider la moitié du TP sans toucher aux dictionnaires.
---
## PERRIAND-7
| Fichier | Pour qui | Publié sur hub-docs | | Fichier | Pour qui | Publié sur hub-docs |
|---|---|---| |---|---|---|
@@ -9,9 +57,9 @@ Fil rouge unique : le réveil d'une station orbitale après deux mois d'hibernat
| `PERRIAND-7_Reveil_des_systemes.ipynb` | Élèves | oui (`NOTEBOOK_ALLOWLIST`) | | `PERRIAND-7_Reveil_des_systemes.ipynb` | Élèves | oui (`NOTEBOOK_ALLOWLIST`) |
| `PERRIAND-7_CORRIGE.ipynb` | Professeur | non (aucun notebook Terminale hors allowlist) | | `PERRIAND-7_CORRIGE.ipynb` | Professeur | non (aucun notebook Terminale hors allowlist) |
| `NOTES_PROF.md` | Professeur | non (`EXCLUDE_FILES`) | | `NOTES_PROF.md` | Professeur | non (`EXCLUDE_FILES`) |
| `_generateur.py` | Professeur | non (`EXCLUDE_FILES`, il contient le corrigé) | | `_generateur.py` | Professeur | non (préfixe `_`, il contient le corrigé) |
Notebooks ouvrables sur [Basthon](https://basthon.fr), Jupyter ou VS Code. Notebooks ouvrables sur [notebook.florianmathieu.prof](https://notebook.florianmathieu.prof) (instance Basthon du cours), sur [Basthon](https://basthon.fr), ou dans VSCodium.
Aucune bibliothèque externe, aucun fichier annexe. Aucune bibliothèque externe, aucun fichier annexe.
--- ---
@@ -93,15 +141,22 @@ Les deux notebooks sont produits par un script unique (énoncé et corrigé part
même source, ce qui évite qu'ils divergent) : même source, ce qui évite qu'ils divergent) :
```bash ```bash
python3 _generateur.py python3 _generateur.py # PERRIAND-7
python3 _generateur_zevent.py # Z-Event
``` ```
Pour Z-Event, les sorties attendues affichées en commentaire ne sont pas écrites à la main :
le générateur exécute les solutions de référence et injecte les valeurs obtenues. Modifier
un montant dans le jeu de données met donc à jour tous les résultats de l'énoncé.
Modifier un exercice dans `_generateur.py` puis relancer met à jour les deux notebooks. Modifier un exercice dans `_generateur.py` puis relancer met à jour les deux notebooks.
Attention : `_generateur.py` contient le corrigé en clair, et `build_docs.py` publie les Attention : les deux générateurs contiennent le corrigé en clair, et `build_docs.py` publie
fichiers `.py` comme ressources téléchargeables. Il est explicitement exclu du build les fichiers `.py` comme ressources téléchargeables. Depuis le 7 septembre 2026, **tout
(`EXCLUDE_FILES`), tout comme le présent fichier. Ne pas le renommer sans mettre à jour fichier dont le nom commence par `_` est exclu du build**, quel que soit son type. C'est une
cette exclusion côté serveur. règle de préfixe et non une liste de noms : un nouveau fichier de travail est couvert
d'office. Le nommage `_quelquechose` est donc la convention à respecter pour tout ce qui ne
doit pas partir en ligne. `NOTES_PROF.md`, lui, reste exclu par son nom (`EXCLUDE_FILES`).
--- ---
*Lycée Charlotte Perriand, NSI Terminale, Licence CC BY-SA 4.0* *Lycée Charlotte Perriand, NSI Terminale, Licence CC BY-SA 4.0*
+29 -22
View File
@@ -1,34 +1,41 @@
# Révisions de rentrée : PERRIAND-7 # Révisions de rentrée
Activité de reprise, à faire en début d'année de Terminale. Deux activités de reprise, à faire en début d'année de Terminale. Elles couvrent les mêmes
notions de Première : variables, fonctions, boucles, listes, dictionnaires. Prenez celle
que vous voulez, elles sont indépendantes.
La station orbitale PERRIAND-7 sort de deux mois d'hibernation et ses programmes de bord Rien de nouveau là-dedans : tout a été vu l'an dernier. L'objectif est de remettre le pied
ne répondent plus correctement. À vous de les réveiller, un module à la fois. à l'étrier avant d'attaquer les chapitres de Terminale.
## Ce qu'on révise ## Z-Event : la régie de données d'un marathon caritatif
| Module | Notion | Vous êtes bénévole à la régie d'un marathon caritatif de streaming. Vous écrivez les outils
|---|---| Python qui alimentent le compteur de dons, les classements par chaîne et le planning.
| 1. Le réacteur | variables, affectation, mutabilité |
| 2. Les protocoles | fonctions, paramètres, `return` |
| 3. Le balayage | boucles `for` et `while` |
| 4. La soute | listes, listes de listes |
| 5. L'équipage | dictionnaires |
| 6. Sortie d'orbite | synthèse |
Rien de nouveau : tout a été vu en Première. Mais chaque notion révisée ici resservira - [Lire l'énoncé](TP_ZEvent.md)
directement cette année, en récursivité, en POO, sur les arbres et sur les graphes. - [Télécharger le notebook](https://florianmathieu.prof/hub-docs/nsi/terminale/Revisions_Rentree/TP_ZEvent/TP_ZEvent.ipynb)
## Le notebook Le contenu est identique dans les deux formats. Prenez le notebook si vous travaillez sur
machine, l'énoncé si vous préférez lire à l'écran et coder dans votre éditeur.
[PERRIAND-7_Reveil_des_systemes.ipynb](PERRIAND-7_Reveil_des_systemes.ipynb) ## PERRIAND-7 : le réveil d'une station orbitale
Ouvrable sur [Basthon](https://basthon.fr), Jupyter ou VS Code. Aucune installation, Une station spatiale sort de deux mois d'hibernation et ses programmes de bord ne répondent
aucun fichier annexe. Chaque exercice est accompagné de ses tests : si `OK` s'affiche, plus. Vous les réparez, module par module.
c'est gagné.
Les modules sont indépendants. Chaque module commence par une cellule SECOURS qui remet - [Télécharger le notebook](https://florianmathieu.prof/hub-docs/nsi/terminale/Revisions_Rentree/PERRIAND-7_Reveil_des_systemes/PERRIAND-7_Reveil_des_systemes.ipynb)
en place ses données : vous pouvez passer au suivant sans avoir fini le précédent.
Chaque exercice est accompagné de ses tests : si `OK` s'affiche, c'est gagné. Les modules
sont indépendants, chacun commence par une cellule SECOURS qui remet en place ses données.
## Ouvrir un notebook
Le fichier se termine par `.ipynb`. Une fois téléchargé, ouvrez-le avec le
[notebook du cours](https://notebook.florianmathieu.prof) : menu `Fichier`, puis `Ouvrir...`.
Rien à installer. Il fonctionne aussi avec VSCodium, l'éditeur installé au lycée, ou avec
[Basthon](https://basthon.fr).
Aucune bibliothèque, aucun fichier annexe : tout est dans le notebook.
--- ---
*Lycée Charlotte Perriand, NSI Terminale, Licence CC BY-SA 4.0* *Lycée Charlotte Perriand, NSI Terminale, Licence CC BY-SA 4.0*
+857
View File
@@ -0,0 +1,857 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "bf05817a",
"metadata": {},
"source": [
"# TP : Z-Event - La régie de données d'un marathon caritatif\n",
"\n",
"> **Thème** : Révisions Python - variables, fonctions, boucles, listes, dictionnaires\n",
"\n",
"---\n",
"\n",
"## Contexte\n",
"\n",
"Depuis 2016, le **Z-Event** réunit chaque année des dizaines de streamers francophones\n",
"pendant plusieurs jours d'affilée, dans un même lieu, pour récolter des dons au profit\n",
"d'une association. L'événement a été créé par **ZeratoR** et **Dach**. Le principe est\n",
"simple : les spectateurs donnent, les streamers relèvent des défis, et un compteur géant\n",
"affiche la cagnotte en direct.\n",
"\n",
"Derrière ce compteur, il y a du code. Chaque don qui tombe doit être enregistré, additionné,\n",
"attribué à la chaîne sur laquelle il a été fait, comparé aux paliers annoncés, puis réaffiché\n",
"en moins d'une seconde sur tous les écrans. C'est ce travail que vous allez faire ici.\n",
"\n",
"Vous êtes bénévole à la **régie de données** de l'événement. Votre mission : écrire les\n",
"outils Python qui alimentent le compteur et les classements.\n",
"\n",
"> Les données de ce TP (montants, pseudos, horaires, cagnottes) sont **entièrement inventées**\n",
"> pour l'exercice. Elles ne correspondent à aucune édition réelle du Z-Event.\n",
"\n",
"---\n",
"\n",
"## Objectifs pédagogiques\n",
"\n",
"- Manipuler des variables et reconnaître une erreur de type\n",
"- Écrire des fonctions qui **renvoient** une valeur au lieu de l'afficher\n",
"- Parcourir une liste avec une boucle et accumuler un résultat\n",
"- Construire et exploiter des dictionnaires, y compris des dictionnaires de listes\n",
"- Réinvestir ces notions dans les chapitres de Terminale : POO, structures linéaires, graphes\n",
"\n",
"Aucune de ces notions n'est nouvelle : tout a été vu en Première. L'objectif est de les\n",
"remettre en service après la coupure estivale.\n",
"\n",
"---\n",
"\n",
"## Les données\n",
"\n",
"Copiez ce bloc en tête de votre fichier. Toutes les parties du TP s'appuient dessus."
]
},
{
"cell_type": "code",
"id": "62c3fbf5",
"metadata": {},
"source": [
"# Toutes ces données sont inventées pour l'exercice.\n",
"objectif = 2500000\n",
"collecte = 1837450.75\n",
"\n",
"dons = [\n",
" {\"pseudo\": \"Nyx_42\", \"streamer\": \"Antoine Daniel\", \"montant\": 5.0, \"heure\": \"18:04\"},\n",
" {\"pseudo\": \"TitiLeBg\", \"streamer\": \"MisterMV\", \"montant\": 20.0, \"heure\": \"18:11\"},\n",
" {\"pseudo\": \"Camille_R\", \"streamer\": \"Joueur du Grenier\", \"montant\": 50.0, \"heure\": \"18:23\"},\n",
" {\"pseudo\": \"Nyx_42\", \"streamer\": \"Baghera Jones\", \"montant\": 15.5, \"heure\": \"18:37\"},\n",
" {\"pseudo\": \"Poulpe3000\", \"streamer\": \"Horty\", \"montant\": 2.0, \"heure\": \"18:41\"},\n",
" {\"pseudo\": \"Anonyme\", \"streamer\": \"Ultia\", \"montant\": 120.0, \"heure\": \"18:52\"},\n",
" {\"pseudo\": \"Sam_du_59\", \"streamer\": \"Antoine Daniel\", \"montant\": 10.0, \"heure\": \"19:03\"},\n",
" {\"pseudo\": \"Camille_R\", \"streamer\": \"Sylvain Levy\", \"montant\": 30.0, \"heure\": \"19:14\"},\n",
" {\"pseudo\": \"Lulu_2007\", \"streamer\": \"ZeratoR\", \"montant\": 8.5, \"heure\": \"19:20\"},\n",
" {\"pseudo\": \"TitiLeBg\", \"streamer\": \"Etoiles\", \"montant\": 45.0, \"heure\": \"19:33\"},\n",
" {\"pseudo\": \"Marion_NSI\", \"streamer\": \"Joueur du Grenier\", \"montant\": 75.0, \"heure\": \"19:47\"},\n",
" {\"pseudo\": \"Poulpe3000\", \"streamer\": \"MisterMV\", \"montant\": 12.0, \"heure\": \"19:55\"},\n",
" {\"pseudo\": \"Anonyme\", \"streamer\": \"Domingo\", \"montant\": 250.0, \"heure\": \"20:08\"},\n",
" {\"pseudo\": \"Kevin_B\", \"streamer\": \"Baghera Jones\", \"montant\": 5.0, \"heure\": \"20:16\"},\n",
" {\"pseudo\": \"Nyx_42\", \"streamer\": \"Maghla\", \"montant\": 60.0, \"heure\": \"20:29\"},\n",
" {\"pseudo\": \"Sam_du_59\", \"streamer\": \"Horty\", \"montant\": 3.5, \"heure\": \"20:38\"},\n",
" {\"pseudo\": \"Marion_NSI\", \"streamer\": \"Antoine Daniel\", \"montant\": 100.0, \"heure\": \"20:44\"},\n",
" {\"pseudo\": \"Lulu_2007\", \"streamer\": \"Alphacast\", \"montant\": 18.0, \"heure\": \"20:57\"},\n",
" {\"pseudo\": \"Camille_R\", \"streamer\": \"Ultia\", \"montant\": 40.0, \"heure\": \"21:05\"},\n",
" {\"pseudo\": \"Kevin_B\", \"streamer\": \"Ponce\", \"montant\": 22.5, \"heure\": \"21:12\"},\n",
" {\"pseudo\": \"TitiLeBg\", \"streamer\": \"Joueur du Grenier\", \"montant\": 15.0, \"heure\": \"21:26\"},\n",
" {\"pseudo\": \"Anonyme\", \"streamer\": \"MisterMV\", \"montant\": 500.0, \"heure\": \"21:39\"},\n",
" {\"pseudo\": \"Poulpe3000\", \"streamer\": \"Sylvain Levy\", \"montant\": 7.5, \"heure\": \"21:48\"},\n",
" {\"pseudo\": \"Marion_NSI\", \"streamer\": \"Baghera Jones\", \"montant\": 35.0, \"heure\": \"21:59\"},\n",
"]"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "bc90ce2a",
"metadata": {},
"source": [
"---\n",
"\n",
"## Partie 1 : Le compteur (variables et types)\n",
"\n",
"### 1.1. État de la cagnotte"
]
},
{
"cell_type": "markdown",
"id": "245368be",
"metadata": {},
"source": [
"**Question 1.1** : Calculez dans une variable `pourcentage` la part de l'objectif déjà\n",
"atteinte, arrondie à deux décimales."
]
},
{
"cell_type": "code",
"id": "2052888a",
"metadata": {},
"source": [
"pourcentage = # À compléter\n",
"\n",
"print(f\"Objectif atteint à {pourcentage} %\") # Objectif atteint à 73.5 %"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "b97c5eaf",
"metadata": {},
"source": [
"**Question 1.2** : Calculez dans une variable `reste` la somme qu'il manque encore pour\n",
"atteindre l'objectif."
]
},
{
"cell_type": "code",
"id": "00b6e536",
"metadata": {},
"source": [
"reste = # À compléter\n",
"\n",
"print(f\"Il manque {reste} euros\") # Il manque 662549.25 euros"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "35235678",
"metadata": {},
"source": [
"### 1.2. Le piège du compteur\n",
"\n",
"Le compteur du site reçoit les montants sous forme de **chaînes de caractères**, parce qu'ils\n",
"arrivent d'un formulaire web. Le code ci-dessous ne fait donc pas ce qu'on croit."
]
},
{
"cell_type": "markdown",
"id": "729d870a",
"metadata": {},
"source": [
"**Question 1.3** : Exécutez ce code, observez le résultat, puis corrigez-le pour que\n",
"`total` contienne bien la somme des deux dons."
]
},
{
"cell_type": "code",
"id": "0ba70c50",
"metadata": {},
"source": [
"don_1 = \"25\"\n",
"don_2 = \"40\"\n",
"\n",
"total = don_1 + don_2\n",
"print(total) # affiche 2540 au lieu de 65\n",
"\n",
"# Correction :\n",
"total = # À compléter\n",
"print(total) # 65"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "ca07a1b6",
"metadata": {},
"source": [
"---\n",
"\n",
"## Partie 2 : L'annonce des dons (fonctions)\n",
"\n",
"Un don qui tombe doit être annoncé à l'écran. Ces fonctions produisent les messages.\n",
"\n",
"### 2.1. Formater un don"
]
},
{
"cell_type": "markdown",
"id": "2d6d0204",
"metadata": {},
"source": [
"**Question 2.1** : Écrivez la fonction `formater_don(pseudo, montant)` qui **renvoie** la\n",
"chaîne d'annonce. Attention : elle doit la renvoyer, pas l'afficher."
]
},
{
"cell_type": "code",
"id": "91af8ea0",
"metadata": {},
"source": [
"def formater_don(pseudo, montant):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(formater_don(\"Nyx_42\", 5.0)) # Nyx_42 vient de donner 5.0 euros\n",
"print(formater_don(\"Marion_NSI\", 100.0)) # Marion_NSI vient de donner 100.0 euros"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "dbccc201",
"metadata": {},
"source": [
"### 2.2. Repérer les gros dons\n",
"\n",
"La régie déclenche une animation spéciale au-delà d'un certain montant."
]
},
{
"cell_type": "markdown",
"id": "e5808117",
"metadata": {},
"source": [
"**Question 2.2** : Écrivez `est_gros_don(montant, seuil)` qui renvoie `True` si le montant\n",
"est **supérieur ou égal** au seuil, `False` sinon. Le seuil vaut 50 par défaut."
]
},
{
"cell_type": "code",
"id": "927dd827",
"metadata": {},
"source": [
"def est_gros_don(montant, seuil=50):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(est_gros_don(120.0)) # True\n",
"print(est_gros_don(50.0)) # True\n",
"print(est_gros_don(20.0)) # False\n",
"print(est_gros_don(20.0, 10)) # True"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "88a5c245",
"metadata": {},
"source": [
"### 2.3. Les paliers\n",
"\n",
"Les organisateurs annoncent des paliers au fil de la collecte.\n",
"\n",
"| Cagnotte | Palier |\n",
"|----------|--------|\n",
"| moins de 500 000 | `\"Départ\"` |\n",
"| de 500 000 à 999 999 | `\"Palier 1\"` |\n",
"| de 1 000 000 à 1 999 999 | `\"Palier 2\"` |\n",
"| 2 000 000 et plus | `\"Objectif\"` |"
]
},
{
"cell_type": "markdown",
"id": "c0f285d0",
"metadata": {},
"source": [
"**Question 2.3** : Écrivez `palier(total)` qui renvoie le nom du palier atteint."
]
},
{
"cell_type": "code",
"id": "5a353ce4",
"metadata": {},
"source": [
"def palier(total):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(palier(120000)) # Départ\n",
"print(palier(750000)) # Palier 1\n",
"print(palier(1837450.75)) # Palier 2\n",
"print(palier(2500000)) # Objectif"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "fd1db284",
"metadata": {},
"source": [
"### 2.4. Le message complet"
]
},
{
"cell_type": "markdown",
"id": "0eda16ce",
"metadata": {},
"source": [
"**Question 2.4** : Écrivez `annonce_regie(pseudo, montant)` qui renvoie le message affiché\n",
"en régie. Votre fonction **doit appeler** `formater_don` et `est_gros_don` : ne recopiez pas\n",
"leur contenu.\n",
"\n",
"Le message se termine par `\" [GROS DON]\"` lorsque le montant déclenche l'animation."
]
},
{
"cell_type": "code",
"id": "63490a01",
"metadata": {},
"source": [
"def annonce_regie(pseudo, montant):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(annonce_regie(\"Poulpe3000\", 2.0)) # Poulpe3000 vient de donner 2.0 euros\n",
"print(annonce_regie(\"Anonyme\", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON]"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "2e4c7e7f",
"metadata": {},
"source": [
"---\n",
"\n",
"## Partie 3 : L'historique des dons (boucles et listes)\n",
"\n",
"La liste `dons` contient les vingt-quatre derniers dons enregistrés. Chaque don est un\n",
"dictionnaire à quatre clés : `pseudo`, `streamer`, `montant`, `heure`.\n",
"\n",
"### 3.1. Le total collecté"
]
},
{
"cell_type": "markdown",
"id": "3c9df8f3",
"metadata": {},
"source": [
"**Question 3.1** : Écrivez `total_dons(liste_dons)` qui renvoie la somme de tous les\n",
"montants. Utilisez une boucle et un accumulateur, pas la fonction `sum`."
]
},
{
"cell_type": "code",
"id": "b2e587a4",
"metadata": {},
"source": [
"def total_dons(liste_dons):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(total_dons(dons)) # 1449.5\n",
"print(total_dons([])) # 0"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "c65fb763",
"metadata": {},
"source": [
"### 3.2. Le don le plus élevé"
]
},
{
"cell_type": "markdown",
"id": "3d3882b8",
"metadata": {},
"source": [
"**Question 3.2** : Écrivez `plus_gros_don(liste_dons)` qui renvoie le **dictionnaire** du\n",
"don le plus élevé. L'usage de `max` est interdit. On suppose la liste non vide."
]
},
{
"cell_type": "code",
"id": "9eaac029",
"metadata": {},
"source": [
"def plus_gros_don(liste_dons):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"record = plus_gros_don(dons)\n",
"print(record[\"pseudo\"], record[\"montant\"]) # Anonyme 500.0"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "aa89e941",
"metadata": {},
"source": [
"### 3.3. Compter et filtrer"
]
},
{
"cell_type": "markdown",
"id": "d357b858",
"metadata": {},
"source": [
"**Question 3.3** : Écrivez `compter_gros_dons(liste_dons, seuil)` qui renvoie le nombre de\n",
"dons supérieurs ou égaux au seuil. Réutilisez `est_gros_don`."
]
},
{
"cell_type": "code",
"id": "4c02a2b9",
"metadata": {},
"source": [
"def compter_gros_dons(liste_dons, seuil=50):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(compter_gros_dons(dons)) # 7\n",
"print(compter_gros_dons(dons, 100)) # 4"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "fae9409f",
"metadata": {},
"source": [
"**Question 3.4** : Écrivez `donateurs(liste_dons)` qui renvoie la liste des pseudos **sans\n",
"doublon**, dans l'ordre de leur premier don."
]
},
{
"cell_type": "code",
"id": "b98464c0",
"metadata": {},
"source": [
"def donateurs(liste_dons):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(donateurs(dons))\n",
"# ['Nyx_42', 'TitiLeBg', 'Camille_R', 'Poulpe3000', 'Anonyme', 'Sam_du_59', 'Lulu_2007', 'Marion_NSI', 'Kevin_B']"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "87fd84e5",
"metadata": {},
"source": [
"### 3.4. Le bandeau des derniers dons\n",
"\n",
"Le site n'affiche que les tout derniers dons reçus."
]
},
{
"cell_type": "markdown",
"id": "a57d2849",
"metadata": {},
"source": [
"**Question 3.5** : Écrivez `derniers_dons(liste_dons, n)` qui renvoie les `n` derniers dons\n",
"de la liste, en utilisant une **tranche**."
]
},
{
"cell_type": "code",
"id": "769c9428",
"metadata": {},
"source": [
"def derniers_dons(liste_dons, n):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"for don in derniers_dons(dons, 3):\n",
" print(don[\"heure\"], don[\"pseudo\"])\n",
"# 21:39 Anonyme\n",
"# 21:48 Poulpe3000\n",
"# 21:59 Marion_NSI"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "9e3fe8d8",
"metadata": {},
"source": [
"---\n",
"\n",
"## Partie 4 : Le classement des chaînes (dictionnaires)\n",
"\n",
"Chaque don est attribué à la chaîne sur laquelle il a été fait. La régie tient à jour la\n",
"cagnotte de chaque streamer.\n",
"\n",
"### 4.1. La cagnotte de chaque chaîne"
]
},
{
"cell_type": "markdown",
"id": "166e98fd",
"metadata": {},
"source": [
"**Question 4.1** : Écrivez `cagnottes(liste_dons)` qui renvoie un dictionnaire associant à\n",
"chaque streamer le total des dons reçus sur sa chaîne."
]
},
{
"cell_type": "code",
"id": "1c270111",
"metadata": {},
"source": [
"def cagnottes(liste_dons):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"totaux = cagnottes(dons)\n",
"print(totaux[\"Antoine Daniel\"]) # 115.0\n",
"print(totaux[\"Joueur du Grenier\"]) # 140.0\n",
"print(len(totaux)) # 13"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "9f6b6bf5",
"metadata": {},
"source": [
"### 4.2. La chaîne en tête"
]
},
{
"cell_type": "markdown",
"id": "a759ae20",
"metadata": {},
"source": [
"**Question 4.2** : Écrivez `meilleure_chaine(totaux)` qui renvoie le nom du streamer dont\n",
"la cagnotte est la plus élevée.\n",
"\n",
"Rappel : `for cle in dictionnaire` parcourt les clés."
]
},
{
"cell_type": "code",
"id": "0a6ce0d9",
"metadata": {},
"source": [
"def meilleure_chaine(totaux):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(meilleure_chaine(cagnottes(dons))) # MisterMV"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "a2ee2282",
"metadata": {},
"source": [
"### 4.3. Le détail des dons\n",
"\n",
"On veut maintenant, pour chaque streamer, la **liste** des montants reçus et plus seulement\n",
"leur somme : un dictionnaire dont les valeurs sont des listes."
]
},
{
"cell_type": "markdown",
"id": "b9eac959",
"metadata": {},
"source": [
"**Question 4.3** : Écrivez `dons_par_chaine(liste_dons)` qui renvoie un dictionnaire\n",
"associant à chaque streamer la liste des montants reçus, dans l'ordre chronologique."
]
},
{
"cell_type": "code",
"id": "d47e5375",
"metadata": {},
"source": [
"def dons_par_chaine(liste_dons):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"detail = dons_par_chaine(dons)\n",
"print(detail[\"MisterMV\"]) # [20.0, 12.0, 500.0]\n",
"print(detail[\"Baghera Jones\"]) # [15.5, 5.0, 35.0]"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "f5a46137",
"metadata": {},
"source": [
"### 4.4. Le podium"
]
},
{
"cell_type": "markdown",
"id": "ea80de46",
"metadata": {},
"source": [
"**Question 4.4** : Écrivez `podium(totaux)` qui renvoie la liste des trois premiers\n",
"streamers, du plus gros total au plus petit.\n",
"\n",
"Indication : `sorted(totaux, key=..., reverse=True)` trie les clés selon un critère."
]
},
{
"cell_type": "code",
"id": "262c0672",
"metadata": {},
"source": [
"def podium(totaux):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(podium(cagnottes(dons)))\n",
"# ['MisterMV', 'Domingo', 'Ultia']"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "4ebedc4c",
"metadata": {},
"source": [
"---\n",
"\n",
"## Partie 5 : Le planning et les raids (synthèse)\n",
"\n",
"Un **raid**, c'est lorsqu'un streamer envoie son public sur une autre chaîne à la fin de son\n",
"passage. Cette partie combine tout ce qui précède.\n",
"\n",
"Copiez ce second bloc de données à la suite du premier."
]
},
{
"cell_type": "code",
"id": "2ed28b19",
"metadata": {},
"source": [
"planning = [\n",
" (\"Antoine Daniel\", 18, 20),\n",
" (\"MisterMV\", 20, 22),\n",
" (\"Joueur du Grenier\", 22, 24),\n",
" (\"Baghera Jones\", 0, 3),\n",
" (\"Ultia\", 3, 6),\n",
"]\n",
"\n",
"raids = {\n",
" \"Antoine Daniel\": [\"MisterMV\"],\n",
" \"MisterMV\": [\"Joueur du Grenier\", \"Horty\"],\n",
" \"Joueur du Grenier\": [\"Baghera Jones\"],\n",
" \"Baghera Jones\": [\"Ultia\"],\n",
" \"Ultia\": [],\n",
" \"Horty\": [\"Sylvain Levy\"],\n",
" \"Sylvain Levy\": [],\n",
"}"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "d741bfa1",
"metadata": {},
"source": [
"### 5.1. Qui est à l'antenne\n",
"\n",
"Le planning est une liste de triplets `(streamer, heure_de_debut, heure_de_fin)`, en heures\n",
"entières sur vingt-quatre heures. Attention au créneau de Baghera Jones : il commence à\n",
"minuit, donc son heure de début est plus petite que celle de la personne qui la précède."
]
},
{
"cell_type": "markdown",
"id": "18718573",
"metadata": {},
"source": [
"**Question 5.1** : Écrivez `a_l_antenne(planning, heure)` qui renvoie le nom du streamer en\n",
"direct à cette heure, ou `None` si personne ne l'est.\n",
"\n",
"L'heure de début est incluse, l'heure de fin est exclue."
]
},
{
"cell_type": "code",
"id": "9f1639da",
"metadata": {},
"source": [
"def a_l_antenne(planning, heure):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(a_l_antenne(planning, 19)) # Antoine Daniel\n",
"print(a_l_antenne(planning, 22)) # Joueur du Grenier\n",
"print(a_l_antenne(planning, 1)) # Baghera Jones\n",
"print(a_l_antenne(planning, 10)) # None"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "0dbf8608",
"metadata": {},
"source": [
"### 5.2. Suivre la chaîne des raids\n",
"\n",
"Le dictionnaire `raids` associe à chaque streamer la liste de ceux vers qui il envoie son\n",
"public. Certains n'envoient personne : leur liste est vide."
]
},
{
"cell_type": "markdown",
"id": "342c85a1",
"metadata": {},
"source": [
"**Question 5.2** : Écrivez `suivre_raids(raids, depart)` qui renvoie la liste des chaînes\n",
"traversées en partant de `depart` et en suivant à chaque fois le **premier** raid, jusqu'à\n",
"tomber sur un streamer qui n'en fait aucun.\n",
"\n",
"Le point de départ fait partie du résultat."
]
},
{
"cell_type": "code",
"id": "67ef757e",
"metadata": {},
"source": [
"def suivre_raids(raids, depart):\n",
" # À compléter\n",
" pass\n",
"\n",
"# Test\n",
"print(suivre_raids(raids, \"Antoine Daniel\"))\n",
"# ['Antoine Daniel', 'MisterMV', 'Joueur du Grenier', 'Baghera Jones', 'Ultia']\n",
"print(suivre_raids(raids, \"Horty\"))\n",
"# ['Horty', 'Sylvain Levy']\n",
"print(suivre_raids(raids, \"Ultia\"))\n",
"# ['Ultia']"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"id": "f66fcb63",
"metadata": {},
"source": [
"---\n",
"\n",
"## Questions de synthèse\n",
"\n",
"Répondez en quelques lignes, sans machine.\n",
"\n",
"1. À la question 1.3, `\"25\" + \"40\"` donne `\"2540\"`. Expliquez pourquoi Python ne signale\n",
" aucune erreur alors que le résultat est faux du point de vue de la régie.\n",
"\n",
"2. À la question 3.2, pourquoi initialise-t-on `record` avec le premier don de la liste\n",
" plutôt qu'avec un montant de 0 ?\n",
"\n",
"3. Aux questions 4.1 et 4.3, on parcourt la même liste de dons mais on ne construit pas la\n",
" même structure. Dans quel cas le dictionnaire de listes est-il préférable au dictionnaire\n",
" de totaux ?\n",
"\n",
"4. La question 5.2 s'arrête lorsqu'un streamer ne fait aucun raid. Que se passerait-il si\n",
" deux streamers se renvoyaient mutuellement leur public ? Comment modifieriez-vous la\n",
" fonction pour l'éviter ?\n",
"\n",
"---\n",
"\n",
"## Ce que vous venez de réviser\n",
"\n",
"| Partie | Notion | Chapitre de Terminale concerné |\n",
"|--------|--------|--------------------------------|\n",
"| 1 | Variables, types, conversions | POO, structures linéaires |\n",
"| 2 | Fonctions, `return`, paramètre par défaut | Récursivité, Modularité |\n",
"| 3 | Boucles, accumulateur, listes, tranches | Parcours d'arbres et de graphes, tri fusion |\n",
"| 4 | Dictionnaires, dictionnaires de listes | Graphes, programmation dynamique |\n",
"| 5 | Synthèse, parcours d'un enchaînement | Graphes, Pile et File |\n",
"\n",
"La question 5.2 est un parcours de graphe : `raids` est une **liste d'adjacence**, exactement\n",
"la structure que vous utiliserez au chapitre Graphes.\n",
"\n",
"---\n",
"\n",
"## Barème indicatif\n",
"\n",
"| Partie | Points |\n",
"|--------|--------|\n",
"| Partie 1 : Le compteur | 3 |\n",
"| Partie 2 : L'annonce des dons | 5 |\n",
"| Partie 3 : L'historique des dons | 5 |\n",
"| Partie 4 : Le classement des chaînes | 4 |\n",
"| Partie 5 : Le planning et les raids | 2 |\n",
"| Questions de synthèse | 1 |\n",
"| **Total** | **20** |\n",
"\n",
"---\n",
"\n",
"Auteur : Florian Mathieu\n",
"\n",
"Licence CC BY-SA\n",
"\n",
"<a rel=\"license\" href=\"http://creativecommons.org/licenses/by-sa/4.0/\"><img alt=\"Licence Creative Commons\" style=\"border-width:0\" src=\"https://i.creativecommons.org/l/by-sa/4.0/88x31.png\" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel=\"license\" href=\"http://creativecommons.org/licenses/by-sa/4.0/\">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+497
View File
@@ -0,0 +1,497 @@
# TP : Z-Event - La régie de données d'un marathon caritatif
> **Thème** : Révisions Python - variables, fonctions, boucles, listes, dictionnaires
---
## Contexte
Depuis 2016, le **Z-Event** réunit chaque année des dizaines de streamers francophones
pendant plusieurs jours d'affilée, dans un même lieu, pour récolter des dons au profit
d'une association. L'événement a été créé par **ZeratoR** et **Dach**. Le principe est
simple : les spectateurs donnent, les streamers relèvent des défis, et un compteur géant
affiche la cagnotte en direct.
Derrière ce compteur, il y a du code. Chaque don qui tombe doit être enregistré, additionné,
attribué à la chaîne sur laquelle il a été fait, comparé aux paliers annoncés, puis réaffiché
en moins d'une seconde sur tous les écrans. C'est ce travail que vous allez faire ici.
Vous êtes bénévole à la **régie de données** de l'événement. Votre mission : écrire les
outils Python qui alimentent le compteur et les classements.
> Les données de ce TP (montants, pseudos, horaires, cagnottes) sont **entièrement inventées**
> pour l'exercice. Elles ne correspondent à aucune édition réelle du Z-Event.
---
## Objectifs pédagogiques
- Manipuler des variables et reconnaître une erreur de type
- Écrire des fonctions qui **renvoient** une valeur au lieu de l'afficher
- Parcourir une liste avec une boucle et accumuler un résultat
- Construire et exploiter des dictionnaires, y compris des dictionnaires de listes
- Réinvestir ces notions dans les chapitres de Terminale : POO, structures linéaires, graphes
Aucune de ces notions n'est nouvelle : tout a été vu en Première. L'objectif est de les
remettre en service après la coupure estivale.
---
## Les données
Copiez ce bloc en tête de votre fichier. Toutes les parties du TP s'appuient dessus.
```python
# Toutes ces données sont inventées pour l'exercice.
objectif = 2500000
collecte = 1837450.75
dons = [
{"pseudo": "Nyx_42", "streamer": "Antoine Daniel", "montant": 5.0, "heure": "18:04"},
{"pseudo": "TitiLeBg", "streamer": "MisterMV", "montant": 20.0, "heure": "18:11"},
{"pseudo": "Camille_R", "streamer": "Joueur du Grenier", "montant": 50.0, "heure": "18:23"},
{"pseudo": "Nyx_42", "streamer": "Baghera Jones", "montant": 15.5, "heure": "18:37"},
{"pseudo": "Poulpe3000", "streamer": "Horty", "montant": 2.0, "heure": "18:41"},
{"pseudo": "Anonyme", "streamer": "Ultia", "montant": 120.0, "heure": "18:52"},
{"pseudo": "Sam_du_59", "streamer": "Antoine Daniel", "montant": 10.0, "heure": "19:03"},
{"pseudo": "Camille_R", "streamer": "Sylvain Levy", "montant": 30.0, "heure": "19:14"},
{"pseudo": "Lulu_2007", "streamer": "ZeratoR", "montant": 8.5, "heure": "19:20"},
{"pseudo": "TitiLeBg", "streamer": "Etoiles", "montant": 45.0, "heure": "19:33"},
{"pseudo": "Marion_NSI", "streamer": "Joueur du Grenier", "montant": 75.0, "heure": "19:47"},
{"pseudo": "Poulpe3000", "streamer": "MisterMV", "montant": 12.0, "heure": "19:55"},
{"pseudo": "Anonyme", "streamer": "Domingo", "montant": 250.0, "heure": "20:08"},
{"pseudo": "Kevin_B", "streamer": "Baghera Jones", "montant": 5.0, "heure": "20:16"},
{"pseudo": "Nyx_42", "streamer": "Maghla", "montant": 60.0, "heure": "20:29"},
{"pseudo": "Sam_du_59", "streamer": "Horty", "montant": 3.5, "heure": "20:38"},
{"pseudo": "Marion_NSI", "streamer": "Antoine Daniel", "montant": 100.0, "heure": "20:44"},
{"pseudo": "Lulu_2007", "streamer": "Alphacast", "montant": 18.0, "heure": "20:57"},
{"pseudo": "Camille_R", "streamer": "Ultia", "montant": 40.0, "heure": "21:05"},
{"pseudo": "Kevin_B", "streamer": "Ponce", "montant": 22.5, "heure": "21:12"},
{"pseudo": "TitiLeBg", "streamer": "Joueur du Grenier", "montant": 15.0, "heure": "21:26"},
{"pseudo": "Anonyme", "streamer": "MisterMV", "montant": 500.0, "heure": "21:39"},
{"pseudo": "Poulpe3000", "streamer": "Sylvain Levy", "montant": 7.5, "heure": "21:48"},
{"pseudo": "Marion_NSI", "streamer": "Baghera Jones", "montant": 35.0, "heure": "21:59"},
]
```
---
## Partie 1 : Le compteur (variables et types)
### 1.1. État de la cagnotte
**Question 1.1** : Calculez dans une variable `pourcentage` la part de l'objectif déjà
atteinte, arrondie à deux décimales.
```python
pourcentage = # À compléter
print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à 73.5 %
```
**Question 1.2** : Calculez dans une variable `reste` la somme qu'il manque encore pour
atteindre l'objectif.
```python
reste = # À compléter
print(f"Il manque {reste} euros") # Il manque 662549.25 euros
```
### 1.2. Le piège du compteur
Le compteur du site reçoit les montants sous forme de **chaînes de caractères**, parce qu'ils
arrivent d'un formulaire web. Le code ci-dessous ne fait donc pas ce qu'on croit.
**Question 1.3** : Exécutez ce code, observez le résultat, puis corrigez-le pour que
`total` contienne bien la somme des deux dons.
```python
don_1 = "25"
don_2 = "40"
total = don_1 + don_2
print(total) # affiche 2540 au lieu de 65
# Correction :
total = # À compléter
print(total) # 65
```
---
## Partie 2 : L'annonce des dons (fonctions)
Un don qui tombe doit être annoncé à l'écran. Ces fonctions produisent les messages.
### 2.1. Formater un don
**Question 2.1** : Écrivez la fonction `formater_don(pseudo, montant)` qui **renvoie** la
chaîne d'annonce. Attention : elle doit la renvoyer, pas l'afficher.
```python
def formater_don(pseudo, montant):
# À compléter
pass
# Test
print(formater_don("Nyx_42", 5.0)) # Nyx_42 vient de donner 5.0 euros
print(formater_don("Marion_NSI", 100.0)) # Marion_NSI vient de donner 100.0 euros
```
### 2.2. Repérer les gros dons
La régie déclenche une animation spéciale au-delà d'un certain montant.
**Question 2.2** : Écrivez `est_gros_don(montant, seuil)` qui renvoie `True` si le montant
est **supérieur ou égal** au seuil, `False` sinon. Le seuil vaut 50 par défaut.
```python
def est_gros_don(montant, seuil=50):
# À compléter
pass
# Test
print(est_gros_don(120.0)) # True
print(est_gros_don(50.0)) # True
print(est_gros_don(20.0)) # False
print(est_gros_don(20.0, 10)) # True
```
### 2.3. Les paliers
Les organisateurs annoncent des paliers au fil de la collecte.
| Cagnotte | Palier |
|----------|--------|
| moins de 500 000 | `"Départ"` |
| de 500 000 à 999 999 | `"Palier 1"` |
| de 1 000 000 à 1 999 999 | `"Palier 2"` |
| 2 000 000 et plus | `"Objectif"` |
**Question 2.3** : Écrivez `palier(total)` qui renvoie le nom du palier atteint.
```python
def palier(total):
# À compléter
pass
# Test
print(palier(120000)) # Départ
print(palier(750000)) # Palier 1
print(palier(1837450.75)) # Palier 2
print(palier(2500000)) # Objectif
```
### 2.4. Le message complet
**Question 2.4** : Écrivez `annonce_regie(pseudo, montant)` qui renvoie le message affiché
en régie. Votre fonction **doit appeler** `formater_don` et `est_gros_don` : ne recopiez pas
leur contenu.
Le message se termine par `" [GROS DON]"` lorsque le montant déclenche l'animation.
```python
def annonce_regie(pseudo, montant):
# À compléter
pass
# Test
print(annonce_regie("Poulpe3000", 2.0)) # Poulpe3000 vient de donner 2.0 euros
print(annonce_regie("Anonyme", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON]
```
---
## Partie 3 : L'historique des dons (boucles et listes)
La liste `dons` contient les vingt-quatre derniers dons enregistrés. Chaque don est un
dictionnaire à quatre clés : `pseudo`, `streamer`, `montant`, `heure`.
### 3.1. Le total collecté
**Question 3.1** : Écrivez `total_dons(liste_dons)` qui renvoie la somme de tous les
montants. Utilisez une boucle et un accumulateur, pas la fonction `sum`.
```python
def total_dons(liste_dons):
# À compléter
pass
# Test
print(total_dons(dons)) # 1449.5
print(total_dons([])) # 0
```
### 3.2. Le don le plus élevé
**Question 3.2** : Écrivez `plus_gros_don(liste_dons)` qui renvoie le **dictionnaire** du
don le plus élevé. L'usage de `max` est interdit. On suppose la liste non vide.
```python
def plus_gros_don(liste_dons):
# À compléter
pass
# Test
record = plus_gros_don(dons)
print(record["pseudo"], record["montant"]) # Anonyme 500.0
```
### 3.3. Compter et filtrer
**Question 3.3** : Écrivez `compter_gros_dons(liste_dons, seuil)` qui renvoie le nombre de
dons supérieurs ou égaux au seuil. Réutilisez `est_gros_don`.
```python
def compter_gros_dons(liste_dons, seuil=50):
# À compléter
pass
# Test
print(compter_gros_dons(dons)) # 7
print(compter_gros_dons(dons, 100)) # 4
```
**Question 3.4** : Écrivez `donateurs(liste_dons)` qui renvoie la liste des pseudos **sans
doublon**, dans l'ordre de leur premier don.
```python
def donateurs(liste_dons):
# À compléter
pass
# Test
print(donateurs(dons))
# ['Nyx_42', 'TitiLeBg', 'Camille_R', 'Poulpe3000', 'Anonyme', 'Sam_du_59', 'Lulu_2007', 'Marion_NSI', 'Kevin_B']
```
### 3.4. Le bandeau des derniers dons
Le site n'affiche que les tout derniers dons reçus.
**Question 3.5** : Écrivez `derniers_dons(liste_dons, n)` qui renvoie les `n` derniers dons
de la liste, en utilisant une **tranche**.
```python
def derniers_dons(liste_dons, n):
# À compléter
pass
# Test
for don in derniers_dons(dons, 3):
print(don["heure"], don["pseudo"])
# 21:39 Anonyme
# 21:48 Poulpe3000
# 21:59 Marion_NSI
```
---
## Partie 4 : Le classement des chaînes (dictionnaires)
Chaque don est attribué à la chaîne sur laquelle il a été fait. La régie tient à jour la
cagnotte de chaque streamer.
### 4.1. La cagnotte de chaque chaîne
**Question 4.1** : Écrivez `cagnottes(liste_dons)` qui renvoie un dictionnaire associant à
chaque streamer le total des dons reçus sur sa chaîne.
```python
def cagnottes(liste_dons):
# À compléter
pass
# Test
totaux = cagnottes(dons)
print(totaux["Antoine Daniel"]) # 115.0
print(totaux["Joueur du Grenier"]) # 140.0
print(len(totaux)) # 13
```
### 4.2. La chaîne en tête
**Question 4.2** : Écrivez `meilleure_chaine(totaux)` qui renvoie le nom du streamer dont
la cagnotte est la plus élevée.
Rappel : `for cle in dictionnaire` parcourt les clés.
```python
def meilleure_chaine(totaux):
# À compléter
pass
# Test
print(meilleure_chaine(cagnottes(dons))) # MisterMV
```
### 4.3. Le détail des dons
On veut maintenant, pour chaque streamer, la **liste** des montants reçus et plus seulement
leur somme : un dictionnaire dont les valeurs sont des listes.
**Question 4.3** : Écrivez `dons_par_chaine(liste_dons)` qui renvoie un dictionnaire
associant à chaque streamer la liste des montants reçus, dans l'ordre chronologique.
```python
def dons_par_chaine(liste_dons):
# À compléter
pass
# Test
detail = dons_par_chaine(dons)
print(detail["MisterMV"]) # [20.0, 12.0, 500.0]
print(detail["Baghera Jones"]) # [15.5, 5.0, 35.0]
```
### 4.4. Le podium
**Question 4.4** : Écrivez `podium(totaux)` qui renvoie la liste des trois premiers
streamers, du plus gros total au plus petit.
Indication : `sorted(totaux, key=..., reverse=True)` trie les clés selon un critère.
```python
def podium(totaux):
# À compléter
pass
# Test
print(podium(cagnottes(dons)))
# ['MisterMV', 'Domingo', 'Ultia']
```
---
## Partie 5 : Le planning et les raids (synthèse)
Un **raid**, c'est lorsqu'un streamer envoie son public sur une autre chaîne à la fin de son
passage. Cette partie combine tout ce qui précède.
Copiez ce second bloc de données à la suite du premier.
```python
planning = [
("Antoine Daniel", 18, 20),
("MisterMV", 20, 22),
("Joueur du Grenier", 22, 24),
("Baghera Jones", 0, 3),
("Ultia", 3, 6),
]
raids = {
"Antoine Daniel": ["MisterMV"],
"MisterMV": ["Joueur du Grenier", "Horty"],
"Joueur du Grenier": ["Baghera Jones"],
"Baghera Jones": ["Ultia"],
"Ultia": [],
"Horty": ["Sylvain Levy"],
"Sylvain Levy": [],
}
```
### 5.1. Qui est à l'antenne
Le planning est une liste de triplets `(streamer, heure_de_debut, heure_de_fin)`, en heures
entières sur vingt-quatre heures. Attention au créneau de Baghera Jones : il commence à
minuit, donc son heure de début est plus petite que celle de la personne qui la précède.
**Question 5.1** : Écrivez `a_l_antenne(planning, heure)` qui renvoie le nom du streamer en
direct à cette heure, ou `None` si personne ne l'est.
L'heure de début est incluse, l'heure de fin est exclue.
```python
def a_l_antenne(planning, heure):
# À compléter
pass
# Test
print(a_l_antenne(planning, 19)) # Antoine Daniel
print(a_l_antenne(planning, 22)) # Joueur du Grenier
print(a_l_antenne(planning, 1)) # Baghera Jones
print(a_l_antenne(planning, 10)) # None
```
### 5.2. Suivre la chaîne des raids
Le dictionnaire `raids` associe à chaque streamer la liste de ceux vers qui il envoie son
public. Certains n'envoient personne : leur liste est vide.
**Question 5.2** : Écrivez `suivre_raids(raids, depart)` qui renvoie la liste des chaînes
traversées en partant de `depart` et en suivant à chaque fois le **premier** raid, jusqu'à
tomber sur un streamer qui n'en fait aucun.
Le point de départ fait partie du résultat.
```python
def suivre_raids(raids, depart):
# À compléter
pass
# Test
print(suivre_raids(raids, "Antoine Daniel"))
# ['Antoine Daniel', 'MisterMV', 'Joueur du Grenier', 'Baghera Jones', 'Ultia']
print(suivre_raids(raids, "Horty"))
# ['Horty', 'Sylvain Levy']
print(suivre_raids(raids, "Ultia"))
# ['Ultia']
```
---
## Questions de synthèse
Répondez en quelques lignes, sans machine.
1. À la question 1.3, `"25" + "40"` donne `"2540"`. Expliquez pourquoi Python ne signale
aucune erreur alors que le résultat est faux du point de vue de la régie.
2. À la question 3.2, pourquoi initialise-t-on `record` avec le premier don de la liste
plutôt qu'avec un montant de 0 ?
3. Aux questions 4.1 et 4.3, on parcourt la même liste de dons mais on ne construit pas la
même structure. Dans quel cas le dictionnaire de listes est-il préférable au dictionnaire
de totaux ?
4. La question 5.2 s'arrête lorsqu'un streamer ne fait aucun raid. Que se passerait-il si
deux streamers se renvoyaient mutuellement leur public ? Comment modifieriez-vous la
fonction pour l'éviter ?
---
## Ce que vous venez de réviser
| Partie | Notion | Chapitre de Terminale concerné |
|--------|--------|--------------------------------|
| 1 | Variables, types, conversions | POO, structures linéaires |
| 2 | Fonctions, `return`, paramètre par défaut | Récursivité, Modularité |
| 3 | Boucles, accumulateur, listes, tranches | Parcours d'arbres et de graphes, tri fusion |
| 4 | Dictionnaires, dictionnaires de listes | Graphes, programmation dynamique |
| 5 | Synthèse, parcours d'un enchaînement | Graphes, Pile et File |
La question 5.2 est un parcours de graphe : `raids` est une **liste d'adjacence**, exactement
la structure que vous utiliserez au chapitre Graphes.
---
## Barème indicatif
| Partie | Points |
|--------|--------|
| Partie 1 : Le compteur | 3 |
| Partie 2 : L'annonce des dons | 5 |
| Partie 3 : L'historique des dons | 5 |
| Partie 4 : Le classement des chaînes | 4 |
| Partie 5 : Le planning et les raids | 2 |
| Questions de synthèse | 1 |
| **Total** | **20** |
---
Auteur : Florian Mathieu
Licence CC BY-SA
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>.
+862
View File
@@ -0,0 +1,862 @@
# -*- coding: utf-8 -*-
"""Génère le TP Z-Event : énoncé Markdown, corrigé Markdown et notebook élève.
Source unique : les trois fichiers sortent d'ici, ils ne peuvent pas diverger.
Les sorties attendues affichées dans les commentaires (# Test) ne sont PAS écrites
à la main : elles sont calculées en exécutant les solutions de référence au moment
de la génération.
"""
import io, json, os, uuid
DEST = os.path.dirname(os.path.abspath(__file__))
LICENCE = """Auteur : Florian Mathieu
Licence CC BY-SA
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>."""
# ---------------------------------------------------------------- LES DONNÉES
DONNEES_SRC = '''# Toutes ces données sont inventées pour l'exercice.
objectif = 2500000
collecte = 1837450.75
dons = [
{"pseudo": "Nyx_42", "streamer": "Antoine Daniel", "montant": 5.0, "heure": "18:04"},
{"pseudo": "TitiLeBg", "streamer": "MisterMV", "montant": 20.0, "heure": "18:11"},
{"pseudo": "Camille_R", "streamer": "Joueur du Grenier", "montant": 50.0, "heure": "18:23"},
{"pseudo": "Nyx_42", "streamer": "Baghera Jones", "montant": 15.5, "heure": "18:37"},
{"pseudo": "Poulpe3000", "streamer": "Horty", "montant": 2.0, "heure": "18:41"},
{"pseudo": "Anonyme", "streamer": "Ultia", "montant": 120.0, "heure": "18:52"},
{"pseudo": "Sam_du_59", "streamer": "Antoine Daniel", "montant": 10.0, "heure": "19:03"},
{"pseudo": "Camille_R", "streamer": "Sylvain Levy", "montant": 30.0, "heure": "19:14"},
{"pseudo": "Lulu_2007", "streamer": "ZeratoR", "montant": 8.5, "heure": "19:20"},
{"pseudo": "TitiLeBg", "streamer": "Etoiles", "montant": 45.0, "heure": "19:33"},
{"pseudo": "Marion_NSI", "streamer": "Joueur du Grenier", "montant": 75.0, "heure": "19:47"},
{"pseudo": "Poulpe3000", "streamer": "MisterMV", "montant": 12.0, "heure": "19:55"},
{"pseudo": "Anonyme", "streamer": "Domingo", "montant": 250.0, "heure": "20:08"},
{"pseudo": "Kevin_B", "streamer": "Baghera Jones", "montant": 5.0, "heure": "20:16"},
{"pseudo": "Nyx_42", "streamer": "Maghla", "montant": 60.0, "heure": "20:29"},
{"pseudo": "Sam_du_59", "streamer": "Horty", "montant": 3.5, "heure": "20:38"},
{"pseudo": "Marion_NSI", "streamer": "Antoine Daniel", "montant": 100.0, "heure": "20:44"},
{"pseudo": "Lulu_2007", "streamer": "Alphacast", "montant": 18.0, "heure": "20:57"},
{"pseudo": "Camille_R", "streamer": "Ultia", "montant": 40.0, "heure": "21:05"},
{"pseudo": "Kevin_B", "streamer": "Ponce", "montant": 22.5, "heure": "21:12"},
{"pseudo": "TitiLeBg", "streamer": "Joueur du Grenier", "montant": 15.0, "heure": "21:26"},
{"pseudo": "Anonyme", "streamer": "MisterMV", "montant": 500.0, "heure": "21:39"},
{"pseudo": "Poulpe3000", "streamer": "Sylvain Levy", "montant": 7.5, "heure": "21:48"},
{"pseudo": "Marion_NSI", "streamer": "Baghera Jones", "montant": 35.0, "heure": "21:59"},
]'''
PLANNING_SRC = '''planning = [
("Antoine Daniel", 18, 20),
("MisterMV", 20, 22),
("Joueur du Grenier", 22, 24),
("Baghera Jones", 0, 3),
("Ultia", 3, 6),
]
raids = {
"Antoine Daniel": ["MisterMV"],
"MisterMV": ["Joueur du Grenier", "Horty"],
"Joueur du Grenier": ["Baghera Jones"],
"Baghera Jones": ["Ultia"],
"Ultia": [],
"Horty": ["Sylvain Levy"],
"Sylvain Levy": [],
}'''
# Exécution des données pour calculer les sorties attendues.
ENV = {}
exec(DONNEES_SRC, ENV)
exec(PLANNING_SRC, ENV)
dons, planning, raids = ENV["dons"], ENV["planning"], ENV["raids"]
objectif, collecte = ENV["objectif"], ENV["collecte"]
pourcentage_att = round(collecte / objectif * 100, 2)
reste_att = objectif - collecte
total_att = 0
for d in dons:
total_att += d["montant"]
record_att = dons[0]
for d in dons:
if d["montant"] > record_att["montant"]:
record_att = d
gros50_att = len([d for d in dons if d["montant"] >= 50])
gros100_att = len([d for d in dons if d["montant"] >= 100])
donateurs_att = []
for d in dons:
if d["pseudo"] not in donateurs_att:
donateurs_att.append(d["pseudo"])
trois_derniers = dons[-3:]
cagnottes_att = {}
for d in dons:
cagnottes_att[d["streamer"]] = cagnottes_att.get(d["streamer"], 0) + d["montant"]
meilleure_att = max(cagnottes_att, key=lambda s: cagnottes_att[s])
podium_att = sorted(cagnottes_att, key=lambda s: cagnottes_att[s], reverse=True)[:3]
detail_att = {}
for d in dons:
detail_att.setdefault(d["streamer"], []).append(d["montant"])
def antenne_att(h):
for nom, debut, fin in planning:
if debut < fin:
if debut <= h < fin:
return nom
else:
if h >= debut or h < fin:
return nom
return None
def raids_att(depart):
parcours = [depart]
courant = depart
while raids.get(courant):
courant = raids[courant][0]
parcours.append(courant)
return parcours
# ---------------------------------------------------------------- LES BLOCS
BLOCS = []
def md(texte, dans_corrige=False):
BLOCS.append(("md", texte, dans_corrige))
def code(source):
BLOCS.append(("code", source, source))
def question(num, enonce, eleve, solution):
BLOCS.append(("question", num, enonce, eleve, solution))
# ---------------------------------------------------------------- EN-TÊTE
md("""# TP : Z-Event - La régie de données d'un marathon caritatif
> **Thème** : Révisions Python - variables, fonctions, boucles, listes, dictionnaires
---
## Contexte
Depuis 2016, le **Z-Event** réunit chaque année des dizaines de streamers francophones
pendant plusieurs jours d'affilée, dans un même lieu, pour récolter des dons au profit
d'une association. L'événement a été créé par **ZeratoR** et **Dach**. Le principe est
simple : les spectateurs donnent, les streamers relèvent des défis, et un compteur géant
affiche la cagnotte en direct.
Derrière ce compteur, il y a du code. Chaque don qui tombe doit être enregistré, additionné,
attribué à la chaîne sur laquelle il a été fait, comparé aux paliers annoncés, puis réaffiché
en moins d'une seconde sur tous les écrans. C'est ce travail que vous allez faire ici.
Vous êtes bénévole à la **régie de données** de l'événement. Votre mission : écrire les
outils Python qui alimentent le compteur et les classements.
> Les données de ce TP (montants, pseudos, horaires, cagnottes) sont **entièrement inventées**
> pour l'exercice. Elles ne correspondent à aucune édition réelle du Z-Event.
---
## Objectifs pédagogiques
- Manipuler des variables et reconnaître une erreur de type
- Écrire des fonctions qui **renvoient** une valeur au lieu de l'afficher
- Parcourir une liste avec une boucle et accumuler un résultat
- Construire et exploiter des dictionnaires, y compris des dictionnaires de listes
- Réinvestir ces notions dans les chapitres de Terminale : POO, structures linéaires, graphes
Aucune de ces notions n'est nouvelle : tout a été vu en Première. L'objectif est de les
remettre en service après la coupure estivale.
---
## Les données
Copiez ce bloc en tête de votre fichier. Toutes les parties du TP s'appuient dessus.
""")
code(DONNEES_SRC)
# ---------------------------------------------------------------- PARTIE 1
md("""---
## Partie 1 : Le compteur (variables et types)
### 1.1. État de la cagnotte
""", dans_corrige=True)
question(
"1.1",
"""**Question 1.1** : Calculez dans une variable `pourcentage` la part de l'objectif déjà
atteinte, arrondie à deux décimales.""",
'''pourcentage = # À compléter
print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à ''' + str(pourcentage_att) + " %",
'''pourcentage = round(collecte / objectif * 100, 2)
print(f"Objectif atteint à {pourcentage} %") # Objectif atteint à ''' + str(pourcentage_att) + " %",
)
question(
"1.2",
"""**Question 1.2** : Calculez dans une variable `reste` la somme qu'il manque encore pour
atteindre l'objectif.""",
'''reste = # À compléter
print(f"Il manque {reste} euros") # Il manque ''' + str(reste_att) + " euros",
'''reste = objectif - collecte
print(f"Il manque {reste} euros") # Il manque ''' + str(reste_att) + " euros",
)
md("""### 1.2. Le piège du compteur
Le compteur du site reçoit les montants sous forme de **chaînes de caractères**, parce qu'ils
arrivent d'un formulaire web. Le code ci-dessous ne fait donc pas ce qu'on croit.
""")
question(
"1.3",
"""**Question 1.3** : Exécutez ce code, observez le résultat, puis corrigez-le pour que
`total` contienne bien la somme des deux dons.""",
'''don_1 = "25"
don_2 = "40"
total = don_1 + don_2
print(total) # affiche 2540 au lieu de 65
# Correction :
total = # À compléter
print(total) # 65''',
'''don_1 = "25"
don_2 = "40"
total = don_1 + don_2
print(total) # affiche 2540 : sur deux chaînes, + est une concaténation
# Correction : convertir les chaînes en nombres avant d'additionner
total = int(don_1) + int(don_2)
print(total) # 65''',
)
# ---------------------------------------------------------------- PARTIE 2
md("""---
## Partie 2 : L'annonce des dons (fonctions)
Un don qui tombe doit être annoncé à l'écran. Ces fonctions produisent les messages.
### 2.1. Formater un don
""", dans_corrige=True)
question(
"2.1",
"""**Question 2.1** : Écrivez la fonction `formater_don(pseudo, montant)` qui **renvoie** la
chaîne d'annonce. Attention : elle doit la renvoyer, pas l'afficher.""",
'''def formater_don(pseudo, montant):
# À compléter
pass
# Test
print(formater_don("Nyx_42", 5.0)) # Nyx_42 vient de donner 5.0 euros
print(formater_don("Marion_NSI", 100.0)) # Marion_NSI vient de donner 100.0 euros''',
'''def formater_don(pseudo, montant):
return f"{pseudo} vient de donner {montant} euros"
# Test
print(formater_don("Nyx_42", 5.0)) # Nyx_42 vient de donner 5.0 euros
print(formater_don("Marion_NSI", 100.0)) # Marion_NSI vient de donner 100.0 euros''',
)
md("""### 2.2. Repérer les gros dons
La régie déclenche une animation spéciale au-delà d'un certain montant.
""")
question(
"2.2",
"""**Question 2.2** : Écrivez `est_gros_don(montant, seuil)` qui renvoie `True` si le montant
est **supérieur ou égal** au seuil, `False` sinon. Le seuil vaut 50 par défaut.""",
'''def est_gros_don(montant, seuil=50):
# À compléter
pass
# Test
print(est_gros_don(120.0)) # True
print(est_gros_don(50.0)) # True
print(est_gros_don(20.0)) # False
print(est_gros_don(20.0, 10)) # True''',
'''def est_gros_don(montant, seuil=50):
return montant >= seuil
# Test
print(est_gros_don(120.0)) # True
print(est_gros_don(50.0)) # True
print(est_gros_don(20.0)) # False
print(est_gros_don(20.0, 10)) # True''',
)
md("""### 2.3. Les paliers
Les organisateurs annoncent des paliers au fil de la collecte.
| Cagnotte | Palier |
|----------|--------|
| moins de 500 000 | `"Départ"` |
| de 500 000 à 999 999 | `"Palier 1"` |
| de 1 000 000 à 1 999 999 | `"Palier 2"` |
| 2 000 000 et plus | `"Objectif"` |
""")
question(
"2.3",
"""**Question 2.3** : Écrivez `palier(total)` qui renvoie le nom du palier atteint.""",
'''def palier(total):
# À compléter
pass
# Test
print(palier(120000)) # Départ
print(palier(750000)) # Palier 1
print(palier(1837450.75)) # Palier 2
print(palier(2500000)) # Objectif''',
'''def palier(total):
if total < 500000:
return "Départ"
elif total < 1000000:
return "Palier 1"
elif total < 2000000:
return "Palier 2"
else:
return "Objectif"
# Test
print(palier(120000)) # Départ
print(palier(750000)) # Palier 1
print(palier(1837450.75)) # Palier 2
print(palier(2500000)) # Objectif''',
)
md("""### 2.4. Le message complet
""")
question(
"2.4",
"""**Question 2.4** : Écrivez `annonce_regie(pseudo, montant)` qui renvoie le message affiché
en régie. Votre fonction **doit appeler** `formater_don` et `est_gros_don` : ne recopiez pas
leur contenu.
Le message se termine par `" [GROS DON]"` lorsque le montant déclenche l'animation.""",
'''def annonce_regie(pseudo, montant):
# À compléter
pass
# Test
print(annonce_regie("Poulpe3000", 2.0)) # Poulpe3000 vient de donner 2.0 euros
print(annonce_regie("Anonyme", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON]''',
'''def annonce_regie(pseudo, montant):
message = formater_don(pseudo, montant)
if est_gros_don(montant):
message = message + " [GROS DON]"
return message
# Test
print(annonce_regie("Poulpe3000", 2.0)) # Poulpe3000 vient de donner 2.0 euros
print(annonce_regie("Anonyme", 250.0)) # Anonyme vient de donner 250.0 euros [GROS DON]''',
)
# ---------------------------------------------------------------- PARTIE 3
md("""---
## Partie 3 : L'historique des dons (boucles et listes)
La liste `dons` contient les vingt-quatre derniers dons enregistrés. Chaque don est un
dictionnaire à quatre clés : `pseudo`, `streamer`, `montant`, `heure`.
### 3.1. Le total collecté
""", dans_corrige=True)
question(
"3.1",
"""**Question 3.1** : Écrivez `total_dons(liste_dons)` qui renvoie la somme de tous les
montants. Utilisez une boucle et un accumulateur, pas la fonction `sum`.""",
'''def total_dons(liste_dons):
# À compléter
pass
# Test
print(total_dons(dons)) # ''' + str(total_att) + '''
print(total_dons([])) # 0''',
'''def total_dons(liste_dons):
total = 0
for don in liste_dons:
total = total + don["montant"]
return total
# Test
print(total_dons(dons)) # ''' + str(total_att) + '''
print(total_dons([])) # 0''',
)
md("""### 3.2. Le don le plus élevé
""")
question(
"3.2",
"""**Question 3.2** : Écrivez `plus_gros_don(liste_dons)` qui renvoie le **dictionnaire** du
don le plus élevé. L'usage de `max` est interdit. On suppose la liste non vide.""",
'''def plus_gros_don(liste_dons):
# À compléter
pass
# Test
record = plus_gros_don(dons)
print(record["pseudo"], record["montant"]) # ''' + record_att["pseudo"] + " " + str(record_att["montant"]),
'''def plus_gros_don(liste_dons):
record = liste_dons[0] # on part du premier don, surtout pas de 0
for don in liste_dons:
if don["montant"] > record["montant"]:
record = don
return record
# Test
record = plus_gros_don(dons)
print(record["pseudo"], record["montant"]) # ''' + record_att["pseudo"] + " " + str(record_att["montant"]),
)
md("""### 3.3. Compter et filtrer
""")
question(
"3.3",
"""**Question 3.3** : Écrivez `compter_gros_dons(liste_dons, seuil)` qui renvoie le nombre de
dons supérieurs ou égaux au seuil. Réutilisez `est_gros_don`.""",
'''def compter_gros_dons(liste_dons, seuil=50):
# À compléter
pass
# Test
print(compter_gros_dons(dons)) # ''' + str(gros50_att) + '''
print(compter_gros_dons(dons, 100)) # ''' + str(gros100_att),
'''def compter_gros_dons(liste_dons, seuil=50):
compteur = 0
for don in liste_dons:
if est_gros_don(don["montant"], seuil):
compteur = compteur + 1
return compteur
# Test
print(compter_gros_dons(dons)) # ''' + str(gros50_att) + '''
print(compter_gros_dons(dons, 100)) # ''' + str(gros100_att),
)
question(
"3.4",
"""**Question 3.4** : Écrivez `donateurs(liste_dons)` qui renvoie la liste des pseudos **sans
doublon**, dans l'ordre de leur premier don.""",
'''def donateurs(liste_dons):
# À compléter
pass
# Test
print(donateurs(dons))
# ''' + str(donateurs_att),
'''def donateurs(liste_dons):
pseudos = []
for don in liste_dons:
if don["pseudo"] not in pseudos:
pseudos.append(don["pseudo"])
return pseudos
# Test
print(donateurs(dons))
# ''' + str(donateurs_att),
)
md("""### 3.4. Le bandeau des derniers dons
Le site n'affiche que les tout derniers dons reçus.
""")
question(
"3.5",
"""**Question 3.5** : Écrivez `derniers_dons(liste_dons, n)` qui renvoie les `n` derniers dons
de la liste, en utilisant une **tranche**.""",
'''def derniers_dons(liste_dons, n):
# À compléter
pass
# Test
for don in derniers_dons(dons, 3):
print(don["heure"], don["pseudo"])
# ''' + "\n# ".join("%s %s" % (d["heure"], d["pseudo"]) for d in trois_derniers),
'''def derniers_dons(liste_dons, n):
return liste_dons[-n:]
# Test
for don in derniers_dons(dons, 3):
print(don["heure"], don["pseudo"])
# ''' + "\n# ".join("%s %s" % (d["heure"], d["pseudo"]) for d in trois_derniers),
)
# ---------------------------------------------------------------- PARTIE 4
md("""---
## Partie 4 : Le classement des chaînes (dictionnaires)
Chaque don est attribué à la chaîne sur laquelle il a été fait. La régie tient à jour la
cagnotte de chaque streamer.
### 4.1. La cagnotte de chaque chaîne
""", dans_corrige=True)
question(
"4.1",
"""**Question 4.1** : Écrivez `cagnottes(liste_dons)` qui renvoie un dictionnaire associant à
chaque streamer le total des dons reçus sur sa chaîne.""",
'''def cagnottes(liste_dons):
# À compléter
pass
# Test
totaux = cagnottes(dons)
print(totaux["Antoine Daniel"]) # ''' + str(cagnottes_att["Antoine Daniel"]) + '''
print(totaux["Joueur du Grenier"]) # ''' + str(cagnottes_att["Joueur du Grenier"]) + '''
print(len(totaux)) # ''' + str(len(cagnottes_att)),
'''def cagnottes(liste_dons):
totaux = {}
for don in liste_dons:
streamer = don["streamer"]
if streamer in totaux:
totaux[streamer] = totaux[streamer] + don["montant"]
else:
totaux[streamer] = don["montant"]
return totaux
# Test
totaux = cagnottes(dons)
print(totaux["Antoine Daniel"]) # ''' + str(cagnottes_att["Antoine Daniel"]) + '''
print(totaux["Joueur du Grenier"]) # ''' + str(cagnottes_att["Joueur du Grenier"]) + '''
print(len(totaux)) # ''' + str(len(cagnottes_att)),
)
md("""### 4.2. La chaîne en tête
""")
question(
"4.2",
"""**Question 4.2** : Écrivez `meilleure_chaine(totaux)` qui renvoie le nom du streamer dont
la cagnotte est la plus élevée.
Rappel : `for cle in dictionnaire` parcourt les clés.""",
'''def meilleure_chaine(totaux):
# À compléter
pass
# Test
print(meilleure_chaine(cagnottes(dons))) # ''' + meilleure_att,
'''def meilleure_chaine(totaux):
meilleur = None
for streamer in totaux:
if meilleur is None or totaux[streamer] > totaux[meilleur]:
meilleur = streamer
return meilleur
# Test
print(meilleure_chaine(cagnottes(dons))) # ''' + meilleure_att,
)
md("""### 4.3. Le détail des dons
On veut maintenant, pour chaque streamer, la **liste** des montants reçus et plus seulement
leur somme : un dictionnaire dont les valeurs sont des listes.
""")
question(
"4.3",
"""**Question 4.3** : Écrivez `dons_par_chaine(liste_dons)` qui renvoie un dictionnaire
associant à chaque streamer la liste des montants reçus, dans l'ordre chronologique.""",
'''def dons_par_chaine(liste_dons):
# À compléter
pass
# Test
detail = dons_par_chaine(dons)
print(detail["MisterMV"]) # ''' + str(detail_att["MisterMV"]) + '''
print(detail["Baghera Jones"]) # ''' + str(detail_att["Baghera Jones"]),
'''def dons_par_chaine(liste_dons):
detail = {}
for don in liste_dons:
streamer = don["streamer"]
if streamer not in detail:
detail[streamer] = []
detail[streamer].append(don["montant"])
return detail
# Test
detail = dons_par_chaine(dons)
print(detail["MisterMV"]) # ''' + str(detail_att["MisterMV"]) + '''
print(detail["Baghera Jones"]) # ''' + str(detail_att["Baghera Jones"]),
)
md("""### 4.4. Le podium
""")
question(
"4.4",
"""**Question 4.4** : Écrivez `podium(totaux)` qui renvoie la liste des trois premiers
streamers, du plus gros total au plus petit.
Indication : `sorted(totaux, key=..., reverse=True)` trie les clés selon un critère.""",
'''def podium(totaux):
# À compléter
pass
# Test
print(podium(cagnottes(dons)))
# ''' + str(podium_att),
'''def podium(totaux):
classement = sorted(totaux, key=lambda streamer: totaux[streamer], reverse=True)
return classement[:3]
# Test
print(podium(cagnottes(dons)))
# ''' + str(podium_att),
)
# ---------------------------------------------------------------- PARTIE 5
md("""---
## Partie 5 : Le planning et les raids (synthèse)
Un **raid**, c'est lorsqu'un streamer envoie son public sur une autre chaîne à la fin de son
passage. Cette partie combine tout ce qui précède.
Copiez ce second bloc de données à la suite du premier.
""", dans_corrige=True)
code(PLANNING_SRC)
md("""### 5.1. Qui est à l'antenne
Le planning est une liste de triplets `(streamer, heure_de_debut, heure_de_fin)`, en heures
entières sur vingt-quatre heures. Attention au créneau de Baghera Jones : il commence à
minuit, donc son heure de début est plus petite que celle de la personne qui la précède.
""")
question(
"5.1",
"""**Question 5.1** : Écrivez `a_l_antenne(planning, heure)` qui renvoie le nom du streamer en
direct à cette heure, ou `None` si personne ne l'est.
L'heure de début est incluse, l'heure de fin est exclue.""",
'''def a_l_antenne(planning, heure):
# À compléter
pass
# Test
print(a_l_antenne(planning, 19)) # ''' + str(antenne_att(19)) + '''
print(a_l_antenne(planning, 22)) # ''' + str(antenne_att(22)) + '''
print(a_l_antenne(planning, 1)) # ''' + str(antenne_att(1)) + '''
print(a_l_antenne(planning, 10)) # ''' + str(antenne_att(10)),
'''def a_l_antenne(planning, heure):
for streamer, debut, fin in planning:
if debut < fin:
if debut <= heure < fin:
return streamer
else:
# créneau à cheval sur minuit
if heure >= debut or heure < fin:
return streamer
return None
# Test
print(a_l_antenne(planning, 19)) # ''' + str(antenne_att(19)) + '''
print(a_l_antenne(planning, 22)) # ''' + str(antenne_att(22)) + '''
print(a_l_antenne(planning, 1)) # ''' + str(antenne_att(1)) + '''
print(a_l_antenne(planning, 10)) # ''' + str(antenne_att(10)),
)
md("""### 5.2. Suivre la chaîne des raids
Le dictionnaire `raids` associe à chaque streamer la liste de ceux vers qui il envoie son
public. Certains n'envoient personne : leur liste est vide.
""")
question(
"5.2",
"""**Question 5.2** : Écrivez `suivre_raids(raids, depart)` qui renvoie la liste des chaînes
traversées en partant de `depart` et en suivant à chaque fois le **premier** raid, jusqu'à
tomber sur un streamer qui n'en fait aucun.
Le point de départ fait partie du résultat.""",
'''def suivre_raids(raids, depart):
# À compléter
pass
# Test
print(suivre_raids(raids, "Antoine Daniel"))
# ''' + str(raids_att("Antoine Daniel")) + '''
print(suivre_raids(raids, "Horty"))
# ''' + str(raids_att("Horty")) + '''
print(suivre_raids(raids, "Ultia"))
# ''' + str(raids_att("Ultia")),
'''def suivre_raids(raids, depart):
parcours = [depart]
courant = depart
while raids.get(courant): # .get évite l'erreur si la clé est absente
courant = raids[courant][0]
parcours.append(courant)
return parcours
# Test
print(suivre_raids(raids, "Antoine Daniel"))
# ''' + str(raids_att("Antoine Daniel")) + '''
print(suivre_raids(raids, "Horty"))
# ''' + str(raids_att("Horty")) + '''
print(suivre_raids(raids, "Ultia"))
# ''' + str(raids_att("Ultia")),
)
# ---------------------------------------------------------------- FIN
md("""---
## Questions de synthèse
Répondez en quelques lignes, sans machine.
1. À la question 1.3, `"25" + "40"` donne `"2540"`. Expliquez pourquoi Python ne signale
aucune erreur alors que le résultat est faux du point de vue de la régie.
2. À la question 3.2, pourquoi initialise-t-on `record` avec le premier don de la liste
plutôt qu'avec un montant de 0 ?
3. Aux questions 4.1 et 4.3, on parcourt la même liste de dons mais on ne construit pas la
même structure. Dans quel cas le dictionnaire de listes est-il préférable au dictionnaire
de totaux ?
4. La question 5.2 s'arrête lorsqu'un streamer ne fait aucun raid. Que se passerait-il si
deux streamers se renvoyaient mutuellement leur public ? Comment modifieriez-vous la
fonction pour l'éviter ?
---
## Ce que vous venez de réviser
| Partie | Notion | Chapitre de Terminale concerné |
|--------|--------|--------------------------------|
| 1 | Variables, types, conversions | POO, structures linéaires |
| 2 | Fonctions, `return`, paramètre par défaut | Récursivité, Modularité |
| 3 | Boucles, accumulateur, listes, tranches | Parcours d'arbres et de graphes, tri fusion |
| 4 | Dictionnaires, dictionnaires de listes | Graphes, programmation dynamique |
| 5 | Synthèse, parcours d'un enchaînement | Graphes, Pile et File |
La question 5.2 est un parcours de graphe : `raids` est une **liste d'adjacence**, exactement
la structure que vous utiliserez au chapitre Graphes.
---
## Barème indicatif
| Partie | Points |
|--------|--------|
| Partie 1 : Le compteur | 3 |
| Partie 2 : L'annonce des dons | 5 |
| Partie 3 : L'historique des dons | 5 |
| Partie 4 : Le classement des chaînes | 4 |
| Partie 5 : Le planning et les raids | 2 |
| Questions de synthèse | 1 |
| **Total** | **20** |
---
""" + LICENCE)
# ---------------------------------------------------------------- SORTIES
def enonce_md():
parts = []
for b in BLOCS:
if b[0] == "md":
parts.append(b[1].strip("\n"))
elif b[0] == "code":
parts.append("```python\n" + b[1] + "\n```")
else:
parts.append(b[2].strip("\n") + "\n\n```python\n" + b[3] + "\n```")
return "\n\n".join(parts) + "\n"
def corrige_md():
parts = ["# Corrigé du TP Z-Event", "---"]
exclus = ("## Contexte", "## Objectifs pédagogiques", "## Les données",
"## Questions de synthèse", "## Ce que vous venez de réviser",
"## Barème indicatif")
for b in BLOCS:
if b[0] == "md":
# On reprend toute la hiérarchie de titres de l'énoncé (parties et
# sous-parties), sauf le titre du TP et les sections de clôture.
titres = [l for l in b[1].split("\n")
if l.startswith("## ") or l.startswith("### ")]
titres = [l for l in titres if l not in exclus]
if titres:
parts.append("\n\n".join(titres))
elif b[0] == "question":
parts.append("**Question %s** :\n\n```python\n%s\n```" % (b[1], b[4]))
complet = [DONNEES_SRC, PLANNING_SRC] + [b[4] for b in BLOCS if b[0] == "question"]
parts.append("---")
parts.append("## Code complet fonctionnel")
parts.append("```python\n" + "\n\n".join(complet) + "\n```")
parts.append("---")
parts.append(LICENCE)
return "\n\n".join(parts) + "\n"
def notebook():
cells = []
def add(kind, src):
c = {"cell_type": kind, "id": uuid.uuid4().hex[:8], "metadata": {},
"source": src.splitlines(keepends=True)}
if kind == "code":
c["execution_count"] = None
c["outputs"] = []
cells.append(c)
for b in BLOCS:
if b[0] == "md":
add("markdown", b[1].strip("\n"))
elif b[0] == "code":
add("code", b[1])
else:
add("markdown", b[2].strip("\n"))
add("code", b[3])
return {"cells": cells,
"metadata": {"kernelspec": {"display_name": "Python 3", "language": "python",
"name": "python3"},
"language_info": {"name": "python", "version": "3.11"}},
"nbformat": 4, "nbformat_minor": 5}
io.open(os.path.join(DEST, "TP_ZEvent.md"), "w", encoding="utf-8").write(enonce_md())
io.open(os.path.join(DEST, "Corrige_TP_ZEvent.md"), "w", encoding="utf-8").write(corrige_md())
with io.open(os.path.join(DEST, "TP_ZEvent.ipynb"), "w", encoding="utf-8") as f:
json.dump(notebook(), f, ensure_ascii=False, indent=1)
print("écrits : TP_ZEvent.md, Corrige_TP_ZEvent.md, TP_ZEvent.ipynb (%d questions)"
% sum(1 for b in BLOCS if b[0] == "question"))
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Icone du chapitre Paradigmes : un lambda (paradigme fonctionnel) encadre par
une paire d'accolades (paradigme imperatif). Dessinee pour le cours de
Terminale NSI du lycee Charlotte Perriand, licence CC BY-SA 4.0. -->
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px"
viewBox="0 0 512 512" version="1.1">
<g fill="none" stroke="#000000" stroke-width="28"
stroke-linecap="round" stroke-linejoin="round">
<!-- accolade gauche -->
<path d="M152,108 C114,108 124,158 124,196 C124,232 106,248 86,256
C106,264 124,280 124,316 C124,354 114,404 152,404"/>
<!-- accolade droite -->
<path d="M360,108 C398,108 388,158 388,196 C388,232 406,248 426,256
C406,264 388,280 388,316 C388,354 398,404 360,404"/>
<!-- lambda -->
<path d="M194,142 L330,378"/>
<path d="M268,270 L186,378"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 922 B