football-manager · motor de simulação
Um motor de carreira escrito em Python que simula temporadas inteiras de futebol com um modelo de Poisson calibrado contra distribuições reais. Abaixo ele roda no seu navegador: simule uma temporada, depois rode o portão de calibração — milhares de temporadas medidas contra onze faixas de futebol de verdade.
Returno duplo, 380 partidas, três pontos por vitória. As forças dos clubes são as do repositório. A mesma semente sempre devolve a mesma temporada — o motor é determinístico.
| # | Clube | P | J | V | E | D | GP | GC | SG | Força |
|---|
Esta é a peça central do projeto. O motor roda milhares de temporadas e compara onze métricas com faixas de futebol real. Quebrar um alvo é build vermelho. Se o melhor elenco é campeão em 90% das temporadas, o jogo é uma planilha; se é em 10%, é um dado.
Rode o portão para ver a distribuição de títulos.
Poisson com diferença de força saturada. Três constantes são o coração do jogo — mexer nelas exige rodar o portão de novo.
// fm/match.py — reproduzido linha a linha nesta página z = (ovr_efetivo - 68) / 20 d = tanh((z_casa - z_fora) / 1.20) ← a saturação lambda_casa = gols_base * exp(+0.58*d + mando/2) lambda_fora = gols_base * exp(-0.58*d - mando/2) gols ~ Poisson(max(lambda, 0.34)) ← ninguém é nulo
Sem o tanh, um gap de 26 pontos de overall gera 22% de partidas com 3+ de
margem, contra ~14% no futebol real. A saturação reproduz o que acontece de verdade:
time goleando administra o resultado, time perdendo se fecha.
A versão com mistura Gamma foi testada e o otimizador empurrou a dispersão para zero. Toda a variância vem de causa com nome — desgaste, moral, forma, clássico, mando. O jogador entende “meu time estava morto”; não entende “o multiplicador deu 0,6”.
O lado fraco nunca cai abaixo de ~0,60 gol esperado. Em 82 contra 56, o pequeno ainda vence 9,3% das vezes e o jogo ainda termina 0-0 em 5%.
Não existe if country == "BRA" em lugar nenhum. As três ligas rodam com as
mesmas constantes de motor. O que muda é a distribuição de força dos clubes, declarada no
arquivo da liga — e daí emergem equilíbrio, empates, pontuação do campeão e
previsibilidade do título.
| Liga | Campeão | Lanterna | Empate | Gols/jogo | Margem 3+ | Maior é campeão |
|---|---|---|---|---|---|---|
| Série A | 73,8 | 32,1 | 25,5% | 2,52 | 13,3% | 36,1% |
| Série B | 68,7 | 35,2 | 26,8% | 2,37 | 11,5% | 16,0% |
| La Liga | 79,2 | 30,4 | 24,6% | 2,61 | 14,4% | 41,1% |
O caso mais bonito é a Série B: mesmos parâmetros de motor, elencos quase equivalentes, e o resultado é campeão com 68 pontos e o melhor elenco do papel levando o título em apenas 16% das temporadas. A Série B ser maluca não foi programado — é consequência de a liga ser achatada.
Esta página reimplementa fm/match.py em JavaScript para rodar sem instalar
nada. O motor real gera elencos completos por clube e usa o overall do onze titular; aqui
a força declarada do clube entra direto. Medido em 400 temporadas, a diferença fica dentro
do ruído de Monte Carlo: Série A com campeão em 73,7 contra 73,8 do motor real, 2,52
gols por jogo nos dois, margem 3+ em 13,3% nos dois.
As faixas dos alvos e as forças dos clubes são estimativa editorial marcada
verificado = false no repositório, não dado conferido. Re-derivar com as
tabelas reais é o primeiro item da lista de pendências.