// por dentro
Como foi construído
O Andante é um único andante.py de cerca de 4.200 linhas, só com a biblioteca padrão do Python 3.8+. O modo idle é um laço de ticks com velocidade ajustável (de 2 s a 0,1 s por passo), leitura de tecla sem bloqueio (msvcrt no Windows, termios + select no Linux), autosave a cada 10 ticks e progresso offline calculado pelo tempo desde o último acesso, limitado a 12 horas e com 50% de eficiência. O site é Next.js 16 e roda esse mesmo arquivo, sem alterações, no Pyodide (Python em WebAssembly) dentro de um Web Worker, com a saída desenhada no xterm.js. Um adaptador em Python (web_runtime.py) troca stdout, input, time.sleep, tamanho do terminal e leitura de teclas por versões que conversam com a página, e cada save é copiado para o localStorage. A versão gráfica usa o andante.py como motor e só desenha por cima com Pyxel.
- Jogo
- Python 3.8+ só com a biblioteca padrãocores ANSIleitura de tecla com msvcrt/termios
- Versão gráfica (protótipo)
- Pyxel 2.9.9sprites 16×16 escritos no próprio códigofonte PixelMplus10empacotada em .pyxapp
- Site
- Next.js 16.4React 19.3Tailwind CSS 4shadcn/ui com Base UITypeScript 5
- Terminal no navegador
- Pyodide 0.27.2 (jsDelivr) em Web Workerxterm.js 5.5 com addons fit e unicode11SharedArrayBuffer + Atomics
- Testes
- script de regressão com um teste por bug já corrigido e smoke test de todas as telasrodando em pasta temporária sem tocar nos saves
Arquitetura
O mesmo arquivo no terminal e no navegador
O site baixa o andante.py original e roda no Pyodide. Em vez de manter uma versão web, o web_runtime.py substitui só o que depende de um terminal de verdade (stdout, input, sleep, clear e a leitura de teclas), então qualquer mudança no jogo chega ao site copiando um arquivo.
Performance
input() bloqueante dentro de um Web Worker
O Python fica parado em input() e sleep e não consegue receber postMessage. O teclado vai por um anel de 8 KB num SharedArrayBuffer, com contadores de leitura e escrita; o worker dorme com Atomics.wait e acorda com Atomics.notify quando chega tecla. A saída volta em lotes por postMessage.
Segurança
Isolamento de origem para liberar o SharedArrayBuffer
O SharedArrayBuffer só existe com a página isolada (crossOriginIsolated). O next.config.ts envia Cross-Origin-Opener-Policy same-origin e Cross-Origin-Embedder-Policy require-corp em todas as rotas, e serve o andante.py como text/plain.
Dados
Progresso offline com limite e eficiência
Ao abrir o jogo, progresso_offline() calcula XP, ouro, níveis e compras a partir do tempo desde o último acesso, com teto de 12 horas e 50% do rendimento do jogo aberto. Um bônus do Altar aumenta esse teto.
UX
Save em texto que o jogador pode editar
O save é um .txt com campos legíveis, flags em checklist e inventário em lista. O mesmo formato é usado no terminal, no navegador (localStorage) e na versão gráfica, então o progresso passa de um para o outro.
Testes
Regressão por bug corrigido
Cada bug corrigido vira um teste no regressao.py, que importa o jogo numa pasta temporária, simula as teclas e confere o resultado. Um dos testes deixa o idle rodar 6.000 ticks sozinho e exige que ele não trave e vença os cinco chefes da torre.