Nesta aula, vamos construir um jogo simples de adivinhação no terminal usando a linguagem C. O objetivo é consolidar os conceitos fundamentais que você aprendeu até aqui: variáveis, tipos de dados, loops, condicionais, funções e interação com o usuário. Ao final, você terá um programa funcional que sorteia um número secreto e desafia o jogador a adivinhá-lo, com feedback a cada tentativa.

Este projeto é ideal para praticar a lógica de programação e a estruturação de código em C. Vamos dividir o desenvolvimento em quatro partes principais: estado, loop principal, entrada do usuário e lógica. Cada parte será explicada em detalhes, com exemplos de código e boas práticas. Ao final, você terá um jogo completo e poderá expandi-lo com novos recursos.

Estado

O estado do jogo refere-se às variáveis que armazenam as informações necessárias para o funcionamento do programa. Em nosso jogo de adivinhação, precisamos de algumas variáveis essenciais:

  • Número secreto: o valor aleatório que o jogador deve adivinhar.
  • Tentativas: o número de palpites já realizados.
  • Limite de tentativas: quantidade máxima de palpites permitidos (opcional, mas adiciona desafio).
  • Palpite atual: o valor digitado pelo jogador em cada iteração.
  • Acertou: um flag (booleano) que indica se o jogador acertou.

Em C, não temos um tipo booleano nativo, então usamos int com valores 0 (falso) ou 1 (verdadeiro). Vamos declarar essas variáveis no início da função principal ou em uma estrutura, dependendo da organização. Para um jogo simples, podemos usar variáveis locais na função main. Vamos também usar a função srand e rand para gerar o número aleatório.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    // Estado do jogo
    int numeroSecreto;
    int tentativas = 0;
    const int LIMITE_TENTATIVAS = 10;
    int palpite;
    int acertou = 0;

    // Inicializa o gerador de números aleatórios
    srand(time(NULL));
    numeroSecreto = rand() % 100 + 1; // Número entre 1 e 100

    // Restante do código virá aqui
    return 0;
}

Observe que usamos const para o limite de tentativas, tornando-o uma constante imutável. Isso é uma boa prática, pois evita alterações acidentais. O número secreto é gerado com rand() % 100 + 1, que produz um valor entre 1 e 100.

Loop principal

O loop principal é o coração do jogo: ele controla as rodadas de tentativas até que o jogador acerte ou esgote o número máximo de palpites. Usaremos um loop while que continua enquanto o jogador não acertou e ainda tem tentativas restantes.

Dentro do loop, exibimos o status atual (número da tentativa, quantidade restante) e solicitamos o palpite. Após cada palpite, incrementamos o contador de tentativas e verificamos se acertou. Se acertar, saímos do loop; caso contrário, damos uma dica (maior ou menor) e continuamos.

    // Loop principal
    while (!acertou && tentativas < LIMITE_TENTATIVAS) {
        printf("Tentativa %d de %d\n", tentativas + 1, LIMITE_TENTATIVAS);
        printf("Digite seu palpite (1-100): ");
        scanf("%d", &palpite);
        tentativas++;

        // Lógica será implementada na próxima seção
    }

É importante notar que a condição do while verifica duas coisas: se o jogador ainda não acertou e se ainda há tentativas. Isso garante que o loop termine quando qualquer uma das condições for falsa. A variável acertou será atualizada na lógica.

Entrada do usuário

A entrada do usuário é feita com a função scanf, que lê dados formatados da entrada padrão. No nosso caso, lemos um inteiro. No entanto, é importante tratar possíveis erros de entrada (por exemplo, se o usuário digitar caracteres não numéricos). Para simplificar, vamos supor que o usuário sempre digita números válidos, mas em um jogo robusto, seria necessário validar.

Uma melhoria comum é usar fgets para ler uma linha e depois converter com atoi ou sscanf, evitando problemas com caracteres extras. Nesta aula, manteremos o uso de scanf, mas com uma pequena validação: verificamos se o retorno de scanf é igual a 1 (ou seja, se conseguiu ler um inteiro). Caso contrário, limpamos o buffer de entrada e pedimos novamente.

        printf("Digite seu palpite (1-100): ");
        int resultado = scanf("%d", &palpite);
        if (resultado != 1) {
            // Limpa o buffer de entrada
            while (getchar() != '\n');
            printf("Entrada inválida! Tente novamente.\n");
            continue;
        }
        // Validação de faixa (opcional)
        if (palpite < 1 || palpite > 100) {
            printf("Palpite fora do intervalo! Tente novamente.\n");
            continue;
        }
        tentativas++;

O continue faz com que o loop pule para a próxima iteração sem incrementar tentativas (no caso de entrada inválida) ou sem contar a tentativa (no caso de fora do intervalo). Isso evita que o jogador perca tentativas por erros de digitação.

Lógica

A lógica do jogo envolve comparar o palpite com o número secreto e dar feedback ao jogador. Se o palpite for igual, acertou; se for menor, informamos que o número é maior; se for maior, informamos que é menor. Além disso, precisamos atualizar o estado (a variável acertou) e, ao final do loop, exibir a mensagem de vitória ou derrota.

Vamos implementar a lógica dentro do loop, após a entrada válida. Também incluiremos uma mensagem de parabéns ou de fim de jogo após o loop.

        // Lógica do jogo
        if (palpite == numeroSecreto) {
            acertou = 1;
            printf("Parabéns! Você acertou em %d tentativa(s).\n", tentativas);
        } else if (palpite < numeroSecreto) {
            printf("O número secreto é maior.\n");
        } else {
            printf("O número secreto é menor.\n");
        }

Após o loop, se acertou for 0, significa que as tentativas acabaram e o jogador não acertou. Nesse caso, exibimos uma mensagem revelando o número secreto.

    if (!acertou) {
        printf("\nVocê esgotou suas tentativas! O número secreto era %d.\n", numeroSecreto);
    }

Combinando tudo, temos o jogo completo. O código final fica assim:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int numeroSecreto;
    int tentativas = 0;
    const int LIMITE_TENTATIVAS = 10;
    int palpite;
    int acertou = 0;

    srand(time(NULL));
    numeroSecreto = rand() % 100 + 1;

    printf("Bem-vindo ao Jogo de Adivinhação!\n");
    printf("Tente adivinhar o número entre 1 e 100.\n\n");

    while (!acertou && tentativas < LIMITE_TENTATIVAS) {
        printf("Tentativa %d de %d\n", tentativas + 1, LIMITE_TENTATIVAS);
        printf("Digite seu palpite: ");
        int resultado = scanf("%d", &palpite);
        if (resultado != 1) {
            while (getchar() != '\n');
            printf("Entrada inválida! Tente novamente.\n");
            continue;
        }
        if (palpite < 1 || palpite > 100) {
            printf("Palpite fora do intervalo! Tente novamente.\n");
            continue;
        }
        tentativas++;

        if (palpite == numeroSecreto) {
            acertou = 1;
            printf("\nParabéns! Você acertou em %d tentativa(s).\n", tentativas);
        } else if (palpite < numeroSecreto) {
            printf("O número secreto é maior.\n");
        } else {
            printf("O número secreto é menor.\n");
        }
    }

    if (!acertou) {
        printf("\nVocê esgotou suas tentativas! O número secreto era %d.\n", numeroSecreto);
    }

    return 0;
}

Este código está completo e funcional. Você pode compilá-lo com gcc jogo.c -o jogo e executá-lo. Experimente jogar algumas vezes para ver como funciona.

Boas práticas e observações finais

Ao desenvolver projetos em C, é importante seguir algumas boas práticas:

  • Use constantes para valores fixos, como o limite de tentativas, para facilitar a manutenção.
  • Valide entradas para evitar comportamentos inesperados.
  • Organize o código em funções para melhor legibilidade e reutilização. Por exemplo, você pode criar uma função gerarNumeroSecreto ou lerPalpite.
  • Comente o código para explicar partes complexas.

Este jogo pode ser estendido de várias formas: adicionar níveis de dificuldade, permitir que o jogador jogue novamente, usar um número maior de tentativas, ou até mesmo criar um sistema de pontuação. Explore essas ideias para aprimorar suas habilidades.

Referências

Exercícios

  1. Modifique o jogo para permitir que o jogador escolha o intervalo do número secreto (ex.: entre 1 e 1000).
  2. ✓ Resposta: Você pode adicionar uma variável limiteSuperior e pedir ao jogador para informar o valor máximo antes de gerar o número. Exemplo:
    int limiteSuperior;
    printf("Digite o valor máximo do intervalo: ");
    scanf("%d", &limiteSuperior);
    numeroSecreto = rand() % limiteSuperior + 1;
    Lembre-se de ajustar as mensagens e validações para usar limiteSuperior.
  3. Implemente um sistema de pontuação: comece com 100 pontos e subtraia 10 a cada tentativa errada. Mostre a pontuação final.
  4. ✓ Resposta: Adicione uma variável pontuacao = 100. Dentro do loop, após um palpite errado, faça pontuacao -= 10. Ao final, exiba a pontuação. Exemplo:
    int pontuacao = 100;
    // ... dentro do loop, quando errar:
    pontuacao -= 10;
    // ... no final:
    printf("Sua pontuação final: %d\n", pontuacao);
  5. Crie uma função lerPalpite() que valida a entrada e retorna o palpite. Use-a no loop principal.
  6. ✓ Resposta: Defina a função antes do main:
    int lerPalpite() {
        int palpite;
        while (1) {
            printf("Digite seu palpite: ");
            if (scanf("%d", &palpite) != 1) {
                while (getchar() != '\n');
                printf("Entrada inválida!\n");
            } else if (palpite < 1 || palpite > 100) {
                printf("Fora do intervalo!\n");
            } else {
                return palpite;
            }
        }
    }
    E no main, use palpite = lerPalpite();.
  7. Adicione a opção de jogar novamente após o fim do jogo, perguntando ao usuário se deseja continuar.
  8. ✓ Resposta: Envolva o jogo em um loop do-while. Ao final, pergunte: printf("Jogar novamente? (s/n): "); e leia um char. Se for 's', repete; senão, sai. Exemplo:
    char opcao;
    do {
        // ... jogo completo ...
        printf("Jogar novamente? (s/n): ");
        scanf(" %c", &opcao);
    } while (opcao == 's');
  9. Implemente um modo de dificuldade: fácil (15 tentativas), médio (10) e difícil (5). Permita que o jogador escolha antes de começar.
  10. ✓ Resposta: Crie uma variável limite e peça ao usuário para escolher a dificuldade:
    int limite;
    int dificuldade;
    printf("Escolha a dificuldade: 1-Fácil, 2-Médio, 3-Difícil: ");
    scanf("%d", &dificuldade);
    switch (dificuldade) {
        case 1: limite = 15; break;
        case 2: limite = 10; break;
        case 3: limite = 5; break;
        default: limite = 10;
    }
    Use limite no lugar de LIMITE_TENTATIVAS.