Projeto Jogo de Adivinhação

O computador pensa num número de 1 a 100 e você tenta adivinhar. A cada palpite, ele diz "é maior" ou "é menor". É o projeto clássico de quem está aprendendo a programar, porque junta quase tudo do Nível 1 num programa curto e divertido: decisões, laços, contadores, números aleatórios e funções.

Você vai praticar
  • Comparar o palpite com o número secreto com if e else if.
  • Repetir até acertar com while e contar as tentativas com ++.
  • Sortear números com rand e srand (Números Aleatórios).
  • Limitar as tentativas com for e uma constante #define (Constantes).
  • Separar uma partida numa função que devolve um valor.
  • Testar um programa que sorteia números (sem depender da sorte).

O que vamos construir

Este é o jogo pronto, na máquina da Ana. Ela jogou três partidas: na primeira e na terceira, foi esperta e sempre chutou o meio do intervalo que sobrava; na segunda, chutou de 10 em 10 e perdeu.

$ ./adivinhacao
=== Jogo de Adivinhação ===

Pensei num número de 1 a 100. Você tem 7 tentativas.
Tentativa 1: 50
É maior.
Tentativa 2: 75
É maior.
Tentativa 3: 88
É menor.
Tentativa 4: 81
É maior.
Tentativa 5: 84
É maior.
Tentativa 6: 86
Acertou em 6 tentativa(s)!
Novo recorde: 6 tentativa(s)!
Jogar de novo? (s/n): s

Pensei num número de 1 a 100. Você tem 7 tentativas.
Tentativa 1: 90
É menor.
Tentativa 2: 80
É menor.
Tentativa 3: 70
É menor.
Tentativa 4: 60
É menor.
Tentativa 5: 55
É menor.
Tentativa 6: 52
É menor.
Tentativa 7: 51
É menor.
Suas tentativas acabaram. O número era 1.      « perdeu: o número era o menor possível
Jogar de novo? (s/n): s

Pensei num número de 1 a 100. Você tem 7 tentativas.
Tentativa 1: 50
É maior.
Tentativa 2: 75
É menor.
Tentativa 3: 62
É menor.
Tentativa 4: 56
Acertou em 4 tentativa(s)!
Novo recorde: 4 tentativa(s)!
Jogar de novo? (s/n): n
Seu recorde: 4 tentativa(s).
Até a próxima!

Se você rodar, os números serão outros: a cada execução o jogo sorteia um número novo. Vamos chegar lá em quatro versões:

  1. Um palpite contra um número fixo (scanf, if/else if).
  2. Repetir até acertar e contar as tentativas (while, ++).
  3. Sortear o número (rand, srand, time).
  4. Limite, recorde e jogar de novo (#define, for, uma função).

Antes de começar

Você precisa saber compilar e rodar um programa (Compilar com gcc), ler um inteiro com scanf e conhecer if e else. A pasta do projeto e o arquivo adivinhacao.c (sem acento e sem ç: nomes de arquivo com acento dão trabalho no terminal):

$ mkdir -p c/adivinhacao
$ cd c/adivinhacao

Versão 1 · Um palpite contra um número fixo

Antes de sortear, vamos fazer o jogo funcionar com um número fixo: 42. Assim dá para testar sabendo a resposta. A pessoa dá um palpite e o programa diz se acertou ou para que lado errou.

#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");
    printf("Seu palpite: ");
    scanf("%d", &palpite);

    if (palpite == secreto) {
        printf("Acertou!\n");
    } else if (palpite < secreto) {
        printf("Errou. O número é maior.\n");
    } else {
        printf("Errou. O número é menor.\n");
    }
    return 0;
}
LinhaO que faz
int secreto = 42;O número que a pessoa tem de adivinhar. Na versão 3, ele vai ser sorteado.
scanf("%d", &palpite);Lê um inteiro (%d) e guarda em palpite. O & entrega o endereço da variável (veja Entrada com scanf).
if (palpite == secreto)Dois sinais de igual: compara. Um só (=) guardaria 42 no palpite (veja o cenário abaixo).
else if (palpite < secreto)Se não acertou e o palpite é menor, o número secreto é maior.
elseSobrou um caso só: o palpite é maior, então o número é menor.
$ gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 30
Errou. O número é maior.
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 70
Errou. O número é menor.
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 42
Acertou!
Um = só no if: todo palpite "acerta"

Este código tem um erro de propósito: if (palpite = secreto).

$ gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao
adivinhacao.c: In function ‘main’:
adivinhacao.c:12:9: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
   12 |     if (palpite = secreto) {
      |         ^~~~~~~
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 30
Acertou!

O que significa: "sugiro parênteses em volta de uma atribuição usada como valor verdade". palpite = secreto guarda 42 em palpite, e o resultado da expressão é 42, que não é zero, ou seja, verdadeiro. Por isso o palpite 30 "acertou". O -Wall desconfiou e avisou.

Como consertar: para comparar, use ==. Veja Operadores de Comparação.

Missão (versão 1): o jogo só sabe dizer "Acertou!". Acrescente um else if que mostra Errou. O número é maior. quando o palpite é menor que o secreto, e um else que mostra Errou. O número é menor.. A entrada da missão é o palpite 30.

#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");
    printf("Seu palpite: ");
    scanf("%d", &palpite);

    if (palpite == secreto) {
        printf("Acertou!\n");
    }
    // e se errou? diga se o número é maior ou menor
    return 0;
}
#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");
    printf("Seu palpite: ");
    scanf("%d", &palpite);

    if (palpite == secreto) {
        printf("Acertou!\n");
    } else if (palpite < secreto) {
        printf("Errou. O número é maior.\n");
    } else {
        printf("Errou. O número é menor.\n");
    }
    return 0;
}

Versão 2 · Repetir até acertar e contar as tentativas

Um palpite só é pouco. A versão 2 repete a pergunta enquanto o palpite for diferente do secreto: isso é um while. E conta quantos palpites a pessoa deu, com uma variável que começa em 0 e ganha 1 a cada volta (tentativas++, veja ++ e --).

#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;
    int tentativas = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");

    while (palpite != secreto) {
        printf("Seu palpite: ");
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            return 1;
        }
        tentativas++;

        if (palpite < secreto) {
            printf("É maior.\n");
        } else if (palpite > secreto) {
            printf("É menor.\n");
        }
    }
    printf("Acertou em %d tentativas!\n", tentativas);
    return 0;
}
int tentativas = 0;
O contador. Começa em zero, porque ainda ninguém chutou nada.
while (palpite != secreto)
"Enquanto o palpite for diferente do secreto, repita." palpite começa em 0, que nunca é o secreto (de 1 a 100), então o laço sempre entra na primeira vez.
if (scanf("%d", &palpite) != 1)
O scanf devolve quantos valores leu. Se a pessoa digitou texto ou a entrada acabou, ele devolve 0 ou EOF, e o jogo para em vez de rodar para sempre (veja o cenário abaixo).
tentativas++;
Mais um palpite contado.
else if (palpite > secreto)
Agora são três casos: menor, maior ou igual. Quando é igual, nenhum dos dois if mostra nada, o laço testa a condição, vê que acertou e sai.
printf("Acertou em %d tentativas!\n", tentativas);
Fora do laço: só chega aqui quem acertou.
$ gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 50
É menor.
Seu palpite: 25
É maior.
Seu palpite: 37
É maior.
Seu palpite: 43
É menor.
Seu palpite: 40
É maior.
Seu palpite: 42
Acertou em 6 tentativas!
Sem conferir o scanf: um texto trava o jogo num laço infinito

Este código tem um erro de propósito: scanf("%d", &palpite); sem o if em volta.

$ gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao
$ printf 'abc\n' | timeout 3 ./adivinhacao | head -n 6
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: É maior.
Seu palpite: É maior.
Seu palpite: É maior.
Seu palpite: É maior.

O comando "digita" abc para o jogo (printf … |), corta o programa depois de 3 segundos (timeout 3) e mostra só as 6 primeiras linhas (head -n 6); sem isso, a tela encheria sem parar.

O que aconteceu: o %d não consegue ler abc, desiste e deixa o abc na entrada. O palpite continua 0, o laço repete, o scanf tenta de novo, esbarra no mesmo abc… para sempre. No seu terminal, aperte Ctrl+C para parar.

Como consertar: confira o retorno: if (scanf("%d", &palpite) != 1), como na versão 2. Para pedir de novo em vez de encerrar, veja a versão 4 do projeto Calculadora e Validar a Entrada.

Missão (versão 2): o jogo pergunta uma vez só. Coloque o trecho que pergunta, lê, conta e compara dentro de um while (palpite != secreto) { … }. A entrada da missão é 50, 25, 37, 43, 42: o jogo deve terminar com Acertou em 5 tentativas!.

#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;
    int tentativas = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");

    // repita este bloco enquanto palpite != secreto
    printf("Seu palpite: ");
    if (scanf("%d", &palpite) != 1) {
        printf("\nFim de jogo: não consegui ler um número.\n");
        return 1;
    }
    tentativas++;

    if (palpite < secreto) {
        printf("É maior.\n");
    } else if (palpite > secreto) {
        printf("É menor.\n");
    }

    printf("Acertou em %d tentativas!\n", tentativas);
    return 0;
}
#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;
    int tentativas = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");

    while (palpite != secreto) {
        printf("Seu palpite: ");
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            return 1;
        }
        tentativas++;

        if (palpite < secreto) {
            printf("É maior.\n");
        } else if (palpite > secreto) {
            printf("É menor.\n");
        }
    }
    printf("Acertou em %d tentativas!\n", tentativas);
    return 0;
}

Versão 3 · Sortear o número

Com o 42 fixo, o jogo perde a graça na segunda partida. Hora de deixar o computador escolher. A função rand() ("random", aleatório), do <stdlib.h>, devolve um inteiro entre 0 e RAND_MAX (no Linux, 2147483647). Para transformar isso num número de 1 a 100, usamos o resto da divisão (veja Números Aleatórios):

ExpressãoResultadoPor quê
rand()de 0 a 2147483647Um número grande qualquer.
rand() % 100de 0 a 99O resto da divisão por 100 sempre fica entre 0 e 99.
rand() % 100 + 1de 1 a 100Soma 1 nos dois extremos.

Mas tem uma pegadinha. Veja este programa, que sorteia cinco números, rodando três vezes:

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

int main(void) {
    for (int i = 0; i < 5; i++) {
        printf("%d ", rand() % 100 + 1);
    }
    printf("\n");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra sorteio.c -o sorteio
$ ./sorteio
84 87 78 16 94
$ ./sorteio
84 87 78 16 94
$ ./sorteio
84 87 78 16 94 

Os mesmos cinco números, toda vez! O rand() não é sorte de verdade: é uma conta que, a partir de um número inicial (a semente), gera uma sequência que parece aleatória. Mesma semente, mesma sequência. Sem você dizer nada, a semente é sempre 1.

A solução é trocar a semente a cada execução com srand ("seed random", semear o aleatório). E o que muda a cada execução? O relógio: time(NULL), do <time.h>, devolve quantos segundos se passaram desde 1º de janeiro de 1970.

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

int main(void) {
    srand(time(NULL));                 // embaralha: uma sequência nova a cada execução
    int secreto = rand() % 100 + 1;    // um número de 1 a 100
    int palpite = 0;
    int tentativas = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");

    while (palpite != secreto) {
        printf("Seu palpite: ");
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            return 1;
        }
        tentativas++;

        if (palpite < secreto) {
            printf("É maior.\n");
        } else if (palpite > secreto) {
            printf("É menor.\n");
        }
    }
    printf("Acertou em %d tentativas!\n", tentativas);
    return 0;
}
#include <stdlib.h> e <time.h>
stdlib.h traz rand e srand; time.h traz time.
srand(time(NULL));
Usa a hora atual como semente. Chame uma vez só, no começo do programa.
int secreto = rand() % 100 + 1;
Sorteia o número de 1 a 100. O resto do jogo não mudou nada.

Para ganhar sempre em poucas tentativas, a Ana usa uma estratégia: chutar o meio do intervalo que ainda é possível. Começa em 50; se "é menor", o número está entre 1 e 49, e ela chuta 25; e assim por diante. A cada palpite, o intervalo cai pela metade:

$ gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 50
É menor.
Seu palpite: 25
É menor.
Seu palpite: 12
É maior.
Seu palpite: 18
É maior.
Seu palpite: 21
É maior.
Seu palpite: 23
Acertou em 6 tentativas!
$ ./adivinhacao
=== Jogo de Adivinhação ===
Pensei num número de 1 a 100.
Seu palpite: 50
É menor.
Seu palpite: 25
É menor.
Seu palpite: 12
É menor.
Seu palpite: 6
É maior.
Seu palpite: 9
É maior.
Seu palpite: 10
Acertou em 6 tentativas!
No editor do guia, os números sorteados são outros

O rand() é uma conta, e cada biblioteca C usa a sua. O gcc do Linux usa a da glibc; o editor do guia usa a da wasi-libc (baseada na musl). Por isso o sorteio.c mostra 84 87 78 16 94 no terminal da Ana e outra sequência no editor. Nos dois, a sequência se repete enquanto a semente for a mesma. Moral: nunca escreva um programa que depende de quais números o rand() vai dar.

Como testar um jogo que sorteia?

Se o número muda a cada execução, como conferir que o jogo funciona? Dois truques que os programadores usam de verdade:

  • Fixar o número durante o teste, como fizemos nas versões 1 e 2. Depois, troca-se pelo sorteio.
  • Escolher palpites que funcionam para qualquer número. Se a entrada for 1, 2, 3, …, 100, o jogo tem de acertar em algum momento, seja qual for o número. É o que a missão abaixo faz.

Missão (versão 3): troque o 42 fixo por um número sorteado de 1 a 100 (rand() % 100 + 1) e chame srand(time(NULL)); uma vez, no começo do main. Não esqueça os dois #include. A entrada da missão chuta 1, 2, 3… até 100, então o jogo acerta seja qual for o número sorteado. Rode duas ou três vezes e veja o número de tentativas mudar.

#include <stdio.h>

int main(void) {
    int secreto = 42;  // por enquanto, o número é sempre o mesmo
    int palpite = 0;
    int tentativas = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");

    while (palpite != secreto) {
        printf("Seu palpite: ");
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            return 1;
        }
        tentativas++;

        if (palpite < secreto) {
            printf("É maior.\n");
        } else if (palpite > secreto) {
            printf("É menor.\n");
        }
    }
    printf("Acertou em %d tentativas!\n", tentativas);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srand(time(NULL));                 // embaralha: uma sequência nova a cada execução
    int secreto = rand() % 100 + 1;    // um número de 1 a 100
    int palpite = 0;
    int tentativas = 0;

    printf("=== Jogo de Adivinhação ===\n");
    printf("Pensei num número de 1 a 100.\n");

    while (palpite != secreto) {
        printf("Seu palpite: ");
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            return 1;
        }
        tentativas++;

        if (palpite < secreto) {
            printf("É maior.\n");
        } else if (palpite > secreto) {
            printf("É menor.\n");
        }
    }
    printf("Acertou em %d tentativas!\n", tentativas);
    return 0;
}

Versão 4 · Limite, recorde e jogar de novo

Com a estratégia do meio, dá para achar qualquer número de 1 a 100 em no máximo 7 palpites: a cada palpite errado, sobra no máximo a metade dos números (100 → 50 → 25 → 12 → 6 → 3 → 1); depois de 6 erros sobra um número só, e o 7º palpite acerta. Então a versão final dá 7 tentativas: quem chuta à toa perde. E mais duas coisas: jogar de novo sem rodar o programa outra vez, e guardar o recorde (a partida com menos tentativas).

O código de uma partida foi para uma função, jogar. Ela recebe o número secreto e devolve quantas tentativas a pessoa usou, ou 0 se perdeu. O main só cuida do que acontece entre as partidas.

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

#define MAX_TENTATIVAS 7

// Joga uma partida. Devolve quantas tentativas a pessoa usou,
// ou 0 se ela não acertou dentro do limite.
int jogar(int secreto) {
    printf("\nPensei num número de 1 a 100. Você tem %d tentativas.\n", MAX_TENTATIVAS);
    for (int tentativa = 1; tentativa <= MAX_TENTATIVAS; tentativa++) {
        int palpite = 0;
        printf("Tentativa %d: ", tentativa);
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            exit(1);
        }
        if (palpite == secreto) {
            printf("Acertou em %d tentativa(s)!\n", tentativa);
            return tentativa;
        } else if (palpite < secreto) {
            printf("É maior.\n");
        } else {
            printf("É menor.\n");
        }
    }
    printf("Suas tentativas acabaram. O número era %d.\n", secreto);
    return 0;
}

int main(void) {
    srand(time(NULL));
    int recorde = 0;      // 0 = ainda não há recorde
    char resposta = 's';

    printf("=== Jogo de Adivinhação ===\n");
    while (resposta == 's' || resposta == 'S') {
        int secreto = rand() % 100 + 1;
        int usadas = jogar(secreto);

        if (usadas > 0 && (recorde == 0 || usadas < recorde)) {
            recorde = usadas;
            printf("Novo recorde: %d tentativa(s)!\n", recorde);
        }

        printf("Jogar de novo? (s/n): ");
        if (scanf(" %c", &resposta) != 1) {
            resposta = 'n';
        }
    }

    if (recorde > 0) {
        printf("Seu recorde: %d tentativa(s).\n", recorde);
    }
    printf("Até a próxima!\n");
    return 0;
}
TrechoO que faz
#define MAX_TENTATIVAS 7Uma constante com nome: antes de compilar, o pré-processador troca MAX_TENTATIVAS por 7 em todo o código. Para deixar o jogo mais fácil, você muda num lugar só (veja Constantes).
int jogar(int secreto)A função recebe o número sorteado como parâmetro e devolve um int.
for (int tentativa = 1; tentativa <= MAX_TENTATIVAS; tentativa++)Repete no máximo 7 vezes, com tentativa valendo 1, 2, …, 7. Aqui o for é melhor que o while: sabemos o número máximo de voltas.
return tentativa;Acertou: sai da função e do laço na hora, devolvendo quantas tentativas usou.
printf("Suas tentativas acabaram…") e return 0;Só chega aqui quem passou pelas 7 voltas sem acertar.
exit(1);Dentro de uma função, return só sai da função. Para encerrar o programa de qualquer lugar, use exit, do stdlib.h (veja Encerrar o Programa).
int recorde = 0;0 quer dizer "ainda não tem recorde".
usadas > 0 && (recorde == 0 || usadas < recorde)"Ganhou e (é a primeira vitória ou usou menos tentativas que o recorde)". Os parênteses importam: veja Precedência.
while (resposta == 's' || resposta == 'S')Repete as partidas enquanto a resposta for s. A primeira volta sempre acontece, porque resposta começa com 's'.

A sessão do começo da página é esta versão. Se a pessoa digitar texto, o jogo termina com uma mensagem (e o código de saída 1):

$ ./adivinhacao
=== Jogo de Adivinhação ===

Pensei num número de 1 a 100. Você tem 7 tentativas.
Tentativa 1: cinquenta

Fim de jogo: não consegui ler um número.
$ echo $?
1

Testar o limite de tentativas parece depender da sorte: e se a entrada acertar o número sem querer? Truque: chutar 0. O secreto vai de 1 a 100, então 0 nunca acerta, e a resposta é sempre "É maior.". Sete zeros = derrota garantida, seja qual for o número sorteado.

Missão (versão 4, o limite): a função jogar repete para sempre. Troque o while (1) por um for que vai de 1 até MAX_TENTATIVAS e, depois do laço, mostre Suas tentativas acabaram. O número era N. e devolva 0. A entrada da missão chuta 0 sete vezes e responde n.

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

#define MAX_TENTATIVAS 7

// Joga uma partida. Devolve quantas tentativas a pessoa usou,
// ou 0 se ela não acertou dentro do limite.
int jogar(int secreto) {
    printf("\nPensei num número de 1 a 100. Você tem %d tentativas.\n", MAX_TENTATIVAS);
    // limite as tentativas a MAX_TENTATIVAS
    int tentativa = 0;
    while (1) {
        tentativa++;
        int palpite = 0;
        printf("Tentativa %d: ", tentativa);
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            exit(1);
        }
        if (palpite == secreto) {
            printf("Acertou em %d tentativa(s)!\n", tentativa);
            return tentativa;
        } else if (palpite < secreto) {
            printf("É maior.\n");
        } else {
            printf("É menor.\n");
        }
    }
}

int main(void) {
    srand(time(NULL));
    int recorde = 0;      // 0 = ainda não há recorde
    char resposta = 's';

    printf("=== Jogo de Adivinhação ===\n");
    while (resposta == 's' || resposta == 'S') {
        int secreto = rand() % 100 + 1;
        int usadas = jogar(secreto);

        if (usadas > 0 && (recorde == 0 || usadas < recorde)) {
            recorde = usadas;
            printf("Novo recorde: %d tentativa(s)!\n", recorde);
        }

        printf("Jogar de novo? (s/n): ");
        if (scanf(" %c", &resposta) != 1) {
            resposta = 'n';
        }
    }

    if (recorde > 0) {
        printf("Seu recorde: %d tentativa(s).\n", recorde);
    }
    printf("Até a próxima!\n");
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_TENTATIVAS 7

// Joga uma partida. Devolve quantas tentativas a pessoa usou,
// ou 0 se ela não acertou dentro do limite.
int jogar(int secreto) {
    printf("\nPensei num número de 1 a 100. Você tem %d tentativas.\n", MAX_TENTATIVAS);
    for (int tentativa = 1; tentativa <= MAX_TENTATIVAS; tentativa++) {
        int palpite = 0;
        printf("Tentativa %d: ", tentativa);
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            exit(1);
        }
        if (palpite == secreto) {
            printf("Acertou em %d tentativa(s)!\n", tentativa);
            return tentativa;
        } else if (palpite < secreto) {
            printf("É maior.\n");
        } else {
            printf("É menor.\n");
        }
    }
    printf("Suas tentativas acabaram. O número era %d.\n", secreto);
    return 0;
}

int main(void) {
    srand(time(NULL));
    int recorde = 0;      // 0 = ainda não há recorde
    char resposta = 's';

    printf("=== Jogo de Adivinhação ===\n");
    while (resposta == 's' || resposta == 'S') {
        int secreto = rand() % 100 + 1;
        int usadas = jogar(secreto);

        if (usadas > 0 && (recorde == 0 || usadas < recorde)) {
            recorde = usadas;
            printf("Novo recorde: %d tentativa(s)!\n", recorde);
        }

        printf("Jogar de novo? (s/n): ");
        if (scanf(" %c", &resposta) != 1) {
            resposta = 'n';
        }
    }

    if (recorde > 0) {
        printf("Seu recorde: %d tentativa(s).\n", recorde);
    }
    printf("Até a próxima!\n");
    return 0;
}

Missão (versão 4, jogar de novo): o jogo pergunta "Jogar de novo?", mas ignora a resposta. Troque o bloco { … } do main por while (resposta == 's' || resposta == 'S') { … }. A entrada perde uma partida (sete zeros), responde s, perde outra e responde n: o jogo deve mostrar "Pensei num número" duas vezes.

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

#define MAX_TENTATIVAS 7

// Joga uma partida. Devolve quantas tentativas a pessoa usou,
// ou 0 se ela não acertou dentro do limite.
int jogar(int secreto) {
    printf("\nPensei num número de 1 a 100. Você tem %d tentativas.\n", MAX_TENTATIVAS);
    for (int tentativa = 1; tentativa <= MAX_TENTATIVAS; tentativa++) {
        int palpite = 0;
        printf("Tentativa %d: ", tentativa);
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            exit(1);
        }
        if (palpite == secreto) {
            printf("Acertou em %d tentativa(s)!\n", tentativa);
            return tentativa;
        } else if (palpite < secreto) {
            printf("É maior.\n");
        } else {
            printf("É menor.\n");
        }
    }
    printf("Suas tentativas acabaram. O número era %d.\n", secreto);
    return 0;
}

int main(void) {
    srand(time(NULL));
    int recorde = 0;      // 0 = ainda não há recorde
    char resposta = 's';

    printf("=== Jogo de Adivinhação ===\n");
    // repita a partida enquanto a resposta for s ou S
    {
        int secreto = rand() % 100 + 1;
        int usadas = jogar(secreto);

        if (usadas > 0 && (recorde == 0 || usadas < recorde)) {
            recorde = usadas;
            printf("Novo recorde: %d tentativa(s)!\n", recorde);
        }

        printf("Jogar de novo? (s/n): ");
        if (scanf(" %c", &resposta) != 1) {
            resposta = 'n';
        }
    }

    if (recorde > 0) {
        printf("Seu recorde: %d tentativa(s).\n", recorde);
    }
    printf("Até a próxima!\n");
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_TENTATIVAS 7

// Joga uma partida. Devolve quantas tentativas a pessoa usou,
// ou 0 se ela não acertou dentro do limite.
int jogar(int secreto) {
    printf("\nPensei num número de 1 a 100. Você tem %d tentativas.\n", MAX_TENTATIVAS);
    for (int tentativa = 1; tentativa <= MAX_TENTATIVAS; tentativa++) {
        int palpite = 0;
        printf("Tentativa %d: ", tentativa);
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            exit(1);
        }
        if (palpite == secreto) {
            printf("Acertou em %d tentativa(s)!\n", tentativa);
            return tentativa;
        } else if (palpite < secreto) {
            printf("É maior.\n");
        } else {
            printf("É menor.\n");
        }
    }
    printf("Suas tentativas acabaram. O número era %d.\n", secreto);
    return 0;
}

int main(void) {
    srand(time(NULL));
    int recorde = 0;      // 0 = ainda não há recorde
    char resposta = 's';

    printf("=== Jogo de Adivinhação ===\n");
    while (resposta == 's' || resposta == 'S') {
        int secreto = rand() % 100 + 1;
        int usadas = jogar(secreto);

        if (usadas > 0 && (recorde == 0 || usadas < recorde)) {
            recorde = usadas;
            printf("Novo recorde: %d tentativa(s)!\n", recorde);
        }

        printf("Jogar de novo? (s/n): ");
        if (scanf(" %c", &resposta) != 1) {
            resposta = 'n';
        }
    }

    if (recorde > 0) {
        printf("Seu recorde: %d tentativa(s).\n", recorde);
    }
    printf("Até a próxima!\n");
    return 0;
}

O programa completo

A versão final, igual à versão 4. Compile com gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao:

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

#define MAX_TENTATIVAS 7

// Joga uma partida. Devolve quantas tentativas a pessoa usou,
// ou 0 se ela não acertou dentro do limite.
int jogar(int secreto) {
    printf("\nPensei num número de 1 a 100. Você tem %d tentativas.\n", MAX_TENTATIVAS);
    for (int tentativa = 1; tentativa <= MAX_TENTATIVAS; tentativa++) {
        int palpite = 0;
        printf("Tentativa %d: ", tentativa);
        if (scanf("%d", &palpite) != 1) {
            printf("\nFim de jogo: não consegui ler um número.\n");
            exit(1);
        }
        if (palpite == secreto) {
            printf("Acertou em %d tentativa(s)!\n", tentativa);
            return tentativa;
        } else if (palpite < secreto) {
            printf("É maior.\n");
        } else {
            printf("É menor.\n");
        }
    }
    printf("Suas tentativas acabaram. O número era %d.\n", secreto);
    return 0;
}

int main(void) {
    srand(time(NULL));
    int recorde = 0;      // 0 = ainda não há recorde
    char resposta = 's';

    printf("=== Jogo de Adivinhação ===\n");
    while (resposta == 's' || resposta == 'S') {
        int secreto = rand() % 100 + 1;
        int usadas = jogar(secreto);

        if (usadas > 0 && (recorde == 0 || usadas < recorde)) {
            recorde = usadas;
            printf("Novo recorde: %d tentativa(s)!\n", recorde);
        }

        printf("Jogar de novo? (s/n): ");
        if (scanf(" %c", &resposta) != 1) {
            resposta = 'n';
        }
    }

    if (recorde > 0) {
        printf("Seu recorde: %d tentativa(s).\n", recorde);
    }
    printf("Até a próxima!\n");
    return 0;
}

O que o projeto juntou: scanf, else if, while, for, ++, && e ||, rand e srand, #define, funções e exit.

Rodando no laboratório

Uma partida a mais na máquina da Ana, só para ver o número mudar. Ela chutou o meio e precisou das 7 tentativas, por pouco:

$ gcc -std=c23 -Wall -Wextra adivinhacao.c -o adivinhacao
$ ./adivinhacao
=== Jogo de Adivinhação ===

Pensei num número de 1 a 100. Você tem 7 tentativas.
Tentativa 1: 50
É menor.
Tentativa 2: 25
É maior.
Tentativa 3: 37
É menor.
Tentativa 4: 31
É maior.
Tentativa 5: 34
É maior.
Tentativa 6: 35
É maior.
Tentativa 7: 36
Acertou em 7 tentativa(s)!
Novo recorde: 7 tentativa(s)!
Jogar de novo? (s/n): n
Seu recorde: 7 tentativa(s).
Até a próxima!

Desafios extras

  1. Dica de intervalo. Em vez de "É maior.", mostre o intervalo que sobrou: Está entre 51 e 100. Guarde dois inteiros, menor e maior, e atualize a cada palpite.
  2. Palpite fora do intervalo. Se a pessoa chutar 0 ou 150, avise Escolha de 1 a 100. e não conte a tentativa.
  3. Níveis. Pergunte no começo: fácil (1 a 50, 10 tentativas), médio (1 a 100, 7) ou difícil (1 a 1000, 10). A função jogar passa a receber o máximo e o limite como parâmetros.
  4. Pedir de novo em vez de encerrar. Quando a pessoa digitar texto, peça o palpite de novo. Use fgets e strtol, como a Calculadora faz com strtod.
  5. Recorde guardado. Grave o recorde num arquivo recorde.txt ao sair e leia ao começar, para ele valer de um dia para o outro (veja Escrever Arquivos e Ler Arquivos).

Exercícios

1. Qual expressão sorteia um número de 1 a 6, como um dado?

2. A Ana esqueceu o srand(time(NULL)). O que acontece?

3. Por que a missão do limite chuta 0 e não, por exemplo, 50?

4. Na função jogar, o que o return tentativa; dentro do for faz?

5. Complete a linha que embaralha a sequência do rand() usando o relógio:

(time(NULL));

6. Complete o laço que dá no máximo MAX_TENTATIVAS voltas, começando em 1:

for (int t = 1; t MAX_TENTATIVAS; t++)

Resumo

  • Teste primeiro com um número fixo; sorteie depois.
  • while (palpite != secreto) repete até acertar; um contador com ++ conta as tentativas.
  • Confira o retorno do scanf: texto na entrada vira laço infinito.
  • rand() % 100 + 1 sorteia de 1 a 100; srand(time(NULL)), uma vez no começo, muda a sequência a cada execução.
  • Os números do rand() mudam de uma biblioteca C para outra (gcc × editor do guia): não dependa deles.
  • Para testar sem depender da sorte: palpites que cobrem todos os números, ou palpites que nunca acertam (0).

Próximo projeto: Boletim de Notas.