Visão geral
Sexto jogo do portfólio, terceiro numa linguagem que não é nativa de navegador (depois dos dois em Lua via fengari). Campo Minado clássico: toque pra revelar, marque bandeira nas células que você acha que são mina, limpe o tabuleiro sem detonar nenhuma. Primeiro clique nunca é mina, convenção padrão do gênero.
O diferencial técnico aqui não é Lua, é Pyodide: Python de verdade
compilado pra WebAssembly. E o ponto central do projeto é mais forte que "lógica parecida rodando
nos dois lados": logica/campo_minado.py é o mesmo arquivo, byte a byte, usado pelo pytest
localmente e importado de verdade dentro do navegador. Sem tradução, sem reimplementação em
JavaScript.
Arquitetura
web/main.js busca o texto de logica/campo_minado.py via fetch, escreve ele no sistema de
arquivos virtual do Pyodide (pyodide.FS.writeFile) e roda import campo_minado de dentro do
Python que está executando na VM WASM. É o Python nativo importando o módulo, exatamente como o
pytest faz — a única diferença é que um roda no terminal e o outro dentro do navegador.
web/ponte.py fica no meio: recebe a coordenada do clique, chama revelar ou alternar_bandeira
da lógica pura, devolve o estado inteiro como JSON (json.dumps). O JavaScript só dá
JSON.parse e desenha. Essa fronteira em texto simples evita depender da conversão automática de
tipo complexo do Pyodide (frozenset, tupla) pro lado JS, o tipo de mágica que funciona até não
funcionar mais.
Minas são sorteadas só no primeiro clique (_colocar_minas), evitando a célula clicada e os
vizinhos dela, e a revelação em cascata quando uma célula não tem mina vizinha usa flood fill
clássico baseado em pilha.
O CDN oficial do Pyodide (cdn.jsdelivr.net) ficou bloqueado pelo proxy deste ambiente de
desenvolvimento, então o runtime (~13MB: WASM + biblioteca padrão do Python) foi vendorizado
direto no repositório em vez de carregado externamente.
Testes
14 testes com pytest: primeiro clique nunca é mina (30 sementes diferentes), sorteio de minas
no primeiro clique, derrota ao revelar mina, cascata em célula com 0 minas vizinhas, jogada
ignorada em célula sinalizada ou depois de derrota, contagem de bandeira restante,
reprodutibilidade com a mesma semente e validação de número de minas inválido. Testado também de
ponta a ponta com Playwright: cascata revelando várias células de um clique, bandeira pelo botão
de modo e pelo clique direito, novo jogo resetando de verdade, e uma partida jogada até a derrota.
Stack
Python 3 (via Pyodide/WebAssembly), zero framework, zero build. HTML/CSS estático mais o JS mínimo que só cuida de DOM e bootstrap do Pyodide.