Projeto: jogo simples no terminal
LidoNesta aula, desenvolvemos um jogo simples de adivinhação no terminal usando C, abordando a estrutura de estado, o loop principal, a entrada do usuário e a lógica do jogo. O projeto integra conceitos fundamentais como variáveis, loops, condicionais e funções, proporcionando uma experiência prática completa.
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
gerarNumeroSecretooulerPalpite. - 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
- scanf - cppreference.com
- rand - cppreference.com
- GNU C Library - Input/Output
- C - Loops - TutorialsPoint
- rand and srand in C - GeeksforGeeks
- Número aleatório - Wikipédia
Exercícios
- Modifique o jogo para permitir que o jogador escolha o intervalo do número secreto (ex.: entre 1 e 1000).
- Implemente um sistema de pontuação: comece com 100 pontos e subtraia 10 a cada tentativa errada. Mostre a pontuação final.
- Crie uma função
lerPalpite()que valida a entrada e retorna o palpite. Use-a no loop principal. - Adicione a opção de jogar novamente após o fim do jogo, perguntando ao usuário se deseja continuar.
- 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.
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.
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);
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();.
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');
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.