Como o clube rende com a aba fechada sem derreter o celular
Oito horas offline não são simuladas segundo a segundo. Elas são calculadas de uma vez, e o resultado precisa bater com o de quem ficou com o jogo aberto.
O pedido que parece simples
Num jogo idle, a promessa central é esta: você fecha o jogo, vive sua vida, volta depois e o clube rendeu. É o que faz o gênero existir. E é onde mora um problema de engenharia que não aparece de cara.
O jogo roda avançando o estado em passos pequenos, de uns 100 milissegundos, porque é assim que a barra de progresso anda suave na tela. Agora faça a conta: oito horas são 28.800 segundos, ou seja, 288.000 passos. Se ao abrir o jogo a gente simplesmente rodasse o mesmo laço 288.000 vezes, o aparelho travaria por vários segundos numa tela branca, e em celular mais fraco o navegador mataria a aba.
Pior: quem ficou dois dias fora teria que esperar mais ainda. O tempo de abertura cresceria junto com a ausência, que é exatamente ao contrário do que o jogador espera, porque quanto mais tempo fora, maior a vontade de ver o resultado logo.
A saída é não simular
A resposta é calcular o resultado de uma vez, em tempo constante, sem iterar. Se um ciclo de produção dura 3 segundos e se passaram 8 horas, não é preciso contar os ciclos um a um: dá para dividir.
// a ideia, sem os detalhes
const ciclos = Math.floor((restante + dt) / duracao)
const ganho = porCiclo.times(ciclos)
const sobra = (restante + dt) % duracaoTrês linhas resolvem qualquer intervalo: cinco segundos ou dois dias custam o mesmo. Isso é o que a gente chama de avanço em tempo constante, e é a propriedade que torna o ganho offline possível sem penalizar quem ficou mais tempo fora.
A parte difícil não é a fórmula. É garantir que ela continue dando o mesmo resultado que o caminho longo, porque agora existem dois caminhos para o mesmo estado, e dois caminhos que divergem em silêncio são a pior categoria de bug de jogo: o jogador com a aba aberta e o jogador que voltou depois teriam saldos diferentes, e ninguém saberia qual está certo.
A prova é um teste de equivalência
O projeto tem um teste que faz exatamente isso: pega um estado, avança oito horas de uma vez, e depois pega o mesmo estado e avança 288.000 passos de 100 milissegundos. No fim, compara os dois.
E aqui aparece o detalhe que o post sobre números grandes já contava: os dois resultados não são idênticos. A contagem de ciclos é, porque ciclo é inteiro. Já o caixa difere na casa de 10^-15 relativos, porque somar 288.000 parcelas pequenas arredonda diferente de somar uma grande.
Ciclos são comparados com igualdade exata. Caixa é comparado com tolerância. Usar igualdade nos dois dá um teste que reprova código correto; usar tolerância nos dois deixa passar um erro de contagem de ciclo, que é um erro de verdade.
Duas regras que o teste cobra e não são óbvias
A primeira: a duração de um ciclo precisa ser um número inteiro de milissegundos. Se um negócio rendesse a cada 3,33 segundos, a divisão e o resto deixariam de fechar entre os dois caminhos, e a equivalência quebraria por um motivo que ninguém encontraria olhando o código.
A segunda: o tempo entra por parâmetro. Nenhuma linha do núcleo do jogo chama o relógio do sistema. Quem sabe que horas são é a camada de fora, que passa o instante para dentro. Isso soa burocrático até você precisar testar oito horas de jogo: com o relógio dentro do código, esse teste exigiria esperar oito horas.
A mesma regra vale para sorteio. Nada de aleatório solto: o mercado de jogadores e o resultado das partidas saem de um gerador semeado, o que faz o mesmo save produzir sempre a mesma sequência em qualquer aparelho. É o que permite, mais tarde, verificar no servidor se um ranking foi jogado ou inventado.
O teto, e por que ele é dito em voz alta
O acúmulo offline tem um limite de oito horas. Não é limitação técnica: é escolha de jogo, porque idle que rende para sempre sozinho deixa de ter motivo para você voltar.
O que importa aqui é como isso é apresentado. A versão inicial só mostrava o ganho, e quem ficou dois dias fora via um número que não batia com a ausência, sem explicação. Parecia bug. Hoje o relatório de volta diz as duas coisas: quanto foi creditado e quanto ficou de fora do teto. E o aviso do limite aparece antes de cobrar, na primeira volta, não só na volta em que o jogador já perdeu algo.
É uma regra de produto que vale para além deste jogo: um limite que só se revela no momento em que pune é lido como defeito, mesmo quando é intencional.
Também dá pra só jogar.
Jogar grátis agora