PyQuest — mini-giochi a griglia con Python (motore, renderer, mondo pilota, editor) - #15
Merged
Conversation
Fase 1 (M1), step 1-4 della spec PyQuest. - config.py: espone Config.NODE_ID (node_id grezzo) in set_id(), così pyquest.py può fare notify() sullo stesso div che bryBridge ascolta. - bryBridge.ts: RunOptions.onCustom + BryNotifyDetail.type ora string; nel switch un ramo default inoltra gli eventi fuori protocollo (D3). Retrocompatibile: PyRunner/SQLRunner invariati. - pyquest.py: modello logico puro (World/Hero), _load_level che riassegna il global _world (no contaminazione tra run del modulo cacheato), move/turn, sensori is_blocked/on_goal, action_guard + StepLimitError, contatore sensori, win spec «reach». Emissione eventi come payload JSON (D4). - types.ts: tipi normativi della trace (GameEvent/SceneState/LogLine) + schema dati LevelDef/WorldDef. - runLevel.ts: compone il preCode (from pyquest import * + _load_level + shadow di input) con JSON compatto spogliato dei campi testuali (D2/D7); riparsa gli eventi via onCustom. Logica del motore validata con CPython (stub di config/py_back_trace): move/turn/sensori, vittoria reach, no-op post-vittoria, assenza di contaminazione tra run, step_limit. Bridge Brython/DOM riusa l'infrastruttura PyRunner esistente. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pv1PzGn4LtwKFku39y15V4
Fase 1 (M1), step 5 della spec PyQuest. - index.tsx: prima versione debug del componente PyQuest — editor riusato da PyRunner + bottone Esegui + dump grezzo degli eventi e della console. Contatore di modulo per il suffisso del codeId (D12), così due istanze sulla stessa pagina restano indipendenti. Verrà riscritto allo step 12 (renderer + player). - pyquest-test.mdx: pagina unlisted /docs/pyquest-test con due harness e la checklist di verifica manuale (movimento, indipendenza, errore, vittoria). - MDXComponents.tsx: registra <PyQuest> tra i componenti MDX globali. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pv1PzGn4LtwKFku39y15V4
Test nel browser (Brython 3.12): settrace emette line events solo nei frame di funzione; il codice a livello modulo/exec (dove gira il codice studente) non genera line events, frame.f_trace è inefficace e il raise dal tracer non è contenibile in modo affidabile. Decisione documentata in testa a pyquest.py: rischio residuo `while True: pass` accettato, come oggi in PyRunner. Pagina spike temporanea eliminata. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9dpQX4sKY6tJkTetJz3ho
…ittoria Review fase 1: a mondo congelato (dopo win) move/turn_* ritornavano prima di _action_guard(), quindi 'while True: move()' dopo la vittoria ciclava su no-op senza far avanzare nessun contatore, bloccando il main thread. Ora la guardia scatta sempre: le azioni post-win restano no-op ma costano un passo e il loop muore con StepLimitError (trace: win seguito da step_limit). Minore: makeCodeId con hash base36 a larghezza fissa (padStart 7) per rendere impossibile la collisione teorica della concatenazione hash+contatore. Verificato con test CPython (19/19, stub config/py_back_trace) e live nel browser: scenari a/b/c dello step 5, loop post-win che termina con messaggio didattico, indipendenza tra istanze, smoke PyRunner senza regressioni. typecheck/lint/build verdi. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9dpQX4sKY6tJkTetJz3ho
Plugin Docusaurus `pyquest` che carica e valida a build-time i mondi in static/pyquest/<world>/world.json (schema versionato, guardia quoting sui campi iniettati nel motore Python). GameScene renderizza la griglia e i personaggi, GamePlayer anima la trace step-by-step, applyEvent.ts deriva lo stato del mondo da ogni evento. PyQuest/index.tsx orchestra editor, esecuzione (runLevel) e animazione con la macchina a stati idle → executing → animating → finished, esito won > failed > error. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9dpQX4sKY6tJkTetJz3ho
Step 13 — motore completo (static/bry-libs/pyquest.py): - classi Enemy/Resource; tick nemici deterministico (zero RNG): static attacca se adiacente, melee attacca o fa un passo greedy verso l'eroe (asse col delta maggiore, a parità x, poi l'altro asse, altrimenti fermo; niente sovrapposizioni tra nemici o con l'eroe); - attack()/collect(); sensori enemy_ahead/on_resource/health/inventory; move() e is_blocked() ora trattano il nemico come blocco (bump); - GameOverError (sollevata dopo l'evento death → la precedenza esito won>failed>error mostra la UI di sconfitta, non il pannello errore); - win collect (inventory>=qty) e defeat (tutti i target, o count; guard len>0 anti-vittoria-fantasma); - guardia _action_guard PRIMA del check won anche in attack/collect (emendamento D7). Step 15 — HintPanel.tsx (reveal progressivo, stato in memoria) + slot "Spiegamelo facile" cablato in index.tsx (buildExplainText + copyToClipboard riusati da PyRunner). Step 16 — useProgress.ts (chiave versionata pdb:pyquest:progress, parse difensivo con reset, sync same-tab via CustomEvent + storage cross-tab sul pattern di usePref; recordWin minimizza bestSteps, isUnlocked calcolato; countActions = move/turn/bump/attack/collect fino al win; starsFor D11) + pannello vittoria con stelle e "nuovo record" + striscia "Completato · record N" persistente + stili in PyQuest.module.css. Step 14 (scena combat) era già coperto dalla fase 2 (applyEvent + GameScene gestivano già gli eventi combat/collect, HUD PV/inventario e fx): nessuna modifica, solo verifica. Dati: livello di collaudo "duello" (bug melee + gemma, win defeat+collect, par 7) in mondo-01/world.json; tolto il fantasma decorativo dal livello 2. Harness docs/pyquest-test.mdx aggiornato (sezione combat + note h/i/j). Verifica: motore validato sotto CPython (trace del duello deterministica su 3 run, win a 7 azioni; morte -> death+GameOverError; while True su livello senza nemici -> step_limit; reach win). typecheck+lint+build verdi. Verifica visiva nel browser (Chrome): combat, raccolta, stelle, persistenza del record al reload, hint progressivi, dark mode. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9dpQX4sKY6tJkTetJz3ho
…render (review fase 3)
Review del codice della fase 3. Motore validato sotto CPython (19 check
verdi: determinismo del duello, morte, step limit post-win, guard
sensori, defeat count/kind, nessuna vittoria fantasma né sovrapposizioni
melee): nessun bug lato Python. Correzioni tutte lato UI:
- «nuovo record!» compariva a ogni pareggio: recordWin scrive il best
PRIMA del render del pannello vittoria, quindi il confronto col best
salvato era sempre vero eguagliando il proprio record. Ora recordWin
decide (e restituisce) se il run è un record — prima vittoria o
miglioramento stretto — e index.tsx lo tiene in uno stato catturato
al momento della vittoria.
- La striscia «Completato · record N» compariva all'inizio
dell'animazione della prima vittoria (recordWin scatta in finishRun),
rivelando l'esito in anticipo: durante l'animazione ora è visibile
solo se il livello era già completato all'avvio del run.
- GamePlayer: al re-run il pannello di esito restava visibile per tutto
il replay — l'effect su playKey resettava il cursore DOPO che l'effect
di onStepChange aveva notificato il cursore stantio del run precedente
(già a fine trace) con la trace nuova, e il genitore chiudeva subito
l'animazione. Reset spostato in render (pattern «adjust state during
render»).
- Plurali al valore 1: «1 stella su 3» (aria-label) e «1 azione».
- HintPanel con key={level.id}: i suggerimenti rivelati ripartono da
zero al cambio livello (anteprima live dell'editor, fase 4).
Verifica: typecheck+lint+build verdi. Verifica visiva nel browser sul
livello duello: prima vittoria con «nuovo record!» e striscia solo a
fine animazione; pareggio senza «nuovo record!» né pannello anticipato;
striscia e record persistenti al reload; console senza warning.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9dpQX4sKY6tJkTetJz3ho
…export/import (fase 4)
Nuova pagina interna /level-editor (noindex, non in navbar): disegna un livello
con palette+paint click&drag, ridimensiona la griglia, compila i metadati
(vittoria, nemici, risorse, hint, codice iniziale), lo vede girare
nell'anteprima live <PyQuest levelData={draft} /> e ne esporta/importa il JSON
da incollare in static/pyquest/<mondo>/world.json. Validazione client che
ricalca le regole del plugin build-time. Esporta FACING_DEG da GameScene e
ENEMY_KINDS/RESOURCE_KINDS da characters perché l'editor li riusa nei menu;
index.tsx ora resetta lo stato di gioco quando cambia l'identità del livello
o dello starterCode a runtime, per l'anteprima live dell'editor.
Verificato nel browser (build pulita, paint/resize/validazione, esecuzione
codice nell'anteprima con combat, round-trip export→import esatto, nessun
errore console).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9dpQX4sKY6tJkTetJz3ho
Aggiunge la pagina galleria (src/pages/pyquest/) con lucchetti sui livelli non ancora sbloccati e stelle per livello, più /pyquest/gioca che dopo la vittoria propone la navigazione al livello successivo tramite la nuova prop onWin di PyQuest (callback a fine animazione, dopo il salvataggio del progresso). Rinomina e ribilancia i livelli del mondo pilota mondo-01 (nomi definitivi, nuovo livello sul for/range, ordine gemma-poi-bug nel duello) e allinea la pagina di collaudo pyquest-test.mdx alle nuove soluzioni/hint. Aggiunge la voce di navbar «PyQuest». Rimuove game.py/grid.py (libreria canvas non più usata) e i relativi riferimenti in brython_runner.py.
…list manuale Trovate provando il mondo pilota end-to-end nel browser (light + dark, desktop e viewport stretto) e ricontrollando le solution con un harness CPython. - «Livello successivo» usava `history.push` su un path assoluto, che non antepone il baseUrl come fa <Link>: portava alla 404. Ora il path passa da useBaseUrl. - In dark mode muro (#2b3245) e pavimento (#1e2230) stavano a 1,3:1 di contrasto e la mappa era di fatto illeggibile. Nuova coppia a ~3:1, muro chiaro su pavimento scuro (l'inverso del tema light). - Il pannello d'errore ripeteva per intero il traceback già mostrato nella console del player: ora ne tiene solo l'ultima riga (tipo + messaggio) e rimanda alla console per il dettaglio con la riga esatta. - Commenti degli starterCode senza accenti nel world.json («gia», «e» per «è»). - Target del transport a 34px: su puntatore coarse salgono a 44px. - I global data del plugin includevano `solution` di ogni livello, che finisce nel bundle main.js servito a ogni pagina del sito e non viene mai letta dal client. Ora viene spogliata. Il world.json statico resta accessibile: non è una protezione, è peso in meno e la risposta non arriva insieme al livello. Aggiunte /docs/pyquest-test e /level-editor all'indice /test. Verificato: le 5 solution vincono in esattamente `par` azioni (6/7/7/5/6) e nessuno starterCode vince per sbaglio; vittoria con stelle e record, morte, step limit, precedenza won > failed > error, errore runtime con trace parziale animata, print interleaved, seek e step-back, hint progressivi, sblocco sequenziale e persistenza dopo reload, due istanze indipendenti sulla stessa pagina, roundtrip dell'editor identico all'originale. typecheck, lint e build verdi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uYRFDbz7dZiMnZ9GeUXWj
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uYRFDbz7dZiMnZ9GeUXWj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chiude l'implementazione di PyQuest (spec esecutiva, fasi 1–5 / step 1–21). Milestone PM
m8.Cos'è
Componente riutilizzabile per mini-giochi a griglia: lo studente scrive Python vero, eseguito in-pagina da Brython, che muove Byte il droide su una griglia, combatte nemici e raccoglie risorse. L'esecuzione produce una trace di eventi che React anima passo per passo, con player a velocità regolabile.
/pyquest(voce in navbar) con sblocco sequenziale, stelle e record per livello.mondo-01: cinque livelli a curva didattica — sequenza →for→ sensori ewhile→attack()→ combinato (melee + risorsa + vittoria multipla)./level-editor: pubblico, non linkato enoindex, con anteprima live e import/export JSON.<PyQuest world="…" level="…" />, registrato tra i componenti globali.Architettura (decisioni della spec)
static/bry-libs/pyquest.py): il codice studente gira davvero, per intero e sincrono, contro un modello logico in puro Python; ogni azione emette subito un evento. I sensori funzionano perché leggono lo stato aggiornato; seek, step-back e replay sono gratis perché la scena è derivata dalla trace.preCode(nessun ramo template inbrython_runner.py), payload eventi come stringa JSON,onCustomaggiunto abryBridge(~6 righe, retrocompatibile).GameSceneconsuma loSceneStatederivato, con identità DOM stabile (nemici morti e risorse raccolte non vengono smontati).localStorage(pdb:pyquest:progress, versionata, parse difensivo); sblocco calcolato, mai salvato.Verifica
npm run typecheck && npm run lint && npm run buildverdi. Checklist manuale completa nel browser (light e dark, desktop e viewport stretto):solutionvincono in esattamenteparazioni (6/7/7/5/6), verificate anche con un harness CPython; nessunostarterCodevince per sbaglio;won > failed > error;print()interleaved allo step corretto;L'ultimo commit corregge sei difetti emersi proprio da questa checklist: 404 del bottone «Livello successivo» (baseUrl), contrasto muro/pavimento in dark a 1,3:1, traceback duplicato nel pannello d'errore, accenti mancanti nei commenti degli
starterCode, target touch sotto i 44px esolutionspedita nel bundle di ogni pagina.Note
while True: passsenza chiamate API blocca il main thread, come oggi in PyRunner. Lo spike susys.settrace(step 6) ha mostrato che in Brython 3.12 il codice a livello modulo — cioè quello dello studente — non genera line events, quindi la guardia non vedrebbe il caso canonico. Motivazione completa in testa apyquest.py. I loop che chiamano qualcosa restano coperti da step limit e contatore sensori.static/bry-libs/grid.pyegame.py(superseded) e le tre regex orfane inbrython_runner.py./nuovo-capitolo.🤖 Generated with Claude Code
https://claude.ai/code/session_015uYRFDbz7dZiMnZ9GeUXWj