Dez falhas encontradas na leitura do código. Duas encerram o jogo em menos de um minuto, três anulam o conceito de programação que o desafio existe para ensinar, o resto acelera a economia muito além do ritmo planejado. No fim, os scripts verificados que resolvem cada tipo de cliente.
A vitória pretendida
Comprar o upgrade Zerar (UpgradeData.gd:118), que exige 5 diamantes e o delivery_online comprado. Diamantes só vêm de declarações aprovadas no Delivery — uma por relatório, com 60 s de cooldown. O caminho legítimo custa R$ 1.800 em upgrades e cerca de 4 minutos de espera pura.
Vitória instantânea 2 falhas · < 1 min
Qualquer uma das duas leva do save novo à tela de conclusão sem atender um único cliente corretamente.
submit() intercepta o argumento antes de validar a resposta e libera o menu secreto. A string está em texto puro no repositório, e o desbloqueio é gravado no save — vale para sempre naquele slot.
Acesso rápido
Com um cliente na tela — serve o do tutorial, que aparece no passo 5 — rode no console:
int main() {
send("cheat");
}
Abra o menu: um clique no canto superior esquerdo (hotspot invisível de 36×36 px) ou triplo clique no painel de dinheiro, com menos de 1200 ms entre cliques.
Ligue Dinheiro infinito, compre os 11 upgrades da árvore, clique +1 diamante cinco vezes e compre Zerar.
Custo: um cliente perdido — o cheat força correto = false.
load_save_data aplica todo upgrade listado sem checar preço nem requisito. E _looks_like_save_data aceita o arquivo se ele tiver uma única chave conhecida.
Substitua o conteúdo por uma linha — isso já dispara complete_game() no load:
{"shop": {"comprados": {"zerar": true}}}
Na versão web nem precisa de editor de texto: os botões EXPORTAR e IMPORTAR do menu principal fazem o mesmo. Variantes úteis: game.diamonds: 5 com delivery_online: true deixa o Zerar comprável de verdade, com overlay de vitória; tutorial.completed: true pula o tutorial; delivery.next_report_unix: 0 zera o cooldown.
Anulam o desafio educacional 3 falhas
Não aceleram o relógio, mas permitem chegar ao fim sem exercitar nenhum dos conceitos que o jogo se propõe a ensinar.
Os valores são checados antes da validação pedagógica, e _reject() não tem custo, não tem limite de tentativas e não consome o relatório.
Acesso rápido
São 216 combinações — entregas de 0 a 5 em três categorias. Um while triplo dentro de um único main() acerta sempre. Pior: o painel do Delivery já mostra os três números na tela, então uma tentativa basta.
04. A recursão obrigatória aceita uma função-iscaAlto
Nada liga a recursão ao valor declarado. O validador só olha a estrutura do AST — quer uma função não-main que chame a si mesma, com um if e um ramo que retorna sem recursão. O runtime só verifica se essa função apareceu duas vezes na pilha depois de get_deliveries().
Acesso rápido
Declare uma função que não faz nada de útil:
int isca(int n) {
if (n <= 0) {
return 0;
}
return isca(n - 1);
}
Chame isca(2) uma vez, depois de get_deliveries() — antes disso delivery_report_id ainda é 0 e o runtime nem observa a pilha (executor.gd:525).
Calcule o lucro com a forma fechada base * (2^n - 1) ou com uma tabela de 6 entradas. Passa nos dois checks.
05. Errar é grátis e o cliente não tem paciênciaAlto
Resposta errada não custa dinheiro nem estoque — consume_items só roda se acertar — e não existe timer de paciência. Enquanto isso, os valores do pedido aparecem escritos no diálogo.
Acesso rápido
Ler os números na tela e mandar send(31.50) fixo, cliente a cliente. Todo desafio fora do Delivery é resolvível sem escrever uma linha de lógica, sem risco nenhum em errar.
Aceleram a progressão 5 falhas
Jogando dentro das regras, encurtam drasticamente as duas barreiras do jogo: o dinheiro e o tempo.
06. Recompensa de estoque fixa com bônus cumulativoMédio
ChallengeSystem.gd:33-40 · ChallengeSystem.gd:138
São 24 fixos por pedido, independente do valor pedido, enquanto os itens custam de R$ 3 a R$ 6 e o cliente consome só dois tipos.
Acesso rápido
Com premium 1-3 (×1,75), troco (+4), cliente ouro (+8, que em pedidos de estoque nem aumenta a dificuldade) e estoque cheio (×1,5): ~94 por cliente a cada 1-3 s, contra ~9 a 24 de custo. Um loop while com sensor() que atende e chama buy_stock() mantém o multiplicador 1,5× permanente. A árvore inteira se paga em poucos minutos.
07. dinheiro_minimo é código mortoMédio
UpgradeData.gd · 12 ocorrências
O campo está declarado em todo upgrade, mas nenhum arquivo do projeto o lê.
Acesso rápido
Não há nada a fazer para explorar: o ritmo de progressão planejado simplesmente não existe. Cada upgrade fica comprável assim que a cadeia requer é satisfeita — lang_if deveria surgir aos R$ 120 e está disponível aos R$ 100.
08. As palavras da linguagem não são travadasMédio
parser.gd:410-435 · builtins.gd:9-18
Só o if checa FeatureManager antes de compilar (parser.gd:411). while, for, funções e arrays funcionam desde o save novo, sem comprar nada. get_stock() e buy_stock() também não têm trava de feature nos builtins.
Acesso rápido
Os R$ 110 do Aprender while() não compram a palavra while, compram o tipo de cliente que ela resolve. Um loop já roda antes disso — só não existe ainda um pedido que precise dele.
09. Espaço pula os diálogosMédio
dialog_screen.gd:29-35
A recompensa só é creditada quando o diálogo de resultado fecha, e o próximo cliente só nasce depois disso.
Acesso rápido
Martelar Espaço. Corta até 4 s por linha (AUTO_ADVANCE_MAX_DELAY) vezes cerca de 5 linhas por cliente. Triplica o throughput sem nada além do teclado.
10. O cooldown depende do relógio do sistemaMédio
DeliverySystem.gd:224
O tempo restante sai de Time.get_unix_time_from_system() comparado com um valor persistido no save.
Acesso rápido
Adiantar o relógio do sistema operacional libera o relatório na hora. É o único freio temporal real do jogo — 4 × 60 s para juntar os 5 diamantes — e não tem defesa alguma.
Códigos que resolvem verificados contra o interpretador
Se o objetivo é só zerar, a falha 01 é mais rápida que qualquer script. O que vem abaixo é o caminho jogando dentro das regras: um script pronto por tipo de pedido, na ordem em que os upgrades liberam cada tipo.
Três regras que quebram o script antes de rodar
Leia antes de copiar qualquer coisa
void não existe. O lexer monta a tabela de palavras-chave a partir dos tokens KW_* (lexer.gd:24-29), e só há int e float. Em void main(), void vira um identificador solto e o parser cobra o ponto e vírgula que viria depois dele — é exatamente o Linha 1, Col 10: Faltou 'SEMICOLON' aqui. Encontrei 'IDENTIFIER' no lugar. Toda função começa com int ou float.
Parâmetro de função só pode ser int.parser.gd:356 consome KW_INT fixo na lista de parâmetros. int f(float x) não compila; variáveis locais float funcionam normalmente.
if, while e for exigem chaves. O executor empilha o corpo direto como bloco (executor.gd:389, :404, :821). Um corpo sem {} não é um BlockNode, não tem .statements, e a execução quebra dentro da Godot sem mensagem no console do jogo.
A fila de entradas e a quantidade de valores esperados mudam a cada upgrade comprado. Errar o formato é o motivo mais comum de um script correto perder o cliente.
A partir de
Fila do input()
send() espera
Save novo
a, b
send(total)
Aprender if()
a, b
send(total) com desconto
Aprender while()
p1 … pn, -1
send(total)
Cliente com troco
p1 … pn, -1, pago
send(total, troco)
Abrir estoque
preço, qtd, …, -1, pago
send(total, troco)
Um send() por cliente
submit() marca transaction_closing = true na primeira chamada (TransactionManager.gd:64). send(total); send(troco); em duas linhas perde o cliente: a primeira é validada com um valor a menos que o esperado e a segunda retorna false sem fazer nada. Os dois valores vão na mesma chamada — send(total, troco).
Cliente de soma save novo
Dois preços na fila, um total esperado. Sem if comprado ainda, nenhum desconto é aplicado no cálculo do jogo.
int main() {
float a = input();
float b = input();
send(a + b);
}
Lista de preços com sentinela Aprender while()
De 3 a 5 preços terminados em -1. Como Aprender if() é pré-requisito, o desconto de 10% acima de R$ 50 já está valendo (ChallengeSystem.gd:26-29) e precisa entrar na conta.
int main() {
float total = 0;
float preco = input();
while (preco > 0) {
total = total + preco;
preco = input();
}
if (total > 50) {
total = total * 0.9;
}
send(total);
}
A condição é preco > 0, não preco != -1: com a fila esgotada next_input() passa a devolver 0 para sempre (TransactionManager.gd:42-43), e o teste contra -1 nunca sairia do loop.
Cliente com troco Cliente com troco
Mesma lista, mais um valor no fim: o que o cliente entregou. Agora são dois valores esperados, na ordem total e troco.
int main() {
float total = 0;
float preco = input();
while (preco > 0) {
total = total + preco;
preco = input();
}
if (total > 50) {
total = total * 0.9;
}
float pago = input();
send(total, pago - total);
}
Caixa automático com estoque Abrir estoque
O pedido vira pares de preço, qtd (ChallengeSystem.gd:15-21). Este é o script de regime permanente: sensor() veio com Aprender sensor() e await() com Abrir estoque (builtins.gd:129), então o loop só é possível aqui. Deixe rodando e a árvore de upgrades se paga sozinha.
int main() {
while (1) {
if (sensor("cliente_na_tela")) {
float total = 0;
float preco = input();
while (preco > 0) {
float qtd = input();
total = total + preco * qtd;
preco = input();
}
if (total > 50) {
total = total * 0.9;
}
float pago = input();
send(total, pago - total);
}
await(0.1);
}
}
O while (1) não trava o jogo: o escalonador dá 200 operações por frame a cada script (ScriptRuntimeManager.gd:16). O await(0.1) devolve o resto do orçamento para os outros scripts. sensor("cliente_na_tela") volta a false dentro do próprio submit(), então não há risco de atender o mesmo cliente duas vezes.
Delivery Online aba Delivery
Precisa rodar na aba Delivery: _validate_runtime_context compara o script_id com o da aba e recusa qualquer outro (DeliverySystem.gd:364-370). O lucro de cada categoria é lucro = 2 × lucro + base repetido uma vez por entrega (DeliverySystem.gd:210-214), com bases fixas [2, 4, 7] (DeliveryConfig.gd:5).
int lucro(int n, int b) {
if (n == 0) {
return 0;
}
return 2 * lucro(n - 1, b) + b;
}
int main() {
int bases[3];
int entregas[3];
int lucros[3];
int i = 0;
bases[0] = 2;
bases[1] = 4;
bases[2] = 7;
entregas = get_deliveries();
while (i < 3) {
lucros[i] = lucro(entregas[i], bases[i]);
i = i + 1;
}
declare_profit(lucros);
}
Os quatro requisitos do validador estão todos aqui (DeliveryProgramValidator.gd:78-94): uma função fora da main, recursão com if e caso-base que retorna sem se chamar, um laço, e declare_profit() recebendo um array declarado com tamanho literal 3. int entregas[3] precisa existir antes do =: get_deliveries() devolve o array inteiro, mas atribuir a uma variável não declarada é erro de runtime.
Zerar, do save novo
Sequência completa
Caixa de soma até R$ 100 → Aprender if(). Depois Aprender while(), Aprender sensor(), Cliente com troco, Abrir estoque, trocando o script a cada compra.
Com o caixa automático rodando, comprar premium 1-3 e marketing 1-3 e manter o estoque cheio com buy_stock() para o bônus de 1,5×.
R$ 370 no Delivery Online. Rodar o script da aba Delivery a cada relatório: 5 aprovações, 5 diamantes, 60 s de cooldown entre elas (contornável pela falha 10).
Comprar Zerar.
Ordem de correção por custo-benefício
Correção
Onde
Esforço
Envolver o cheat e o hotspot em OS.is_debug_build()
TransactionManager.gd:74 game.gd:106-119
Trivial
Usar dinheiro_minimo em verificar_desbloqueios() ou remover o campo
UpgradeData.gd
Trivial
Aceitar corpo sem chaves em if/while/for, ou recusar no parser com erro legível
executor.gd:389
Trivial
Validar o programa antes dos valores e limitar tentativas por relatório
DeliverySystem.gd:127
Pequeno
Recompensa proporcional ao pedido; bônus 1,5× uma vez por reposição
ChallengeSystem.gd:33
Pequeno
Contar cooldown por tempo acumulado em vez de unix time
DeliverySystem.gd:224
Pequeno
HMAC no save e validar requer ao aplicar upgrades no load
SaveManager.gd UpgradeManager.gd:184
Médio
Ligar o valor declarado ao retorno efetivo da função recursiva