From d823c1d0b939c9f3f6f5c08a57a30987d133f686 Mon Sep 17 00:00:00 2001 From: homer Date: Sun, 18 Oct 2026 21:19:01 +0200 Subject: [PATCH] Initialise le catalogue de jeux Skori MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Structure games/-/ : game.json, scoring.json, rules.fr.json - 7 jeux intégrés à l'app (modèles de score), fiches de règles Azul et 6 qui prend - Schémas JSON générés depuis l'app (autocomplétion dans l'éditeur) - CI : validation avec le validateur de l'app, schémas à jour - Licence CC BY-SA 4.0, guide de contribution --- .editorconfig | 9 + .gitattributes | 1 + .gitea/workflows/validate.yml | 44 +++ CONTRIBUTING.md | 71 ++++ LICENSE | 11 + README.md | 44 +++ games/230802-azul/game.json | 7 + games/230802-azul/rules.fr.json | 12 + games/230802-azul/scoring.json | 25 ++ games/271321-cabo/game.json | 7 + games/271321-cabo/scoring.json | 38 ++ games/406854-odin/game.json | 7 + games/406854-odin/scoring.json | 59 ++++ games/414317-harmonies/game.json | 7 + games/414317-harmonies/scoring.json | 73 ++++ games/432-6-qui-prend/game.json | 7 + games/432-6-qui-prend/rules.fr.json | 12 + games/432-6-qui-prend/scoring.json | 24 ++ games/448428-moustache/game.json | 7 + games/448428-moustache/scoring.json | 35 ++ games/453781-chouineurs/game.json | 7 + games/453781-chouineurs/scoring.json | 20 ++ schemas/game.schema.json | 39 +++ schemas/rules.schema.json | 64 ++++ schemas/scoring.schema.json | 498 +++++++++++++++++++++++++++ 25 files changed, 1128 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitea/workflows/validate.yml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 games/230802-azul/game.json create mode 100644 games/230802-azul/rules.fr.json create mode 100644 games/230802-azul/scoring.json create mode 100644 games/271321-cabo/game.json create mode 100644 games/271321-cabo/scoring.json create mode 100644 games/406854-odin/game.json create mode 100644 games/406854-odin/scoring.json create mode 100644 games/414317-harmonies/game.json create mode 100644 games/414317-harmonies/scoring.json create mode 100644 games/432-6-qui-prend/game.json create mode 100644 games/432-6-qui-prend/rules.fr.json create mode 100644 games/432-6-qui-prend/scoring.json create mode 100644 games/448428-moustache/game.json create mode 100644 games/448428-moustache/scoring.json create mode 100644 games/453781-chouineurs/game.json create mode 100644 games/453781-chouineurs/scoring.json create mode 100644 schemas/game.schema.json create mode 100644 schemas/rules.schema.json create mode 100644 schemas/scoring.schema.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..86a63dc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitea/workflows/validate.yml b/.gitea/workflows/validate.yml new file mode 100644 index 0000000..745e19c --- /dev/null +++ b/.gitea/workflows/validate.yml @@ -0,0 +1,44 @@ +name: Validation du contenu + +on: + push: + pull_request: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Contenu + uses: actions/checkout@v4 + with: + path: content + + # Le validateur vit dans le dépôt de l'app (mêmes schémas que le hub). + # Si ce dépôt est privé, créer le secret SKORI_CODE_TOKEN (jeton Gitea + # avec accès en lecture au dépôt homer/skori_v2). + - name: Code de l'app + uses: actions/checkout@v4 + with: + repository: homer/skori_v2 + path: code + token: ${{ secrets.SKORI_CODE_TOKEN || github.token }} + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Installation + working-directory: code + run: | + corepack enable + pnpm install --frozen-lockfile --filter @skori/server... + + - name: Validation des jeux + working-directory: code + run: pnpm --filter @skori/server content:validate "$GITHUB_WORKSPACE/content" + + - name: Schémas à jour + working-directory: code + run: | + pnpm --filter @skori/server content:schemas /tmp/schemas + diff -r /tmp/schemas "$GITHUB_WORKSPACE/content/schemas" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cbbeb23 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# Contribuer + +Chaque ajout ou correction passe par une demande de fusion (pull request), +relue avant publication. Le CI vérifie automatiquement les fichiers. + +## Ajouter un jeu + +1. Trouvez le jeu sur [BoardGameGeek](https://boardgamegeek.com) et notez son + `bggId` (le nombre dans l'adresse de sa page). +2. Créez le dossier `games/-/`, par exemple + `games/9209-les-aventuriers-du-rail/`. +3. Ajoutez `game.json` : + + ```json + { + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 9209, + "name": "Les Aventuriers du Rail", + "officialRulesUrl": "https://www.daysofwonder.com/…" + } + ``` + +4. Ajoutez `scoring.json`, le modèle de score. Son `id` vaut `bgg-`. + Le plus simple : dans l'app, **Jeux → Ajouter un jeu**, créez le modèle, + puis **Exporter le modèle** depuis la fiche du jeu. Inspirez-vous aussi des + jeux existants (catégories, options, cible de fin de partie, équipes…). + +5. Facultatif : `rules.fr.json`, la fiche de règles : + + ```json + { + "$schema": "../../schemas/rules.schema.json", + "schemaVersion": 1, + "locale": "fr", + "summary": "Le jeu en une ou deux phrases.", + "goal": "But du jeu.", + "setup": "Mise en place.", + "turn": "Déroulement d'un tour.", + "end": "Fin de partie.", + "scoring": "Décompte des points.", + "tips": "Conseils." + } + ``` + + `summary`, `goal` et `turn` sont obligatoires. Texte brut, retours à la + ligne autorisés. **Rédigez avec vos propres mots** : pas de copier-coller + du livret de l'éditeur. + +## Vérifier avant d'envoyer + +Avec un éditeur qui comprend les schémas JSON (VS Code…), la clé `$schema` +donne l'autocomplétion et signale les erreurs au fil de la frappe. + +La validation complète (celle du CI) se lance depuis le dépôt de l'app : + +```bash +pnpm --filter @skori/server content:validate ../skori-games +``` + +## Jeux intégrés à l'app + +Les dossiers dont `game.json` contient un `slug` (`azul`, `cabo`…) +correspondent aux jeux livrés avec l'app ; leur `scoring.json` garde l'`id` +du jeu intégré. Une correction de leur modèle de score doit aussi être +reportée dans le dépôt de l'app. + +## Licence + +En contribuant, vous acceptez que votre contribution soit publiée sous +licence [CC BY-SA 4.0](LICENSE). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..48171c5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +Le contenu de ce dépôt (modèles de score, fiches de règles, métadonnées) est +mis à disposition selon les termes de la licence Creative Commons +Attribution - Partage dans les mêmes conditions 4.0 International +(CC BY-SA 4.0). + +Résumé : https://creativecommons.org/licenses/by-sa/4.0/deed.fr +Texte juridique : https://creativecommons.org/licenses/by-sa/4.0/legalcode.fr + +Les noms de jeux appartiennent à leurs éditeurs respectifs. Les fiches de +règles sont des résumés rédigés par la communauté Skori ; elles ne +remplacent pas les règles officielles des éditeurs. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4e7275 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Skori — catalogue de jeux + +Contenu communautaire de [Skori](https://gitea.ton618.ovh/homer/skori_v2), +le compteur de points pour jeux de société : **comment compter les points** +de chaque jeu, et une **fiche de règles** rédigée par la communauté. + +Le hub Skori importe ce dépôt automatiquement : un jeu ajouté ici devient +disponible dans l'app pour tout le monde (recherche dans **Jeux → Ajouter un +jeu**). + +## Organisation + +``` +games/ + 230802-azul/ # - + game.json # obligatoire : nom affiché, lien vers les règles officielles + scoring.json # modèle de score (comment compter les points) + rules.fr.json # fiche de règles en français + README.md # notes libres (facultatif) +schemas/ # schémas JSON (autocomplétion dans l'éditeur) +``` + +Le `bggId` est l'identifiant du jeu sur [BoardGameGeek](https://boardgamegeek.com) : +c'est le nombre dans l'adresse de sa page (`boardgamegeek.com/boardgame/230802/azul`). +Les informations du jeu (année, nombre de joueurs, auteurs, image…) viennent +de BoardGameGeek : on ne les recopie pas ici. + +## Ce qui n'a pas sa place ici + +- **Du code** : les modèles de score sont des fichiers JSON déclaratifs. Si un + jeu demande une règle de calcul que le format ne sait pas exprimer, ouvrez + un ticket sur le dépôt de l'app. +- **Des images, logos ou PDF** : ce sont des œuvres des éditeurs. +- **Le texte des livrets de règles** : une fiche de règles est un résumé + rédigé avec vos propres mots, qui renvoie aux règles officielles. + +Le CI refuse tout autre fichier que ceux listés ci-dessus. + +Voir [CONTRIBUTING.md](CONTRIBUTING.md) pour ajouter ou corriger un jeu. + +## Licence + +Contenu sous licence [CC BY-SA 4.0](LICENSE). Données de jeux : Powered by +[BoardGameGeek](https://boardgamegeek.com). diff --git a/games/230802-azul/game.json b/games/230802-azul/game.json new file mode 100644 index 0000000..e8cb02e --- /dev/null +++ b/games/230802-azul/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 230802, + "name": "Azul", + "slug": "azul" +} diff --git a/games/230802-azul/rules.fr.json b/games/230802-azul/rules.fr.json new file mode 100644 index 0000000..a78e1a2 --- /dev/null +++ b/games/230802-azul/rules.fr.json @@ -0,0 +1,12 @@ +{ + "$schema": "../../schemas/rules.schema.json", + "schemaVersion": 1, + "locale": "fr", + "summary": "Prenez des tuiles colorées et complétez le mur de votre palais pour marquer le plus de points.", + "goal": "Avoir le plus de points à la fin de la partie.", + "setup": "Chaque joueur prend un plateau. On place des fabriques au centre (5 à 2 joueurs, 7 à 3, 9 à 4) et on pose 4 tuiles tirées au hasard sur chacune. Le jeton premier joueur va au centre de la table.", + "turn": "À son tour, on prend toutes les tuiles d'une même couleur sur une fabrique (le reste part au centre) ou toutes celles d'une couleur au centre. Le premier à se servir au centre prend aussi le jeton premier joueur, qui va sur sa ligne plancher.\nLes tuiles prises vont sur une seule ligne de motif, qui ne peut contenir qu'une couleur, absente de la ligne correspondante du mur. Les tuiles en trop tombent sur la ligne plancher.\nQuand toutes les tuiles ont été prises, chaque ligne de motif complète envoie une tuile sur le mur et marque des points.", + "end": "La partie s'arrête à la fin de la manche où un joueur complète au moins une ligne horizontale de son mur.", + "scoring": "Une tuile posée sur le mur rapporte 1 point, ou autant de points que de tuiles reliées horizontalement et verticalement.\nLa ligne plancher fait perdre 1, 1, 2, 2, 2, 3 et 3 points.\nEn fin de partie : +2 par ligne horizontale complète, +7 par colonne complète, +10 par couleur posée 5 fois.", + "tips": "Évitez de prendre plus de tuiles que vous ne pouvez en placer : la ligne plancher coûte cher." +} diff --git a/games/230802-azul/scoring.json b/games/230802-azul/scoring.json new file mode 100644 index 0000000..3827e37 --- /dev/null +++ b/games/230802-azul/scoring.json @@ -0,0 +1,25 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "azul", + "bggId": 230802, + "name": "Azul", + "description": "Placez des tuiles pour marquer un maximum de points.", + "players": { + "min": 2, + "max": 4 + }, + "winner": "highest", + "icon": "Palette", + "colors": { + "primary": "#005b8f" + }, + "options": [ + { + "type": "boolean", + "id": "variants", + "label": "Variante plateau gris", + "default": false + } + ] +} diff --git a/games/271321-cabo/game.json b/games/271321-cabo/game.json new file mode 100644 index 0000000..a607caa --- /dev/null +++ b/games/271321-cabo/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 271321, + "name": "Cabo", + "slug": "cabo" +} diff --git a/games/271321-cabo/scoring.json b/games/271321-cabo/scoring.json new file mode 100644 index 0000000..719cdb6 --- /dev/null +++ b/games/271321-cabo/scoring.json @@ -0,0 +1,38 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "cabo", + "bggId": 271321, + "name": "Cabo", + "description": "Trouvez la licorne. La partie se termine si on atteint ou dépasse 100 points. Tomber pile sur 100 fait redescendre le score à 50 (une fois par joueur).", + "players": { + "min": 2, + "max": 5 + }, + "winner": "lowest", + "aggregation": { + "type": "sum_with_reset", + "threshold": 100, + "resetTo": 50, + "oncePerPlayer": true + }, + "end": { + "target": { + "type": "fixed", + "value": 100 + }, + "targetCondition": "reaches" + }, + "icon": "Ghost", + "colors": { + "primary": "#6e1d5a" + }, + "options": [ + { + "type": "boolean", + "id": "kamikaze", + "label": "Règle Kamikaze (2 cartes 13 + 2 cartes 12)", + "default": true + } + ] +} diff --git a/games/406854-odin/game.json b/games/406854-odin/game.json new file mode 100644 index 0000000..a08c0cf --- /dev/null +++ b/games/406854-odin/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 406854, + "name": "Odin", + "slug": "odin_cards" +} diff --git a/games/406854-odin/scoring.json b/games/406854-odin/scoring.json new file mode 100644 index 0000000..e321e2e --- /dev/null +++ b/games/406854-odin/scoring.json @@ -0,0 +1,59 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "odin_cards", + "bggId": 406854, + "name": "Odin", + "description": "Videz votre main le plus vite possible. Un point par carte restante. La partie s'arrête quand le score cible est atteint, le plus petit score gagne.", + "players": { + "min": 2, + "max": 6 + }, + "winner": "lowest", + "end": { + "target": { + "type": "option", + "optionId": "target_score", + "customValue": "custom", + "customOptionId": "custom_target_score" + }, + "targetCondition": "reaches" + }, + "icon": "Sword", + "colors": { + "primary": "#c2410c" + }, + "options": [ + { + "type": "select", + "id": "target_score", + "label": "Score de fin de partie", + "default": "15", + "choices": [ + { + "value": "15", + "label": "Partie courte (15 pts)" + }, + { + "value": "20", + "label": "Partie classique (20 pts)" + }, + { + "value": "custom", + "label": "Personnalisé" + } + ] + }, + { + "type": "number", + "id": "custom_target_score", + "label": "Score personnalisé", + "default": 25, + "min": 1, + "visibleWhen": { + "optionId": "target_score", + "equals": "custom" + } + } + ] +} diff --git a/games/414317-harmonies/game.json b/games/414317-harmonies/game.json new file mode 100644 index 0000000..ee93dfe --- /dev/null +++ b/games/414317-harmonies/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 414317, + "name": "Harmonies", + "slug": "harmonie" +} diff --git a/games/414317-harmonies/scoring.json b/games/414317-harmonies/scoring.json new file mode 100644 index 0000000..1be6813 --- /dev/null +++ b/games/414317-harmonies/scoring.json @@ -0,0 +1,73 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "harmonie", + "bggId": 414317, + "name": "Harmonies", + "description": "Créez des paysages pour accueillir des animaux.", + "players": { + "min": 1, + "max": 4 + }, + "winner": "highest", + "end": { + "rounds": 1 + }, + "icon": "TreePine", + "colors": { + "primary": "#065f46" + }, + "options": [ + { + "type": "boolean", + "id": "esprits_nature", + "label": "Extension : Esprits de la nature", + "default": false + } + ], + "categories": [ + { + "id": "arbres", + "label": "Arbres", + "icon": "TreePine", + "color": "#16a34a" + }, + { + "id": "montagnes", + "label": "Montagnes", + "icon": "Mountain", + "color": "#64748b" + }, + { + "id": "champs", + "label": "Champs", + "icon": "Wheat", + "color": "#d97706" + }, + { + "id": "batiments", + "label": "Bâtiments", + "icon": "Building2", + "color": "#dc2626" + }, + { + "id": "eau", + "label": "Rivière", + "icon": "Waves", + "color": "#0ea5e9" + }, + { + "id": "animaux", + "label": "Animaux", + "icon": "Rabbit", + "color": "#a16207" + }, + { + "id": "esprits_nature", + "label": "Esprits de la nature", + "icon": "Sparkles", + "color": "#8b5cf6", + "requiresOption": "esprits_nature" + } + ] +} diff --git a/games/432-6-qui-prend/game.json b/games/432-6-qui-prend/game.json new file mode 100644 index 0000000..ee041fb --- /dev/null +++ b/games/432-6-qui-prend/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 432, + "name": "6 qui prend !", + "slug": "six_qui_prend" +} diff --git a/games/432-6-qui-prend/rules.fr.json b/games/432-6-qui-prend/rules.fr.json new file mode 100644 index 0000000..53558eb --- /dev/null +++ b/games/432-6-qui-prend/rules.fr.json @@ -0,0 +1,12 @@ +{ + "$schema": "../../schemas/rules.schema.json", + "schemaVersion": 1, + "locale": "fr", + "summary": "Posez vos cartes au bon endroit pour éviter de ramasser des têtes de bœuf.", + "goal": "Avoir le moins de têtes de bœuf possible à la fin de la partie.", + "setup": "Chaque joueur reçoit 10 cartes. On pose 4 cartes face visible au centre : chacune démarre une rangée.", + "turn": "Tous les joueurs choisissent une carte en secret puis la révèlent en même temps.\nLes cartes sont placées dans l'ordre croissant, de la plus petite à la plus grande.\nChaque carte va dans la rangée dont la dernière carte est la plus proche en dessous de sa valeur.\nSi la carte serait la 6e de sa rangée, son joueur ramasse les 5 cartes de la rangée et sa carte en démarre une nouvelle.\nSi la carte est plus petite que la dernière carte de toutes les rangées, son joueur choisit une rangée, la ramasse, et sa carte la remplace.", + "end": "Une manche dure 10 tours. On joue des manches jusqu'à ce qu'un joueur atteigne 66 têtes de bœuf.", + "scoring": "En fin de manche, chacun compte les têtes de bœuf des cartes ramassées et les ajoute à son total. Le plus petit total gagne.", + "tips": "Garder des cartes très basses ou très hautes en fin de manche aide à se sortir des situations délicates." +} diff --git a/games/432-6-qui-prend/scoring.json b/games/432-6-qui-prend/scoring.json new file mode 100644 index 0000000..31117e0 --- /dev/null +++ b/games/432-6-qui-prend/scoring.json @@ -0,0 +1,24 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "six_qui_prend", + "bggId": 432, + "name": "6 qui prend !", + "description": "Évitez de ramasser les têtes de bœuf ! La partie s'arrête quand un joueur atteint 66 points. Le plus petit score gagne.", + "players": { + "min": 2, + "max": 10 + }, + "winner": "lowest", + "end": { + "target": { + "type": "fixed", + "value": 66 + }, + "targetCondition": "reaches" + }, + "icon": "Beef", + "colors": { + "primary": "#d81f26" + } +} diff --git a/games/448428-moustache/game.json b/games/448428-moustache/game.json new file mode 100644 index 0000000..e2f87a7 --- /dev/null +++ b/games/448428-moustache/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 448428, + "name": "Moustache", + "slug": "moustache" +} diff --git a/games/448428-moustache/scoring.json b/games/448428-moustache/scoring.json new file mode 100644 index 0000000..6bc5b4f --- /dev/null +++ b/games/448428-moustache/scoring.json @@ -0,0 +1,35 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "moustache", + "bggId": 448428, + "name": "Moustache", + "description": "Les équipes changent à chaque manche : notez l'équipe gagnante des 4 manches, puis chacun révèle ses points. Le plus de points gagne.", + "players": { + "min": 3, + "max": 6 + }, + "winner": "highest", + "end": { + "rounds": 5 + }, + "teams": { + "rounds": 4, + "list": [ + { + "id": "bleu", + "label": "Équipe bleue", + "color": "#2563eb" + }, + { + "id": "rouge", + "label": "Équipe rouge", + "color": "#dc2626" + } + ] + }, + "icon": "Drama", + "colors": { + "primary": "#e5709b" + } +} diff --git a/games/453781-chouineurs/game.json b/games/453781-chouineurs/game.json new file mode 100644 index 0000000..e0e8848 --- /dev/null +++ b/games/453781-chouineurs/game.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../schemas/game.schema.json", + "schemaVersion": 1, + "bggId": 453781, + "name": "Chouineurs", + "slug": "chouineurs" +} diff --git a/games/453781-chouineurs/scoring.json b/games/453781-chouineurs/scoring.json new file mode 100644 index 0000000..6284502 --- /dev/null +++ b/games/453781-chouineurs/scoring.json @@ -0,0 +1,20 @@ +{ + "$schema": "../../schemas/scoring.schema.json", + "schemaVersion": 2, + "id": "chouineurs", + "bggId": 453781, + "name": "Chouineurs", + "description": "Retournez la situation ! Marquez un maximum de points en 4 manches.", + "players": { + "min": 3, + "max": 5 + }, + "winner": "highest", + "end": { + "rounds": 4 + }, + "icon": "Drama", + "colors": { + "primary": "#6d4a95" + } +} diff --git a/schemas/game.schema.json b/schemas/game.schema.json new file mode 100644 index 0000000..a670010 --- /dev/null +++ b/schemas/game.schema.json @@ -0,0 +1,39 @@ +{ + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "schemaVersion": { + "type": "number", + "const": 1 + }, + "bggId": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 60 + }, + "slug": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9_-]*$", + "maxLength": 60 + }, + "officialRulesUrl": { + "type": "string", + "format": "uri", + "pattern": "^https\\:\\/\\/" + } + }, + "required": [ + "schemaVersion", + "bggId", + "name" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Jeu Skori (game.json)" +} diff --git a/schemas/rules.schema.json b/schemas/rules.schema.json new file mode 100644 index 0000000..4164d73 --- /dev/null +++ b/schemas/rules.schema.json @@ -0,0 +1,64 @@ +{ + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "schemaVersion": { + "type": "number", + "const": 1 + }, + "locale": { + "type": "string", + "enum": [ + "fr" + ], + "default": "fr" + }, + "summary": { + "type": "string", + "maxLength": 300, + "minLength": 1 + }, + "goal": { + "type": "string", + "maxLength": 1000, + "minLength": 1 + }, + "setup": { + "type": "string", + "maxLength": 3000 + }, + "turn": { + "type": "string", + "maxLength": 5000, + "minLength": 1 + }, + "end": { + "type": "string", + "maxLength": 2000 + }, + "scoring": { + "type": "string", + "maxLength": 3000 + }, + "tips": { + "type": "string", + "maxLength": 2000 + }, + "officialRulesUrl": { + "type": "string", + "format": "uri", + "pattern": "^https\\:\\/\\/" + } + }, + "required": [ + "schemaVersion", + "summary", + "goal", + "turn" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Fiche de règles Skori (rules.fr.json)" +} diff --git a/schemas/scoring.schema.json b/schemas/scoring.schema.json new file mode 100644 index 0000000..2dc79c1 --- /dev/null +++ b/schemas/scoring.schema.json @@ -0,0 +1,498 @@ +{ + "type": "object", + "properties": { + "$schema": { + "type": "string" + }, + "schemaVersion": { + "type": "number", + "const": 2 + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "bggId": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 60 + }, + "description": { + "type": "string", + "maxLength": 400 + }, + "players": { + "type": "object", + "properties": { + "min": { + "type": "integer", + "minimum": 1, + "maximum": 30 + }, + "max": { + "type": "integer", + "minimum": 1, + "maximum": 30 + } + }, + "required": [ + "min", + "max" + ], + "additionalProperties": false + }, + "winner": { + "type": "string", + "enum": [ + "highest", + "lowest" + ] + }, + "aggregation": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "sum" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "sum_with_reset" + }, + "threshold": { + "type": "number" + }, + "resetTo": { + "type": "number" + }, + "oncePerPlayer": { + "type": "boolean" + } + }, + "required": [ + "type", + "threshold", + "resetTo", + "oncePerPlayer" + ], + "additionalProperties": false + } + ], + "default": { + "type": "sum" + } + }, + "end": { + "type": "object", + "properties": { + "target": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "fixed" + }, + "value": { + "type": "number" + } + }, + "required": [ + "type", + "value" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "option" + }, + "optionId": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "customValue": { + "type": "string" + }, + "customOptionId": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + } + }, + "required": [ + "type", + "optionId" + ], + "additionalProperties": false + } + ] + }, + "targetCondition": { + "type": "string", + "enum": [ + "reaches", + "exceeds" + ], + "default": "reaches" + }, + "rounds": { + "type": "integer", + "exclusiveMinimum": 0 + } + }, + "additionalProperties": false, + "default": {} + }, + "teams": { + "type": "object", + "properties": { + "rounds": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "color": { + "type": "string", + "pattern": "^#[0-9a-fA-F]{6}$" + } + }, + "required": [ + "id", + "label", + "color" + ], + "additionalProperties": false + }, + "minItems": 2 + } + }, + "required": [ + "rounds", + "list" + ], + "additionalProperties": false + }, + "categories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "icon": { + "type": "string", + "maxLength": 40 + }, + "color": { + "type": "string", + "pattern": "^#[0-9a-fA-F]{6}$" + }, + "requiresOption": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + } + }, + "required": [ + "id", + "label" + ], + "additionalProperties": false + }, + "default": [] + }, + "options": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "boolean" + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "default": { + "type": "boolean" + }, + "visibleWhen": { + "type": "object", + "properties": { + "optionId": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "equals": { + "type": [ + "boolean", + "number", + "string" + ] + } + }, + "required": [ + "optionId", + "equals" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "id", + "label", + "default" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "number" + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "default": { + "type": "number" + }, + "min": { + "type": "number" + }, + "max": { + "type": "number" + }, + "visibleWhen": { + "type": "object", + "properties": { + "optionId": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "equals": { + "type": [ + "boolean", + "number", + "string" + ] + } + }, + "required": [ + "optionId", + "equals" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "id", + "label", + "default" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "select" + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80 + }, + "default": { + "type": "string" + }, + "choices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string", + "minLength": 1, + "maxLength": 80 + } + }, + "required": [ + "value", + "label" + ], + "additionalProperties": false + }, + "minItems": 1 + }, + "visibleWhen": { + "type": "object", + "properties": { + "optionId": { + "type": "string", + "minLength": 1, + "maxLength": 60, + "pattern": "^[a-z0-9][a-z0-9_-]*$" + }, + "equals": { + "type": [ + "boolean", + "number", + "string" + ] + } + }, + "required": [ + "optionId", + "equals" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "id", + "label", + "default", + "choices" + ], + "additionalProperties": false + } + ] + }, + "default": [] + }, + "icon": { + "type": "string", + "maxLength": 40 + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "pattern": "^#[0-9a-fA-F]{6}$" + }, + "secondary": { + "type": "string", + "pattern": "^#[0-9a-fA-F]{6}$" + } + }, + "required": [ + "primary" + ], + "additionalProperties": false + }, + "assets": { + "type": "object", + "properties": { + "logo": { + "type": "string" + }, + "banner": { + "type": "string" + }, + "rules": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "required": [ + "schemaVersion", + "id", + "name", + "players", + "winner", + "colors" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Modèle de score Skori (scoring.json)" +}