Aller au contenu

Règles du jeu

Règles telles qu'implémentées

Cette page décrit le comportement du code (orapa-core pour la géométrie, orapa-server/src/logic.rs pour le déroulé de partie), pas le livret officiel. Les formes des pièces de base sont une reconstruction (voir Pièces).

Plateau et points de tir

  • Grille 10 × 8 (grid dans data/pieces.json). Repère : x de 0 à 9 vers la droite, y de 0 à 7 vers le bas, case (0,0) en haut à gauche.
  • 36 points de tir sur le pourtour, générés par orapa_core::beam::entry_points (et répliqués côté client dans web/src/lib/points.ts).
     1  2  3  4  5  6  7  8  9  10
  A  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  11
  B  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  12
  C  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  13
  D  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  14
  E  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  15
  F  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  16
  G  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  17
  H  ·  ·  ·  ·  ·  ·  ·  ·  ·  ·  18
     I  J  K  L  M  N  O  P  Q  R
Côté Identifiants Ordre L'onde entre en allant vers…
Gauche A–H haut → bas (y = 0…7) la droite
Bas I–R gauche → droite (x = 0…9) le haut
Haut 1–10 gauche → droite (x = 0…9) le bas
Droite 11–18 haut → bas (y = 0…7) la gauche

Un point sert à la fois d'entrée et de sortie : une onde qui quitte la grille est annoncée au point situé à cet endroit du bord.

Pièces

Cases et demi-cases

Une pièce est une liste de cases relatives {x, y, kind} :

kind Contenu Côtés pleins (« cathètes ») Hypoténuse
square case pleine les 4 —
tri_nw (◤) moitié de case côté coin nord-ouest nord, ouest diagonale /
tri_ne (◥) moitié côté coin nord-est nord, est diagonale \
tri_se (◢) moitié côté coin sud-est sud, est diagonale /
tri_sw (◣) moitié côté coin sud-ouest sud, ouest diagonale \

Catalogue (data/pieces.json)

Formes de base (orientation 0), avec la notation de la démo CLI (· = vide dans la boîte englobante) :

Rouge   Jaune   Bleu / Blanc (grand triangle)   Blanc (losange) / Diamant   Corps noir
◥■◣     ■◤      ◥■■◤                            ◢◣                          ■■
        ◤·      ·◥◤·                            ◥◤
id Nom Couleur Forme Aire (cases) Orientations Extension
red Rouge rouge parallélogramme 2 4 —
yellow Jaune jaune triangle rectangle isocèle (cathètes de 2) 2 4 —
blue Bleu bleu grand triangle (hypoténuse de 4) 4 4 —
white1 Blanc (grand triangle) blanc identique à blue 4 4 —
white2 Blanc (losange) blanc losange de 4 demi-cases 2 1 —
diamond Diamant aucune (special: transparent) losange 2 1 diamond
black Corps noir aucune (special: absorb) rectangle 1×2 2 2 black
  • Partie de base : red, yellow, blue, white1, white2 (5 pièces). Les options de lobby ajoutent diamond et/ou black (6 ou 7 pièces).
  • Orientations : le moteur les génère à partir de la forme de base (4 rotations de 90°, plus leurs miroirs), puis supprime les doublons. On ne les liste jamais à la main.
  • Dans l'interface, le bouton Pivoter (ou la touche R) fait défiler toutes les orientations, miroirs compris. Les flèches déplacent la pièce sélectionnée, Del ou Backspace la retire.

Formes reconstruites

Le livret officiel n'était pas disponible pendant le développement (README, « Limites connues »). Pour corriger une forme, on modifie uniquement le tableau cells de la pièce dans data/pieces.json, puis on lance cargo test -p orapa-core (voir Développement).

Contraintes de placement

Le moteur (orapa_core::validate_placement) renvoie toutes les violations, pas seulement la première. Il est appelé en direct dans le navigateur (WASM) pendant le placement, puis par le serveur à la réception de SetPlacement.

Violation (kind) Règle
OutOfBounds Une case de la pièce sort de la grille.
Overlap Deux pièces occupent la même case, même partiellement : une case ne contient qu'une seule demi-case ou case pleine.
EdgeContact Deux cases voisines (horizontalement ou verticalement) de pièces différentes, dont les deux côtés en vis-à-vis sont pleins (côté d'un carré ou cathète). Un contact coin à coin, ou contre une hypoténuse, est autorisé.
Unreachable Aucune des 36 ondes ne touche la pièce (calculé en tirant réellement les 36 ondes sur le placement).
WhiteSymmetry Les cases occupées par les deux gemmes blanches forment un ensemble identique à son image par symétrie d'axe vertical (x ↦ 9 − x) ou horizontal (y ↦ 7 − y) du plateau. Activée par rules.white_symmetry_forbidden: true.

Symétrie des blanches

La comparaison porte sur les coordonnées des cases, pas sur l'orientation des triangles. Comme les deux blanches n'ont pas la même forme, la violation ne survient que si chacune est symétrique par rapport au même axe central (par exemple grand triangle pointe en bas et losange, tous deux centrés sur x = 4,5).

Le bouton Placement aléatoire valide demande au serveur un tirage qui respecte toutes les contraintes (tirage avec rejet, orapa_core::random_valid_placement).

Actions pendant la partie

À son tour, un joueur fait une action sur la grille adverse :

Action Résultat renvoyé Passe le tour
Envoyer une onde depuis un point du bord point de sortie et couleur, ou Absorbed (signal absorbé), ou Lost (onde perdue) oui
Sonder une case (x, y) empty (vide), couleur de la gemme, ou occupied_no_color (Diamant ou Corps noir). Une demi-case compte comme occupée. oui
Proposer une solution (grille d'hypothèses complète) juste / fausse oui, sauf si la partie se termine
  • Les résultats de toutes les actions (des deux joueurs) sont visibles dans l'historique.
  • Côté client, un point déjà tiré ne peut pas être retiré : un clic dessus met son trajet en évidence. Le serveur, lui, accepte de retirer depuis le même point.
  • Une proposition est comparée case par case au plateau secret (type de case, couleur, propriété spéciale), pas pièce par pièce. Deux placements qui donnent exactement le même plateau sont équivalents.

Trajet d'une onde

Modèle « centre de case » (crates/orapa-core/src/beam.rs) : l'onde avance de centre de case en centre de case, et entre ou sort par le milieu des côtés. Elle ne passe jamais par un sommet de la grille, donc les cas « coin exact » et « onde qui longe une arête » sont impossibles par construction.

À chaque case atteinte :

  1. Case vide : l'onde continue tout droit.
  2. Case occupée : la pièce compte comme touchée et sa couleur (si elle en a une) est ajoutée au mélange. Ensuite :
    • Corps noir : l'onde est absorbée (Absorbed), sans point de sortie ni couleur.
    • Carré, ou triangle touché par une cathète : demi-tour.
    • Triangle touché côté hypoténuse (l'onde entre par la moitié vide) : déviation à 90°.
  3. Sortie de grille : résultat Exit { point, color }.
  4. État (case, direction) déjà vu : boucle fermée, résultat Lost. Un nombre maximal de pas (4 × largeur × hauteur + 1) sert de filet de sécurité.
Triangle L'onde arrive en allant vers… Elle repart vers…
tri_nw ◤ la gauche / le haut le bas / la droite
tri_se ◢ la droite / le bas le haut / la gauche
tri_ne ◥ la droite / le haut le bas / la gauche
tri_sw ◣ la gauche / le bas le haut / la droite

Bon à savoir

  • Une couleur compte même si l'onde ne fait que rebondir sur la pièce.
  • Le Diamant se comporte géométriquement comme n'importe quelle demi-case (rebond ou déviation) mais n'ajoute aucune couleur.
  • Les trajets sont réversibles : tirer depuis le point de sortie ramène au point d'entrée avec la même couleur (test beam_paths_are_reversible).
  • Un carré renvoie l'onde sur elle-même : si rien d'autre n'est touché, elle ressort par son point d'entrée.

Mélange des couleurs

Fonction orapa_core::mix_colors : chaque couleur de gemme touchée compte une seule fois, quel que soit l'ordre.

Gemmes touchées Résultat Valeur JSON
aucune Transparent transparent
Rouge Rouge red
Bleu Bleu blue
Jaune Jaune yellow
Blanc Blanc white
Rouge + Bleu Violet purple
Rouge + Jaune Orange orange
Bleu + Jaune Vert green
Rouge + Blanc Rose pink
Bleu + Blanc Bleu ciel sky_blue
Jaune + Blanc Citron lemon
Rouge + Bleu + Jaune Noir black
Rouge + Bleu + Blanc Violet clair light_purple
Bleu + Jaune + Blanc Vert clair light_green
Rouge + Jaune + Blanc Orange clair light_orange
Rouge + Bleu + Jaune + Blanc Gris gray

Note

Le résultat Noir (rouge + bleu + jaune) est une sortie normale avec un point de sortie. Il ne faut pas le confondre avec Signal absorbé (Corps noir), qui n'a ni sortie ni couleur.

Tours, vies et fin de partie

  • Premier joueur : tiré au hasard quand les deux joueurs sont prêts. Le tour alterne ensuite après chaque action.
  • Vies : 1 par défaut (règle officielle selon le code), ou 2 en option de lobby. Affichées sous forme de ♦.
  • Limite de temps par tour (option : aucune, 30 s, 60 s, 120 s) : à l'expiration, une entrée Timeout est ajoutée à l'historique et le tour passe. Duel uniquement.
Situation Issue
Proposition juste du joueur qui n'a pas commencé Victoire immédiate
Proposition juste du premier joueur Mort subite : l'adversaire a un dernier tour pour égaliser
Mort subite, puis proposition juste Égalité
Mort subite, puis proposition fausse Victoire du premier joueur
Proposition fausse, il reste des vies −1 vie, le tour passe
Proposition fausse, plus de vie Élimination, l'adversaire gagne

Mort subite : comportement actuel du code

Seule une proposition clôt la mort subite. Si l'adversaire envoie une onde ou sonde une case (ou si son temps expire), la main revient au premier joueur et la partie continue en mode mort subite. La proposition suivante, quel que soit son auteur, termine la partie selon le tableau ci-dessus : juste = égalité, fausse = victoire du premier joueur.

Mode solo (entraînement)

  • Le serveur tire une disposition aléatoire valide (avec les extensions choisies). La partie démarre directement en phase de jeu, sans placement ni alternance de tours ni minuteur.
  • Les vies s'appliquent : une proposition juste gagne. Si les vies tombent à 0, la partie se termine sans vainqueur (winner: null).
  • Le bouton Voir la solution affiche la disposition sans terminer la partie (refusé par le serveur en duel).

Affichage d'une défaite en solo

ResultScreen interprète winner: null comme une égalité : une défaite en solo s'affiche donc « Égalité ! ».

Mode aide

  • Option de lobby Mode aide (vérifie tes hypothèses), cochée par défaut dans le client.
  • Pendant la partie, le joueur construit sa grille d'hypothèses (même éditeur que pour le placement). Le bouton Vérifier la cohérence rejoue localement, en WebAssembly, toutes ses ondes et ses sondages contre cette grille, puis liste les indices contredits (onde C, sondage (3,4)…).
  • Ne consomme pas de tour. Aucune donnée secrète adverse n'est utilisée : seul l'historique public sert à la vérification.

Réactions

En duel, trois réactions rapides sont diffusées aux deux joueurs : « Bien joué ! » (gg), « Aïe ! » (oops), « Je réfléchis… » (think).