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!", até você acertar. No fim, mostra quantas tentativas você precisou e se bateu o recorde. É o projeto clássico de quem está aprendendo a programar, e por um bom motivo: ele junta decisões, laços, contadores, funções e números aleatórios num programa curto e divertido.

Você vai praticar

O que vamos construir

Esta é uma partida do jogo pronto na máquina da Ana. Os números depois de "Seu palpite" foram digitados por ela; a estratégia é sempre chutar o meio do que sobrou:

$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É maior!
Seu palpite (1 a 100): 75
É menor!
Seu palpite (1 a 100): 62
É menor!
Seu palpite (1 a 100): 56
É menor!
Seu palpite (1 a 100): 53
É maior!
Seu palpite (1 a 100): 54
Acertou em 6 tentativas!
Novo recorde!
Recorde: 6 tentativas
Jogar de novo? (s/n): n
Até a próxima!

Seis etapas até lá:

  1. Um palpite, com o número secreto fixo (if, else if).
  2. Repetir até acertar (while).
  3. Contar as tentativas (++).
  4. Sortear o número (<random> com semente fixa).
  5. Jogar de novo (funções, do-while, referência).
  6. Recorde, e a semente de verdade no código final.

Antes de começar

Você precisa saber ler números com cin, usar if/else e while. A Calculadora é um bom aquecimento: este projeto reaproveita várias ideias dela. Crie a pasta do projeto; o arquivo vai se chamar adivinhacao.cpp (sem acento e sem cedilha no nome do arquivo, para evitar problemas no terminal).

$ mkdir -p cpp/adivinhacao
$ cd cpp/adivinhacao

Etapa 1 · Um palpite

Antes de sortear qualquer coisa, vamos fazer o jogo funcionar com um número secreto fixo: 42. Assim você sabe a resposta e consegue testar cada caminho do programa. Esse é um truque que todo programador usa: primeiro faça funcionar com valores conhecidos, depois troque pelos valores de verdade.

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    std::cout << "Seu palpite (1 a 100): ";
    std::cin >> palpite;

    if (palpite < secreto) {
        std::cout << "É maior!\n";
    } else if (palpite > secreto) {
        std::cout << "É menor!\n";
    } else {
        std::cout << "Acertou!\n";
    }
}
const int secreto = 42;
O número a adivinhar. É const porque não muda durante o jogo (veja Constantes).
if (palpite < secreto)
Se o palpite for menor que o secreto, o secreto é maior: a dica é "É maior!". Cuidado para não inverter: a dica fala do número secreto, não do palpite.
else if (palpite > secreto)
Senão, se o palpite for maior, a dica é "É menor!" (veja else if).
else
Nem menor nem maior: só pode ser igual. Acertou!
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Seu palpite (1 a 100): 50
É menor!
$ ./adivinhacao
=== Adivinhe o número ===
Seu palpite (1 a 100): 42
Acertou!

Missão (etapa 1): o programa só sabe dizer "É maior!". Complete com um else if que mostre É menor! quando o palpite for maior que o secreto, e um else que mostre Acertou!. A entrada da missão é 50. Depois, troque a entrada por 42 e por 10 e confira os outros caminhos.

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    std::cout << "Seu palpite (1 a 100): ";
    std::cin >> palpite;

    if (palpite < secreto) {
        std::cout << "É maior!\n";
    }
    // acrescente: se o palpite for maior que o secreto, "É menor!";
    // em qualquer outro caso, "Acertou!"
}
#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    std::cout << "Seu palpite (1 a 100): ";
    std::cin >> palpite;

    if (palpite < secreto) {
        std::cout << "É maior!\n";
    } else if (palpite > secreto) {
        std::cout << "É menor!\n";
    } else {
        std::cout << "Acertou!\n";
    }
}

Etapa 2 · Repetir até acertar

Um palpite só não é jogo. Queremos repetir "pergunta, dica" enquanto o palpite for diferente do secreto. Essa frase já é o código: while (palpite != secreto) (veja while).

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        } else {
            std::cout << "Acertou!\n";
        }
    }
}
LinhaO que faz
int palpite = 0;Agora a variável precisa existir antes do laço, porque a condição do while a usa logo na primeira volta. Começa com 0, que nunca é o secreto: o laço sempre roda pelo menos uma vez.
while (palpite != secreto)"Enquanto o palpite for diferente do secreto, repita." Quando a pessoa acerta, a dica "Acertou!" aparece e a condição fica falsa: o laço termina.
if (!(std::cin >> palpite))Lê o palpite e confere se a leitura deu certo. Se a pessoa digitou um texto (ou a entrada acabou), encerra o jogo com return 1;. O porquê está logo abaixo.
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 42
Acertou!
Por que conferir a leitura: o laço infinito

Na etapa 1, se a Ana digitasse abc, o cin falhava, o palpite ficava 0 e o programa dizia "É maior!" e terminava. Com o laço e sem o if (!(std::cin >> palpite)), o cin fica em estado de falha, cada volta "lê" 0 sem esperar, e o laço nunca termina. A Ana rodou essa versão com timeout 3 e head -5 para não travar o terminal:

$ printf "abc\n" | timeout 3 ./adivinhacao | head -5
=== Adivinhe o número ===
Seu palpite (1 a 100): É maior!
Seu palpite (1 a 100): É maior!
Seu palpite (1 a 100): É maior!
Seu palpite (1 a 100): É maior!

Como consertar: sempre que um laço lê do teclado, confira se a leitura deu certo. Aqui, simplesmente encerramos o jogo. A etapa 5 da Calculadora mostra como pedir de novo em vez de encerrar (com clear() e getline), e a aula Validar a Entrada explica tudo com calma.

Missão (etapa 2): este é o código da etapa 1. Coloque a pergunta, a leitura e as dicas dentro de um while (palpite != secreto). A entrada da missão tem três palpites: 50, 25 e 42.

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    std::cout << "Seu palpite (1 a 100): ";
    std::cin >> palpite;

    if (palpite < secreto) {
        std::cout << "É maior!\n";
    } else if (palpite > secreto) {
        std::cout << "É menor!\n";
    } else {
        std::cout << "Acertou!\n";
    }
}
#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        } else {
            std::cout << "Acertou!\n";
        }
    }
}

Etapa 3 · Contar as tentativas

Quantos palpites a pessoa precisou? Para saber, usamos um contador: uma variável que começa em 0 e ganha 1 a cada palpite.

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;
    int tentativas = 0;

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }
        ++tentativas;

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
}
int tentativas = 0;
O contador, criado antes do laço. Se fosse criado dentro, voltaria a zero a cada volta.
++tentativas;
Soma 1 (veja ++ e --). Fica logo depois de uma leitura que deu certo: só conta palpites de verdade.
std::cout << "Acertou em " …
A mensagem de acerto saiu de dentro do laço e foi para depois dele. Quando o laço termina, é porque a pessoa acertou: é o lugar certo para comemorar e mostrar o total. Por isso o else sumiu.
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 42
Acertou em 3 tentativas!

Missão (etapa 3): acrescente o contador tentativas e, quando o laço terminar, mostre Acertou em 3 tentativas! (com o número vindo da variável). Não esqueça de tirar o "Acertou!" de dentro do laço.

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        } else {
            std::cout << "Acertou!\n";
        }
    }
}
#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;
    int tentativas = 0;

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }
        ++tentativas;

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
}

Etapa 4 · Sortear o número

Hora de deixar o computador escolher. Números aleatórios em C++ moderno usam o cabeçalho <random> e duas peças, explicadas em detalhes na aula Números Aleatórios:

PeçaComparaçãoNo código
Gerador (motor)Uma máquina que solta números enormes, sem padrão aparente.std::mt19937 gerador(42);
DistribuiçãoUm "funil" que transforma esses números enormes num intervalo que você escolhe, com chances iguais.std::uniform_int_distribution<int> sorteio(1, 100);
SortearPassar o gerador pelo funil.sorteio(gerador)
#include <iostream>
#include <random>

int main() {
    std::cout << "=== Adivinhe o número ===\n";

    std::mt19937 gerador(42);  // semente fixa: sorteia sempre o mesmo número (bom para testar)
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);

    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }
        ++tentativas;

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
}
std::mt19937
O gerador mais usado. O nome vem do algoritmo Mersenne Twister (mt) e do número 19937 que faz parte da matemática dele. Não precisa decorar.
gerador(42)
O 42 é a semente: o ponto de partida do gerador. A mesma semente produz sempre a mesma sequência de números. Parece um defeito, mas é ótimo para testar: o jogo fica previsível enquanto você o constrói.
std::uniform_int_distribution<int> sorteio(1, 100);
"Distribuição uniforme de inteiros": todos os números de 1 a 100 (incluindo os dois) têm a mesma chance.
const int secreto = sorteio(gerador);
Sorteia uma vez e guarda. Continua const: depois de sorteado, o secreto não muda.
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É maior!
Seu palpite (1 a 100): 43
É menor!
Seu palpite (1 a 100): 40
É menor!
Seu palpite (1 a 100): 38
Acertou em 6 tentativas!

Com a semente 42, o g++ da Ana sorteia o 38, e vai sortear 38 toda vez que o programa rodar. Um programa só para ver os primeiros sorteios, rodado duas vezes:

#include <iostream>
#include <random>

int main() {
    std::mt19937 gerador(42);
    std::uniform_int_distribution<int> sorteio(1, 100);
    for (int i = 0; i < 5; ++i) std::cout << sorteio(gerador) << ' ';
    std::cout << '\n';
    std::mt19937 g2(42);
    for (int i = 0; i < 3; ++i) std::cout << g2() << ' ';
    std::cout << '\n';
}
$ g++ -std=c++23 -Wall -Wextra semente.cpp -o semente
$ ./semente
38 80 96 19 74
1608637542 3421126067 4083286876
$ ./semente
38 80 96 19 74                      « mesma semente, mesma sequência
1608637542 3421126067 4083286876    « os números "crus" do gerador, antes do funil
No editor do guia, o número sorteado pode ser outro

O C++ garante que o mt19937 com a mesma semente solta os mesmos números "crus" em qualquer computador (a segunda linha acima). Mas o "funil" (uniform_int_distribution) é escrito por cada biblioteca do seu jeito. O g++ usa a biblioteca libstdc++ e sorteia 38 80 96 19 74 com a semente 42; o editor do guia usa o Clang com a biblioteca libc++ e, com a mesma semente, sorteia 52 93 15 72 61. Os dois estão certos: o C++ só exige que todos os números tenham a mesma chance. Por isso as missões a partir daqui não dependem de qual número sai: a entrada da missão chuta 1, 2, 3, … até 100, todos na mesma linha, e para quando acerta.

Experimente: rode o semente.cpp no editor e compare com a saída do g++ acima. A segunda linha (os números crus) é igual; a primeira, não. Depois troque a semente 42 por outro número e rode de novo.

#include <iostream>
#include <random>

int main() {
    std::mt19937 gerador(42);
    std::uniform_int_distribution<int> sorteio(1, 100);
    for (int i = 0; i < 5; ++i) std::cout << sorteio(gerador) << ' ';
    std::cout << '\n';
    std::mt19937 g2(42);
    for (int i = 0; i < 3; ++i) std::cout << g2() << ' ';
    std::cout << '\n';
}
Esqueceu o #include <random>
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
adivinhacao.cpp: In function ‘int main()’:
adivinhacao.cpp:6:10: error: ‘mt19937’ is not a member of ‘std’
    6 |     std::mt19937 gerador(42);  // semente fixa: sorteia sempre o mesmo número (bom para testar)
      |          ^~~~~~~
adivinhacao.cpp:8:33: error: ‘gerador’ was not declared in this scope
    8 |     const int secreto = sorteio(gerador);
      |                                 ^~~~~~~

O que significa: "mt19937 não é membro de std": sem o cabeçalho, o compilador não conhece o gerador. O segundo erro é consequência do primeiro: como a linha 6 falhou, a variável gerador não existe. Curiosamente, o g++ não reclamou do uniform_int_distribution: o <iostream> do g++ 15 acaba trazendo essa parte por acaso. Não conte com isso: em outro compilador, ela também faltaria.

Como consertar: #include <random>. Inclua sempre o cabeçalho de tudo o que você usa.

Missão (etapa 4): troque o const int secreto = 42; por um sorteio: crie um std::mt19937 gerador(42);, uma std::uniform_int_distribution<int> sorteio(1, 100); e sorteie o secreto. Não esqueça o #include <random>. A entrada chuta 1, 2, 3… e para no número sorteado; o total de tentativas é o próprio número.

#include <iostream>

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    const int secreto = 42;
    int palpite = 0;
    int tentativas = 0;

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }
        ++tentativas;

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
}
#include <iostream>
#include <random>

int main() {
    std::cout << "=== Adivinhe o número ===\n";

    std::mt19937 gerador(42);  // semente fixa: sorteia sempre o mesmo número (bom para testar)
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);

    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            return 1;
        }
        ++tentativas;

        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
}

Etapa 5 · Jogar de novo

Para jogar várias partidas, o jeito mais limpo é transformar "uma partida" numa função e chamá-la num do-while, como o "Outra conta?" da Calculadora. Vamos criar três funções:

FunçãoTarefa
int ler_palpite()Pergunta e lê um palpite. Se a leitura falhar, encerra o jogo.
int jogar(std::mt19937& gerador)Sorteia o número, joga uma partida inteira e devolve o número de tentativas.
bool quer_jogar_de_novo()Pergunta "Jogar de novo? (s/n)".
#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    do {
        jogar(gerador);
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}
std::exit(1);
Dentro de ler_palpite, um return só sairia da função. Para encerrar o programa inteiro de dentro de uma função, usamos std::exit, do <cstdlib>.
int jogar(std::mt19937& gerador)
O & é essencial: a função recebe o mesmo gerador do main, por referência. Cada partida avança o gerador, e a próxima sorteia um número novo. Veja o cenário abaixo para o que acontece sem o &.
std::uniform_int_distribution<int> sorteio(1, 100);
A distribuição pode ser criada dentro de jogar: ela é só o "funil" e não guarda nada importante entre uma partida e outra.
return tentativas;
A partida devolve quantas tentativas foram precisas. Nesta etapa o main ainda ignora esse valor; na etapa 6 ele vira o recorde.
std::getline(std::cin, resto);
Em quer_jogar_de_novo: descarta o que sobrou na linha do último palpite. Se a pessoa digitou 38 40, o >> leu o 38 (e acertou), e o 40 ainda está lá. Sem o descarte, ele seria lido como resposta da pergunta seguinte.
std::mt19937 gerador(42);
Agora o gerador mora no main e é criado uma vez só, antes de todas as partidas.
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É maior!
Seu palpite (1 a 100): 43
É menor!
Seu palpite (1 a 100): 40
É menor!
Seu palpite (1 a 100): 38
Acertou em 6 tentativas!
Jogar de novo? (s/n): s
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É maior!
Seu palpite (1 a 100): 75
É maior!
Seu palpite (1 a 100): 88
É menor!
Seu palpite (1 a 100): 81
É menor!
Seu palpite (1 a 100): 78
É maior!
Seu palpite (1 a 100): 79
É maior!
Seu palpite (1 a 100): 80
Acertou em 7 tentativas!               « segunda partida: 80, o segundo número da sequência
Jogar de novo? (s/n): n
Até a próxima!
Sem o &: toda partida sorteia o mesmo número

Este código tem um erro de propósito: int jogar(std::mt19937 gerador), sem o &.

$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É maior!
Seu palpite (1 a 100): 43
É menor!
Seu palpite (1 a 100): 40
É menor!
Seu palpite (1 a 100): 38
Acertou em 6 tentativas!
Jogar de novo? (s/n): s
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 38
Acertou em 1 tentativas!               « a Ana já sabia a resposta…

O que significa: nenhum erro nem aviso: o programa está "certo" para o compilador. Mas, sem o &, a função recebe uma cópia do gerador (veja Por Valor × Por Referência). A cópia sorteia, avança, e é jogada fora no fim da partida. O gerador do main nunca sai do lugar, e toda partida começa do mesmo ponto: 38 de novo.

Como consertar: passe o gerador por referência: std::mt19937& gerador. Regra geral: gerador de números aleatórios, sempre por referência.

Sem o descarte da linha: o jogo termina sozinho

Na versão sem o std::getline(std::cin, resto);, a Ana digitou 38 40 no último palpite:

$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É maior!
Seu palpite (1 a 100): 43
É menor!
Seu palpite (1 a 100): 40
É menor!
Seu palpite (1 a 100): 38 40
Acertou em 6 tentativas!
Jogar de novo? (s/n): Até a próxima!

O 40 que sobrou virou a resposta: o char resposta leu o '4', que não é s, e o jogo acabou sem perguntar. Com o getline, a sobra é descartada e a pergunta espera a Ana responder.

Missão (etapa 5): as funções estão prontas, mas o main joga uma partida só. Coloque a chamada jogar(gerador); dentro de um do { … } while (quer_jogar_de_novo());. A entrada joga duas partidas (chutando 1, 2, 3…) e responde s e depois n.

#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    jogar(gerador);
    // repita a partida enquanto quer_jogar_de_novo() devolver true

    std::cout << "Até a próxima!\n";
}
#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    do {
        jogar(gerador);
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}

Etapa 6 · Recorde

O recorde é o menor número de tentativas entre todas as partidas. Para guardá-lo, uma variável no main, que sobrevive de uma partida para outra. Depois de cada partida, comparamos: se foi melhor, temos um novo recorde.

Só o main muda:

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    int recorde = 0;  // 0 = ainda não existe recorde

    do {
        const int tentativas = jogar(gerador);
        if (recorde == 0 || tentativas < recorde) {
            recorde = tentativas;
            std::cout << "Novo recorde!\n";
        }
        std::cout << "Recorde: " << recorde << " tentativas\n";
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}
int recorde = 0;
Criado fora do laço, para não ser zerado a cada partida. O 0 tem um significado combinado: "ainda não existe recorde". Ninguém acerta com 0 tentativas, então não há confusão.
const int tentativas = jogar(gerador);
Agora o main guarda o que a partida devolveu.
if (recorde == 0 || tentativas < recorde)
"Se ainda não havia recorde (primeira partida) ou se esta partida foi melhor." O || é o "ou" (veja Operadores Lógicos). Sem a primeira parte, nenhuma partida bateria o "recorde" 0.
$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É maior!
Seu palpite (1 a 100): 43
É menor!
Seu palpite (1 a 100): 40
É menor!
Seu palpite (1 a 100): 38
Acertou em 6 tentativas!
Novo recorde!                          « a primeira partida sempre é recorde
Recorde: 6 tentativas
Jogar de novo? (s/n): s
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É maior!
Seu palpite (1 a 100): 75
É maior!
Seu palpite (1 a 100): 80
Acertou em 3 tentativas!               « um chute de sorte
Novo recorde!
Recorde: 3 tentativas
Jogar de novo? (s/n): s
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É maior!
Seu palpite (1 a 100): 75
É maior!
Seu palpite (1 a 100): 90
É maior!
Seu palpite (1 a 100): 95
É maior!
Seu palpite (1 a 100): 98
É menor!
Seu palpite (1 a 100): 96
Acertou em 6 tentativas!
Recorde: 3 tentativas                  « 6 não bate 3: o recorde continua
Jogar de novo? (s/n): n
Até a próxima!

Missão (etapa 6): guarde o recorde. Crie int recorde = 0; antes do laço, guarde o retorno de jogar e, se for a primeira partida ou se for melhor, atualize o recorde e mostre Novo recorde!. Depois de cada partida, mostre Recorde: N tentativas.

#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    do {
        jogar(gerador);
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}
#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    int recorde = 0;  // 0 = ainda não existe recorde

    do {
        const int tentativas = jogar(gerador);
        if (recorde == 0 || tentativas < recorde) {
            recorde = tentativas;
            std::cout << "Novo recorde!\n";
        }
        std::cout << "Recorde: " << recorde << " tentativas\n";
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}

A semente de verdade

O jogo está pronto, mas com a semente 42 a primeira partida é sempre o 38. Para o jogo de verdade, a semente precisa mudar a cada vez que o programa roda. Quem fornece essa semente é o std::random_device, que pede ao sistema operacional um número imprevisível (veja Números Aleatórios). Uma linha muda:

    std::mt19937 gerador(std::random_device{}());  // semente real: muda a cada execução
std::random_device{}
Cria um random_device temporário (as chaves vazias criam o objeto).
()
Chama esse objeto para obter um número imprevisível.
gerador(…)
Esse número vira a semente do mt19937.

Por que não usar o random_device para sortear tudo? Ele pode ser lento (às vezes conversa com o hardware). O costume é usá-lo uma vez, para a semente, e deixar o mt19937, que é rápido, fazer o resto.

Duas partidas com a versão final, uma logo depois da outra:

$ g++ -std=c++23 -Wall -Wextra adivinhacao.cpp -o adivinhacao
$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É maior!
Seu palpite (1 a 100): 75
É menor!
Seu palpite (1 a 100): 62
É menor!
Seu palpite (1 a 100): 56
É menor!
Seu palpite (1 a 100): 53
É maior!
Seu palpite (1 a 100): 54
Acertou em 6 tentativas!
Novo recorde!
Recorde: 6 tentativas
Jogar de novo? (s/n): n
Até a próxima!
$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É menor!
Seu palpite (1 a 100): 31
É menor!
Seu palpite (1 a 100): 28
Acertou em 5 tentativas!               « outro número: 54 e depois 28
Novo recorde!
Recorde: 5 tentativas
Jogar de novo? (s/n): n
Até a próxima!

Quando você rodar, os números serão outros: essa é a ideia. Repare também que o recorde só vale enquanto o programa está aberto; o desafio 3 abaixo resolve isso.


O código completo

#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(std::random_device{}());  // semente real: muda a cada execução

    int recorde = 0;  // 0 = ainda não existe recorde

    do {
        const int tentativas = jogar(gerador);
        if (recorde == 0 || tentativas < recorde) {
            recorde = tentativas;
            std::cout << "Novo recorde!\n";
        }
        std::cout << "Recorde: " << recorde << " tentativas\n";
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}

O projeto juntou if/else if, while, do-while, contadores, <random>, funções e referência. A estratégia de chutar o meio, que a Ana usou, se chama busca binária: com ela, nenhum número de 1 a 100 precisa de mais de 7 tentativas. Você vai reencontrá-la em Algoritmos.


Desafios extras

  1. Palpite fora do intervalo. Se a pessoa digitar 0 ou 150, mostre Digite um número de 1 a 100. e pergunte de novo, sem contar a tentativa. (Faça no editor, logo abaixo.)
  2. Singular. Quem acerta de primeira lê "Acertou em 1 tentativas!". Use o operador ternário para escrever "tentativa" quando for 1.
  3. Recorde que não se perde. Grave o recorde num arquivo recorde.txt ao sair e leia-o ao abrir (veja Ler e Escrever Arquivos e o projeto Agenda).
  4. Níveis de dificuldade. Pergunte o nível no começo: fácil (1 a 10), médio (1 a 100) ou difícil (1 a 1000). Use um enum class para o nível.
  5. Texto no palpite. Em vez de encerrar o jogo quando alguém digita abc, peça de novo, como na Calculadora.

Desafio 1 no editor: mude ler_palpite para repetir a pergunta enquanto o palpite estiver fora de 1 a 100, mostrando Digite um número de 1 a 100.. A entrada começa com 0 e 150.

#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite. Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    std::cout << "Seu palpite (1 a 100): ";
    if (!(std::cin >> palpite)) {
        std::cout << "\nEntrada inválida. Fim do jogo.\n";
        std::exit(1);
    }
    return palpite;
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    int recorde = 0;  // 0 = ainda não existe recorde

    do {
        const int tentativas = jogar(gerador);
        if (recorde == 0 || tentativas < recorde) {
            recorde = tentativas;
            std::cout << "Novo recorde!\n";
        }
        std::cout << "Recorde: " << recorde << " tentativas\n";
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}
#include <cstdlib>
#include <iostream>
#include <random>
#include <string>

// Lê um palpite de 1 a 100. Repete se estiver fora do intervalo.
// Se a entrada acabar ou não for número, encerra o jogo.
int ler_palpite() {
    int palpite = 0;
    while (true) {
        std::cout << "Seu palpite (1 a 100): ";
        if (!(std::cin >> palpite)) {
            std::cout << "\nEntrada inválida. Fim do jogo.\n";
            std::exit(1);
        }
        if (palpite >= 1 && palpite <= 100) {
            return palpite;
        }
        std::cout << "Digite um número de 1 a 100.\n";
    }
}

// Joga uma partida e devolve quantas tentativas foram precisas.
int jogar(std::mt19937& gerador) {
    std::uniform_int_distribution<int> sorteio(1, 100);
    const int secreto = sorteio(gerador);
    int palpite = 0;
    int tentativas = 0;
    std::cout << "Pensei num número de 1 a 100.\n";

    while (palpite != secreto) {
        palpite = ler_palpite();
        ++tentativas;
        if (palpite < secreto) {
            std::cout << "É maior!\n";
        } else if (palpite > secreto) {
            std::cout << "É menor!\n";
        }
    }
    std::cout << "Acertou em " << tentativas << " tentativas!\n";
    return tentativas;
}

bool quer_jogar_de_novo() {
    std::string resto;
    std::getline(std::cin, resto);  // descarta o resto da linha do último palpite
    char resposta = 'n';
    std::cout << "Jogar de novo? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Adivinhe o número ===\n";
    std::mt19937 gerador(42);  // semente fixa, para testar

    int recorde = 0;  // 0 = ainda não existe recorde

    do {
        const int tentativas = jogar(gerador);
        if (recorde == 0 || tentativas < recorde) {
            recorde = tentativas;
            std::cout << "Novo recorde!\n";
        }
        std::cout << "Recorde: " << recorde << " tentativas\n";
    } while (quer_jogar_de_novo());

    std::cout << "Até a próxima!\n";
}

Na máquina da Ana, a solução do desafio 1 rodou assim (com a semente 42):

$ ./adivinhacao
=== Adivinhe o número ===
Pensei num número de 1 a 100.
Seu palpite (1 a 100): 0
Digite um número de 1 a 100.
Seu palpite (1 a 100): 150
Digite um número de 1 a 100.
Seu palpite (1 a 100): 50
É menor!
Seu palpite (1 a 100): 25
É maior!
Seu palpite (1 a 100): 37
É maior!
Seu palpite (1 a 100): 43
É menor!
Seu palpite (1 a 100): 40
É menor!
Seu palpite (1 a 100): 38
Acertou em 6 tentativas!               « o 0 e o 150 não contaram
Novo recorde!
Recorde: 6 tentativas
Jogar de novo? (s/n): n
Até a próxima!

Exercícios

1. O palpite foi 30 e o número secreto é 42. Qual dica o jogo deve mostrar?

2. Com std::mt19937 gerador(42);, o que acontece ao rodar o programa várias vezes?

3. Por que jogar recebe std::mt19937& gerador, com &?

4. Onde deve ser criada a variável recorde?

5. Complete a distribuição que sorteia números de 1 a 6, como um dado:

std::uniform_int_distribution<int> dado();

6. Complete o laço que repete enquanto a pessoa não acertar:

while (palpite secreto) {

Resumo

  • Comece com valores fixos (secreto = 42) para testar; troque pelos valores de verdade no fim.
  • while (palpite != secreto) repete até acertar; um contador ++tentativas conta os palpites.
  • Laço que lê do teclado precisa conferir a leitura (if (!(std::cin >> x))), senão vira laço infinito.
  • <random>: um gerador (std::mt19937), uma distribuição (uniform_int_distribution) e uma semente. Semente fixa = sequência repetível (por compilador); std::random_device{}() = semente que muda.
  • Passe o gerador por referência (&), senão cada chamada usa uma cópia e repete os números.
  • O recorde mora fora do laço e começa num valor que significa "nenhum".

Próximo projeto: Agenda de Contatos, com struct, vector, menu e arquivo.