Um jogo de xadrez contra um bot, escrito em TypeScript.
Zugzwang — do termo enxadrístico alemão para a situação em que qualquer lance disponível piora a própria posição — é um projeto de portfólio de longo prazo: um jogo de xadrez jogável contra um bot, construído em fases, do zero.
O foco não é só o produto final, mas o processo: arquitetura limpa, testes, convenções de commit/branch/PR e evolução incremental documentada.
- Monorepo (pnpm workspaces). Engine, servidor e cliente compartilham tipos e ferramentas (TypeScript, ESLint, Prettier) e evoluem juntos, sem o atrito de repositórios separados.
chess.jsisolado num wrapper. As regras do xadrez são consumidas apenas através da classeChessEngine(empackages/engine). Nenhum outro pacote importachess.jsdiretamente, o que permite trocar ou complementar a biblioteca de regras no futuro sem refatorar o resto do código.- Separação engine / server / web. A lógica de jogo e do bot vive no
engine, agnóstica a HTTP e a UI. Oserverexpõe; owebdesenha. - TypeScript estrito e testes desde o início. Toda a base já roda com
strictligado eanyproibido por lint.
| Pacote | Tecnologia | Papel |
|---|---|---|
@zugzwang/analysis |
TypeScript | Contratos, parser UCI e política de qualidade |
@zugzwang/engine |
TypeScript, chess.js, Vitest | Regras do xadrez e bot minimax |
@zugzwang/server |
TypeScript, Express, Stockfish 18/WASM | API de jogo e fila assíncrona de análise profunda |
@zugzwang/web |
TypeScript, React, Vite, Stockfish/WASM | Tabuleiro, histórico, revisão e fallback local |
Ferramentas transversais: pnpm workspaces, ESLint, Prettier.
git clone https://github.com/mateusands/Zugzwang.git
cd Zugzwang
pnpm installNo navegador (web + server juntos):
pnpm dev
# abra http://localhost:5173 e jogue clicando nas peçasNo terminal (sem navegador):
pnpm --filter @zugzwang/engine playpnpm --filter @zugzwang/web dev # cliente Vite em http://localhost:5173
pnpm --filter @zugzwang/server dev # API em http://localhost:3000
pnpm --filter @zugzwang/engine test # testes do engineVerifique a API com:
curl http://localhost:3000/health
# {"status":"ok","service":"zugzwang-server"}O servidor inicia um pool limitado do Stockfish 18 completo para revisar
partidas em jobs assíncronos. Os resultados e o cache sobrevivem a reinícios em
.data/analysis.json; se o pool não puder iniciar, a revisão continua com o
motor lite do navegador. O orçamento pode ser ajustado sem alterar código:
ANALYSIS_POOL_SIZE=2 ANALYSIS_HASH_MB=512 ANALYSIS_MAXIMUM_DEPTH=30 pnpm --filter @zugzwang/server devANALYSIS_POOL_SIZE aceita 1–8 processos e, por padrão, sobe um motor por
núcleo livre (no máximo 6) — ou seja, um thread por motor. Como os jobs
buscam por profundidade fixa, thread extra quase não compra tempo: o lazy SMP
alarga a busca para chegar à mesma profundidade. Medido num lote de 7 posições
profundas numa máquina de 12 núcleos, mediana de repetições: 57s com 2 motores
de 5 threads, 21s com 4 de 2, 19s com 5 de 2 e 13s com 6 de 1.
ANALYSIS_HASH_MB define o hash total
dividido pelo pool e ANALYSIS_DATA_PATH troca o caminho da persistência. A
profundidade dos perfis pode ser calibrada com ANALYSIS_FAST_DEPTH (18),
ANALYSIS_DEEP_DEPTH (22) e ANALYSIS_MAXIMUM_DEPTH (26, até 40). Profundidades
altas crescem exponencialmente e são indicadas para uma máquina dedicada. A
saúde do motor está em GET /analysis/health; jobs usam
POST /analysis/jobs, GET /analysis/jobs/:id, SSE em
GET /analysis/jobs/:id/events e cancelamento em DELETE /analysis/jobs/:id.
pnpm build # builda todos os pacotes
pnpm test # roda todos os testes
pnpm lint # ESLint
pnpm format # Prettier --writezugzwang/
├── packages/
│ ├── engine/ # Regras (wrapper chess.js) + bot (minimax) + análise + CLI
│ ├── analysis/ # Contratos e política compartilhados da análise Stockfish
│ ├── server/ # API de jogo + jobs assíncronos de análise
│ └── web/ # Cliente React + Vite (tabuleiro, histórico e revisão)
├── tsconfig.base.json # Config TypeScript compartilhada
├── eslint.config.js # Lint compartilhado
├── pnpm-workspace.yaml
├── CLAUDE.md # Guia de contribuição / convenções do projeto
└── README.md
Concluído:
- Base local — monorepo, wrapper do engine, tooling.
- Regras completas — movimentos legais, xeque/mate/afogamento, roque, en passant, promoção, histórico/PGN.
- Bot com minimax — avaliação de posição e poda alfa-beta.
- Bot refinado — estrutura de peões, segurança do rei, controle de centro; ordenação de lances, profundidade adaptativa e tabela de transposição.
- Polish — desfazer, níveis de dificuldade, análise pós-jogo, CLI.
- Interação no navegador — API de jogo no server + tabuleiro jogável no web (drag/clique, dicas, anotações, sons, promoção, tela de fim, persistência).
- Takeback + navegação — desfazer o par de lances e navegar o histórico (◀▶, lista de lances clicável, teclado).
- Histórico de partidas — salvar partidas encerradas (localStorage) e revê-las lance a lance (replay).
- Motor de avaliação — Stockfish/WASM num Web Worker; barra de avaliação ao lado do tabuleiro (avaliação, melhor lance, probabilidade de vitória).
- Revisão de partida — classificação lance a lance, precisão, destaques e análise Stockfish full assíncrona no backend com cache persistente. A passagem rápida e o refinamento dos quatro lances mais críticos acontecem em segundo plano durante a própria partida; o encerramento conclui apenas o que ainda não chegou ao cache.
Próximo: treinador → bots com personalidade. Por fim, CI/CD e deploy.
Detalhes das convenções (commits, branches, PRs) estão no CLAUDE.md.
🚧 Em desenvolvimento ativo. Já é jogável contra o bot — no navegador
(pnpm dev) e no terminal (pnpm --filter @zugzwang/engine play) — com
histórico de partidas, barra de avaliação e revisão profunda com Stockfish.
Fases 1–10 concluídas; a próxima etapa é o treinador.
Distribuído sob a licença GNU GPL v3.
As peças do tabuleiro são o conjunto Cburnett, de Colin M.L. Burnett, obtido
do Wikimedia Commons
e multi-licenciado sob GPLv2+, BSD e CC BY-SA 3.0 — usamos sob a GPL. A arte vem
vendorizada em packages/web/src/pieceArt.ts (e não como asset externo, porque a
página é cross-origin isolated).
A análise de posições usa o Stockfish compilado
para WebAssembly (via nmrugg/stockfish.js),
que é software livre sob GPLv3 — por isso este projeto também adota a GPLv3.
O binário do motor não é versionado aqui: é obtido do pacote stockfish (npm).
As variantes lite são copiadas para packages/web/public/engine/ no
dev/build; o servidor executa a variante full diretamente do pacote.