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.
- if e else if para as dicas.
- O laço while ("até acertar") e o do-while ("jogar de novo?").
- Contadores com ++.
- Números aleatórios com
<random>: gerador, distribuição e semente. - Funções e passagem por referência (e o que acontece sem ela).
- Guardar o melhor resultado: o recorde.
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á:
- Um palpite, com o número secreto fixo (
if,else if). - Repetir até acertar (
while). - Contar as tentativas (
++). - Sortear o número (
<random>com semente fixa). - Jogar de novo (funções,
do-while, referência). - 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. É
constporque 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";
}
}
}
| Linha | O 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!
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
elsesumiu.
$ 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ça | Comparação | No código |
|---|---|---|
| Gerador (motor) | Uma máquina que solta números enormes, sem padrão aparente. | std::mt19937 gerador(42); |
| Distribuição | Um "funil" que transforma esses números enormes num intervalo que você escolhe, com chances iguais. | std::uniform_int_distribution<int> sorteio(1, 100); |
| Sortear | Passar 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
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';
}
#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ção | Tarefa |
|---|---|
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, umreturnsó sairia da função. Para encerrar o programa inteiro de dentro de uma função, usamosstd::exit, do<cstdlib>. - int jogar(std::mt19937& gerador)
- O
&é essencial: a função recebe o mesmo gerador domain, 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
mainainda 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 digitou38 40, o>>leu o 38 (e acertou), e o40ainda está lá. Sem o descarte, ele seria lido como resposta da pergunta seguinte. - std::mt19937 gerador(42);
- Agora o gerador mora no
maine é 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!
&: 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.
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
mainguarda 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_devicetemporá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
- Palpite fora do intervalo. Se a pessoa digitar
0ou150, mostreDigite um número de 1 a 100.e pergunte de novo, sem contar a tentativa. (Faça no editor, logo abaixo.) - Singular. Quem acerta de primeira lê "Acertou em 1 tentativas!". Use o operador ternário para escrever "tentativa" quando for 1.
- Recorde que não se perde. Grave o recorde num arquivo
recorde.txtao sair e leia-o ao abrir (veja Ler e Escrever Arquivos e o projeto Agenda). - 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.
- 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:
6. Complete o laço que repete enquanto a pessoa não acertar:
Resumo
- Comece com valores fixos (
secreto = 42) para testar; troque pelos valores de verdade no fim. while (palpite != secreto)repete até acertar; um contador++tentativasconta 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.