Metis è un assistente per l'analisi delle partite di scacchi giocate a tavolino. Tu (l'unico amministratore) incolli una partita a un bot Telegram; Metis la fa analizzare a Stockfish, commenta ogni mossa in italiano, riconosce l'apertura, tiene un profilo statistico di ogni giocatore, genera un video animato della partita e una scheda giocatore, e invia tutto in privato a ciascun giocatore su Telegram.
Questa parte spiega come si usa, senza tecnicismi. La parte tecnica è più sotto.
C'è un bot Telegram (il tuo: @metis_chess_bot). Solo tu, l'amministratore, puoi mandargli le
partite. Ogni giocatore registrato riceve poi in chat privata la propria analisi.
Per ogni partita analizzata, ciascun giocatore riceve quattro cose:
- 📊 Un messaggio di riassunto, scritto dal suo punto di vista (esito: hai vinto / hai perso / patta) — apertura riconosciuta, la sua precisione e quella dell'avversario, numero di errori gravi (blunder) e i 2–3 momenti chiave della sua partita, ciascuno con un'icona sulla qualità della mossa e una spiegazione discorsiva (cosa è cambiato e cosa si poteva fare meglio).
- 🖼️ La sua scheda giocatore (immagine) — record vittorie/patte/sconfitte, precisione media, livello stimato, rendimento per fase (apertura/mediogioco/finale), e lo storico delle aperture con le percentuali di successo.
- 🎬 Un video animato della partita — la scacchiera che si muove mossa per mossa, con la mossa evidenziata, un'etichetta colorata sulla qualità (verde = ottima … rosso = blunder) e il commento. Gli errori restano più a lungo a schermo per capirli bene.
- 📄 Un documento di testo col commento completo, mossa per mossa.
Passo 1 — Registra i giocatori (una volta sola per persona). Scrivi al bot:
/giocatore mario Mario Rossi
Il bot ti risponde con un link d'invito. Mandalo a Mario: lui lo apre, il bot gli dice «sei collegato», e da quel momento riceverà le sue analisi. (Telegram permette al bot di scrivere a una persona solo se quella ha aperto il bot: per questo serve l'invito.)
Passo 2 — Manda una partita. Scrivi il comando con i due giocatori e il risultato, e a capo incolli le mosse (oppure un PGN completo copiato da un sito/app):
/analizza mario luigi 1-0
1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Ba4 Nf6 ...
Il risultato è uno tra 1-0 (vince il Bianco), 0-1 (vince il Nero), 1/2-1/2 (patta).
Il bot risponde subito «⏳ Ricevuto! Sto analizzando…» e dopo qualche minuto invia le analisi.
Passo 3 — Ricevi i risultati. Ogni giocatore collegato riceve in privato le sue 4 cose. Se un giocatore non si è ancora collegato, la sua copia arriva a te, così non si perde niente.
| Comando | Chi | Cosa fa |
|---|---|---|
/giocatore <id> <nome> |
tu | crea un giocatore e genera il link d'invito. Es: /giocatore mario Mario Rossi |
/start <token> |
i giocatori | apre il link d'invito e collega la loro chat |
/analizza <idBianco> <idNero> <risultato> + mosse |
tu | analizza una partita e la invia |
/profilo <id> |
tu | mostra la scheda di un giocatore |
/help |
tu | elenca i comandi |
Note utili:
- L'id del giocatore è un'etichetta corta a tua scelta (es.
mario), serve a ritrovarlo. - Puoi incollare anche un PGN completo con i tag
[White ...] [Black ...] [Result ...]: Metis legge nomi e data dai tag (il risultato però è quello che scrivi nel comando). Eventuali commenti e varianti nel PGN vengono ignorati: si analizza la linea principale.
Tu non tieni il bot sempre acceso: lo avvii quando devi mandare le partite e lo spegni quando hai finito. Dal terminale, nella cartella del progetto:
bash deploy/start.shQuesto accende tutto il necessario (il motore di commento AI, e il bot) e resta in ascolto. Manda pure le tue partite; quando vedi «Analisi completata» puoi chiudere con Ctrl-C.
- Linguaggio/build: Java 17, Maven. Fat-jar:
target/metis.jar(maincom.metis.app.Main). - Motore: binario Stockfish esterno, pilotato via protocollo UCI.
- Database: Supabase (PostgreSQL); lo schema viene creato da zero da Flyway all'avvio.
- Commenti: commentatore a regole deterministico (sempre corretto), discorsivo e didattico (spiega l'errore e l'alternativa, non solo le percentuali) + opzionale riformulatore Ollama (LLM locale).
- Media: video MP4 (frame Java2D montati con ffmpeg) e scheda PNG (Java2D).
com.metis
├─ app/ bootstrap: Config (variabili d'ambiente), Main (wiring + shutdown hook)
├─ engine/ UciEngine (UCI/Stockfish), Eval, EngineConfig
├─ analysis/ GameAnalyzer, MoveScoring, MoveJudge, BoardFacts, Notation, Analysis (modello)
├─ opening/ OpeningBook (tabelle aperture Lichess)
├─ comment/ Commentator, RuleCommentator, OllamaCommentator
├─ render/ BoardRenderer + GameVideoRenderer (video), ProfileCardRenderer (scheda PNG)
├─ store/ Db (Hikari + Flyway), *Repository (interfaccia + JDBC), StatsCalculator
└─ bot/ MetisBot, AnalysisService, GameInputParser, MessageFormatter, InviteService, Notifier
Le dipendenze puntano verso il dominio; l'accesso ai dati passa per interfacce (repository); motore, connessioni DB e processi esterni sono sempre chiusi (try-with-resources / shutdown hook).
| Libreria | Versione | Note |
|---|---|---|
com.github.bhlangonijr:chesslib |
1.3.6 | modello scacchi (SAN/FEN/PGN). Su JitPack, non su Maven Central. |
org.postgresql:postgresql |
42.7.11 | driver JDBC |
com.zaxxer:HikariCP |
7.0.2 | pool di connessioni (Java 17+) |
org.flywaydb:flyway-core + flyway-database-postgresql |
12.8.1 | migrazioni. Il modulo PostgreSQL è separato e obbligatorio dalla v10. |
com.github.pengrad:java-telegram-bot-api |
10.0.0 | bot Telegram (API pulita per long-polling) |
org.slf4j:slf4j-api + slf4j-simple |
2.0.18 | logging |
org.junit.jupiter:junit-jupiter |
6.1.0 | test (baseline Java 17) |
Il fat-jar (maven-shade-plugin) usa il ServicesResourceTransformer così che i file
META-INF/services del driver JDBC e dei plugin Flyway vengano uniti correttamente.
Tutto da variabili d'ambiente, lette nella classe Config. Le variabili obbligatorie sono
validate all'avvio: se ne manca una, Metis si ferma con un messaggio chiaro. Vedi .env.example.
| Variabile | Obbligatoria | Default | Significato |
|---|---|---|---|
METIS_BOT_TOKEN |
sì | — | token del bot (@BotFather) |
METIS_ADMIN_ID |
sì | — | il tuo id numerico Telegram (unico autorizzato) |
METIS_DB_URL |
sì | — | URL JDBC del Session Pooler Supabase (con ?sslmode=require) |
METIS_DB_USER |
sì | — | utente Session Pooler, es. postgres.<project-ref> |
METIS_DB_PASSWORD |
sì | — | password del database |
METIS_STOCKFISH_PATH |
no | stockfish |
percorso/comando del binario Stockfish |
METIS_FFMPEG_PATH |
no | ffmpeg |
percorso/comando di ffmpeg (per il video) |
METIS_ANALYSIS_DEPTH |
no | 16 |
profondità di ricerca di Stockfish |
METIS_ANALYSIS_MOVETIME_MS |
no | 0 |
tempo fisso per mossa in ms (0 = usa la profondità) |
METIS_ENGINE_THREADS |
no | 1 |
opzione UCI Threads |
METIS_ENGINE_HASH_MB |
no | 128 |
opzione UCI Hash (MB) |
METIS_LLM_ENABLED |
no | false |
attiva il riformulatore Ollama |
METIS_OLLAMA_MODEL |
no | llama3.1 |
modello Ollama quando l'LLM è attivo |
I segreti non vanno mai committati: i valori reali stanno in un file .env (git-ignored);
.env.example contiene solo i segnaposto.
Usa la stringa del Session Pooler dalla dashboard (Connect → Session pooler): è compatibile
IPv4 e supporta i prepared statement (a differenza del Transaction Pooler sulla 6543, e della
connessione diretta solo-IPv6). Nel .env la URL JDBC va senza utente/password (passati a parte):
METIS_DB_URL=jdbc:postgresql://aws-0-<regione>.pooler.supabase.com:5432/postgres?sslmode=require
METIS_DB_USER=postgres.<project-ref>
METIS_DB_PASSWORD=<password>
Nessun SQL manuale: all'avvio Metis applica la migrazione V1__init.sql e crea lo schema. Se lo
schema public contiene già altri oggetti, Metis fa il baseline a versione 0 e applica comunque
V1 (le create ... if not exists la rendono idempotente; i riavvii non duplicano nulla).
mvn -q -DskipTests package # crea target/metis.jar
mvn -q test # esegue gli unit test (nessuna rete/Stockfish/DB necessari)Esiste anche un test d'integrazione manuale (LiveSmokeIT) che esercita tutta la pipeline contro
Stockfish, Supabase e Ollama reali; è escluso da mvn test e si auto-salta senza .env:
set -a; . ./.env; set +a
mvn test -Dtest=LiveSmokeIT -DfailIfNoTests=false -Dsurefire.useFile=falsePer sviluppare sul progetto, vedi
CLAUDE.md: comandi rapidi, convenzioni e i "fatti critici" da non far regredire (chesslib su JitPack, en passant, modulo Flyway Postgres, video viaimage2pipe, API pengrad, riformulazione selettiva).
deploy/start.sh è pensato per l'uso a richiesta su un Mac: avvia Ollama (se l'LLM è attivo),
ne riscalda il modello, ricostruisce il jar se manca o se i sorgenti (src/main/pom.xml)
sono più recenti del jar (così a ogni avvio parte l'ultima versione del codice), poi esegue il bot
in foreground. Prerequisiti (Homebrew):
brew install stockfish
brew install ffmpeg
brew install --cask ollama # app ufficiale; la *formula* Homebrew 0.30.x è incompleta
ollama pull llama3.1:8b # solo se vuoi l'LLMIn alternativa, esegui il jar dopo aver caricato il .env:
set -a; . ./.env; set +a
java -jar target/metis.jarSe ffmpeg manca, l'analisi funziona lo stesso: viene saltato solo il video.
Il RuleCommentator produce commenti deterministici e sempre corretti dai fatti dell'analisi.
Lo stile è discorsivo e didattico: per imprecisioni, errori e blunder non si limita alle
percentuali ma le traduce in stati di posizione a parole (vincente / di vantaggio / equilibrata
/ di svantaggio / compromessa) e spiega cosa era meglio e perché — la mossa del motore e lo stato
che avrebbe preservato. Esempio:
Errore. Le tue probabilità di vittoria scendono dal 80% al 55%: da una posizione di vantaggio a una posizione equilibrata. Era meglio Nf3 (d5 e3), che avrebbe mantenuto una posizione di vantaggio.
Tutto è derivato solo dai fatti già presenti nell'analisi: nessun fatto scacchistico viene inventato. Le mosse ottime/buone e quelle di teoria restano concise (compaiono su ogni riga del documento mossa per mossa).
Se METIS_LLM_ENABLED=true, l'OllamaCommentator riformula quel commento in 2-3 frasi più
scorrevoli, con tono da allenatore (POST a http://localhost:11434/api/generate, temperature 0 per
restare fedele), con fallback automatico al commento a regole su qualsiasi errore.
Riformulazione selettiva: l'LLM viene usato solo sui commenti sostanziosi (imprecisioni, errori, blunder); quelli brevi e netti («Ottima, la scelta migliore.», «Mossa di teoria (…)») restano testuali. Il prompt vincola il modello a non inventare nulla (mosse, pezzi, minacce, numeri): riformula soltanto il commento a regole. Questo, insieme alla selettività, evita che il modello «ricami» fatti inventati e riduce molto il numero di chiamate (quindi i tempi). Per la massima sicurezza factuale puoi comunque tenere l'LLM spento.
BoardRenderer disegna ogni posizione con Java2D (scacchiera verde, coordinate, pezzi in stile
Staunton dal set cburnett, mossa evidenziata, re sotto scacco cerchiato, didascalia con
mossa, badge colorato per classe, variazione di probabilità e commento). I pezzi sono 12 PNG
bundlati in src/main/resources/pieces/cburnett/, decodificati e scalati una volta in cache (con
fallback a lettere se mancano); il frame è reso a risoluzione doppia (supersampling 2×) e poi
ridotto a 720×900, per contorni dei pezzi e testo più nitidi. GameVideoRenderer invia i frame a
ffmpeg via image2pipe a FPS fisso, ripetendo ogni frame per la sua durata (i blunder durano di
più). Così la durata del video è esattamente numero_frame / fps (niente trucchi del
concat-demuxer, che su ffmpeg 8.x raddoppiavano l'ultima durata). Output: H.264 720×900 yuv420p
(libx264 -preset slow -tune stillimage -crf 18), compatibile con Telegram.
Crediti pezzi. Il set cburnett è opera di Colin M.L. Burnett, usato sotto licenza 3-clause BSD (vedi
src/main/resources/pieces/cburnett/LICENSE.txt).
PNG largo 820 px con tema scuro: intestazione, barra impilata vittorie/patte/sconfitte, barra di precisione, barre per fase (con la fase più debole evidenziata), punti per colore e storico aperture. Nessun testo viene troncato: i nomi delle aperture vanno a capo su più righe, il nome del giocatore si auto-riduce per restare nei margini e l'altezza della scheda è dinamica (cresce col contenuto, con un minimo). Inviata come foto; se la generazione fallisce, si ripiega sul profilo testuale.
Tutte le statistiche sono ricalcolate dai dati (game + moves) a ogni richiesta, quindi si
affinano con le partite. Il profilo include: record V/P/S, precisione media, ACPL medio, livello
stimato (clamp(600, 2700, 2500 − 13·ACPL) arrotondato a 50), rendimento per fase con la fase più
debole, ripartizione punti per colore, scontri diretti, e lo storico aperture (le più giocate e
le meno giocate, con record V/P/S per ciascuna).
Se invece volessi tenerlo sempre acceso su una VM Linux (es. Oracle Cloud Always Free, ARM), usa
deploy/metis.service (systemd, Restart=always): metti il jar in /opt/metis, i segreti in
/etc/metis.env (permessi 600), installa Stockfish/ffmpeg via apt, poi
systemctl enable --now metis. chesslib, PostgreSQL, HikariCP e la libreria Telegram sono Java puro;
l'unico binario dipendente dall'architettura è Stockfish (build ARM disponibile via apt).
- Fedeltà LLM: anche a temperature 0 un modello piccolo può «arricchire» i commenti; per questo la riformulazione è selettiva ed esiste sempre il fallback a regole. Il commento a regole è la fonte di verità.
- Segreti: se token o password vengono esposti, ruotali (@BotFather e Supabase) e aggiorna
il
.env.
Implementato: analisi UCI, commenti (regole + LLM selettivo), riconoscimento aperture, profilo con storico aperture, scheda PNG, video animato. Possibili sviluppi futuri: temi/orientamento scacchiera configurabili, riconoscimento aperture ancora più ricco.