Vai al contenuto

Progetto:BridgeShape Motore e frontend

Da BridgePedia.

Documentazione BridgeShape: API e sintassi DSL · Ricette DSL

BridgeShape — Specifica del motore di smazzate e del frontend (per Play)

Destinatario: il team/sessione di Play (generatore di smazzate e relativo frontend). Obiettivo: realizzare un motore completo che generi mani per qualsiasi scenario descritto nelle pagine di BridgePedia, e un frontend per consultare gli scenari, generare, creare e gestire smazzate.

Documenti collegati (leggere in quest'ordine):

  1. DEAL_GENERATOR_API.md — riferimento della sintassi (DSL v1 + v2) e del JSON dell'API.
  2. questo file — semantica normativa, architettura del motore, test di conformità, specifica del frontend, piano di consegna.
  3. Wiki: Progetto:BridgeShape API; le pagine di convenzione con shape_constraints (elenco: GET /api.php?action=bridgeshape&shapeaction=list).

Fonte di verità del contratto: l'API action=bridgeshape di https://bridgepedia.it. Se questo documento e l'API divergono vale l'API; segnalatelo a BridgePedia.


1. Contratto, versioni e compatibilità

Elemento Dove Uso
dslVersion risposta di get e validate oggi 2. Cambia solo con modifiche non retrocompatibili
scenarios[].requires get, validate tag delle capacità richieste dallo scenario (§1.1)
errors get, validate contraddizioni semantiche: non generare
warnings get, validate frammenti del DSL scartati dal parser: non generare
descriptions get, validate testo leggibile per scenario, tradotto (lang=it|en|...)
valid validate 1/0

Regole di compatibilità:

  • I campi nuovi compaiono solo se usati: un motore v1 continua a funzionare sulle pagine v1.
  • Campi sconosciuti: ignorarli non è permesso se compaiono in requires. Se il motore non supporta un tag di requires, deve rifiutare lo scenario con codice unsupported_feature (e nome del tag) e il frontend deve mostrarlo come "non ancora generabile", non nasconderlo.
  • Il booleano nel JSON è sempre 1/0 (MediaWiki rimuove i false).
  • Tutte le chiavi di seme sono S H D C oppure una variabile "$a".."$d".

1.1 Tag requires

Tag Costrutti Impatto sul motore
suit_cards S=KJT assegnazione di onori precisi
suit_patterns S~AKQ.|A... alternative di mano nel seme
vars $a $b, vars seme scelto a runtime, condiviso tra posizioni
stoppers stop:S filtro/vincolo sulla mano
card_presence has:HQ, lacks:SK carte fisse
controls aces kings queens controls kc conteggi di onori
points tp dp ltc punteggi derivati
pair_points pairhcp pairtp defhcp vincolo sulla coppia
compare H>S, longest: relazioni tra lunghezze
shape_pattern shape:5-4-x-x forma ordinata
any_not any(...), not(...) disgiunzioni e negazioni
context dealer vul auction contract declarer lead metadati e carta d'attacco
weight weight:N scelta ponderata dello scenario

Le macro @M=H,S e quiet sono già espanse dal parser (non compaiono mai).

1.2 Endpoint consigliati lato Play

Il motore deve esporre (nomi indicativi; l'importante è la semantica):

<syntaxhighlight lang="text"> POST /api/deals/generate {

 "source": { "page": "Splinter", "scenario": "1H-4C" }      // oppure { "dsl": "..." }
         | { "page": "Splinter" },                           // scenario scelto per peso
 "count": 10,                                                // 1..100
 "seed": 12345,                                              // opzionale, riproducibilità
 "fixed": { "opener": "SAK2.H.D.C", "responder": "..." },    // carte/mani già fissate (§5.6)
 "lang": "it"

} → 200 { "seed":..., "deals":[ {

       "scenario": "1H-4C", "index": 0,
       "dealer": "opener", "vul": "none",
       "hands": { "opener":"AK2.QJ53.K84.T65", "responder":"...", "opp1":"...", "opp2":"..." },
       "bindings": { "$a": "H", "x:opener": "D" },          // come sono state risolte variabili e jolly
       "pbn": "[Deal \"N:...\"]", "lin": "...",
       "explanation": { "opener": ["♥: 5+ carte", ...], ... },   // dalle descriptions
       "stats": { "attempts": 412, "ms": 38 }
 } ] }

→ 422 { "code": "...", "reasons": [ {"code":"...", "position":"opp1", "detail":"..."} ] } GET /api/deals/capabilities → { "dslVersion": 2, "supports": ["suit_cards", ...] } </syntaxhighlight>

Posizioni → sedi: opener = Nord per default; il motore ruota in base a context.dealer solo per la numerazione/PBN; i vincoli restano sempre in termini di ruoli (opener, responder, opp1 = a sinistra dell'apertore, opp2 = a destra).


2. Definizioni normative

Il motore e il frontend devono usare esattamente queste definizioni (le descrizioni in italiano dell'API le presuppongono).

  • Carte: A K Q J 10 9 8 7 6 5 4 3 2. Nei vincoli T = 10. "Carte basse" = 8..2.
  • PO / HCP: A=4, K=3, Q=2, J=1. Shcp ecc. = PO nel solo seme.
  • Posizioni e coppie: coppia dichiarante = opener+responder; difesa = opp1+opp2. Senso di gioco: opener → opp1 → responder → opp2 (orario). Lo stesso per l'attaccante: siede a sinistra del dichiarante (declarer = opener → attacca opp1; responder → opp2; opp1 → responder; opp2 → opener).
  • Forma ordinata: le 4 lunghezze in ordine decrescente (es. 5431).
  • Classi di forma (possono sovrapporsi; le pagine oggi usano solo balanced):
    • balanced: 4333, 4432, 5332.
    • semi_balanced: 5422, 6322.
    • one_suiter: seme più lungo ≥ 6 e secondo ≤ 3.
    • two_suiter: i due semi più lunghi hanno entrambi ≥ 4 carte e insieme ≥ 9 (5-4, 6-4, 5-5, ...).
    • three_suiter: tre semi ≥ 4 (4441, 5440, 4450...).
    • any: nessun vincolo. Se la vostra implementazione attuale differisce, allineatela a questa o segnalate la differenza.
  • shape_pattern (54xx): la forma ordinata deve corrispondere cifra per cifra; x = qualsiasi.
  • longest:H: la lunghezza di H è ≥ di ogni altro seme (pari merito ammesso).
  • Confronti A op B: sulle lunghezze dei due semi nella stessa mano; == uguali.
  • Controlli: A=2, K=1 (le dame non contano). controls:6+ somma sulla mano.
  • Carte chiave per l'atout T (kc:T): numero di assi (0-4) + 1 se possiede il Re di T. Valori 0-5. La Dama di atout si esprime con has:TQ.
  • Fermata nel seme X (stop:X): possiede almeno una tra: A; Kx (Re con ≥ 2 carte nel seme); Qxx (Dama con ≥ 3); Jxxx (Fante con ≥ 4). nostop:X = nessuna di queste.
  • Punti totali tp: PO + (per ogni seme, max(0, lunghezza − 4)).
  • Punti distribuzione dp: PO + 3 per ogni chicane + 2 per ogni singolo + 1 per ogni doubleton.
  • Perdenti ltc (Losing Trick Count): per ogni seme con n carte, considerare m = min(n,3); perdenti del seme = m − (numero di A, K, Q posseduti nel seme); somma sui 4 semi. (Seme vuoto = 0.)
  • S=KJT (suit_cards): tra A K Q J 10 9 possiede esattamente quelle elencate; 8..2 liberi. Stringa vuota = nessuna carta alta. Gli onori elencati sono esclusi dalle altre mani solo per quel che riguarda la proprietà: ogni carta ha un unico proprietario.
  • S~... (suit_patterns): una sola alternativa (scelta a caso, uniformemente tra quelle compatibili con gli altri vincoli) descrive tutta la mano in quel seme: cards + tra low_min e low_max carte basse (low_max: null = illimitato, ma ≤ 13 − len(cards)).
  • x: (jolly di posizione): un seme scelto a caso tra quelli non vincolati esplicitamente in quella posizione; xhcp: si riferisce allo stesso seme. Il jolly non è condiviso con altre posizioni.
  • Variabili $a..$d: a inizio generazione ogni variabile usata riceve un seme dal proprio dominio (vars, default tutti e quattro). Variabili diverse hanno semi diversi; una variabile non dichiarata ha dominio completo. Una variabile può coincidere con un seme nominato esplicitamente ($a e H): se non è voluto, restringere il dominio ($a=SD). La scelta è uniforme tra le assegnazioni che rendono lo scenario soddisfacibile.
  • fit: somma delle lunghezze dei due compagni nel seme (o in qualunque seme del tipo major/ minor/any, a scelta del motore, uniformemente tra i semi che soddisfano il vincolo).
  • any(A,B): la mano soddisfa almeno una alternativa. not(A,B): non ne soddisfa nessuna. Più gruppi → AND. Dentro A,B valgono gli stessi campi di una posizione (senza any/not/fit).
  • pair_hcp.declarer = PO(opener)+PO(responder); defense = PO(opp1)+PO(opp2). pair_tp: somma tp.
  • Posizioni non nominate: ricevono le carte rimanenti a caso (nessun vincolo); non applicare quiet implicito.
  • weight: probabilità relativa di scelta dello scenario quando non è scelto dall'utente (default 1). Non influenza la generazione dentro lo scenario.
  • context: metadati. dealer e vul (us = coppia dichiarante) vanno riportati nell'output (PBN/LIN) e mostrati. auction è la sequenza da mostrare/verificare (notazione BridgeCards: P, X, XX, 1NT...). contract/declarer/lead: vedi §5.7.

3. Architettura consigliata del motore

Pipeline per ogni mano richiesta:

<syntaxhighlight lang="text"> parse risposta API → controllo requires/errors/warnings

→ scelta scenario (utente | peso)
→ ASSEGNAZIONE SIMBOLICA   : variabili $a.., jolly x:, fit, alternative any()/S~, pattern di forma
→ MATRICE DELLE LUNGHEZZE  : 4 posizioni × 4 semi (somme 13 per riga e per colonna)
→ ASSEGNAZIONE DELLE CARTE : carte fissate/vincolate, poi resto casuale
→ RIPARAZIONE PUNTI        : scambi mirati per entrare negli intervalli PO/tp/dp/ltc/controlli
→ VERIFICA FINALE          : valutazione indipendente di TUTTI i vincoli sulla smazzata completa
→ output + spiegazione

</syntaxhighlight>

Punti chiave:

  1. Verifica finale indipendente (il "giudice"): una funzione pura satisfies(deal, scenario, bindings) che valuta ogni vincolo dalle definizioni di §2. Ogni smazzata emessa deve passarla. Questa funzione è anche la base dei test e del pulsante "verifica" del frontend.
  2. Costruzione, non solo scarto. Il rejection sampling puro non regge scenari stretti (es. Gran rever nel Multirever, quiet con coppia 25+ PO). Costruire prima la matrice delle lunghezze con propagazione dei vincoli (backtracking con limiti per riga/colonna), poi assegnare le carte onore per posizione secondo suit_cards/suit_patterns/has/lacks/aces..., infine le basse.
  3. Riparazione PO: dopo l'assegnazione, se i PO (o tp, dp, ltc, controlli, pair_*) sono fuori intervallo, scambiare due carte tra due mani preservando lunghezze e vincoli rigidi (carte fissate, pattern). Ricerca locale con budget; poi nuovo tentativo dall'inizio.
  4. Uniformità: l'obiettivo è una distribuzione quasi uniforme tra le smazzate che soddisfano lo scenario. Le euristiche di costruzione introducono distorsioni: misuratele (§7.3) e non dichiarate "uniformi" se non lo sono.
  5. Budget e fallimenti: limite di tempo (oggi 2,5 s CPU/mano), massimo tentativi, e diagnosi. Su fallimento restituire 422 con i reasons di §6, mai una smazzata che viola i vincoli.
  6. Riproducibilità: stesso seed + stessa versione del DSL → stessa smazzata. Salvare nel record seed, dslVersion, source.page, revid della pagina se disponibile.
  7. Concorrenza e cache: cache del JSON dell'API per pagina con TTL breve (60 s) e rivalidazione sulla list; mai servire DSL non validato.

3.1 Ordine di risoluzione dei vincoli

  1. Variabili $a.., jolly x:, fit (decidono quali semi).
  2. Alternative: scegliere un ramo per ogni any() e ogni S~ (escludendo i rami impossibili con i vincoli già noti); applicare not() come vincoli di esclusione.
  3. Lunghezze (suits, compare, longest, shape, shape_pattern, fit).
  4. Carte precise (suit_cards, suit_patterns, cards_held/not_held, lead).
  5. Conteggi (aces, kings, queens, controls, keycards, suit_hcp, hcp).
  6. Punteggi derivati (tp, dp, ltc, pair_*) e stoppers.
  7. Verifica finale.

Un vincolo è "rigido" se riguarda carte/lunghezze fissate (1-4); "morbido" se si ripara per scambio (5-6).


4. Semantica delle combinazioni delicate

  • quiet è già espanso in suits, suit_hcp, hcp: nessun trattamento speciale (il campo quiet: 1 serve solo alla descrizione). Una difesa quiet riduce il margine PO per la coppia dichiarante: verificare la compatibilità prima di costruire (suit_minimums_exceed_hcp_max...).
  • S=... / S~... + S:N + Shcp: devono essere coerenti; l'API segnala le contraddizioni evidenti.
  • Una carta (has:HQ) può comparire in una sola posizione; due posizioni con la stessa carta sono un errore di validazione, non un caso da risolvere a caso.
  • any() dentro una posizione con variabili: le variabili si legano prima, poi si sceglie il ramo.
  • not() con S~: escludere dall'insieme di alternative, non solo filtrare a posteriori.
  • longest: e compare valgono sulle lunghezze finali (dopo il bind delle variabili).
  • context.lead (SK): la carta deve stare nella mano dell'attaccante (§2) e quella mano deve soddisfare i suoi vincoli (es. se ha S=KJT, lead:SK è coerente, lead:SQ no).

5. Funzionalità del motore (elenco completo da realizzare)

5.1 Core (parità v1) — già in produzione, da conservare

Lunghezze, PO totali/per seme, x:, shape:, fit, suit_cards, suit_patterns, quiet (espanso), 422 con ragioni, tetto 2,5 s.

5.2 Nuove funzioni DSL v2

Tutte le righe di §1.1. Priorità di consegna in §9.

5.3 Mani multiple e serie

count ≤ 100 per richiesta; opzione distinct: true (nessuna mano duplicata nella serie); opzione balance: "round_robin" | "weighted" per distribuire gli scenari della stessa convenzione.

5.4 Spiegazione per mano

Per ogni smazzata: per posizione, elenco dei vincoli con esito (✓) e valore effettivo ("♥: 5 carte, richiesto 5+"), e i binding ($a = ♥, x di opener = ♦). Serve al frontend.

5.5 Verifica di una smazzata data

POST /api/deals/check con quattro mani e una source → elenco di vincoli soddisfatti/violati. Usa lo stesso giudice del §3. Serve a: smazzate inserite a mano, correzione in aula, test.

5.6 Mani parzialmente fissate (fixed)

Accettare mani complete o parziali ("opener": "SAK2.", o carte singole) e completare il resto rispettando lo scenario. Se impossibile → 422 fixed_cards_conflict.

5.7 Contesto di gioco della carta

contract, declarer, lead: il motore garantisce che la carta d'attacco sia nella mano giusta e fornisce nel risultato play: { "contract":"4H", "declarer":"responder", "leader":"opp2", "lead":"SK" }. (Il gioco successivo, oltre la prima carta, non è nel DSL: per Segnali e Attacchi la prima carta e le mani bastano a mostrare la situazione.)

5.8 Statistiche

Per scenario: tasso di successo, tentativi medi, p95 ms, codici di fallimento. Esposte in GET /api/deals/stats e inviate a BridgePedia (§8.5) per tarare i vincoli.


6. Codici di errore (422 e simili)

Già in uso: exact_cards_always_conflict, generation_time_limit, suit_cards_below_hcp_min, suit_minimums_exceed_hcp_max, suit_cards_exceed_hcp_max. Da aggiungere:

Codice Quando
unsupported_feature lo scenario richiede un tag non supportato (detail: il tag)
dsl_errors l'API ha restituito errors o warnings (riportarli)
variable_unsatisfiable nessuna assegnazione dei semi variabili rende lo scenario possibile
fixed_cards_conflict fixed contraddice lo scenario o carte duplicate
pair_points_unreachable pairhcp/pairtp incompatibili con i limiti per posizione
lead_conflict lead incompatibile con la mano dell'attaccante
no_branch_left any()/S~/not() eliminano tutti i rami
shape_unreachable forma/lunghezze incompatibili con 13 carte per mano e per seme
invalid_request parametri non validi (count, seed...)

Ogni 422 porta reasons: [{code, position?, detail}] leggibili dal frontend.


7. Test di conformità e qualità

7.1 Invarianti (property test, ≥ 10.000 smazzate per scenario del wiki)

  • 52 carte distinte; 13 per mano; nessun duplicato.
  • satisfies() vero per ogni smazzata emessa (il giudice non deve condividere codice con il generatore).
  • Stesso seed → stesso risultato.

7.2 Casi minimi per costrutto (la DSL → cosa deve valere)

# DSL Asserzione
1 opener S:5+ hcp:12-21 ♠ ≥ 5, PO in intervallo
2 @M=H,S opener ${M}:5+ due scenari generabili (H, S)
3 $a=M opener $a:5+; responder $a:3+ in ogni mano: un nobile ≥5 in opener, lo stesso seme ≥3 in responder; entrambi i nobili compaiono su 1000 mani
4 $a=M $b=m opener $a:5+ $b:4+ $a nobile, $b minore, mai lo stesso seme
5 opener stop:S su 1000 mani tutte le mani hanno A, Kx, Qxx o Jxxx a ♠
6 opener nostop:S S:3+ nessuna mano con A, né Kx, Qxx o Jxxx a ♠ (con 3 carte: né A, né K, né Q)
7 opener aces:2 kc:H4 2 assi; assi + K♥ = 4
8 opener has:HQ lacks:SK Q♥ presente, K♠ assente
9 opener tp:20-22 tp corretto con la definizione di §2
10 opener ltc:6-7 LTC con la definizione di §2
11 opener H>S e opener H>=S longest:H confronti rispettati; longest con pari merito
12 opener shape:5-4-x-x 5 e 4 come due semi più lunghi in ordine; gli altri due sommano 4
13 opener shape:semi_balanced solo 5422 e 6322
14 opener any(S:5+, H:5+) almeno un nobile ≥ 5
15 opener not(S:4+, H:4+) nessun nobile ≥ 4
16 opener any(S:4+ hcp:12+, H:4+) rami composti
17 pairhcp:25+ ... opp1 quiet; opp2 quiet coppia ≥ 25, avversari entro i limiti quiet
18 weight:1 ... // weight:3 ... 1000 estrazioni ~ 25%/75% (± tolleranza)
19 dealer:opp1 vul:both output con dealer/vul corretti e PBN ruotato
20 declarer:responder lead:SK opp2 S=KJT K♠ in opp2; con lead:SQ → 422 lead_conflict
21 fixed con Q♥ in opener opener ha Q♥ e rispetta lo scenario
22 scenario impossibile (S=AK Shcp:0-2) 422 con ragione
23 scenario con unsupported simulato 422 unsupported_feature

Aggiungere come test di regressione tutti gli scenari reali delle pagine del wiki (shapeaction=list → get): per ciascuno ≥ 1000 smazzate valide entro il budget, oppure una diagnosi documentata. Gli scenari più duri oggi: Multirever (Gran rever, 20 scenari) e Segnale di Smith (20 scenari con suit_cards).

7.3 Qualità statistica

Per ogni scenario del wiki misurare su 5000 mani: distribuzione delle lunghezze e dei PO nei limiti consentiti, tasso di riuscita al primo tentativo, tempo p50/p95. Segnalare distorsioni evidenti (es. la posizione A prende sempre i PO alti).

7.4 Prestazioni

Obiettivo: p95 < 300 ms per mano sugli scenari comuni; < 2,5 s sul peggiore. Se non si rispetta, non allargare i vincoli: segnalare lo scenario a BridgePedia (§8.5) che può rivederlo.


8. Frontend di Play

Il frontend deve permettere di consultare, generare, creare e gestire smazzate. Lingua: italiano, con i18n predisposta (en, fr, es, de). Tutti i testi dei vincoli vengono dall'API (descriptions, parametro lang): non riscriverli.

8.1 Catalogo delle convenzioni

  • Elenco da shapeaction=list (con vincoli) e listall (tutte, con hasShape).
  • Per ogni voce: titolo, numero di scenari, link alla pagina del wiki, stato: generabile, parzialmente generabile (alcuni scenari con requires non supportato), non generabile.
  • Ricerca testuale; filtri per tipologia/sistema se esposti; badge "da verificare" per le traduzioni non verificate.
  • Le 27 convenzioni senza vincoli compaiono come "scenari in arrivo" con link al wiki.

8.2 Visualizzatore di scenario

  • Tabella a colonne per posizione (come la tabella del wiki): etichetta scenario, frasi di descriptions[i].positions, positionLabels (rispettare i ruoli personalizzati, es. "Attaccante"), fit, context.
  • Semi ♥ ♦ in rosso. Mostrare requires come chip (tooltip con spiegazione).
  • Pulsante "Mostra DSL" (testo raw) e "Apri la pagina nel wiki".

8.3 Generatore

  • Scelta convenzione → scenario (singolo, "a caso", "tutti a rotazione", pesato) → quantità (1-100) → opzioni (seed, mani distinte, nascondi avversari).
  • Risultato: schede smazzata con 4 mani (formato BridgeCards: ♠♥♦♣, bordi, dealer, vulnerabilità), etichetta scenario, pannello "perché questa mano": per posizione le frasi di explanation con spunta, binding ($a = ♥), PO/lunghezze effettive.
  • Azioni per smazzata: rigenera, "blocca questa mano e rigenera le altre" (usa fixed), copia link con seed, esporta PBN/LIN/immagine, aggiungi alla raccolta.
  • Se la licita auction è presente: mostrarla con <bidding> (stile BridgeCards) o nascosta per esercizio ("indovina la risposta").
  • Gestione 422: messaggio per codice (§6) con suggerimento ("lo scenario è impossibile perché..."). Per unsupported_feature: spiegare quale capacità manca.

8.4 Editor / creazione di scenari

Due modalità sincronizzate:

  1. Modulo guidato per posizione (Apertore, Compagno, Avv. sinistro, Avv. destro):
    • lunghezze per seme (slider min-max), PO totali e per seme, forma (classi + schema 5-4-x-x);
    • griglia carte per seme (A K Q J 10 9 + "basse") con stati deve avere / non deve avere / indifferente che produce S=...; costruttore di pattern per S~ con anteprima;
    • interruttori: fermata (per seme), assi/re/dame/controlli, carte chiave con atout, tp/dp/ltc, confronti tra semi, any(...)/not(...) come gruppi aggiungibili;
    • variabili di seme $a..$d con dominio (nobili/minori/qualsiasi);
    • contesto: dealer, vulnerabilità, licita, contratto, attacco, peso;
    • avversari: pulsante quiet (e quiet:max);
    • macro: "ripeti per ogni nobile / minore / seme" genera @M=....
  2. Editor testuale del DSL con evidenziazione, completamento e validazione live: chiama shapeaction=validate (debounce 400 ms) e mostra errors, warnings, descriptions e requires (con il supporto del motore). Le due modalità si convertono a vicenda (modulo → DSL è obbligatorio; DSL → modulo almeno per i costrutti elementari, il resto resta testuale).
  • Pulsante "Prova": genera 5 mani di test dallo scenario in modifica e mostra tempi e tassi di riuscita.
  • Salvataggio come bozza personale in Play (non scrive sul wiki). Pulsante "Copia per BridgePedia" produce la riga shape_constraints da incollare nella pagina (gli editor del wiki fanno il resto).
  • Libreria di modelli presi dalle convenzioni del wiki ("parti da Landy").

8.5 Retroazione verso BridgePedia

  • Pulsante "Segnala scenario problematico" (lento / troppo raro / 422 / distribuzione strana) che invia: pagina, scenario, dslVersion, codice errore, statistiche (§5.8) al canale concordato (sessione/issue). Nessuna scrittura diretta sul wiki.
  • Report periodico automatico delle statistiche di generazione per scenario.

8.6 Smazzate manuali

  • Compositore: assegnare le 52 carte alle quattro mani con trascinamento o digitazione (AK2.QJ5.K84.T65), controllo duplicati, completamento casuale delle carte mancanti (opzionalmente rispettando uno scenario: usa fixed).
  • Pulsante "Verifica rispetto a uno scenario" (§5.5) con l'elenco dei vincoli soddisfatti e violati.
  • Importa/esporta PBN, LIN, testo semplice.

8.7 Gestione delle smazzate

  • Raccolte ("mazzi di esercizio"): nome, descrizione, convenzione, tag; ordinamento; duplicati evitati.
  • Ricerca per convenzione, scenario, tag, data; filtri per vulnerabilità, dealer.
  • Metadati salvati: seed, source.page, scenario, dslVersion, revid, data, autore.
  • Note personali per smazzata; stampa di una raccolta (fogli A4 con 4 o 6 smazzate per pagina, versione "solo mano dell'apertore" per esercizio).
  • Condivisione: link con seed/raccolta, permessi (privato, gruppo, pubblico) secondo il modello utenti di Play. Esportazione in blocco (PBN).
  • Modalità "esercizio di licita": mostra le mani di un lato, l'utente inserisce la licita, il frontend confronta con context.auction se presente.

8.8 Requisiti trasversali

  • Mobile first; tastiera accessibile; semi anche come lettere per daltonici.
  • Nessuna dipendenza dal wiki per il rendering delle carte (usare componenti propri; stile coerente con BridgeCards).
  • Tempi: la generazione deve mostrare avanzamento e poter essere annullata.
  • Tracciare (analytics interni) tempi, errori e scenari più usati.
  • Testo dei vincoli sempre da descriptions con lang coerente con la lingua dell'interfaccia.

9. Piano di consegna consigliato

Fase Contenuto Esito atteso
P0 — compatibilità (subito) leggere dslVersion, requires, errors, warnings; esporre /capabilities; rifiutare con unsupported_feature; giudice satisfies() per v1 nessuna smazzata errata con le pagine nuove
P1 — core v2 macro (nessun lavoro, già espanse), weight, context, shape_pattern, classi nuove, compare/longest, any/not, pair_* scenari per Landy/Cappelletti/DONT/Smolen con forme e alternative
P2 — variabili e carte vars condivise tra posizioni, has/lacks, stoppers, aces/kings/queens/controls/kc Michaels, Sputnik, Lebensohl, Blackwood/RKCB, Exclusion
P3 — punteggi tp, dp, ltc sistemi a forza (Fiori forte, 2♣ forte, Gazzilli)
P4 — frontend lettura catalogo, visualizzatore, generatore con spiegazioni tutti possono provare le convenzioni
P5 — frontend creazione editor guidato + testuale con validazione live, "Prova", bozze gli editor creano e collaudano scenari senza il wiki
P6 — gestione raccolte, smazzate manuali, check, fixed, esportazioni, esercizi di licita uso didattico completo
P7 — qualità suite di conformità (§7), statistiche, report verso BridgePedia motore misurabile

Ogni fase deve chiudersi con i test di §7 per i costrutti introdotti. BridgePedia estende le pagine del wiki man mano che i tag diventano disponibili: comunicate a ogni fase la lista dei tag supportati (/capabilities) così il wiki passa dalle approssimazioni (pattern S~) ai costrutti nativi solo quando il motore li regge.


10. Cosa fa BridgePedia (per non sovrapporsi)

  • Mantiene il DSL, il parser, le descrizioni e l'API; aggiunge costrutti su richiesta motivata.
  • Scrive e tiene aggiornati gli scenari nelle pagine delle convenzioni; le 27 mancanti saranno coperte via via con i costrutti v2.
  • Fornisce il catalogo di ricette (come descrivere Landy, Blackwood, un attacco sotto onore...).
  • Riceve da Play le segnalazioni di §8.5 e rivede i vincoli troppo duri.

11. Decisioni aperte (da concordare con Play)

  1. Definizione definitiva delle classi di forma (§2) rispetto a quanto già implementato.
  2. Formato di explanation (testo semplice vs struttura con esito per vincolo).
  3. Modello di persistenza e permessi delle raccolte (utenti Play).
  4. Canale per le segnalazioni verso BridgePedia (§8.5).
  5. Se fixed deve accettare anche vincoli aggiuntivi temporanei (override locali dello scenario).