Interfaccia a riga di comando (CLI)

Introduzione

blunderDB include un’interfaccia a riga di comando (CLI) completa nello stesso eseguibile dell’interfaccia grafica. La CLI è particolarmente utile per:

  • l’import in massa di match: importare un’intera cartella di file di match (XG, SGF, MAT, BGF…) con un solo comando,

  • l’automazione: integrare blunderDB in script shell per backup periodici, export pianificati o catene di elaborazione,

  • l’uso su server: gestire database su macchine prive di ambiente grafico,

  • l’ispezione rapida: verificare il contenuto o l’integrità di un database senza avviare l’interfaccia grafica.

La CLI condivide esattamente lo stesso formato di database dell’interfaccia grafica: entrambe scrivono lo stesso file, non c’è nulla da sincronizzare.

Nota

Se l’applicazione è aperta mentre uno script scrive. Il file è in modalità WAL: una lettura non blocca mai una scrittura, ed entrambi i programmi lavorano sullo stesso database senza disturbarsi. Due scritture, invece, si susseguono — la seconda attende il lucchetto di scrittura (dieci secondi per istruzione, più alcuni nuovi tentativi) e fallisce solo se l’attesa si esaurisce, con un messaggio che nomina SQLite:

Error: failed to import match: sqlite: save match: database is locked (5) (SQLITE_BUSY)

L’interfaccia grafica non sorveglia il file: continua a mostrare ciò che aveva caricato finché CTRL-R non ricarica le posizioni. Nulla va perduto, ma lo schermo è in ritardo rispetto al database.

Sintassi generale

La modalità viene rilevata automaticamente: se il primo argomento è un comando CLI, blunderDB si avvia in modalità headless, altrimenti avvia l’interfaccia grafica.

# GUI
./blunderdb

# CLI
./blunderdb <command> [options]

Gli esempi di questa pagina scrivono ./blunderdb: il binario così come è scaricato, richiamato dalla cartella in cui si trova. Installato da un pacchetto, o collegato da una cartella del PATH (vedere Download e installazione), si chiama semplicemente blunderdb.

Le opzioni booleane annunciate « predefinito: sì » si disattivano nella forma --option=false — --recursive=false, --analysis=false. La forma separata da uno spazio non esiste: --recursive false lascia l’opzione al suo valore predefinito e tratta false come un argomento in eccesso.

Comandi disponibili

Comando

Descrizione

create

Crea un nuovo database.

import

Importa dati (match, posizione, lotto).

export

Esporta dati.

identity

Mostra o sposta l’identità di emittente (chiave di firma delle filigrane).

open

Trasforma un file protetto da password (.dbx) in un database ordinario.

search

Cerca posizioni con filtri.

list

Mostra il contenuto del database.

match

Mostra le posizioni e le analisi di un match.

collection

Gestisce le collezioni (elenco, contenuto, creazione, rinomina, eliminazione, esportazione).

anki

Mazzi di ripetizione dilazionata (elenco, statistiche, previsione, sincronizzazione).

rollout

Gioca una posizione fino in fondo per distinguere le sue mosse o la sua decisione di cubo (XGID o OGID).

epc

Calcola l’Effective Pip Count e il verdetto di cubo di una posizione di uscita (XGID o OGID).

bearoff

Genera, elenca, verifica ed elimina i database di uscita.

analyze

Scrive un’analisi gammonNet per ogni posizione che non ne ha alcuna.

info

Mostra i metadati del database.

edit

Modifica i metadati e le soglie del database.

verify

Verifica l’integrità del database.

vacuum

Compatta il file del database, recuperando lo spazio liberato.

repair

Ricalcola ciò che il database deriva da ciò che memorizza.

delete

Elimina dati.

healthcheck

Interroga un demone serve in esecuzione: codice 0 se è pronto.

mcp

Offre gli strumenti del database a un assistente IA (Model Context Protocol).

completion

Stampa uno script di completamento shell (bash, zsh, fish).

help

Mostra la guida.

version

Mostra la versione.

serve, migrate, call

Modalità server e migrazione verso PostgreSQL: vedere Modalità headless (server).

Ogni comando accetta l’opzione --help per mostrare la propria guida dettagliata.

create — Creare un database

Crea un nuovo file di database con metadati opzionali.

./blunderdb create --db <path> [--user <name>] [--description <text>] [--force]

Opzioni:

  • --db — Percorso del file di database da creare (obbligatorio).

  • --user — Nome del proprietario del database.

  • --description — Descrizione del database.

  • --force — Sovrascrivere il file se esiste già.

  • --format — Formato di output: text (predefinito) o json (percorso, versione, utente, descrizione, data di creazione).

L’estensione .db viene aggiunta automaticamente se assente. Le cartelle superiori vengono create se necessario.

Esempio:

./blunderdb create --db mes_matchs.db --user "Jean" --description "Matchs de tournoi 2025"

import — Importare dati

Importa file di match o di posizioni nel database.

./blunderdb import --db <path> --type <type> [options]

Opzioni:

  • --db — Percorso del database (obbligatorio).

  • --type — Tipo di import: match, position o batch (obbligatorio).

  • --file — File da importare (per match e position).

  • --dir — Cartella da importare (per batch).

  • --recursive — Scansionare ricorsivamente le sottocartelle (predefinito: sì).

  • --watch — Con --type batch: non si ferma, e importa ogni file di incontro man mano che compare in --dir (Ctrl-C per fermare).

  • --watch-every — Ogni quanto --watch guarda (predefinito: 10s, minimo 2s).

  • --format — Formato di output: text (predefinito) o json.

  • --fail-on-error — Fallisce se almeno un elemento (position o batch) non è stato importato, anche se altri sono riusciti.

Il codice di ritorno obbedisce a quattro regole:

  • niente è stato riconosciuto — ogni file è fallito — : errore, sia passato --fail-on-error o meno;

  • solo duplicati — ogni file era già in base — : successo. Una cartella rilanciata senza alcun file nuovo, la notte ordinaria di uno script, esce con 0 e con duplicates solo non nullo;

  • fallimento parziale (alcuni elementi importati, altri rifiutati): errore solo se --fail-on-error è passato;

  • almeno un elemento nuovo importato, senza --fail-on-error: successo, con i file rifiutati elencati nella tabella.

Sorvegliare una cartella

--watch trasforma l’importazione di directory in una sorveglianza: il comando non restituisce il controllo e importa ogni file di incontro che compare nella cartella. È la forma senza interfaccia della cartella sorvegliata dell’applicazione.

# Importer ce que le dossier contient déjà, puis surveiller ce qui arrive
./blunderdb import --db base.db --type batch --dir ~/XG/Matches
./blunderdb import --db base.db --type batch --dir ~/XG/Matches --watch

Sono importati solo i file che compaiono: ciò che la cartella contiene all’avvio è registrato come noto e lasciato in pace — puntare una sorveglianza su quattro anni di incontri non deve importarli tutti. I due comandi qui sopra si compongono quindi esattamente come si spera.

Un file è importato solo quando la sua dimensione si è stabilizzata, cioè visto due volte immutato: un incontro che un altro programma sta scrivendo cresce da uno sguardo all’altro, e importarlo scritto a metà darebbe un errore sintattico su cui nessuno può agire. La cartella non è percorsa ricorsivamente. Una condivisione di rete diventata illeggibile non ferma la sorveglianza, e il suo contenuto non passa per nuovo al suo ritorno.

Ctrl-C ferma tra due file, mai nel mezzo di uno: il file in corso termina la sua importazione e il suo resoconto compare prima che il comando restituisca il controllo.

Import di un match

Formati supportati: eXtreme Gammon (.xg, .xgp), GNUbg (.sgf), Jellyfish (.mat, .txt), BGBlitz (.bgf) e HedgeHog (.ogxm).

./blunderdb import --db base.db --type match --file match.xg

# Successfully imported match (ID: 1)
#
# Match Details:
#   Players: Kévin Unger vs Maxence Job
#   Event: HSBT Paris 2023
#   Match Length: 7
#   Games: 7

--format json fornisce gli stessi campi in un solo documento:

{
  "type": "match",
  "match_id": 1,
  "player1": "Kévin Unger",
  "player2": "Maxence Job",
  "event": "HSBT Paris 2023",
  "location": "Paris, Fédération Française de Bridge",
  "match_length": 7,
  "games": 7
}

Import di posizioni

Importa posizioni da un file di testo, una posizione JSON per riga. È esattamente ciò che scrive export --type positions: i due comandi si rispondono, un export si reimporta tale e quale, senza alcuna modifica.

./blunderdb import --db base.db --type position --file positions.txt

# Successfully imported 4 positions

Una riga, così come export la produce — il tavoliere ne occupa l’essenziale, ventisei punti seguiti dalle pedine uscite:

{"id":1,"board":{"points":[{"checkers":0,"color":0},{"checkers":1,"color":1},…],"bearoff":[0,0]},"cube":{"owner":-1,"value":0},"dice":[0,0],"score":[7,7],"player_on_roll":0,"decision_type":1,"has_jacoby":0,"has_beaver":0,"individually_imported":true,"flagged":false}

L’analisi e i commenti non viaggiano tramite questo formato: esso porta la posizione, nient’altro. Per spostare un’intera biblioteca, occorre export --type database.

Import in lotto

Importa tutti i file di match di una cartella in una sola operazione. È il metodo più efficiente per importare un gran numero di match.

./blunderdb import --db base.db --type batch --dir ./matchs/
./blunderdb import --db base.db --type batch --dir ./matchs/ --recursive=false
./blunderdb import --db base.db --type batch --dir ./matchs/ --format json --fail-on-error

Una tabella riassuntiva indica per ogni file se l’importazione è riuscita (✓), fallita (✗) o se si tratta di un duplicato (⊘). Un duplicato non è conteggiato come un fallimento, e un lotto che ne contiene solo duplicati è un successo (vedi le regole sopra).

Batch importing from: ./matchs/ (recursive: true)

Found 3 match file(s) to import

[1/3] Importing: 02_NDT_FR.txt... ERROR: failed to parse file: ingest: parse gnubg file: invalid MAT file: no match header found
[2/3] Importing: test.mat... DUPLICATE
[3/3] Importing: test.xg... OK (ID: 1, 341 positions)

====================================================================
IMPORT SUMMARY
====================================================================
Status  File           ID  Player 1     Player 2     Games  Positions  Error
------  ----           --  --------     --------     -----  ---------  -----
✗       02_NDT_FR.txt                                0      0          failed to parse file: ingest: ...
⊘       test.mat                                     0      0
✓       test.xg        1   Kévin Unger  Maxence Job  7      341
--------------------------------------------------------------------
Total: 3 files | Success: 1 | Duplicates: 1 | Failed: 1 | Positions imported: 341

--format json fornisce la stessa cosa, sfruttabile da uno script: un oggetto per file in files, poi i totali. Una notte tranquilla lascia solo duplicates non nullo e failed a zero, e il codice di ritorno a 0; solo un lotto in cui niente è stato riconosciuto esce in errore.

{
  "files": [
    {"file_path": "02_NDT_FR.txt", "success": false, "error": "failed to parse file: …"},
    {"file_path": "test.xg", "success": true, "positions": 341}
  ],
  "total": 3,
  "success": 1,
  "duplicates": 1,
  "failed": 1,
  "positions_imported": 341
}

export — Esportare dati

Esporta il contenuto del database in file.

./blunderdb export --db <path> --type <type> --file <output> [options]

Opzioni:

  • --db — Database di origine (obbligatorio).

  • --type — Tipo di export: database, positions, matches o mat (export di uno o più match in trascrizione Jellyfish .mat) (obbligatorio).

  • --file — File di output (obbligatorio, tranne che per --type mat usato con --dir).

  • --dir — Directory di output per l’export .mat in blocco (più match, un file per match; senza --match-ids, vengono esportati tutti i match).

  • --analysis — Includere le analisi (predefinito: sì).

  • --comments — Includere i commenti (predefinito: sì).

  • --filters — Includere la libreria di filtri (predefinito: sì).

  • --played-moves — Includere le mosse giocate (predefinito: sì).

  • --matches — Includere i match (predefinito: sì).

  • --collections — Includere le collezioni (predefinito: no).

  • --collection-ids — ID delle collezioni da esportare (separati da virgole).

  • --match-ids — ID dei match da esportare (separati da virgole, vuoto = tutti).

  • --tournament-ids — ID dei tornei da esportare (separati da virgole).

  • --password — Avvolge il risultato in un contenitore cifrato (.dbx).

  • --watermark — Scrive una dichiarazione d’origine firmata nel file esportato (vedere Distribuire un database: origine e password).

  • --watermark-note — Testo libero associato alla filigrana (condizioni d’uso, contatto); si usa con --watermark.

  • --format — Formato di output: text (predefinito) o json (un documento che riassume l’esportazione: percorso, dimensione in byte, conteggi).

Esempi:

./blunderdb export --db base.db --type database --file sauvegarde.db
./blunderdb export --db base.db --type positions --file positions.txt
./blunderdb export --db base.db --type matches --file selection.db --match-ids 1,3,5

# .mat : un match, puis plusieurs (ou tous) dans un répertoire
./blunderdb export --db base.db --type mat --match-ids 5 --file match5.mat
./blunderdb export --db base.db --type mat --match-ids 5,9,12 --dir sorties/
./blunderdb export --db base.db --type mat --dir sorties/

# .dbx : filigrané et protégé par mot de passe
./blunderdb export --db cours.db --type database --file cours-diffusion.dbx \
    --watermark "Cours de Jean Dupont — 12 mars 2026" \
    --watermark-note "Merci de ne pas rediffuser." \
    --password secret

Una filigrana è firmata con l’identità di emittente locale (vedere il comando identity qui sotto): è infalsificabile, ma non inamovibile — il file resta un database SQLite ordinario. Non protegge nulla, indica soltanto da dove viene il file. Una password protegge il trasporto del file (la copia smarrita, l’allegato inviato per errore), non il database stesso: chiunque abbia ricevuto la password può aprirlo. blunderDB non registra mai nulla dal lato del destinatario (nessun registro, nessun giornale) — vedere ADR-0007.

identity — Identità di emittente

Mostra o sposta la tua identità di emittente: la chiave Ed25519 che firma ogni filigrana. Viene creata da sé alla prima filigrana apposta; non c’è nulla da configurare. Appartiene a una persona, non a un database: tutto ciò che marchi porta una sola impronta pubblica.

./blunderdb identity
./blunderdb identity --name "Jean Dupont"
./blunderdb identity --export jean.bdbid --passphrase pw
./blunderdb identity --import jean.bdbid --passphrase pw

Opzioni:

  • --name — Cambia il nome visualizzato dell’identità.

  • --export — Esporta l’identità in un file .bdbid.

  • --import — Importa un’identità da un file .bdbid.

  • --passphrase — Frase segreta facoltativa che protegge il file esportato/importato (l’identità locale resta volutamente non protetta).

  • --format — Formato di output: text (predefinito) o json (nome, impronta, percorso di archiviazione).

Il file esportato consente a chiunque lo possieda di firmare a tuo nome — non condividerlo. Rinominare cambia soltanto un’etichetta: i file già marcati conservano il nome con cui sono stati sigillati e continuano a verificarsi.

open — Aprire un file protetto

Trasforma un file protetto da password (.dbx) in un database ordinario. La password viene chiesta una sola volta; poi è un file normale.

./blunderdb open --db cours.dbx --password secret
./blunderdb open --db cours.dbx --password secret --file ./mon-cours.db

Opzioni:

  • --db — File .dbx da aprire (obbligatorio).

  • --password — Password del contenitore (obbligatoria).

  • --file — Percorso di uscita per il database ordinario (predefinito: stesso nome, estensione .db).

Che cosa protegge la password: il trasporto del file — la copia dimenticata in una cartella di download, l’allegato inviato per errore. Non il database: chiunque abbia ricevuto la password può aprirlo. L’intestazione del contenitore è in chiaro, cosicché blunderdb info legge l’origine di un file protetto senza la sua password.

search — Cercare posizioni

Cerca posizioni nel database secondo criteri combinabili.

./blunderdb search --db <path> [options]

Opzioni principali:

  • --db — Database (obbligatorio).

  • --format — Formato di output: table, json o xgid (predefinito: table).

  • --limit — Numero massimo di risultati (0 = illimitato).

  • --offset — Ignorare i primi n risultati prima di iniziare a contare; con --limit, è la paginazione.

  • --export — Esportare i risultati in un nuovo database.

  • --query-help — Mostra l’elenco dei token che --query comprende, e si ferma lì. Nessun database viene aperto: --db è inutile.

Filtri disponibili:

  • --decision — Tipo di decisione: checker o cube.

  • --dice — Lancio dei dadi. 5,3 cerca le posizioni in cui entrambi i dadi corrispondono (in qualsiasi ordine). 5 cerca le posizioni in cui un 5 compare su uno dei due dadi (il valore del secondo dado viene ignorato). Implica --decision checker se non viene fornito alcun valore di --decision.

  • --pip-min / --pip-max — Intervallo di differenza del pip count.

  • --winrate-min / --winrate-max — Intervallo della percentuale di vittoria (%).

  • --cube — Valore del cubo.

  • --score1 / --score2 — Punteggi dei giocatori.

  • --match-length — Lunghezza del match.

  • --error-min — Soglia su quanto costa un errore nella posizione: lo scarto tra la migliore mossa e la seconda, oppure il più grande dei tre errori di cubo. In punti di equity — --error-min 0.1 mantiene le posizioni in cui sbagliare costa almeno un decimo di punto. Non dice nulla di ciò che vi è stato giocato.

  • --move-error-min / --move-error-max — Soglia sull’errore della mossa effettivamente giocata dal giocatore 1. In millesimi di equity (millipunti): --move-error-min 50, ossia un ventesimo di punto. È il token E della grammatica di ricerca, scritto tale quale.

  • --has-analysis — Solo le posizioni con analisi.

  • --off1-min / --off2-min — Pedine uscite minime (giocatore 1/2).

  • --match-ids — Filtrare per ID dei match (separati da virgole).

  • --tournament-ids — Filtrare per ID dei tornei (separati da virgole).

  • --position-ids — Filtrare per ID di posizioni: intervallo 2,7 (posizioni da 2 a 7) o elenco esplicito separato da punti e virgola 5;10;15.

  • --individual — Solo le posizioni importate singolarmente, cioè quelle che hai aggiunto tu e non quelle portate da un import di partita.

  • --flagged — Solo le posizioni contrassegnate (flag) per lo studio nel software d’origine (contrassegni eXtreme Gammon). Non retroattivo: i match già importati vanno reimportati per fornire i loro contrassegni.

  • --has-comment — Solo le posizioni che portano un commento. L’origine non è distinta: una nota digitata a mano e un commento portato dall’importazione di un match contano entrambi. I commenti di match o di torneo non vengono consultati.

  • --no-comment — Solo le posizioni senza commento. Mutuamente esclusivo con --has-comment.

Avvertimento

--error-min e --move-error-min non misurano la stessa cosa e non prendono la stessa unità: il fattore è mille. Il primo si dà in punti di equity (0.1), gli altri due in millesimi (100) — un punto vale 1000 millesimi. È --move-error-min che risponde a « dove ho sbagliato » ; --error-min risponde a « quali posizioni erano delicate ».

Ciò che search stampa:

--format table (il predefinito) dà una riga per posizione: l’identificatore, il punteggio, il valore del cubo, il tipo di decisione, il lancio, la migliore decisione e la sua equity. Le ultime due colonne restano vuote per una posizione senza analisi.

Found 5 position(s)

ID  Score  Cube  Type  Dice  Best Move  Equity
--  -----  ----  ----  ----  ---------  ------
2   7-7    0     cube        No Double  -0.005
4   7-7    0     cube        No Double  -0.027
6   7-7    0     cube        No Double  -0.161
8   7-7    0     cube        No Double  0.256
10  7-7    0     cube        No Double  -0.234

--format json dà un array delle stesse posizioni. I campi sono id, score, cube, decision_type (checker o cube) e dice, sempre presenti; best_move, equity e xgid compaiono solo se la posizione porta un’analisi che li fornisce. La riga Found n position(s) resta stampata prima dell’array: uno script che attende solo JSON deve saltare la prima riga, oppure passare per --export.

[
  {
    "id": 5266,
    "score": [
      5,
      4
    ],
    "cube": 1,
    "decision_type": "checker",
    "dice": [
      4,
      3
    ],
    "best_move": "10/3",
    "equity": 0.565
  }
]

--format xgid stampa un XGID per riga, e nient’altro. Stampa solo le posizioni la cui analisi registrata porta un XGID: una posizione incollata nell’applicazione da un export testuale, o un file BGF che ne trasporta uno. Le posizioni portate dall’import di un match XG, GNUbg o Jellyfish non ne portano, e l’output è allora vuoto. Il sottocomando collection show, invece, rigenera l’XGID dal tavoliere.

Il linguaggio di interrogazione:

Le opzioni qui sopra coprono solo una parte dei filtri. --query dà accesso al linguaggio di interrogazione dell’applicazione — quello della barra dei comandi — e quindi a tutti i filtri che non si disegnano sul tavoliere: schema di mossa, testo del commento, giocatore, data, equity, dadi esclusi, zone e blot.

La grammatica è scritta in un solo posto, Filtri di ricerca. La sua tabella dà ogni token, la sua forma, e l’opzione di search che gli corrisponde quando ne esiste una. Questa pagina non la ripete.

./blunderdb search --db base.db --query 's p>30 E>50'
./blunderdb search --db base.db --query 's m"13/11" t"blunder" pl"Alice" T>2026/01/01'

L’ordinamento delle vicine di una posizione passa dalla stessa grammatica: --query 's like42', da sola o seguita da altri token per restringere l’insieme ordinato.

--query-help ne richiama l’elenco senza aprire un database:

$ ./blunderdb search --query-help
blunderdb search --query — the interface's query language

A query is the same text the application's command bar takes:
  s cube p>30 E>50        cube decisions, 30+ pips behind, 50+ millipoints of error
  s m"13/11" T>2026/01/01 played 13/11, imported this year

Flags (no value):
  cube score   match the cube / the score of the position on the board
  d            match the decision type (checker or cube)
  …

Ranges — each takes x>n, x<n or xa,b (lower-case: you; upper-case: the opponent):
  p P          pip count difference / absolute pip count
  …
  E            error of the played move, in millipoints
  T            creation date, T>2026/01/01

Values:
  t"tag"       comment text (";" separates alternatives)
  …

--query sostituisce le opzioni di filtro invece di aggiungersi ad esse: combinarle viene rifiutato, nominando l’opzione in causa. Le opzioni che dicono dove cercare e come mostrare — --db, --format, --limit, --offset, --export — restano valide.

Un token che nulla riconosce fa fallire il comando invece di restringere la ricerca in silenzio. Due limiti derivano dall’assenza di tavoliere sulla riga di comando: lo schema delle pedine non si digita, e i cinque token che leggono il tavoliere — cube, score, d, D/D1 e x — si confrontano qui con un tavoliere vuoto. Una ricerca che ha bisogno di uno di essi si scrive interamente con opzioni, poiché --query non si combina con esse — per esempio, le decisioni di cubo in ritardo di 30 pip ed erronee di almeno 50 millipunti:

./blunderdb search --db base.db --decision cube --pip-min 30 --move-error-min 50

Esempi:

./blunderdb search --db base.db --decision cube
./blunderdb search --db base.db --individual
./blunderdb search --db base.db --error-min 0.1
./blunderdb search --db base.db --tournament-ids 1 --export cubes.db

# 6-5 dans les deux ordres, puis un 6 sur l'un des deux dés
./blunderdb search --db base.db --dice 6,5
./blunderdb search --db base.db --dice 6

# Pagination
./blunderdb search --db base.db --format json --limit 10 --offset 20

list — Elencare il contenuto

Mostra il contenuto del database.

./blunderdb list --db <path> --type <type> [--limit <n>] [--offset <n>]

Tipi:

  • matches — Elenco dei match importati.

  • tournaments — Elenco dei tornei.

  • positions — Elenco delle posizioni (10 per impostazione predefinita; --offset <n> salta le prime n). Viene letta solo la finestra mostrata, qualunque sia la dimensione del database. Con --format csv diventa un”esportazione tabellare: una riga per posizione, con XGID, fase, punteggio, cubo, pip e le colonne di analisi derivate.

  • imports — Le importazioni registrate, dalla più recente alla più vecchia: identificativo, data, formato, origine, partite importate / ignorate / arricchite, file illeggibili e posizioni nuove. Con --batch <id> mostra il resoconto completo di un’importazione: posizioni segnate, posizioni senza analisi, PR su quel lotto e le sue cinque decisioni peggiori (vedi Il resoconto d’importazione).

  • stats — Rapporto di statistiche di prestazione: PR / Snowie ER / MWC (globale, pedine, cubo), PR mobile sulle ultime N decisioni, top blunder, ripartizione per azione di cubo e istogramma delle magnitudini di errore.

  • players — Tabella comparativa, una riga per giocatore del database: match, vittorie/sconfitte, decisioni conteggiate, PR globale / pedine / cubo, Snowie ER, errori, blunder e fortuna. È il corrispettivo da riga di comando della scheda Giocatori del pannello Statistiche.

  • moves — Export tabellare delle mosse registrate, una per riga, con l’incontro a cui appartengono ripetuto su ogni riga: identificatori, data, giocatori, lunghezza, numero e tipo di mossa, posizione, dadi, mossa giocata, azione di cubo, fortuna. --format csv obbligatorio.

  • analyses — Export tabellare delle analisi memorizzate, una per riga: motore, profondità, mossa migliore e la sua equità, errore della mossa giocata, migliore azione di cubo e il suo errore, i sei tassi di vittoria. --format csv obbligatorio.

  • tags — Vocabolario di tag del database: ogni #parola scritta in un commento, con il numero di posizioni che la portano, dalla più usata alla meno usata. Su un database senza alcun tag, mostra il vocabolario consigliato invece di una lista vuota (vedere I tag). Accetta --format json e --format csv.

Export tabellari

Tre tipi — positions, moves e analyses — si esportano in CSV per un notebook, un foglio di calcolo o uno script:

./blunderdb list --db base.db --type positions --format csv > positions.csv
./blunderdb list --db base.db --type moves     --format csv > moves.csv
./blunderdb list --db base.db --type analyses  --format csv > analyses.csv

--limit si applica solo se lo passate. Il suo valore predefinito (10) esiste perché un list mostrato in un terminale non faccia scorrere l’intero database; un export invece finisce in un file letto da un programma, e troncarlo silenziosamente a dieci righe sarebbe una trappola che non si nota finché le cifre non sono false.

Le colonne sono un contratto. Un notebook o uno script scritto contro questi nomi deve continuare a funzionare: le colonne si aggiungono alla fine, non sono mai rinominate né riordinate. Tutte le equità sono in millipunti interi, perché è così che sono memorizzate e perché un decimale in un CSV invita una locale a riformattarlo.

Parquet non è proposto, e la scelta è misurata anziché dogmatica: una libreria colonnare pesa svariati megabyte in un binario la cui dimensione è una preoccupazione seguita, mentre tutto ciò a cui serve questo export legge CSV in una riga (pd.read_csv, polars.read_csv, read.csv, un foglio di calcolo). Parquet si giustifica su decine di milioni di righe; una biblioteca di backgammon di dieci anni ne conta centomila. Se un giorno la differenza si misurerà su una base vera, sarà quella misura a riaprire la questione.

Un notebook Jupyter d’esempio accompagna questi export (notebooks/blunderdb-analyse.ipynb nel repository): PR nel tempo, distribuzione delle magnitudini d’errore, dieci peggiori decisioni con il loro XGID. Non usa nulla al di fuori di quei tre file CSV, ed è eseguito ogni notte in integrazione continua — un notebook che nessuno lancia è un notebook che ha smesso di funzionare senza che nessuno lo sappia.

Opzioni (solo tipo stats) :

  • --metric — Metrica visualizzata: pr o mwc (predefinito: pr).

  • --player — Limitare al giocatore indicato.

  • --tournament — Limitare a uno o più ID di tornei (separati da virgole).

  • --from — Data di inizio (AAAA-MM-GG).

  • --to — Data di fine (AAAA-MM-GG).

  • --decision-type — Tipo di decisione: all, checker o cube (predefinito: all).

  • --top-blunders — Numero di errori peggiori elencati (predefinito: 10).

  • --format — Formato di output: text o json (predefinito: text).

Opzioni (solo tipo imports) :

  • --batch — Identificativo di un lotto: mostra il suo resoconto completo invece dell’elenco.

  • --queue — Con --batch: la coda di studio del lotto anziché il suo resoconto — le posizioni che meritano un secondo sguardo, nell’ordine in cui percorrerle (vedere La coda di studio). Prima le decisioni che sono costate qualcosa, poi le posizioni segnate nel programma d’origine, poi le decisioni di cubo serrate; una posizione vi figura una sola volta.

  • --format — Formato di output: text o json (predefinito: text).

La metà misurata del resoconto è ricalcolata a ogni chiamata: un lotto le cui posizioni sono state analizzate nel frattempo restituisce le cifre di oggi, non quelle del giorno dell’importazione.

Opzioni (solo tipo players) :

  • --from / --to — Limiti di date (AAAA-MM-GG), per esempio i giorni di una competizione.

  • --tournament — Restringere a uno o più ID di tornei.

  • --format — Formato di uscita: text, json o csv (predefinito: text).

--player e --decision-type non si applicano a questo tipo: la tabella riguarda tutti i giocatori e ripartisce già pedine e cubo in colonne distinte.

Nota

Un trattino «—» (campo vuoto in CSV) segnala un valore mai misurato, da non confondere con zero. È il caso della fortuna per ogni match importato prima della versione 2.15.0 dello schema, così come per i formati che non la trasportano (BGF, Jellyfish .mat): reimporta i file di origine per ottenerla. La colonna luck_rolls indica su quanti lanci è calcolata la media.

Ogni tipo stampa un blocco per elemento, preceduto dal totale trovato. L’ultima riga indica quale finestra è mostrata, troncata da --limit (il cui valore predefinito è 10 per le posizioni) e spostata da --offset:

Found 3859 position(s):

ID: 1
  Score: 7-7
  Player on roll: 0
  Decision: Checker play

ID: 2
  Score: 7-7
  Player on roll: 0
  Decision: Cube action

…

(Showing 1-10 of 3859 positions, use --offset and --limit to see more)

Esempi:

# Les imports enregistrés, puis le compte rendu de l'un d'eux
./blunderdb list --db base.db --type imports
./blunderdb list --db base.db --type imports --batch 3

./blunderdb list --db base.db --type stats
./blunderdb list --db base.db --type stats --metric mwc --player "Alice"
./blunderdb list --db base.db --type stats --decision-type checker --from 2026-01-01
./blunderdb list --db base.db --type stats --format json

# Un tableau par joueur, borné aux dates d'une compétition
./blunderdb list --db base.db --type players --from 2026-03-01 --to 2026-03-08
./blunderdb list --db base.db --type players --format csv

./blunderdb list --db base.db --type matches
./blunderdb list --db base.db --type positions --limit 20

match — Visualizzare un match

Mostra le posizioni e le analisi di un match importato.

./blunderdb match --db <path> --id <id> [--format <format>] [--output <file>]

Opzioni:

  • --db — Database (obbligatorio).

  • --id — ID del match da visualizzare (obbligatorio).

  • --format — Formato di output: json, text o summary (predefinito: json).

  • --output — File di output (predefinito: output standard).

Esempi:

./blunderdb match --db base.db --id 1 --format summary
./blunderdb match --db base.db --id 1 --format text
./blunderdb match --db base.db --id 1 --output match1.json

collection — Gestire le collezioni

Gestisce le collezioni, questi insiemi di posizioni scelte a mano nel pannello Collezioni dell’interfaccia grafica. Ogni sottocomando richiede --db; list e show accettano --format text (predefinito), json o csv, come list.

./blunderdb collection <subcommand> [options]

Sottocomandi:

  • list — Elenco delle collezioni: id, nome, numero di posizioni, descrizione.

  • show --id <id> — Posizioni di una collezione: id, indice (il numero a base 1 mostrato nella barra di stato dell’interfaccia grafica), punteggio, tipo di decisione e XGID.

  • create --name <nome> [--description <testo>] — Crea una collezione vuota.

  • filter --id <id> --query <query> — Rende una raccolta viva: il suo contenuto diventa il risultato di una ricerca, rivalutato ogni volta che viene aperta. La query si scrive nella grammatica di ricerca dell’applicazione (vedere Filtri di ricerca). --clear la riporta a un elenco fatto a mano, conservando le posizioni che conteneva.

  • rename --id <id> --name <nome> [--description <testo>] — Rinomina una collezione (la descrizione è conservata se non viene fornita).

  • delete --id <id> [--confirm] — Elimina una collezione; le sue posizioni restano nel database.

  • export --id <id[,id…]> --out <file.db> [--analysis=false] [--comments=false] [--watermark <testo>] [--watermark-note <testo>] — Esporta una o più collezioni verso un nuovo file di database, con la stessa chiamata della finestra di esportazione dell’interfaccia grafica (vedere il comando export per la filigrana).

L’XGID mostrato da show è quello registrato con l’analisi della posizione quando esiste (import BGF e XGP); altrimenti viene generato dal board esattamente come fa Copia posizione nell’interfaccia grafica — la lunghezza del match è allora il maggiore dei due punteggi restanti, una posizione registrata non conservando quello reale.

Esempi:

./blunderdb collection list --db base.db

# Found 2 collection(s):
#
# ID  Name              Positions  Description
# --  ----              ---------  -----------
# 1   Ouvertures blitz  0          À revoir
# 2   Videaux ratés     0

Un database senza collezioni risponde No collections found in database ed esce comunque con il codice 0.

./blunderdb collection show --db base.db --id 3 --format csv

./blunderdb collection create --db base.db --name "Ouvertures blitz"
./blunderdb collection rename --db base.db --id 3 --name "Ouvertures"
./blunderdb collection delete --db base.db --id 3 --confirm

# Exporter deux collections, marquées de leur origine
./blunderdb collection export --db base.db --id 3,4 --out ouvertures.db \
    --watermark "Cours de Jean Dupont - 12 mars 2026"

anki — Mazzi di ripetizione dilazionata

Consulta e mantiene i mazzi di ripetizione dilazionata (FSRS) del pannello Anki dell’interfaccia grafica. La revisione di una carta richiede il board e resta nell’interfaccia grafica; la CLI elenca, misura e risincronizza.

./blunderdb anki <subcommand> [options]

Sottocomandi:

  • decks [--format text|json|csv] — Elenco dei mazzi: sorgente, numero di carte, carte in scadenza, carte nuove.

  • stats --deck <id> [--format text|json] — Statistiche di revisione di un mazzo: totale, nuove, in apprendimento, da rivedere, in scadenza ora, e i suoi parametri FSRS.

  • forecast [--deck <id>] [--days <n>] [--format text|json|csv] — Carte in scadenza per giorno civile (UTC) sui prossimi n giorni (predefinito 30, massimo 365); il giorno 0 assorbe tutte le carte in ritardo; --deck 0 (predefinito) copre tutti i mazzi.

  • sync --deck <id> — Aggiunge una carta per ogni posizione della sorgente del mazzo che non ne ha ancora una; le carte esistenti conservano la loro pianificazione.

  • retention --deck <id> [--format text|json] — ritenzione misurata di un mazzo, confrontata con l’obiettivo scelto dal suo proprietario.

  • card --id <id> --action suspend|unsuspend|bury|remove [--format text|json] — agisce su una carta. Sospendere la mette da parte senza perderne lo storico (non esce più in sessione); seppellire la nasconde fino al giorno dopo, senza dire nulla del suo valore; rimuovere la cancella dal mazzo — la posizione resta nella libreria, essendo un mazzo solo una lista di studio posata sopra.

  • log [--deck <id>] [--limit <n>] [--format text|json] — il registro delle revisioni, la più recente per prima (--deck 0, l’impostazione predefinita, copre tutti i mazzi; --limit vale 20 per impostazione predefinita). Il registro è ciò che è stato realmente detto al pianificatore, in opposizione a ciò che prevede oggi: l’unico posto in cui si vede un voto inserito per errore.

Un mazzo fondato su una collezione rilegge la sua collezione. Un mazzo fondato su una ricerca conserva la ricerca così come l’interfaccia grafica l’ha registrata (comando, board e identificatori delle posizioni trovate in quel momento): la grammatica di ricerca vive nell’interfaccia grafica, la CLI risincronizza quindi a partire dagli identificatori registrati e lo segnala sull’output di errore — aprite il mazzo nell’interfaccia grafica per rigiocare la ricerca stessa.

Esempi:

./blunderdb anki decks --db base.db
./blunderdb anki stats --db base.db --deck 2 --format json
./blunderdb anki forecast --db base.db --deck 2 --days 14
./blunderdb anki sync --db base.db --deck 2
./blunderdb anki card --db base.db --id 12 --action suspend
./blunderdb anki log --db base.db --deck 2 --limit 50

# Day         Due
# ---         ---
# 2026-09-02  12
# 2026-09-03  4
# ...
#
# 37 card(s) due over 14 day(s)

stats — Errori ricorrenti

Raggruppa gli errori di un filtro per piano di gioco e per tema, il più costoso per primo: la tabella Errori ricorrenti della scheda Errori del pannello Stats (vedi Pannello Stats). Le statistiche globali restano sotto list --type stats.

./blunderdb stats recurring --db <fichier> [options]

Opzioni:

  • --player <nom> — Solo le decisioni di questo giocatore.

  • --tournament <ids>, --from <AAAA-MM-JJ>, --to <AAAA-MM-JJ>, --decision-type all|checker|cube — Lo stesso filtro di list --type stats.

  • --limit <n> — Numero di gruppi mostrati in testo (predefinito 20, 0 per tutti).

  • --format text|json — Il JSON riporta ogni gruppo con l’elenco completo delle sue posizioni.

  • --quiz — Estrae a caso posizioni tra quelle dei tre gruppi più costosi (--quiz-size <n>, predefinito 20) e le stampa: sono gli identificatori che il quiz e quiz_grade giudicano. In JSON, il campo Quiz.

  • --deck <nome> — Crea un mazzo Anki con quel nome, riempito con tutte le posizioni dei tre gruppi più costosi.

  • --group <rango> — Con --quiz o --deck: il gruppo di quel rango (1 per il più costoso) al posto dei primi tre.

Un tema di mossa di pedine è gammon, blots, point o passive; un tema di cubo è offer_missed, offer_premature, answer_wrong_pass o answer_wrong_take. Gli errori che nessuna regola nomina escono dalla classifica: sono elencati a parte, una riga per piano di gioco (campo Unthemed in JSON), perché la spiegazione si pronuncia solo a partire da 60 mp, sopra la soglia Errore. La colonna COST (PR) è la quota del PR del filtro che il gruppo rappresenta.

Esempi:

./blunderdb stats recurring --db base.db --player "Alice"
./blunderdb stats recurring --db base.db --decision-type checker --format json
./blunderdb stats recurring --db base.db --quiz --format json
./blunderdb stats recurring --db base.db --group 1 --deck "Mon pire groupe"

stats training — Il PR del quiz Decisione, il PR delle partite e la ritenzione Anki, raggruppati per finestra di calendario, come la scheda Allenamento del pannello Stats (vedi Pannello Stats).

./blunderdb stats training --db <fichier> [options]

Opzioni:

  • --window week|month — La finestra di calendario (predefinita week).

  • --player <nome>, --tournament <ids>, --from <AAAA-MM-GG>, --to <AAAA-MM-GG>, --decision-type all|checker|cube — Il filtro delle partite; i diari del quiz e di Anki non portano un giocatore.

  • --format text|json — Il JSON contiene anche l’elenco delle sessioni di quiz.

Ogni serie conserva il proprio numero di campioni: una finestra senza decisioni è un trattino nel testo e un conteggio nullo nel JSON, mai un valore zero.

Esempi:

./blunderdb stats training --db base.db --player "Alice"
./blunderdb stats training --db base.db --window month --format json

cubematrix — Matrice del cubo

Dà il verdetto del cubo di una posizione a tutti i punteggi di un incontro: per ogni casella away × away, se la posizione si raddoppia e se si prende. Calcolo puro: nessun database viene aperto, la posizione arriva tramite il suo XGID o il suo OGID (OpenGammon).

./blunderdb cubematrix [options] '<XGID|OGID>'

Opzioni:

  • --format — Formato di uscita: text o json (predefinito: text).

  • --match-length — Lunghezza dell’incontro coperta dalla griglia, da 1 a 25 (predefinito: 7).

  • --ply — Profondità di ricerca di ogni casella, 0 o 2 (predefinito: 2).

  • --prune-k — Numero di mosse candidate mantenute dalla rete di potatura (predefinito: 12).

  • --jobs — Ricerche eseguite in parallelo (predefinito: una per core). La griglia è identica qualunque sia il valore; cambia solo il tempo.

Il punteggio proprio della posizione è ignorato — la griglia lo sostituisce — ma il suo cubo è conservato: la domanda posta è a quale punteggio girerei questo cubo. La griglia è post-Crawford da un capo all’altro.

Ogni casella è una ricerca a sé, perché il motore tiene conto del punteggio: una sola ricerca riletta attraverso equità di incontro diverse sarebbe falsa esattamente dove il punteggio conta.

Esempi:

# Grille d'un match en 5 points
./blunderdb cubematrix --match-length 5 'XGID=-b----E-C---eE---c-e----B-:0:0:1:00:0:0:0:7:10'

# Les équités de chaque case, pour un script
./blunderdb cubematrix --format json '<XGID>'

Uscita text: una griglia le cui righe sono i punti ancora necessari al giocatore di turno e le cui colonne sono quelli dell’avversario, poi la legenda delle sigle ND / DT / DP / TG e il motivo di ogni casella rifiutata.

rollout — Rollout di una posizione

Gioca una posizione un gran numero di volte con gammonNet, per decidere ciò che una ricerca non decide: due mosse a pochi millesimi di distanza, o una decisione di cubo su cui il modello esita. Con i dadi si giocano le sue mosse (le migliori alla profondità del rollout, almeno 2 ply, o quelle indicate con --move); senza dadi, la sua decisione di cubo (Non raddoppiare e Raddoppio/Prendo; Raddoppio/Passo vale esattamente +1). La posizione proviene da un XGID o da un OGID, o da un database (--db e --id); senza --store, nulla viene registrato.

./blunderdb rollout [options] '<XGID|OGID>'
./blunderdb rollout --db <path> --id <position> [--store] [options]

Opzioni:

  • --preset — Impostazione di partenza: fast (predefinito: 216 partite troncate a 7 semimosse, arresto a JSD 3 dopo 108) o standard (1296 partite troncate a 11, arresto a JSD 3 dopo 324). Entrambi giocano a 0 ply — la sola rete, per le mosse, il cubo e le foglie; --ply 1 o più gioca più in profondità, con un tempo più volte maggiore. Le opzioni seguenti lo sostituiscono una a una.

  • --games, --min-games, --truncation, --jsd, --ply, --candidates — I parametri del rollout (--truncation 0 gioca ogni partita fino in fondo, --jsd 0 non si ferma mai prima della fine).

  • --move — Una mossa da giocare, in notazione blunderDB (ripetibile).

  • --seed — Seme dei dadi, fisso per impostazione predefinita: lo stesso comando dà gli stessi numeri.

  • --jobs — Partite giocate in parallelo (predefinito: una per core); cambia solo il tempo.

  • --format — Formato di uscita: text o json (predefinito: text).

  • --db, --id — Il database e l’identificatore della posizione da giocare, al posto di un XGID.

  • --store — Registra il rollout terminato sulla posizione, come una seconda analisi con le proprie impostazioni, accanto all’analisi importata o valutata, che non sostituisce mai. Un rollout interrotto non viene registrato; di due rollout con le stesse impostazioni si conserva la serie più lunga, e un rollout con altre impostazioni si aggiunge accanto.

  • --list — Mostra i rollout salvati sulla posizione, dal più recente al più vecchio, invece di eseguirne uno.

Tutti i candidati giocano gli stessi dadi, la fortuna di ogni lancio viene tolta dal risultato di ogni partita (riduzione della varianza), i primi due lanci sono stratificati e una partita si ferma dove il database di bearoff two-sided la copre. Ogni riga riporta l’equity, il suo intervallo al 95 %, il numero di partite giocate e la JSD, il divario dal migliore in deviazioni standard della differenza. Il cubo viene giocato durante le partite: la classifica è più affidabile dell’equity assoluta. Ctrl-C mostra ciò che le partite terminate hanno stabilito.

Esempi:

./blunderdb rollout 'XGID=-b----E-C---eE---c-e----B-:0:0:1:31:0:0:0:0:10'
./blunderdb rollout --move '8/5 6/5' --move '24/23 13/10' '<XGID>'
./blunderdb rollout --db base.db --id 42 --preset standard --store
./blunderdb rollout --db base.db --id 42 --list

epc — Calcolatrice EPC

Calcola l’Effective Pip Count, la probabilità di vittoria e il verdetto di cubo money di una posizione di uscita data tramite XGID o tramite OGID (OpenGammon). Calcolo puro: non è coinvolto alcun file di database.

./blunderdb epc [options] '<XGID|OGID>'

Opzioni:

  • --format — Formato di output: text o json (predefinito: text).

  • --bearoff-ts — Database bearoff two-sided facoltativo (.bd) che amplia il TS-06-06 integrato (letto anche dalla variabile d’ambiente BLUNDERDB_TS_PATH). Vince il database valido più ampio; un file non valido viene ignorato con un avviso.

Regimi. Nel dominio coperto dal database two-sided, la probabilità di vittoria e l’analisi money del cubo (cubeless, ND, D/T, D/P, verdetto) sono esatte. Al di fuori, la probabilità di vittoria è stimata (convoluzione delle distribuzioni di lanci one-sided più una correzione calibrata) e mostrata con il suo margine d’errore misurato; il verdetto di cubo non viene volutamente mai stimato (vedere ADR-0009).

Esempi:

# Régime exact : six pions ou moins de chaque côté
./blunderdb epc 'XGID=-BBB------------------bbb-:0:0:1:00:0:0:0:0:10'

# Avec la table TS-06-11 calculée : exact jusqu'à onze pions par joueur
./blunderdb epc --bearoff-ts ~/.local/share/blunderdb/gnubg_ts6x11.bd 'XGID=…'

bearoff — Basi di bearoff

Fabbrica e gestisce le basi di bearoff. Nulla viene scaricato e nulla è incorporato: una tabella si calcola qui e si verifica contro l’impronta che gnubg produce per il suo dominio. Nessun sotto-comando parla con una base di dati — una tabella di bearoff è aritmetica sul gioco, non sulle posizioni di qualcuno — quindi nessuno prende --db.

./blunderdb bearoff generate --ts <domain> [options]
./blunderdb bearoff list [options]
./blunderdb bearoff verify <file.bd> [options]
./blunderdb bearoff delete --ts <domain> [options]

Il dominio si scrive come sotto makebearoff: 6x9 per la tabella a due lati con nove pedine per giocatore, os8 per la tabella a un lato su otto punti (os da solo vale os6).

Le due famiglie non rispondono alla stessa domanda. Una tabella a due lati amplia il dominio in cui la probabilità di vittoria e il verdetto del cubo sono esatti; una tabella a un lato amplia la distanza a cui una pedina può trovarsi senza che l’EPC taccia (fino a dieci punti).

generate. Dichiara la dimensione, la memoria e il tempo stimato prima di iniziare, poi mostra la percentuale e il tempo rimanente misurato.

  • --ts — Dominio a due lati da calcolare, per esempio 6x9.

  • --os — Dominio a un lato da calcolare, in numero di punti: da 6 a 12. È richiesto esattamente uno dei due.

  • --cores — Core da usare (predefinito: tutti tranne uno).

  • --data-dir — Dove scrivere (predefinito: la cartella dati dell’applicazione).

  • --quiet — Nessuna riga di avanzamento.

CTRL-C mette in pausa. Il segnale viene catturato: lo stato è scritto accanto alla tabella e lo stesso comando rilanciato riprende da dove si era fermato invece di ricalcolare tutto. Mezz’ora di aritmetica merita di essere scritta. bearoff delete getta via una ripresa in attesa. Solo il passaggio a due lati si mette in pausa; quello a un lato è sequenziale e --cores non gli serve.

list. Quantifica ogni dominio — dimensione, memoria, tempo su questa macchina — e dice quali sono già presenti, con il loro verdetto, e quali hanno un calcolo in pausa. --format json per uno script, --cores per cambiare l’ipotesi della stima.

verify. Risponde verified (gli stessi byte del riferimento), unverified (ben formata, ma nessuna impronta è registrata per quel dominio) o corrupt (il file si contraddice). Esce con errore nell’ultimo caso: questo comando è fatto per stare in uno script.

delete. Rimuove la tabella, la ripresa in attesa e i resti di un calcolo morto. Un dominio predefinito viene ricalcolato al prossimo avvio dell’applicazione; uno più ampio no.

Esempi:

# Ce que cette machine a, et ce que chaque domaine coûterait
./blunderdb bearoff list

./blunderdb bearoff generate --ts 6x9 --cores 4

# OS-08 : l'EPC répond alors jusqu'à un pion sur la 8
./blunderdb bearoff generate --os 8

# Sur un serveur, dans le volume que lit le démon
./blunderdb bearoff generate --ts 6x11 --data-dir /srv/bearoff

./blunderdb bearoff verify /srv/bearoff/gnubg_ts6x11.bd

analyze — Recupero gammonNet

Scrive un’analisi gammonNet per ogni posizione che non ne ha alcuna — il recupero di una libreria costituita prima che questa funzionalità esistesse (ADR-0013, ADR-0015). È la stessa operazione dell’avvio automatico dopo l’importazione e del pulsante « Analizza ora » dell’interfaccia grafica, e del punto di accesso /v1/gammonnet.analyzeMissing del demone serve per un tenant — tre forme diverse della stessa operazione, non tre logiche distinte (vedere Modalità headless (server)).

./blunderdb analyze --db <path> [options]

Opzioni:

  • --db — Database (obbligatorio).

  • --ply — Profondità di ricerca (predefinito: 2, il parametro canonico).

  • --prune-k — Ampiezza di potatura (predefinito: 12, il parametro canonico).

  • --candidates — Numero di mosse candidate conservate per ogni decisione di movimento (predefinito: 10).

  • --jobs — Numero di posizioni analizzate in parallelo (predefinito: il numero di core della macchina).

  • --match — Limita il lotto alle posizioni di una sola partita (0, il valore predefinito, significa l’intera biblioteca).

  • --compare — Non scrive nulla: confronta gammonNet con le analisi importate invece di colmare buchi (vedi sotto).

  • --limit — Con --compare, si ferma dopo questo numero di posizioni (0 = tutte).

  • --format — Formato di output: text (predefinito, con avanzamento) o json (un unico documento riassuntivo, stampato alla fine).

  • --rollout — Fa un rollout delle posizioni scelte da --query invece di colmare le lacune (vedi sotto).

  • --query — Con --rollout, le posizioni da giocare, nel linguaggio di query della ricerca (search --query-help); vuoto, tutte.

Un incontro importato senza analisi ottiene così un PR. È il caso di un incontro giocato online, o di un file Jellyfish .mat, che nessuno ha fatto passare da XG. blunderDB ne conosceva le posizioni e le mosse giocate, ma nulla diceva quanto valessero; una volta passato il lotto, la mossa effettivamente giocata è confrontata con la classifica di gammonNet e lo scarto alimenta il PR e tutti gli altri indicatori. La mossa giocata proviene dalla tabella delle mosse dell’incontro, scritta all’importazione, che il file portasse un’analisi o no — non è mai indovinata.

Un database analizzato con una versione precedente a questa non ha bisogno di essere rivalutato: repair ricalcola le colonne a partire da ciò che è già in archivio e restituisce il loro PR a quegli incontri.

Una sola partita (--match). Con l’identificatore mostrato da list --type matches, il lotto percorre solo le posizioni di quella partita: stessa regola del vuoto, stesse garanzie, ambito più ristretto. Una partita appena importata riceve le sue analisi senza che il resto della biblioteca venga percorso, e una partita corretta e poi analizzata una seconda volta costa solo le posizioni create dalla correzione, poiché tutte le altre portano già un’analisi. L’opzione non si combina né con --stale né con --compare, che guardano entrambe posizioni che hanno già un’analisi: chiedere entrambe è un errore, anziché un ambito silenziosamente ignorato.

Il parallelismo (--jobs). Le posizioni di un lotto sono indipendenti — nessuna ricerca informa la successiva — quindi vengono distribuite su --jobs thread, ciascuno con il proprio valutatore. Le analisi scritte sono identiche qualunque sia il valore di --jobs; cambia solo il tempo di calcolo. --jobs 1 lascia la macchina libera per altro. L’annullamento non ne è influenzato: Ctrl-C ferma il lotto prima di qualsiasi nuova posizione, e tutto ciò che era già stato calcolato viene scritto.

La regola della lacuna (ADR-0013). Una posizione che porta già un’analisi — XG, GNUbg, BGBlitz o un precedente passaggio di gammonNet — non viene mai toccata, qualunque sia il motore mancante. Viene scritta soltanto una posizione senza alcuna analisi. Il comando può quindi essere rilanciato in qualsiasi momento senza rischi, e interrotto in modo pulito: Ctrl-C annulla senza perdere nulla di quanto già scritto, e l’esecuzione successiva riprende esattamente da dove la precedente si era fermata — non serve alcun registro, poiché « le posizioni senza analisi » vengono ricalcolate a ogni avvio.

Esempio:

./blunderdb analyze --db base.db

# Analyzing 1204 position(s) with gammonNet (2-ply, k=12, 16 job(s))...
#   1/1204 (0%)
#   61/1204 (5%)
#   ...
#   1204/1204 (100%)
# Done.

./blunderdb analyze --db base.db --jobs 1

# Un seul match, celui qui vient d'être importé
./blunderdb analyze --db base.db --match 12

Rollout in blocco (--rollout). Ogni posizione scelta da --query viene giocata da un rollout, una dopo l’altra su tutti i core, e il rollout viene registrato accanto alla sua analisi, mai al suo posto. Il valore è un preset — fast (216 partite troncate a 7) o standard (1296 partite troncate a 11) — oppure impostazioni libere: un preset facoltativo, poi games=, min-games=, truncation=, jsd=, ply=, candidates=, seed=, separati da virgole. Una posizione che porta già un rollout con le stesse impostazioni viene saltata: un’esecuzione interrotta con Ctrl-C riprende da dove si era fermata, la posizione in corso essendo scartata per intero. Una posizione analizzata solo da un rollout viene trovata dalla ricerca attraverso di esso; una posizione già analizzata conserva le colonne della sua analisi.

./blunderdb analyze --db base.db --rollout fast --query 'E>80'
./blunderdb analyze --db base.db --rollout 'standard,ply=1' --query 'c'

--compare: quanto vale gammonNet sulla tua base?

La precisione del motore è misurata altrove su corpus di riferimento e sulla tavola di uscita esatta. Nessuna di quelle misure risponde alla domanda che un utente si pone davvero, che riguarda le sue posizioni: sulle partite importate da XG, dove il motore integrato è in disaccordo con l’analisi arrivata col file, e quanto costerebbe quel disaccordo?

--compare risponde, e non scrive nulla. Non è una precauzione ma il senso del comando: l’ADR-0013 protegge incondizionatamente un’analisi importata, e il confronto può quindi essere lanciato su una base che proprio non si vuole vedere riscritta.

Il resoconto dà:

  • il tasso di accordo sulla migliore risposta, separato tra mosse di pedine e decisioni di cubo — le due non hanno nulla a che vedere e un tasso unico nasconderebbe quale delle due cede;

  • il costo del disaccordo, prezzato sulla scala dell’analisi importata: quanto vale la mossa preferita da gammonNet secondo il motore importato, meno quanto vale la sua stessa mossa migliore. Questo verso è il solo che i due motori possano cifrare insieme; prezzare un disaccordo due volte inviterebbe a leggere il minore dei due numeri;

  • la ripartizione per fase di gioco, che è ciò che dice dove si concentrano i disaccordi;

  • i dieci disaccordi più costosi, posizione per posizione.

Due motori scrivono la stessa mossa in modo diverso — XG scrive «13/7» dove gammonNet scrive «13/8 8/7», le prese sono segnate da un lato e non dall’altro, la ripetizione è talvolta condensata in «(2)». Queste differenze sono dialetto e non disaccordo: il confronto riporta entrambe le notazioni a una forma canonica prima di compararle. Senza di ciò, un corpus di prova mostrava il 78,8 % di accordo invece del 93,2 % — quindici punti di falsi disaccordi.

Una mossa che il motore importato non ha elencato non può essere prezzata sulla sua scala: conta come un disaccordo di costo nullo anziché di un costo inventato.

# Comparer sur un échantillon de 500 positions
./blunderdb analyze --db base.db --compare --limit 500

# compared: 118 decision(s)  (refused 2, failed 0)
# same best answer: 93.2% (110/118)
#   checker play:   93.7% (59/63)
#   cube decision:  92.7% (51/55)
# ...

transcribe — Rigiocare una trascrizione

Rigioca una trascrizione e riferisce ciò che la rilettura vi trova. La sorgente è un file .mat, un match della biblioteca o una bozza di trascrizione — esattamente una delle tre. Un match viene letto tramite il .mat che produrrebbe all’esportazione: ciò che si rigioca è dunque ciò che conterrebbe un’esportazione.

./blunderdb transcribe --mat <fichier> [--check] [--render <sortie>]
./blunderdb transcribe --db <path> --match <id> --check
./blunderdb transcribe --db <path> --draft <id> --check
./blunderdb transcribe --db <path> --match <id> --edit [--accept-losses]
./blunderdb transcribe --db <path> --draft <id> --finish|--abandon

Opzioni:

  • --mat — File .mat da rigiocare.

  • --db — Base di dati, per --match e --draft.

  • --match — Identificativo del match della biblioteca da rigiocare.

  • --draft — Identificativo della bozza di trascrizione da rigiocare.

  • --check — Elenca le incoerenze trovate (comportamento predefinito).

  • --render — Riscrive la trascrizione in .mat in questo percorso.

  • --format — Formato di output: text (predefinito) o json.

  • --edit — Apre una bozza sul --match (o restituisce quella già aperta su di esso).

  • --accept-losses — Con --edit su una partita importata: accetta che le sue analisi e i suoi commenti possano andare persi.

  • --finish — Termina il --draft: scrive la sua partita, o sostituisce quella da cui è stato aperto, e libera la bozza.

  • --abandon — Abbandona il --draft: lo elimina senza partita; una partita da cui è stato aperto resta così com’è.

  • --yes — Con --abandon su una bozza che non ha mai prodotto una partita: conferma che tutto ciò che vi è scritto è perso.

--check nomina ogni incoerenza con il numero dell’azione e la partita in cui si trova: mossa illegale, due turni di seguito per lo stesso giocatore, azione di cubo impossibile, azione oltre la fine del match, mossa i cui passi non usano i propri dadi, prima mossa di una partita con un doppio, che nessun tiro d’apertura può essere, mossa non registrata — la cella ??? che gnubg scrive quando non ha conservato la mossa giocata, e che non è una danza, punteggio dichiarato incoerente — una partita la cui riga di punteggio non è quella che danno le partite precedenti, rigiocata al punteggio scritto.

Un’incoerenza è riferita, mai opposta: nulla viene rifiutato per essa e il codice di uscita resta 0 qualunque cosa la rilettura trovi. Un codice diverso da 0 segnala un vero fallimento — file illeggibile, base che non si apre, uscita impossibile da scrivere. Uno script che vuole agire sui rilievi li legge in --format json, dove un file rotto e una partita che contiene una mossa illegale non si confondono.

--render riscrive la trascrizione in .mat, il che permette di verificare l’andata e ritorno su un file reale, fuori dai test.

Solo tre opzioni scrivono, con gli stessi metodi del pannello Trascrizione: --edit apre una bozza su una partita esistente, --finish la termina — la partita viene sostituita con lo stesso identificatore — e --abandon elimina una bozza senza partita, ed esige --yes per una bozza mai terminata, che porta via tutto ciò che vi è scritto. Una partita importata porta analisi e commenti che un .mat non porta: --edit ne dà il conto al massimo e rifiuta senza --accept-losses.

Esempio:

./blunderdb transcribe --mat match.mat --check

# match.mat: 7 point match, 4 game(s), 203 action(s)
#   Final score: 9-2
# Inconsistencies: none

tournament — Leggere un torneo diretto

Legge un torneo diretto senza interfaccia grafica. Dirigere un torneo in modo interattivo è compito della console del motore Nicomaque; questi sottocomandi leggono, nessuno attende un input, e solo move scrive.

./blunderdb tournament <sous-commande> --db <chemin> [options]

Sottocomandi:

  • list [--format text|json] — I tornei diretti della base, con il loro stato, la versione del motore, l’evento a cui ciascuno appartiene (vuoto se nessuno) e la data dell’ultima decisione.

  • verify --id N [--format text|json] — Riproduce la direzione e segnala ogni avviso residuo. Esce in errore se ne resta uno: è la verifica dopo il torneo, e uno script che la esegue sulle basi di una stagione vuole un codice di ritorno, non una riga da filtrare.

  • standings --id N — La classifica in CSV, premi compresi, nella lingua dell’interfaccia.

  • ranking --season [--rencontre N] [--from AAAA-MM-JJ] [--to AAAA-MM-JJ] [--points 25,18,15] [--participation P] [--elo] [--format csv|json] — La classifica di stagione: i tornei chiusi di un evento o di un periodo (estremi inclusi, sulla data del torneo; senza filtro, tutti i tornei diretti), con ogni posto convertito in punti dal punteggio (il vincitore per primo; per impostazione predefinita 25, 18, 15, 12, 10, 8, 6, 4, 2, 1), più --participation per ogni torneo giocato. I pari merito si dividono la media dei posti che occupano. Una persona è riconosciuta da un torneo all’altro dal nome. --elo aggiunge un Elo di club rigiocato sulle partite della stagione (formula FIBS, partenza a 1500). Il CSV dà una riga per persona e una colonna di punti per torneo; un torneo non chiuso è elencato ma non porta nulla.

  • page --id N|--rencontre N [--out <cartella>] — La pagina HTML di visualizzazione di una prova (--id), oppure la pagina murale di un evento (--rencontre: una riga per tavolo, qualunque sia la prova che lo occupa). Esattamente una delle due è richiesta. Senza --out esce sullo standard output; con essa, viene scritta nella cartella, che diventa quella della direzione o dell’evento.

  • export --id N — Il registro degli eventi grezzo, riproducibile dagli strumenti del motore. Il registro è tutta la verità di una direzione: classifica, tabelloni e avvisi ne sono riprodotti. Uno strumento che legge questo output non ha bisogno di alcun blunderDB.

  • move --id N --match M --table T [--format text|json] — Cambia il tavolo di un incontro in corso, come trascinare una casella su un’altra nella griglia. Se il tavolo di destinazione è occupato, i due incontri scambiano i loro tavoli; un tavolo fuori servizio viene rifiutato. In un evento, se il tavolo è occupato da un’altra prova, lo scambio avviene tra le due prove: un cambio di tavolo viene scritto nel registro di ciascuna. Stampa il tavolo di ogni incontro in corso.

  • hall --rencontre N [--format text|json] — Tutti i tavoli di un evento: una riga per tavolo, qualunque prova lo occupi (prova, incontro, giocatori), poi le proposte di ciascuna prova. È la griglia che mostra la vista Tutti i tavoli della Direction. I tavoli sono raggruppati per sala quando l’evento ne ha, e sono nominati quando hanno un nome.

  • tables --rencontre N|--tournament N [--format text|json] — Le proprietà dei tavoli (nome, sala, riservato, assegnato a) e le sale in cui gioca ciascuna prova di un evento (--rencontre), oppure le proprietà di una prova che gioca da sola (--tournament). Sola lettura: la scrittura passa da call (rencontres.setTables, rencontres.setEventRooms, directions.setTables).

Opzioni comuni: --db (obbligatorio), --id (obbligatorio tranne per list, page --rencontre hall e tables), --format.

Esempi:

./blunderdb tournament list --db base.db
./blunderdb tournament verify --db base.db --id 3
./blunderdb tournament standings --db base.db --id 3 > classement.csv
./blunderdb tournament ranking --db base.db --season --from 2026-01-01 --to 2026-12-31 --elo > saison.csv
./blunderdb tournament page --db base.db --id 3 --out /tmp/affichage
./blunderdb tournament page --db base.db --rencontre 1 --out /tmp/evenement
./blunderdb tournament export --db base.db --id 3 > journal.json
./blunderdb tournament move --db base.db --id 3 --match m4 --table 7
./blunderdb tournament hall --db base.db --rencontre 1
./blunderdb tournament tables --db base.db --rencontre 1

trash — Il cestino

Ciò che è stato eliminato, e con che cosa rimetterlo. Un’eliminazione resta un’eliminazione: prima viene scritta un’istantanea JSON di ciò che sparisce, e nient’altro nella base sa che quella tabella esiste — nessun filtro di ricerca, nessuna statistica, nessuna regola di ritenzione.

./blunderdb trash <sous-commande> --db <chemin> [options]

Sottocomandi:

  • list — Che cosa c’è nel cestino, dal più recentemente eliminato al più vecchio.

  • restore --id N — Rimette la voce N e la toglie dal cestino.

  • discard --id N — Elimina subito la voce N, senza ripristinarla.

  • empty [--older-than G] — Svuota il cestino, o solo ciò che ha più di G giorni.

  • delete --kind K --id N — Elimina un oggetto passando dal cestino, perché il gesto sia annullabile. K vale position, collection o comment.

Opzioni comuni: --db (obbligatoria), --kind, --limit (predefinito 50), --format (text o json).

Nota

blunderdb delete elimina sempre senza rete: uno script che elimina una posizione si aspetta che sparisca, e lasciare un’istantanea in silenzio farebbe crescere un file che nessuno ha chiesto di far crescere. È trash delete che conserva l’annullamento.

Ripristinare una posizione ripassa dalla deduplicazione Zobrist: non crea mai un doppione, ma non restituisce il vecchio identificativo — la riga d’origine non esiste più. Una posizione ripristinata è la stessa posizione, con un numero nuovo.

Ciò che ha più di trenta giorni viene eliminato da blunderdb vacuum — mai all’apertura di una base.

Esempi:

# Supprimer une position en gardant l'annulation
./blunderdb trash delete --db base.db --kind position --id 412

# Voir la corbeille, puis remettre une entrée
./blunderdb trash list --db base.db
./blunderdb trash restore --db base.db --id 3

# Ne garder que ce qui a moins de trente jours
./blunderdb trash empty --db base.db --older-than 30

info — Metadati del database

Mostra i metadati e le statistiche di un database.

./blunderdb info --db <path> [--format <format>]

Opzioni:

  • --db — Database (obbligatorio).

  • --format — Formato di output: text o json (predefinito: text).

Esempi:

./blunderdb info --db base.db

# Database Information
# ==================================================
# Path: /home/jean/bg/base.db
#
# Metadata:
#   Version: 2.20.0
#   User: Jean
#   Description: Matchs de tournoi 2025
#   Date of Creation: 2026-09-06 02:43:51
#
# Statistics:
#   Positions: 3859
#   Analyses: 3855
#   Matches: 11
#   Games: 61
#   Moves: 3766

--format json aggiunge l’origine del file — issuance porta la filigrana se ce n’è una, e l’identità di emittente di questa macchina:

./blunderdb info --db base.db --format json
{
  "issuance": {
    "watermarked": false,
    "issuerFingerprint": "1186-57FA-060C-9378",
    "issuerName": "unger"
  },
  "metadata": {
    "database_version": "2.20.0",
    "dateOfCreation": "2026-09-06 02:43:51",
    "description": "Matchs de tournoi 2025",
    "user": "Jean"
  },
  "path": "/home/jean/bg/base.db",
  "stats": {
    "analysis_count": 3855,
    "game_count": 61,
    "match_count": 11,
    "move_count": 3766,
    "position_count": 3859
  }
}

edit — Modificare i metadati

Modifica il nome utente, la descrizione o le soglie di un database.

./blunderdb edit --db <path> [options]

Opzioni:

  • --db — Database (obbligatorio).

  • --user — Nuovo nome utente.

  • --description — Nuova descrizione.

  • --clear-user — Cancellare il nome utente.

  • --clear-description — Cancellare la descrizione.

  • --error-threshold — Soglia di errore, in millipunti: una decisione che costa almeno tanto è un errore.

  • --blunder-threshold — Soglia di blunder, in millipunti: un errore che costa almeno tanto è un blunder.

  • --format — Formato di output: text (predefinito) o json ({"changes": [...]}).

È richiesta almeno un’opzione di modifica.

Esempi:

./blunderdb edit --db base.db --user "Marie" --description "Ma collection"
./blunderdb edit --db base.db --clear-description
./blunderdb edit --db base.db --error-threshold 20 --blunder-threshold 80

verify — Verificare l’integrità

Verifica l’integrità del database e, facoltativamente, confronta un match con il suo file di origine.

./blunderdb verify --db <path> [--match <id>] [--mat <file.mat>]

Opzioni:

  • --db — Database (obbligatorio).

  • --match — ID del match da verificare.

  • --mat — File MAT da confrontare (usato con --match).

  • --format — Formato di output: text (predefinito) o json (statistiche, righe orfane, deviazione dello schema e la verifica del match se presente).

Senza l’opzione --match, il comando mostra le statistiche generali del database. Con --match, verifica i dati del match e può confrontarli con il file di origine originale.

Ogni esecuzione controlla anche l’integrità referenziale: conta le righe orfane — partite senza incontro, mosse senza partita, analisi di mossa senza mossa, analisi senza posizione, voci del diario di ripasso senza mazzo o senza posizione — e stampa una riga WARNING con il totale se ce ne sono. Un database sano risponde Orphaned rows: none. Delle orfane possono restare in un database scritto da una versione che non applicava le chiavi esterne su ogni connessione, o prima che il diario di ripasso avesse le proprie; non appartengono ad alcun incontro né ad alcun mazzo e occupano soltanto spazio. Il comando termina comunque con codice di uscita 0.

Ogni esecuzione confronta anche lo schema con la DDL di riferimento ed elenca le tabelle, le colonne e gli indici che mancano al database. L’apertura di un database aggiunge ciò che manca quando può e si limita a registrare nel log ciò che non può aggiungere (tipicamente un indice UNIQUE che righe duplicate impediscono di ricostruire): è qui che questo scarto diventa visibile, e una query che nomina uno di questi elementi fallisce finché la causa non è corretta. Un database sano risponde Schema: matches the reference DDL. Come gli orfani, uno scarto di schema è una constatazione, non un fallimento: il codice di uscita resta 0.

Ogni esecuzione verifica infine le regole che la DDL attuale enuncia ma che SQLite non sa aggiungere a una tabella già creata: i vincoli CHECK di intervallo (dadi tra 0 e 6, cubo e pip non negativi, da 0 a 15 pedine uscite, voto di ripasso tra 1 e 4), l’hash Zobrist che a una riga non dovrebbe mai mancare e l’unicità di un’analisi per posizione. Un database creato a partire dalla versione 2.18.0 dello schema li applica; uno più vecchio può ancora contenere righe che un database nuovo rifiuterebbe, ed è quello che viene contato qui, regola per regola. Un database sano risponde Constraints: every row satisfies the current DDL. Una constatazione in più: non viene riparato nulla e il codice di uscita resta 0.

Ogni esecuzione ricalcola infine i due contatori denormalizzati, match.game_count e game.move_count, a partire dalle righe che dichiarano di contare, e indica quanti sono in disaccordo e di quanto nel caso peggiore. Entrambi vengono scritti una sola volta, all’importazione, in base a quanto conteneva il file sorgente, e sono quelli mostrati dall’elenco degli incontri e dalla vista di una partita: uno scarto piccolo è di solito un’importazione che ha saltato ciò che non sapeva convertire. Non viene riscritto nulla — sostituire il contatore con ciò che è stato memorizzato cancellerebbe proprio lo scarto che vale la pena guardare. Un database sano risponde Counters: game_count and move_count agree with the rows.

Esempi:

./blunderdb verify --db base.db
./blunderdb verify --db base.db --match 1
./blunderdb verify --db base.db --match 1 --mat original.mat

Farne una guardia. Il codice di uscita vale 0 qualunque cosa trovi il comando: è --format json che porta il verdetto, e uno script deve leggere i contatori da solo.

{
  "stats": {
    "analysis_count": 3855,
    "game_count": 61,
    "match_count": 11,
    "move_count": 3766,
    "position_count": 3859
  },
  "orphans": {
    "games_without_match": 0,
    "moves_without_game": 0,
    "move_analyses_without_move": 0,
    "analyses_without_position": 0,
    "reviews_without_deck": 0,
    "reviews_without_position": 0
  },
  "orphan_total": 0,
  "schema_drift": {
    "missing_tables": null,
    "missing_columns": null,
    "missing_indexes": null
  },
  "schema_drift_count": 0,
  "constraint_violations": [
    {"name": "position.zobrist_hash NOT NULL", "count": 0},
    {"name": "position.dice_1 BETWEEN 0 AND 6", "count": 0}
  ],
  "constraint_violation_total": 0,
  "counter_drift": {
    "matches_with_wrong_game_count": 0,
    "games_with_wrong_move_count": 53,
    "worst_game_count_gap": 0,
    "worst_move_count_gap": 2
  },
  "counter_drift_total": 53
}

Tre campi valgono un allarme: orphan_total, schema_drift_count e constraint_violation_total. Non nulli, descrivono un database da riparare.

./blunderdb verify --db base.db --format json \
  | jq -e '.orphan_total == 0 and .schema_drift_count == 0 and .constraint_violation_total == 0'

counter_drift_total non ne fa parte, e l’esempio sopra lo dimostra: il database che l’ha prodotto era appena stato importato e mostra già 53 partite il cui contatore di mosse differisce da ciò che contengono le righe. Questi contatori provengono dal file sorgente, non dal database; uno scarto racconta l’import, non segnala una corruzione. Guardatelo, non usatelo come una soglia.

vacuum — Compattare il database

Recupera lo spazio su disco lasciato dalle eliminazioni (match, tornei, purghe): SQLite non riduce mai il file da solo quando si cancellano dati, glielo si deve chiedere esplicitamente. È l’unico modo di avviare una compattazione — non avviene mai automaticamente all’apertura di un database, perché il suo costo è imprevedibile su un database grande.

./blunderdb vacuum --db <path>

Opzioni:

  • --db — Database (obbligatorio).

  • --format — Formato di output: text (predefinito) o json ({"size_before", "size_after", "reclaimed"}, in byte).

Il comando inizia con un wal_checkpoint(TRUNCATE) affinché la dimensione mostrata prima della compattazione sia onesta, verifica che sul disco resti all’incirca il doppio della dimensione attuale del file (SQLite ricostruisce interamente il database prima di passarvi), esegue il VACUUM e poi un ANALYZE per rinfrescare le statistiche usate dal pianificatore di query. Se lo spazio su disco manca, il comando rifiuta di partire con un messaggio esplicito anziché rischiare una compattazione interrotta.

Esempio:

./blunderdb vacuum --db base.db

# Compacting database...
#   Before: 128.4 MiB
#   After:  41.2 MiB
#   Reclaimed: 87.2 MiB

repair — Ricalcolare ciò che è derivato

Ricalcola ciò che il database ricava da ciò che memorizza: le colonne scalari di ogni analisi a partire dall’analisi stessa, di cui non sono che una proiezione; la fase e il tipo di gioco di ogni posizione a partire dalla sua damiera; e la sentinella di Crawford di ogni punteggio a partire dalla partita da cui la posizione proviene, o dall’XGID con cui è entrata. Le analisi non vengono toccate: ciò che viene rifatto sono i valori che ne erano stati ricavati.

./blunderdb repair --db <path>

Opzioni:

  • --db — Database (obbligatorio).

  • --format — Formato di output: text (predefinito) o json — un contatore per passata: repaired (colonne di analisi), phases (posizioni riclassificate) e crawford (posizioni con hash ricalcolato). Ciascuno indica il numero di righe effettivamente modificate.

Utile dopo una correzione del modo in cui viene letta un’analisi importata. Il caso si è già presentato due volte. L’importatore XG scrive un «niente raddoppio» in due modi, e il secondo era inteso come un vero raddoppio — la colonna portava allora l’errore di un raddoppio mai avvenuto. E un’analisi che blunderDB aveva calcolato da sé non sapeva quale mossa fosse stata giocata, così che un incontro importato senza analisi manteneva un errore nullo ovunque e un PR di 0,00; la colonna si ricalcola ora a partire dalle mosse dell’incontro. Correggere la lettura non cambia nulla nelle righe già scritte; questo comando le rifà.

La passata di Crawford, invece, tocca le posizioni stesse. Un punteggio a 1 significa «manca un punto, e questa partita È quella di Crawford»; 0 significa «manca un punto, quella di Crawford è alle spalle». Finché gli importatori non hanno scritto questa distinzione, ogni posizione successiva a Crawford è stata registrata come una posizione di Crawford, e quindi letta con il cubo morto — là dove chi insegue raddoppia in realtà alla prima occasione. Correggere il punteggio cambia l’hash della posizione: la riga viene dunque riprocessata con un nuovo hash e fusa con la sua gemella corretta se il database ne tiene già una — l’analisi, i commenti, le raccolte, le carte Anki e il loro registro dei ripassi, le mosse della partita e le voci del cestino che la nominano seguono la riga superstite. Una posizione che nessuna partita designa viene corretta solo sulla parola dell’XGID che ha portato da un altro programma (XG, BGBlitz…): quando il campo Crawford di quell’XGID dice che la partita non è quella di Crawford, e l’XGID descrive davvero questa posizione. Nel senso inverso, una posizione senza partita registrata a 0 da entrambi i lati passa a 1 quando l’XGID che ha portato è quello di un match a 1 punto e la descrive: l’unica partita di un match a 1 punto comincia a un punto dall’obiettivo, quindi è quella di Crawford, come la scrivono gli importatori. Il DMP dopo quella di Crawford di un match più lungo resta a 0: il suo XGID dà la lunghezza di quel match. Un XGID che blunderDB ha riscritto da sé non fa che ripetere il punteggio registrato e non prova nulla. Ogni altra posizione senza partita viene lasciata com’è: nulla contraddice ciò che il suo punteggio annuncia.

Nulla la attiva automaticamente, ed è voluto: riscrivere le colonne di analisi di tutti, o riprocessare le posizioni con un nuovo hash, al solo atto di aprire un database non è qualcosa che uno strumento debba fare alle spalle del suo utente.

Esempio:

./blunderdb repair --db base.db

# 42 analyses repaired.
# 7 positions reclassified.
# 3 positions rehashed onto the right Crawford sentinel.

delete — Eliminare dati

Elimina un match e tutti i dati associati (partite, mosse, analisi).

./blunderdb delete --db <path> --type match --id <id> [--confirm]

Opzioni:

  • --db — Database (obbligatorio).

  • --type — Tipo di eliminazione: match (obbligatorio).

  • --id — ID dell’elemento da eliminare (obbligatorio).

  • --confirm — Eliminare senza chiedere conferma.

  • --format — Formato di output: text (predefinito) o json ({"match_id": N, "deleted": true}).

Esempi:

# Confirmation interactive, puis sans confirmation (scripts)
./blunderdb delete --db base.db --type match --id 1
./blunderdb delete --db base.db --type match --id 1 --confirm

healthcheck — Sondare un demone

Chiede a un demone serve in esecuzione (vedi Modalità headless (server)) se è pronto: una richiesta GET /readyz, codice di ritorno 0 se il demone risponde 200 (archiviazione raggiungibile, schema alla versione attesa), 1 altrimenti — archiviazione irraggiungibile, schema obsoleto o nulla in ascolto all’indirizzo. Nessun file di database viene aperto.

./blunderdb healthcheck [--addr host:port] [--timeout 2s]

Opzioni:

  • --addr — Indirizzo su cui il demone è in ascolto (predefinito BLUNDERDB_ADDR, altrimenti :8080). Un indirizzo senza host (:8080) o con un host generico (0.0.0.0, [::]) viene sondato sull’interfaccia di loopback.

  • --timeout — Tempo oltre il quale la sonda rinuncia (2s per impostazione predefinita).

È il comando che esegue l”HEALTHCHECK dell’immagine container (un’immagine distroless, senza curl); anche il binario serve compilato da cmd/serve lo comprende. Vale altrettanto in uno script o in un’unità systemd.

Esempio:

./blunderdb serve --db base.db --addr 127.0.0.1:8080 &
./blunderdb healthcheck --addr 127.0.0.1:8080 && echo "démon disponible"

# ready

In caso di fallimento viene mostrato il motivo, che docker inspect riproduce per un container unhealthy:

Error: healthcheck: http://127.0.0.1:8080/readyz answered 503 Service Unavailable (version_mismatch)

mcp — Offrire il database a un assistente IA

Serve gli strumenti del database a un assistente IA tramite il Model Context Protocol, su standard input e output: è l’assistente a lanciare il comando. Gli strumenti cercano posizioni nella grammatica della barra dei comandi, leggono una posizione e la sua analisi, spiegano un errore, calcolano le statistiche di un giocatore, elencano partite, tornei e raccolte e pongono un quiz. Si limitano a leggere, tranne con --write. L’elenco completo e l’equivalente HTTP del demone: Strumenti per un assistente IA (MCP).

./blunderdb mcp --db base.db [--write]

Opzioni:

  • --db — File del database (obbligatorio).

  • --write — Offre anche gli strumenti che scrivono: salvare una posizione, commentarla, creare e riempire una raccolta. Nulla viene cancellato.

Come call, il comando migra lo schema di un database meno recente all’apertura, anche senza --write.

Esempio: dichiarare il database a Claude Code.

claude mcp add blunderdb -- blunderdb mcp --db /chemin/vers/base.db

completion — Completamento shell

Stampa sullo standard output uno script di completamento per i nomi dei sottocomandi. L’elenco dei comandi incorporato in ogni script è generato dalla stessa tabella letta da blunderdb help e dallo smistamento di main.go (handlers()): un nuovo sottocomando viene quindi offerto dal completamento non appena viene collegato, senza nulla da mantenere manualmente.

./blunderdb completion <bash|zsh|fish>

Esempi:

# bash
source <(blunderdb completion bash)
blunderdb completion bash | sudo tee /etc/bash_completion.d/blunderdb > /dev/null

# zsh : un répertoire déjà sur $fpath
blunderdb completion zsh > "${fpath[1]}/_blunderdb"

# fish
blunderdb completion fish | source

I pacchetti lo installano automaticamente: il .deb/.rpm (nfpm) e il pacchetto AUR generano i tre script dal binario impacchettato al momento della compilazione, e il cask Homebrew esegue blunderdb completion <shell> una volta all’installazione tramite generate_completions_from_executable. Nulla viene incluso nel repository, quindi il completamento non può mai discostarsi dalla tabella dei sottocomandi.

version — Mostrare la versione

Mostra la versione di blunderDB e quella dello schema di database che questo binario scrive; è la prima cosa da allegare a una segnalazione di bug.

./blunderdb version
# blunderDB version 0.36.0 (database schema 2.20.0)

Esempi di flusso di lavoro

Import di una cartella di torneo

./blunderdb create --db tournoi_paris.db --user "Jean" --description "Open de Paris 2025"
./blunderdb import --db tournoi_paris.db --type batch --dir ./matchs_open_paris/
./blunderdb list --db tournoi_paris.db --type stats

Backup periodico

./blunderdb export --db production.db --type database --file sauvegarde-$(date +%Y%m%d).db

Analisi degli errori

# Les positions délicates, puis celles de videau
./blunderdb search --db production.db --error-min 0.1 --export blunders.db
./blunderdb search --db production.db --decision cube --error-min 0.05 --export cube_errors.db

# Les coups réellement fautifs : au moins 100 millièmes d'équité perdus
./blunderdb search --db production.db --move-error-min 100 --format json

Codici di uscita

  • 0 — Successo.

  • 1 — Errore.

Questo permette di utilizzare la CLI in script con gestione degli errori:

if ./blunderdb import --db base.db --type match --file match.xg; then
    echo "OK"
else
    echo "KO"
    exit 1
fi