Fix extraction issue by untracking generated binary assets

This commit is contained in:
Clément SAILLANT
2026-02-13 23:15:24 +01:00
parent 6409d70a8b
commit ad061e9047
49 changed files with 1015 additions and 145 deletions
+13
View File
@@ -3,3 +3,16 @@
# Local IDE settings
.vscode/
# Generated game exports
game/exports/*
!game/exports/.gitkeep
!game/exports/previews/
# Locally generated audio/render outputs (not versioned)
audio/generated/*
!audio/generated/README.md
# Locally generated printable PDFs (not versioned)
printables/export/pdf/zacus_v1/*
!printables/export/pdf/zacus_v1/README.md
+12 -22
View File
@@ -1,33 +1,23 @@
# Changelog
## [Unreleased]
- Printables : déplacement et classement des PNG dans `printables/export/png/{general,fiche-enquete,personnages,zones}`.
- Documentation : ajout d'une illustration SVG de structure du dépôt dans le README (`docs/assets/repo-map.svg`).
- Documentation : revue de cohérence étendue sur les Markdown/README et mise à jour de `docs/repo-audit.md`.
- Structure : ajout du dossier `kit-maitre-du-jeu/export/pdf/` pour aligner la documentation avec l'arborescence.
- Printables : clarification de l'état actuel et du niveau de préparation des dossiers `src/` et `export/{pdf,png}/`.
- Documentation : ajout d'un plan de maintenance du dépôt (hors firmware).
- Contribution : ajout d'une checklist PR doc-only dans `CONTRIBUTING.md`.
- Documentation : ajout d'une illustration SVG de structure du dépôt dans le README (`docs/assets/repo-map.svg`).
- Documentation : revue de cohérence étendue sur les Markdown/README et mise à jour de `docs/repo-audit.md`.
- Structure : ajout du dossier `kit-maitre-du-jeu/export/pdf/` pour aligner la documentation avec l'arborescence.
- Printables : clarification de l'état actuel et du niveau de préparation des dossiers `src/` et `export/{pdf,png}/`.
- Correction extraction: suppression des binaires versionnés (MP3/PDF générés) et passage en génération locale ignorée par Git (`audio/generated`, `printables/export/pdf/zacus_v1`).
- Génération locale des assets restants: `audio/generated/*` via script utilitaire + PDF placeholders dans `printables/export/pdf/zacus_v1/`.
- Ajout des utilitaires `tools/audio/generate_local_assets.py` et `tools/printables/generate_local_pdf_placeholders.py`.
- Ajout du canon scénario `game/scenarios/zacus_v1.yaml` (zones, suspects, étapes, hotline, solution unique, anti-chaos).
- Remplissage complet du kit MJ (`script-minute-par-minute`, `solution-complete`, `checklist`, `plan stations`, `distribution`, `guide anti-chaos`).
- Ajout dune structure IA-friendly pour printables (`printables/src/prompts/*`, `printables/WORKFLOW.md`, placeholders).
- Ajout pipeline audio (`audio/README.md`, `audio/manifests/zacus_v1_audio.yaml`, prompts audio, validateur manifest).
- Ajout docs dintégration hardware scénario + story spec `zacus_v1_unlock_and_etape2.yaml`.
- Harmonisation licence vers MIT (code) + CC BY-NC 4.0 (contenu/docs) et déplacement des anciennes licences dans `LICENSES/legacy/`.
- Ajout validateur scénario `tools/scenario/validate_scenario.py`.
- Ajout docs `docs/STYLEGUIDE.md`, `docs/QUICKSTART.md`, `docs/GLOSSARY.md`, `docs/repo-status.md`.
## [0.2.0] - 2026-02-12
### Ajouté
- Workflow de validation audio au boot (touches + commandes série) avec timeout et limite de relecture.
- Outils de diagnostic clavier analogique : `KEY_STATUS`, `KEY_SET`, `KEY_SET_ALL`, `KEY_RAW_ON/OFF`, auto-test `KEY_TEST_*`.
- Calibration micro série et logs de santé micro (`[MIC_CAL] ...`).
- Makefile pour standardiser build/flash/monitor ESP32 + écran ESP8266.
### Modifié
- UX `U_LOCK`/déverrouillage LA et transitions automatiques vers `MODULE U-SON` puis lecteur MP3.
- Amélioration de l'affichage OLED (séquences visuelles de déverrouillage, effet glitch adouci).
- Stabilisation du mapping clavier analogique et robustesse générale des interactions.
### Corrigé
- Robustesse du lien série ESP32 -> ESP8266 et gestion des états de reprise.
## [0.1.0] - 2026-02-03
- Initialisation du dépôt et fichiers de gouvernance
- Initialisation du dépôt et fichiers de gouvernance.
+15 -29
View File
@@ -1,36 +1,22 @@
# Contribuer
Objectif : un kit cohérent, fluide à animer, facile à imprimer (noir & blanc).
Merci de contribuer à Zacus.
## Contributions bienvenues
- Variantes graphiques N&B (lisibilité, hiérarchie, élégance)
- Corrections (orthographe, cohérence, mise en page)
- Exports supplémentaires (A6/A5, recto/verso)
- Améliorations kit MJ (anti-chaos, modularité 614)
- Ajouts hardware (BOM, schémas, firmware simple)
## Principes
- Jouable 911 ans, 614 enfants, 6090 min.
- Lisibilité impression N&B prioritaire.
- Aucune contradiction avec le canon (`game/scenarios/zacus_v1.yaml`).
## Règles (important)
- Noir & blanc uniquement
- Textes courts, hiérarchie claire
- Cohérence stricte : aucune contradiction entre indices/cartes/solution
- Pas de marques/logos externes
## Workflow
1. Fork
2. Branche : `feat/...` ou `fix/...`
3. Ajouts au bon endroit (`src/` et `export/`)
4. Mettre à jour `CHANGELOG.md`
5. PR avec explication + previews PNG si possible
## Workflow recommandé
1. Créer une branche (`feat/...` ou `fix/...`).
2. Modifier les sources (pas uniquement les exports).
3. Lancer les validateurs:
- `python3 tools/scenario/validate_scenario.py`
- `python3 tools/audio/validate_manifest.py`
4. Mettre à jour `CHANGELOG.md`.
## Licence des contributions
- Contenu créatif : CC BY-NC 4.0
- Code : MIT
- Contenu creatif : CC BY-SA 4.0
- Code : GPL-3.0-or-later
- **Code**: MIT.
- **Contenus créatifs/docs/printables**: CC BY-NC 4.0.
## Checklist PR (recommandée)
- [ ] Cohérence narrative validée (indices/cartes/solution).
- [ ] Lisibilité N&B validée (contraste, hiérarchie, impression).
- [ ] `CHANGELOG.md` mis à jour.
- [ ] Previews PNG ajoutées si changement visuel.
- [ ] Si PR doc-only : aucun changement firmware.
En contribuant, vous acceptez que votre apport soit publié sous ce schéma dual.
+12 -10
View File
@@ -1,16 +1,18 @@
# Licence
Ce depot est diffuse avec des licences libres/open-source.
Ce dépôt applique un schéma dual clair:
## Code — GPL-3.0-or-later
S'applique a: firmware, scripts et tout fichier explicitement identifie comme code source.
## 1) Code (scripts, outils, firmware)
- Licence: **MIT**
- Texte: `LICENSES/MIT.txt`
Texte: `LICENSES/GPL-3.0-or-later.txt`
## 2) Contenus créatifs et documentation
- Licence: **CC BY-NC 4.0**
- Texte: `LICENSES/CC-BY-NC-4.0.txt`
## Contenus creatifs et documentation — CC BY-SA 4.0
S'applique a: documents de jeu, textes, PDFs, PNG, SVG, assets graphiques, docs et contenus redactionnels.
## Legacy / historique
Les anciennes licences précédemment utilisées sont conservées pour traçabilité dans:
- `LICENSES/legacy/CC-BY-SA-4.0.txt`
- `LICENSES/legacy/GPL-3.0-or-later.txt`
Texte: `LICENSES/CC-BY-SA-4.0.txt`
## Dependances tierces
Les bibliotheques externes restent sous leurs licences respectives. Voir les README/manifestes de chaque sous-projet.
Statut: **historique / à confirmer** (ne sapplique plus par défaut aux nouveaux apports).
+7
View File
@@ -0,0 +1,7 @@
Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)
Human-readable summary:
https://creativecommons.org/licenses/by-nc/4.0/
Legal code:
https://creativecommons.org/licenses/by-nc/4.0/legalcode
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+20 -68
View File
@@ -1,73 +1,25 @@
# Le Mystère du Professeur Zacus — kit denquête (printables + guide MJ + hardware)
# Le Mystère du Professeur Zacus
Kit denquête pour anniversaire (911 ans), ambiance laboratoire / campus scientifique.
Escape game / enquête modulaire assisté par IA (911 ans, 614 enfants, 6090 min).
## Aperçu visuel
![Vue d'ensemble du dépôt](docs/assets/repo-map.svg)
## Démarrage rapide
- Quickstart: `docs/QUICKSTART.md`
- Canon scénario: `game/scenarios/zacus_v1.yaml`
- Kit MJ prêt à jouer: `kit-maitre-du-jeu/`
## Aperçu visuel
![Vue d'ensemble du dépôt](docs/assets/repo-map.svg)
## Structure
- `game/` : source de vérité scénario + prompts IA
- `kit-maitre-du-jeu/` : animation minute-par-minute, solution, anti-chaos
- `printables/` : prompts et exports imprimables
- `audio/` : manifest audio + pipeline local
- `hardware/` : firmware existant + intégration scénario
- `tools/` : validateurs scénario/audio
## Contenu
### 1) Kit Maître du jeu
Dossier : `kit-maitre-du-jeu/`
- script minute-par-minute
- solution complète (coupable + mobile + méthode + chronologie)
- checklist matériel + mise en place
- distribution des rôles + adaptations (614 enfants)
- guide anti-chaos
- stations (option bonus)
## Validation
- `python3 tools/scenario/validate_scenario.py`
- `python3 tools/audio/validate_manifest.py`
### 2) Printables (imprimables)
Dossier : `printables/`
- invitations
- cartes (personnages / lieux / objets / indices)
- feuille denquête
- badges détective
- règles en 1 page
- ordre dimpression
### 3) Hardware (optionnel)
Dossier : `hardware/`
- BOM (liste de matériel)
- câblage
- firmware Arduino / ESP32
## État du projet
- **Kit MJ** : documentation d'animation complète, exports PDF possibles via `kit-maitre-du-jeu/export/pdf/`.
- **Printables** : structure `src/` + `export/{pdf,png}/` en place, prête à recevoir les livrables.
- **Hardware/firmware** : flux `U_LOCK`/`U-SON`/MP3 opérationnel et documenté (`hardware/firmware/esp32/`).
## Licence (non-commercial pour le contenu)
- Contenus créatifs (documents, PDFs, PNG, SVG, textes de jeu) : **CC BY-NC 4.0**
- Code (firmware, scripts) : **MIT**
- **Kit MJ** : structure documentaire en place (`kit-maitre-du-jeu/`).
- **Printables** : conventions définies, arborescence `src/export` à compléter selon les livrables.
- **Hardware/firmware** : flux `U_LOCK`/`U-SON`/MP3 opérationnel et documenté (`hardware/firmware/esp32/`).
## Licence (open source)
- Contenus créatifs (documents, PDFs, PNG, SVG, textes de jeu) : **CC BY-SA 4.0**
- Code (firmware, scripts) : **GPL-3.0-or-later**
Voir `LICENSE.md` et `LICENSES/`.
## Disclaimer
Projet indépendant, non affilié à aucune marque ou éditeur.
Voir `DISCLAIMER.md`.
## Maintenance du dépôt (hors firmware)
- Plan : `docs/maintenance-repo.md`
- Audit : `docs/repo-audit.md`
- Règles anti-conflits docs : section dédiée dans `docs/maintenance-repo.md`
Voir `docs/maintenance-repo.md`.
## Contribuer
Voir `CONTRIBUTING.md`.
## Historique
Voir `CHANGELOG.md`.
## Mainteneur
L’électron rare
## Exemples transverses
- Index exemples: `examples/README.md`
## Licence
- **Code**: MIT (`LICENSES/MIT.txt`)
- **Contenus créatifs/docs/printables**: CC BY-NC 4.0 (`LICENSES/CC-BY-NC-4.0.txt`)
- Historique licences: `LICENSES/legacy/`
+24
View File
@@ -0,0 +1,24 @@
# Pipeline audio Zacus v1
Objectif: pipeline local IA-friendly sans dépendance cloud obligatoire.
## Outils cibles
- TTS: `espeak`, `mbrola`, ou `piper`
- Conversion: `ffmpeg` (MP3 mono faible débit)
## Convention export
- Mono, 22.05 kHz ou 44.1 kHz
- 4896 kbps
- Nommage depuis `audio/manifests/zacus_v1_audio.yaml`
## Étapes
1. Générer voix (hotline + messages U-SON).
2. Générer textures/musiques via prompts (voir `game/prompts/audio/`).
3. Normaliser volume.
4. Valider présence des fichiers avec `tools/audio/validate_manifest.py`.
## Génération locale rapide (placeholder)
Si vous voulez débloquer rapidement des tests sans TTS externe:
- `python3 tools/audio/generate_local_assets.py`
Ce script crée `audio/generated/*` en réutilisant des MP3 présents dans le repo (placeholders techniques).
+11
View File
@@ -0,0 +1,11 @@
# audio/generated
Ce dossier contient des artefacts audio générés localement (MP3), non versionnés.
Pourquoi: plusieurs workflows d'extraction/patch (et revues diff textuelles) ne gèrent pas bien les binaires.
Génération locale:
- `python3 tools/audio/generate_local_assets.py`
Validation:
- `python3 tools/audio/validate_manifest.py`
+50
View File
@@ -0,0 +1,50 @@
scenario_id: zacus_v1
audio_assets:
- id: uson_boot_corrompu
path: audio/generated/uson_boot_corrompu_30s.mp3
duration_hint_s: 30
device: U-SON
- id: uson_win
path: audio/generated/uson_win_15s.mp3
duration_hint_s: 15
device: U-SON
- id: zone5_radio_v1
path: audio/generated/zone5_radio_brouille_v1_30s.mp3
duration_hint_s: 30
device: telephones_zones
- id: zone5_radio_v2
path: audio/generated/zone5_radio_brouille_v2_45s.mp3
duration_hint_s: 45
device: telephones_zones
- id: hotline_rep_01
path: audio/generated/hotline_validation_01.mp3
duration_hint_s: 8
device: telephone_hotline
- id: hotline_rep_02
path: audio/generated/hotline_indice_01.mp3
duration_hint_s: 10
device: telephone_hotline
- id: hotline_rep_03
path: audio/generated/hotline_relance_01.mp3
duration_hint_s: 10
device: telephone_hotline
- id: hotline_rep_04
path: audio/generated/hotline_validation_02.mp3
duration_hint_s: 9
device: telephone_hotline
- id: hotline_rep_05
path: audio/generated/hotline_indice_02.mp3
duration_hint_s: 9
device: telephone_hotline
- id: hotline_rep_06
path: audio/generated/hotline_relance_02.mp3
duration_hint_s: 9
device: telephone_hotline
- id: ambiance_labo_25min_prompt
path: PROMPT_ONLY
duration_hint_s: 1500
device: chaine_hifi
- id: ambiance_boucle_1h_prompt
path: PROMPT_ONLY
duration_hint_s: 3600
device: chaine_hifi
+8
View File
@@ -0,0 +1,8 @@
# Glossaire
- **U-SON**: module audio narratif principal.
- **U-LOCK**: verrouillage/déverrouillage (souvent via LA).
- **LA**: note de référence à produire à l’étape 5.
- **HOTLINE**: canal Brigade Z (Indice / Validation / Relance).
- **Brigade Z**: cellule fictive de support enquête.
- **Morse partiel**: indice transcodé via oscilloscope/table.
+10
View File
@@ -0,0 +1,10 @@
# QUICKSTART (5 minutes)
1. Imprimer les éléments clés (`printables/src/prompts/` puis export PDF).
2. Préparer stations selon `kit-maitre-du-jeu/plan-stations-et-mise-en-place.md`.
3. Lire `kit-maitre-du-jeu/script-minute-par-minute.md`.
4. Charger audio (optionnel) selon `audio/manifests/zacus_v1_audio.yaml`.
5. Lancer partie: 6090 min, 614 enfants, 911 ans.
Canon unique: `game/scenarios/zacus_v1.yaml`.
Validation: `python3 tools/scenario/validate_scenario.py`.
+31
View File
@@ -0,0 +1,31 @@
# Styleguide visuel Zacus
## Signature
Style "Cluedo premium × labo rétro-futuriste": cadre métal, vis coins, bandeaux noir/doré, glow gold discret, trames circuits, grain/halftone.
## Palette
- Noir panneau `#0F1620`
- Acier clair `#C9D0D8`
- Acier sombre `#4B545E`
- Gris technique `#7A8592`
- Blanc cassé papier `#F2E9D6`
- Texte sombre `#1B232D`
- Gold principal `#F6C453`, ombre `#B07B1E`, glow `#FFD88A`
## Couleurs zones
- Z1 `#2F7D32`
- Z2 `#1E6FA8`
- Z3 `#D9801B`
- Z4 `#6E3AA8`
- Z5 `#1E88A8`
- Z6 `#9B7A4B`
- Hotline `#0E3B5A`
## Typographie
- Titres: Bebas Neue
- Texte: Inter ou Montserrat
- Codes/indices: Roboto Mono
## Contraintes impression
- Toujours lisible en N&B.
- Contraste fort, pas dinfo uniquement portée par la couleur.
+6 -8
View File
@@ -1,9 +1,7 @@
# Le Mystère du Professeur Zacus
# Le Mystère du Professeur Zacus — Documentation
- Kit MJ : `kit-maitre-du-jeu/`
- Printables : `printables/`
- Hardware : `hardware/`
- Maintenance dépôt : `docs/maintenance-repo.md`
- Audit de cohérence : `docs/repo-audit.md`
Astuce : activer GitHub Pages sur `/docs` pour afficher cette page.
- Démarrage 5 minutes: `docs/QUICKSTART.md`
- Canon scénario: `game/scenarios/zacus_v1.yaml`
- Styleguide visuel: `docs/STYLEGUIDE.md`
- Glossaire: `docs/GLOSSARY.md`
- État dépôt (audit): `docs/repo-status.md`
+32
View File
@@ -0,0 +1,32 @@
# État rapide du dépôt (audit automatique)
Date audit: 2026-02-13
## 1) Inventaire et TODO détectés
- Dossiers principaux présents: `kit-maitre-du-jeu/`, `printables/`, `hardware/`, `docs/`, `examples/`.
- Plusieurs fichiers “À compléter” ont été trouvés dans le kit MJ (script, solution, checklist, anti-chaos, etc.) puis remplis dans cette itération.
## 2) Problèmes détectés
- **Fichiers incomplets**: 6 fichiers clés du kit MJ étaient des placeholders (`> À compléter`).
- **Incohérence licences**:
- `README.md` et `CONTRIBUTING.md` mélangeaient deux modèles (CC BY-SA/GPL et CC BY-NC/MIT).
- `LICENSE.md` annonçait CC BY-SA + GPL.
- **Noms non portables**:
- Dossier `include humain:IA/` (caractère `:`).
- Plusieurs fichiers avec accents et espaces (tolérés localement, mais fragiles pour certains scripts CI multi-OS).
- **Chemins/lisibilité**:
- README avec sections dupliquées.
- Liens de navigation “quickstart/canon” absents de `docs/index.md`.
## 3) Correctifs appliqués
- Mise en place dun **canon scénario**: `game/scenarios/zacus_v1.yaml`.
- Création dune pipeline IA-friendly: prompts printables/audio + manifests + validateurs Python.
- Harmonisation licence:
- **Créatif/docs/printables**: CC BY-NC 4.0.
- **Code/scripts/firmware**: MIT.
- Anciennes licences déplacées en `LICENSES/legacy/`.
- Ajout docs daccès rapide: `docs/QUICKSTART.md`, `docs/STYLEGUIDE.md`, `docs/GLOSSARY.md`.
## 4) Risques restants (hors scope immédiat)
- Le dossier historique `include humain:IA/` reste non renommé pour éviter une migration destructive.
- Vérification exhaustive de tous les liens binaires/non-Markdown non réalisée (audit orienté docs/scénario).
View File
+4
View File
@@ -0,0 +1,4 @@
# Prompts IA du jeu
- Audio: `game/prompts/audio/`
- Printables: voir `printables/src/prompts/`
@@ -0,0 +1,10 @@
# Prompts musique / ambiance
## Ambiance labo enquête 2030 min
"Musique électronique légère, rétro-futuriste, tempo modéré, textures de laboratoire, petits bips scientifiques, sans voix, répétition douce, adaptée à un jeu denquête enfant 911 ans."
## Boucle 1h
"Longue boucle ambient techno-lab, peu intrusive, variations subtiles toutes 24 minutes, pas de rupture brutale, mix mono-compatible."
## Message radio brouillé zone 5
"Texture radio AM bruitée + voix distante répétant 'au-dessus du sol...', avec motifs fréquentiels et sensation dindice caché."
+9
View File
@@ -0,0 +1,9 @@
# Prompt TTS Hotline Brigade Z
Voix: française claire, rassurante, dynamique.
Contraintes: phrases 612 mots, articulation nette, pas d'effet robotique exagéré.
Lignes types:
1. "Brigade Z. Hypothèse reçue. Continuez lanalyse."
2. "Indice confirmé. Cherchez la lettre cachée dans le signal."
3. "Relance Brigade Z. Reprenez QUI, OU, COMMENT."
+199
View File
@@ -0,0 +1,199 @@
id: zacus_v1
metadata:
title: "Le Mystère du Professeur Zacus — Scénario canonique v1"
version: "1.0.0"
duration_minutes: 60-90
players: 6-14
ages: 9-11
tone: "enquête coopérative rétro-futuriste, rythme dynamique"
optional_modules:
- U-SON prototype
- U-LOCK (déverrouillage vocal LA)
- Oscilloscope visuel/morse
- Téléphones zones + Hotline
zones:
- id: Z1
name: "Cantine des Savants"
color: "#2F7D32"
home_location: "Entrée / Cuisine"
responsible_suspect_id: S_NONE
- id: Z2
name: "Salle des Démonstrations"
color: "#1E6FA8"
home_location: "Salon"
responsible_suspect_id: S4
features: [HOTLINE, OSCILLOSCOPE]
- id: Z3
name: "Atelier des Circuits"
color: "#D9801B"
home_location: "Atelier électronique"
responsible_suspect_id: S1
features: [OSCILLOSCOPE, U_SON]
- id: Z4
name: "Studio de Résonance"
color: "#6E3AA8"
home_location: "Suite + coin musique"
responsible_suspect_id: S5
features: [GENERATEUR_FREQUENCE]
- id: Z5
name: "Hangar du Droïde R3-D2Z"
color: "#1E88A8"
home_location: "Chambre Isaac"
responsible_suspect_id: S2
features: [AMBIANCE_AUDIO, TABLE_MORSE_CACHEE]
- id: Z6
name: "Archives Interdites"
color: "#9B7A4B"
home_location: "Grenier + portrait"
responsible_suspect_id: S3
suspects:
- id: S1
name: "Dr Oscilla Volt"
role: "Spécialiste oscillations"
gimmick: "Parle en fréquences"
alibi: "Réglait loscilloscope en zone 3"
relation_to_zacus: "Ancien collaborateur"
suspicion_level: 4
- id: S2
name: "Pr Octave Baryon"
role: "Logistique du hangar"
gimmick: "Toujours avec un tournevis"
alibi: "Inspection du droïde R3-D2Z"
relation_to_zacus: "Rival cordial"
suspicion_level: 2
- id: S3
name: "Dr Sacha Néon"
role: "Archiviste du labo"
gimmick: "Classe tout par codes couleur"
alibi: "Inventaire en archives"
relation_to_zacus: "Confiance professionnelle"
suspicion_level: 2
- id: S4
name: "Prof Alma Fractal"
role: "Formatrice démonstrations"
gimmick: "Dessine des fractales sur tout"
alibi: "Tenait la hotline Brigade Z"
relation_to_zacus: "Mentor académique"
suspicion_level: 1
- id: S5
name: "Professeur Électron"
role: "Chef du studio de résonance"
gimmick: "Collectionne les synthés vintage"
alibi: "Test audio privé au studio"
relation_to_zacus: "Partenaire jaloux"
suspicion_level: 5
props:
- {id: P1, name: U-SON, type: hardware}
- {id: P2, name: U-LOCK, type: hardware}
- {id: P3, name: Oscilloscope, type: hardware}
- {id: P4, name: Droïde R3-D2Z, type: decor}
- {id: P5, name: Téléphone hotline maître, type: hardware}
- {id: P6, name: Téléphones zones (L/AFO/EFO/LE/U), type: hardware}
- {id: P7, name: Carte SD finale, type: evidence}
- {id: P8, name: Table Morse cachée, type: clue}
steps:
- id: ETAPE_1
title: "Double départ"
objective: "Collecter premiers signaux"
stations:
- zone_id: Z3
action: "Observer sinus U-SON + oscilloscope"
- zone_id: Z5
action: "Écouter message brouillé « au-dessus du sol… »"
- id: ETAPE_2
title: "Regroupement salon"
objective: "Débrief + premier appel hotline"
stations:
- zone_id: Z2
action: "Validation hypothèses initiales"
- id: ETAPE_3
title: "Fausse piste assumée"
objective: "Tester hypothèse Dr Oscilla Volt"
explicit_false_lead: true
stations:
- zone_id: Z4
action: "Tentative de déverrouillage résonance + pause goûter"
- id: ETAPE_4
title: "Alarme: module manquant"
objective: "Repartir avec feuille QUI/OU/COMMENT"
stations:
- zone_id: Z5
action: "Trouver table Morse derrière R3-D2Z"
- zone_id: Z3
action: "Oscilloscope en mode Morse partiel"
- id: ETAPE_5
title: "Décodage + LA"
objective: "Décoder Morse, valider via hotline, produire un LA"
stations:
- zone_id: Z2
action: "Validation hotline"
- zone_id: Z4
action: "Générateur fréquence = LA"
- id: ETAPE_6
title: "Révélation finale"
objective: "Identifier le coupable et ouvrir l’épilogue"
stations:
- zone_id: Z2
action: "Hotline confirme Professeur Électron"
- zone_id: Z6
action: "Derrière portrait: enveloppe + SD => lecteur MP3"
hotline:
rules:
max_calls_per_team: 3
call_types: [INDICE, VALIDATION, RELANCE]
tone: "Brigade Z, bref, encourageant"
short_script:
greeting: "Brigade Z, ligne sécurisée. Donnez votre hypothèse en 10 secondes."
indice: "Indice validé: cherchez le signal qui transforme le bruit en lettre."
validation: "Hypothèse reçue. Vérification... réponse dans 3, 2, 1."
relance: "Relance Brigade Z: revenez au QUI/OU/COMMENT et au LA."
phone_tokens:
- {phone: Z1, token: L}
- {phone: Z2, token: AFO}
- {phone: Z3, token: EFO}
- {phone: Z4, token: LE}
- {phone: Z5, token: U}
validation_table:
- hypothesis: "Coupable = Dr Oscilla Volt"
response: "FAUSSE PISTE ETAPE 3. Continuez: module manquant + Morse."
- hypothesis: "Coupable = Professeur Électron"
response: "VALIDÉ. Dirigez-vous vers Archives Interdites."
solution:
unique: true
culprit_suspect_id: S5
motive: "Saboter U-SON pour discréditer Zacus avant la démo campus"
method: "Retrait du module critique + brouillage des pistes audio"
chronology:
- "Avant jeu: sabotage en studio de résonance"
- "ETAPE 1-3: diversion orientée Oscilla"
- "ETAPE 4-5: preuves techniques convergent"
- "ETAPE 6: aveu indirect via hotline + preuve SD en archives"
info_distribution:
team_modes:
6_8_players: 2
9_14_players: 3
revelations:
- when: ETAPE_1
who: "Equipe Atelier"
learns: "Signal sinus et module U-SON incomplet"
- when: ETAPE_1
who: "Equipe Hangar"
learns: "Message brouillé répétitif et note/fréquence"
- when: ETAPE_4
who: "Toutes équipes"
learns: "Morse + correspondance téléphones"
- when: ETAPE_6
who: "Toutes équipes"
learns: "Professeur Électron est lunique coupable"
fallbacks:
lost_clue: "Le MJ replace une copie scellée dans la zone concernée après 3 minutes."
blocked_team: "Hotline autorise 1 indice direct supplémentaire (hors quota) si blocage >5 min."
too_fast_group: "Déployer mini-défi 'calibrer le LA' (+7 min) avant révélation finale."
+3
View File
@@ -0,0 +1,3 @@
# Schema
Réservé pour futurs schémas YAML/JSON du canon scénario.
+24
View File
@@ -0,0 +1,24 @@
# Intégration scénario Zacus v1 <-> hardware
## Mapping gameplay -> devices
- **U-LOCK**: verrou dentrée mission; déverrouillage vocal/ton LA.
- **U-SON**: boot corrompu, progression, jingle victoire.
- **Oscilloscope**: sinus ETAPE 1, morse partiel ETAPE 4.
- **Téléphones zones**: indices tokens L / AFO / EFO / LE / U.
- **Téléphone hotline (salon)**: validation/indice/relance Brigade Z.
## Préparer la SD
Exemple structure:
- `/story/` specs story v2 compilées
- `/audio/uson/` fichiers U-SON
- `/audio/hotline/` répliques courtes
- `/audio/zones/` radios brouillées
- `/audio/ambiances/` longues boucles
Nommage conseillé: `zacus_v1_<device>_<cue>_v01.mp3`
## ETAPE 2 (delay story)
- Départ: état verrouillé.
- Après déverrouillage + délai paramétré: step `ETAPE2_HINT`.
- Action: jouer hint audio salon + garder gate MP3 fermé.
- Puis passage `STEP_DONE` pour ouvrir gate MP3 (selon scénario choisi).
@@ -0,0 +1,68 @@
id: ZACUS_V1_UNLOCK_ETAPE2
version: 2
initial_step: STEP_WAIT_UNLOCK
app_bindings:
- id: APP_LA
app: LA_DETECTOR
config:
hold_ms: 3000
unlock_event: UNLOCK
require_listening: true
- id: APP_AUDIO
app: AUDIO_PACK
- id: APP_SCREEN
app: SCREEN_SCENE
- id: APP_GATE
app: MP3_GATE
steps:
- step_id: STEP_WAIT_UNLOCK
screen_scene_id: SCENE_LOCKED
audio_pack_id: ""
actions: [ACTION_TRACE_STEP]
apps: [APP_LA, APP_SCREEN, APP_GATE]
mp3_gate_open: false
transitions:
- trigger: on_event
event_type: unlock
event_name: UNLOCK
target_step_id: STEP_UNLOCK_WIN
after_ms: 0
priority: 100
- step_id: STEP_UNLOCK_WIN
screen_scene_id: SCENE_REWARD
audio_pack_id: PACK_WIN
actions: [ACTION_TRACE_STEP]
apps: [APP_AUDIO, APP_SCREEN, APP_GATE]
mp3_gate_open: false
transitions:
- trigger: on_event
event_type: audio_done
event_name: AUDIO_DONE
target_step_id: ETAPE2_HINT
after_ms: 0
priority: 100
- step_id: ETAPE2_HINT
screen_scene_id: SCENE_INFO
audio_pack_id: PACK_ETAPE2_HINT
actions: [ACTION_TRACE_STEP]
apps: [APP_AUDIO, APP_SCREEN, APP_GATE]
mp3_gate_open: false
transitions:
- trigger: after_ms
event_type: timer
event_name: DELAY_ETAPE2
target_step_id: STEP_DONE
after_ms: 12000
priority: 90
- step_id: STEP_DONE
screen_scene_id: SCENE_READY
audio_pack_id: ""
actions: [ACTION_TRACE_STEP, ACTION_REFRESH_SD]
apps: [APP_SCREEN, APP_GATE]
mp3_gate_open: true
transitions: []
+19 -1
View File
@@ -1,3 +1,21 @@
# Checklist matériel
> À compléter : matériel dimpression, accessoires, déco, mini-expériences “labo” safe.
## Impression (N&B friendly)
- Cartes suspects x5
- Cartes zones x6
- Cartes objets/indices
- Feuille enquête A4 (QUI/OU/COMMENT)
- Affiche hotline A4
## Matériel jeu
- U-SON (optionnel)
- U-LOCK (optionnel)
- Oscilloscope (optionnel ou visuel simulé)
- 1 téléphone hotline + 5 téléphones zones (tokens L/AFO/EFO/LE/U)
- SD finale + enveloppe + portrait archives
- Droïde R3-D2Z (décor/cache)
## Exploitation
- Chrono visible MJ
- Stylos + pinces + scotch + pochettes indices
- Enceinte/chaîne hifi (ambiances)
+13 -1
View File
@@ -1,3 +1,15 @@
# Distribution des rôles
> À compléter : tableau de distribution + variantes 614 enfants.
## Équipe animation (minimum)
- **MJ principal**: narration, rythme, anti-chaos.
- **Opérateur Hotline**: 3 types de réponses (Indice/Validation/Relance).
- **Opérateur Tech** (option): audio, oscillo, U-SON/U-LOCK.
## Répartition enfants
- 68 enfants: 2 équipes (Atelier / Hangar), puis regroupement.
- 914 enfants: 3 équipes (Atelier / Hangar / Cantine support).
## Rôle des suspects (interprétation adulte optionnelle)
- Alma Fractal: canalise le groupe (zone 2).
- Dr Oscilla Volt: fausse piste ETAPE 3.
- Professeur Électron: absent au début, révélé à la fin.
+20 -1
View File
@@ -1,3 +1,22 @@
# Guide anti-chaos
> À compléter : procédures si un indice est perdu, si un enfant bloque, si le groupe accélère, etc.
## 1) Indice perdu
- Attendre 23 min pour laisser chercher.
- Injecter "copie de secours" avec tampon Brigade Z.
## 2) Enfant bloqué / équipe à larrêt
- Donner une relance contextuelle (pas la solution).
- Si blocage >5 min: autoriser 1 indice hotline hors quota.
## 3) Groupe trop rapide
- Ajouter mini-action: calibrer un LA propre (jeu de précision 57 min).
- Lancer une variante de message brouillé supplémentaire.
## 4) Groupe dispersé
- Rappel "30 secondes regroupement" toutes les 12 min.
- Utiliser transitions courtes: "Stop labo -> briefing salon".
## 5) Script hotline (strict)
- Indice: "Regardez le signal qui se transforme en lettres."
- Validation: "Hypothèse validée / non validée."
- Relance: "Revenez au QUI/OU/COMMENT puis au LA."
@@ -1,3 +1,20 @@
# Plan des stations & mise en place
> À compléter : plan de circulation, installation des stations, timing et transitions.
## Zoning
- Z1 Entrée/Cuisine: briefing + transition.
- Z2 Salon: hotline + regroupements ETAPE 2/5/6.
- Z3 Atelier: U-SON + oscillo.
- Z4 Studio: test LA.
- Z5 Chambre Isaac: radio + table Morse cachée.
- Z6 Grenier: révélation finale (portrait/enveloppe/SD).
## Mise en place rapide (3045 min)
1. Poser affiches zones et cartes repère.
2. Préparer indices ETAPE 1 puis ETAPE 4 (séparés).
3. Tester hotline (script court) + audio zones.
4. Vérifier cache Morse derrière droïde.
5. Vérifier enveloppe finale (archives).
## Variantes capacité
- 68 enfants: 2 équipes, rotations plus longues.
- 914 enfants: 3 équipes, transitions toutes 1012 min.
+36 -2
View File
@@ -1,3 +1,37 @@
# Script minute-par-minute
# Script minute-par-minute — Zacus v1
> À compléter : déroulé de laccueil à la révélation finale (rythme enfants 911 ans).
## 0:000:10 Accueil
- Brief "Brigade Z" (mission, sécurité, coopération).
- Répartition en 2 équipes (68 enfants) ou 3 équipes (914).
## 0:100:25 ETAPE 1
- Équipe A: Zone 3 (U-SON + oscilloscope sinus).
- Équipe B: Zone 5 (radio brouillée "au-dessus du sol...").
- Équipe C (si présente): Zone 1, collecte éléments de contexte.
## 0:250:35 ETAPE 2
- Regroupement en Zone 2 (Salon).
- 1 appel hotline par équipe (validation initiale).
## 0:350:45 ETAPE 3 (fausse piste)
- Le MJ pousse lhypothèse "Dr Oscilla Volt".
- Déplacement Zone 4, test de résonance, pause goûter courte.
## 0:451:05 ETAPE 4
- Alerte MJ: "U-SON incomplet".
- Nouvelle feuille enquête QUI/OU/COMMENT.
- Retour Zone 5 (table Morse cachée) + Zone 3 (morse partiel oscillo).
## 1:051:20 ETAPE 5
- Décodage en Zone 2 + appel hotline.
- Aller Zone 4: produire un LA stable (générateur fréquence / app).
## 1:201:30 ETAPE 6
- Hotline valide: coupable = Professeur Électron.
- Aller Zone 6: portrait -> enveloppe + SD -> déblocage MP3 final.
- Clôture: félicitations, photo équipe, mini-débrief.
## Script hotline (max 3 réponses)
- **Indice**: "Brigade Z: cherchez la lettre cachée dans le signal."
- **Validation**: "Hypothèse reçue... validée / non validée."
- **Relance**: "Reprenez QUI/OU/COMMENT puis testez le LA."
+16 -2
View File
@@ -1,3 +1,17 @@
# Solution complète
# Solution complète — Zacus v1
> À compléter : coupable + mobile + méthode + chronologie (une solution unique, sans contradictions).
## Solution unique
- **Coupable**: Professeur Électron.
- **Mobile**: jalousie scientifique, sabotage de la démonstration Zacus.
- **Méthode**: retrait du module clé U-SON + brouillage audio.
## Chronologie
1. Sabotage préparé en Studio de Résonance (zone 4).
2. Diversion volontaire vers Dr Oscilla Volt (fausse piste ETAPE 3).
3. Indices techniques convergents: Morse + tokens téléphones + LA.
4. Confirmation hotline puis preuve finale en Archives (zone 6, SD).
## Pourquoi Dr Oscilla Volt nest PAS coupable
- Hypothèse plausible mais incomplète.
- Hotline indique explicitement "fausse piste" après ETAPE 3.
- Les indices matériels finaux pointent vers le Studio de Résonance.
+22
View File
@@ -0,0 +1,22 @@
# Workflow printables (prompts -> images -> PDF)
1. Choisir un prompt dans `printables/src/prompts/`.
2. Renseigner les variables (`ZONE_NAME`, `COLOR`, etc.).
3. Générer une image HD (300 DPI min).
4. Vérifier lisibilité N&B (test impression brouillon).
5. Exporter PDF dans `printables/export/pdf/...` et PNG preview dans `printables/export/png/...`.
## Nommage
- `zacus_v1_<type>_<id>_v01.(png|pdf)`
- Ex: `zacus_v1_carte-zone_Z3_v01.pdf`
## Génération locale rapide (placeholder PDF)
- `python3 tools/printables/generate_local_pdf_placeholders.py`
- Sortie: `printables/export/pdf/zacus_v1/`
## Check pré-impression
- Marges sécurité OK
- Contraste N&B OK
- Orthographe OK
- Pas de logos/marques tierces
- Cohérence scénario (noms/indices/solution)
+8
View File
@@ -0,0 +1,8 @@
# printables/export/pdf/zacus_v1
Ce dossier contient des PDF générés localement, non versionnés.
Pourquoi: les binaires PDF ne sont pas adaptés aux diff/patch textuels et aux étapes d'extraction automatisée.
Génération locale:
- `python3 tools/printables/generate_local_pdf_placeholders.py`
@@ -0,0 +1,7 @@
# Placeholder cartes (structure)
+---------------------------+
| [ZONE Z3] Atelier |
| Responsable: Dr Oscilla |
| Indice: signal sinus |
+---------------------------+
@@ -0,0 +1,6 @@
# Placeholder feuille enquête
QUI : ______________________
OU : ______________________
COMMENT: _____________________
PREUVE: ______________________
+7
View File
@@ -0,0 +1,7 @@
# Prompt packs IA — printables Zacus v1
Tous les prompts imposent:
- style "Cluedo premium × labo rétro-futuriste"
- lisibilité N&B
- sans logos/marques externes
- marges de sécurité + fond perdu
@@ -0,0 +1,3 @@
Variables: ZONE_NAME, COLOR, ICON, SHORT_DESC
Format: A4 portrait.
Prompt: Affiche zone immersive avec titre massif, code couleur adaptable, texture labo, lisible de loin, version N&B sans perte d'information.
@@ -0,0 +1,3 @@
Variables: DETECTIVE_NAME, TEAM_COLOR, BADGE_ID
Format: A6 (2 badges par page A4 possible).
Prompt: Badge détective rétro-futuriste avec zone prénom manuscrite, code badge mono, contraste imprimable.
@@ -0,0 +1,3 @@
Variables: ZONE_ID, ZONE_NAME, COLOR, HOME_LOCATION, FEATURE
Format: A6 paysage.
Prompt: Carte lieu avec code zone, texture papier technique, cadre acier, icônes de station, lisibilité forte en impression monochrome.
@@ -0,0 +1,3 @@
Variables: ITEM_NAME, ITEM_TYPE, CLUE_TEXT, COLOR
Format: A6 portrait.
Prompt: Carte objet/indice style dossier d'enquête premium, encarts codés, numéro de preuve, sans marque tierce.
@@ -0,0 +1,3 @@
Variables: SUSPECT_NAME, ROLE, GIMMICK, ALIBI, SUSPICION_LEVEL, COLOR
Format: A6 portrait.
Prompt: Carte suspect avec portrait stylisé non-marqué, bandeau nom, bloc alibi, pictogrammes labo, trame circuits fine, déclinable N&B.
@@ -0,0 +1,3 @@
Variables: TEAM_NAME, STEP_LABEL
Format: A4 portrait.
Prompt: Feuille enquête QUI/OU/COMMENT avec grilles claires, zones de notes, rappel hotline, hiérarchie typographique robuste N&B.
+3
View File
@@ -0,0 +1,3 @@
Variables: HOTLINE_NAME, CALL_TYPES, TOKEN_SET
Format: A4 portrait.
Prompt: Affiche hotline Brigade Z avec script court (Indice/Validation/Relance), tableau tokens téléphones L-AFO-EFO-LE-U, interdiction logos externes.
+3
View File
@@ -0,0 +1,3 @@
Variables: EVENT_DATE, PLAYER_NAME, LOCATION, DRESSCODE
Format: A6 recto/verso, fond perdu 3 mm, marge de sécurité 5 mm.
Prompt: Carte invitation rétro-futuriste, cadre métal, vis de coin, bandeau noir/doré, couleurs or discrètes. Inclure zone texte lisible en N&B, contraste élevé, typo titrage Bebas Neue, corps Inter. Interdiction de logos/marques.
+3
View File
@@ -0,0 +1,3 @@
Variables: DURATION, TEAM_COUNT, HOTLINE_RULE
Format: A4 1 page.
Prompt: Règles de jeu enfant 9-11 ans, étapes simples, icônes claires, encadré sécurité, design premium mais sobre N&B.
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
from pathlib import Path
import shutil
ROOT = Path(__file__).resolve().parents[2]
out = ROOT / 'audio' / 'generated'
out.mkdir(parents=True, exist_ok=True)
source_pool = {
'uson_boot_corrompu_30s.mp3': ROOT / 'hardware/firmware/esp32/data/uson_boot_corrompu_lowmono.mp3',
'uson_win_15s.mp3': ROOT / 'hardware/firmware/esp32/data/uson_mode_hero.mp3',
'zone5_radio_brouille_v1_30s.mp3': ROOT / 'hardware/firmware/esp32/data/uson_etape1_radio_lowmono.mp3',
'zone5_radio_brouille_v2_45s.mp3': ROOT / 'hardware/firmware/esp32/sonGPT/uson_etape1_radio_lointaine_audible.mp3',
'hotline_validation_01.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
'hotline_indice_01.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
'hotline_relance_01.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
'hotline_validation_02.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
'hotline_indice_02.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
'hotline_relance_02.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
}
for name, src in source_pool.items():
if not src.exists():
raise SystemExit(f"Missing source asset: {src}")
dst = out / name
shutil.copyfile(src, dst)
print(f"generated {dst.relative_to(ROOT)} <- {src.relative_to(ROOT)}")
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
def load_yaml(path: Path):
ruby = "require 'yaml'; require 'json'; puts JSON.generate(YAML.load_file(ARGV[0]))"
p = subprocess.run(["ruby", "-e", ruby, str(path)], capture_output=True, text=True)
if p.returncode != 0:
raise SystemExit(f"YAML parse error: {p.stderr.strip()}")
return json.loads(p.stdout)
def main() -> int:
manifest = Path("audio/manifests/zacus_v1_audio.yaml")
if not manifest.exists():
print("Manifest not found:", manifest)
return 1
doc = load_yaml(manifest)
missing = []
for asset in doc.get("audio_assets", []):
p = asset.get("path", "")
if p == "PROMPT_ONLY":
continue
if p and not Path(p).exists():
missing.append(p)
if missing:
print("Missing list:")
for m in missing:
print(f" - {m}")
return 2
print("OK: all declared audio files exist (or PROMPT_ONLY).")
return 0
if __name__ == "__main__":
sys.exit(main())
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
OUT = ROOT / 'printables' / 'export' / 'pdf' / 'zacus_v1'
OUT.mkdir(parents=True, exist_ok=True)
# Minimal PDF writer (no external dependency)
def pdf_escape(s: str) -> str:
return s.replace('\\', '\\\\').replace('(', '\\(').replace(')', '\\)')
def write_simple_pdf(path: Path, title: str, lines: list[str]):
content_lines = [
'BT',
'/F1 18 Tf',
'50 790 Td',
f'({pdf_escape(title)}) Tj',
'/F1 11 Tf',
]
y = 760
for ln in lines:
content_lines.append(f'1 0 0 1 50 {y} Tm ({pdf_escape(ln)}) Tj')
y -= 16
if y < 60:
break
content_lines.append('ET')
stream = '\n'.join(content_lines).encode('latin-1', errors='replace')
objs = []
objs.append(b'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n')
objs.append(b'2 0 obj << /Type /Pages /Count 1 /Kids [3 0 R] >> endobj\n')
objs.append(b'3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >> endobj\n')
objs.append(b'4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj\n')
objs.append(f'5 0 obj << /Length {len(stream)} >> stream\n'.encode() + stream + b'\nendstream endobj\n')
header = b'%PDF-1.4\n%\xe2\xe3\xcf\xd3\n'
xref_positions = [0]
body = b''
offset = len(header)
for obj in objs:
xref_positions.append(offset)
body += obj
offset += len(obj)
xref = f'xref\n0 {len(xref_positions)}\n'.encode()
xref += b'0000000000 65535 f \n'
for pos in xref_positions[1:]:
xref += f'{pos:010d} 00000 n \n'.encode()
trailer = f'trailer << /Size {len(xref_positions)} /Root 1 0 R >>\nstartxref\n{offset}\n%%EOF\n'.encode()
path.write_bytes(header + body + xref + trailer)
cards = [
('zacus_v1_regles_a4_v01.pdf', 'Regles Zacus v1', [
'Duree 60-90 min | Age 9-11 ans | 6-14 enfants',
'1) Enquete en equipes 2) Hotline Brigade Z 3) Resolution finale',
'Lisibilite N&B prioritaire, sans logos externes.'
]),
('zacus_v1_hotline_a4_v01.pdf', 'Hotline Brigade Z', [
'Reponses autorisees: Indice / Validation / Relance',
'Tokens telephones: L - AFO - EFO - LE - U',
'Solution finale unique: Professeur Electron'
]),
('zacus_v1_feuille_enquete_a4_v01.pdf', 'Feuille Enquete QUI/OU/COMMENT', [
'QUI : ______________________',
'OU : _______________________',
'COMMENT : __________________',
'PREUVE : ___________________'
]),
]
for filename, title, lines in cards:
write_simple_pdf(OUT / filename, title, lines)
print('generated', (OUT / filename).relative_to(ROOT))
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
SCENARIO = Path("game/scenarios/zacus_v1.yaml")
def load_yaml(path: Path):
ruby = "require 'yaml'; require 'json'; puts JSON.generate(YAML.load_file(ARGV[0]))"
p = subprocess.run(["ruby", "-e", ruby, str(path)], capture_output=True, text=True)
if p.returncode != 0:
raise RuntimeError(p.stderr.strip() or "YAML parse error")
return json.loads(p.stdout)
def dup_ids(items, key="id"):
seen, dups = set(), set()
for i in items:
v = i.get(key)
if v in seen:
dups.add(v)
seen.add(v)
return sorted(dups)
def main() -> int:
if not SCENARIO.exists():
print(f"ERROR: missing {SCENARIO}")
return 1
doc = load_yaml(SCENARIO)
errors = []
zones = doc.get("zones", [])
suspects = doc.get("suspects", [])
props = doc.get("props", [])
steps = doc.get("steps", [])
for label, arr in [("zones", zones), ("suspects", suspects), ("props", props), ("steps", steps)]:
d = dup_ids(arr)
if d:
errors.append(f"duplicate {label} ids: {d}")
suspect_ids = {s.get("id") for s in suspects}
zone_ids = {z.get("id") for z in zones}
prop_ids = {p.get("id") for p in props}
for z in zones:
sid = z.get("responsible_suspect_id")
if sid not in suspect_ids and sid != "S_NONE":
errors.append(f"zone {z.get('id')} references unknown suspect {sid}")
for st in steps:
for station in st.get("stations", []):
zid = station.get("zone_id")
if zid not in zone_ids:
errors.append(f"step {st.get('id')} references unknown zone {zid}")
sol = doc.get("solution", {})
if not sol.get("unique"):
errors.append("solution.unique must be true")
culprit = sol.get("culprit_suspect_id")
if culprit not in suspect_ids:
errors.append("solution culprit invalid")
if any(st.get("id") == "ETAPE_3" and not st.get("explicit_false_lead") for st in steps):
errors.append("ETAPE_3 must be flagged explicit_false_lead")
required_props = {"P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8"}
if not required_props.issubset(prop_ids):
errors.append(f"missing required props: {sorted(required_props - prop_ids)}")
if errors:
print("SCENARIO VALIDATION: FAIL")
for e in errors:
print(" -", e)
return 2
print("SCENARIO VALIDATION: PASS")
print(f" - zones={len(zones)} suspects={len(suspects)} props={len(props)} steps={len(steps)}")
return 0
if __name__ == "__main__":
sys.exit(main())