Dúvida em try/catch(pergunta de continuar jogando)

Estou tentando fazer um do-while com um try-catch, pois quando utilizo apenas o do-while e, quando é inserido um caracter (vogal ou letra), ele encerra o código informando o erro java.util.InputMismatchException. Então tentei tratar o erro com o try-catch, fazendo com q, caso seja digitado um caracter, ele retorne a pergunta se deseja jogar.

A ideia era como se ele atribuísse o pgtnovamente2 = true;

Segue o código abaixo. Desde já, agradeço a atenção!!

do {
  try {
    System.out.println("Deseja continuar (1-sim/2-nao)?");
    System.out.println("Digite o numero correspondente:");
    pgt = sc.nextInt();
    System.out.println();
    
    if (pgt == 1) {
      ptgnovamente2 = false;
      pergunta = "sim";
    } else if (pgt == 2) {
      ptgnovamente2 = false;
      pergunta = "nao";
    } else {
      ptgnovamente2 = true;
    }
  } catch (InputMismatchException err) {
    return;
  }
} while (ptgnovamente2);
} while (pergunta.equalsIgnoreCase("sim"));

Para que o loop continue caso dê alguma exceção, vc não pode usar o return, pois isso irá encerrar a execução do método. Em vez disso use continue.

Uma coisa que reparei é que tem 2 while no seu código.

import java.util.InputMismatchException;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String pergunta = "";
		
		do {
			try {
				System.out.println("Deseja continuar (1-sim/2-nao)?");
				System.out.println("Digite o numero correspondente:");
				int pgt = Integer.parseInt(sc.nextLine());
				System.out.println();

				if (pgt == 1) {
					pergunta = "sim";
				} else if (pgt == 2) {
					pergunta = "nao";
				}
			} catch (InputMismatchException | NumberFormatException err) {
				continue;
			}
		} while (pergunta.isEmpty() || pergunta.equalsIgnoreCase("sim"));
		
		System.out.println("Programa finalizado.");
		
		sc.close();
	}
}
2 curtidas

Se as opções válidas são 1 e 2, acho que vale a pena dar alguma mensagem caso a opção seja inválida (se eu digitar 3, por exemplo).

E se você já definiu que 1 é sim e 2 é não, para que setar uma string com esses valores? Teste os valores numéricos mesmo e pronto:

Scanner sc = new Scanner(System.in);
while (true) {
    try {
        System.out.println("Deseja continuar (1-sim/2-nao)?\n\nDigite o numero correspondente:");
        int opcao = Integer.parseInt(sc.nextLine());
        if (opcao != 1 && opcao != 2) {
            System.out.println("Opção inválida, digite apenas 1 ou 2");
        } else if (opcao == 2) {
            break; // sai do while
        }
    } catch (NumberFormatException e) {
        System.out.println("Você não digitou um número");
    }
}

System.out.println("Programa finalizado.");

Ou seja, no if eu vejo se o número não é 1 nem 2 (aí imprimo a mensagem dizendo que o número não é uma das opções válidas).

Se não entrar nesse if, é porque digitou 1 ou 2. Então eu só preciso ver se é 2, e caso seja, uso break para sair do while. Não precisa criar outra variável com uma string qualquer só para depois comparar no while, é uma complicação desnecessária.

Eu não preciso ver se é 1, pois neste caso o loop deve continuar, então não preciso fazer nada.

E se não digitar um número, cai no catch e mostro a mensagem correspondente. Também retirei o InputMismatchException, pois esta exceção não é lançada por nextLine (somente por nextInt e similares).

Outro detalhe é que não precisa fechar o System.in.

E mudei o nome da variável para opcao, pois pgt ou pergunta não me parece um bom nome. Essa variável não tem a pergunta, e sim a resposta que o usuário digitou (então poderia até ser resposta, mas escolhi opcao por ser a opção que o usuário escolheu). Pode parecer bobagem, mas dar nomes melhores ajuda muito na hora de programar.


Ah, o if também poderia ser assim:

Scanner sc = new Scanner(System.in);
while (true) {
    try {
        System.out.println("Deseja continuar (1-sim/2-nao)?\n\nDigite o numero correspondente:");
        int opcao = Integer.parseInt(sc.nextLine());
        if (opcao == 2) {
            break; // sai do while
        } else if (opcao != 1) {
            System.out.println("Opção inválida, digite apenas 1 ou 2");
        }
    } catch (NumberFormatException e) {
        System.out.println("Você não digitou um número");
    }
}

System.out.println("Programa finalizado.");

Ou seja, se digitou 2, sai do while. Se não entrar nesse if é porque não digitou 2. Então no else eu vejo se o número é diferente de 1. Se for, então não é uma opção válida (é qualquer número diferente de 1 e 2). Se o número for 1, não entra em nenhum dos if's e o while continua executando.

1 curtida

boa noite!
O fato de ter 2 while, é pq são “utilizados em coisas distintas”… segue o código.
OBS: se puder me auxiliar em “como encaixar” essas soluções que me foram apresentas, ao “meu” código, agradeço. Acaba sempre tendo conflito com o “do-while” lá do começo.
package adivinhação;

import java.util.InputMismatchException;
import java.util.Scanner;

public class adv_forca {

public static void main(String[] args) {
	char chute;
	desenhoForca desenhaforca = new desenhoForca();
	int erro = 0;
	int pgt;
	boolean enforcou= false;
	boolean acertou= false;
	boolean ptgnovamente= false;
	boolean ptgnovamente2= false;
	Scanner sc = new Scanner(System.in);
	String palavraMisteriosa[] = {"","banana","melancia","granadilha"}; 
	String pergunta = "";
	do  {
	System.out.println("A dificuldade definirá a complexidade da palavra misteriosa ");
	System.out.println("1-        fácil       ");
	System.out.println("2-        médio       ");
	System.out.println("3-       difícil      ");
    System.out.println("Informe o número da dificuldade escolhida:");
    int dfcl = sc.nextInt();
    String palavraJogo = palavraMisteriosa[dfcl];
    
	System.out.print("A palavra misteriosa é: ");
	char acertos[] = new char[palavraJogo.length()];
    for(int i = 0; i<palavraJogo.length();i++) {
       	System.out.print(" _");   	
    }
    System.out.println();
    String chuteDados =" ";
    while(!enforcou || acertou == false) {
    	 System.out.println("\nDigite a letra que deseja chutar:");
    	 System.out.println("Vc já chutou as letras:"+chuteDados);
    	 chute = sc.next().charAt(0);
    	 chuteDados +=" " + chute ;
    	boolean desenhaCorpo = true;
    	for(int i =0; i < palavraJogo.length();i++) {
    		 acertou = true;
    		 if(chute == palavraJogo.charAt(i)) {
    			 desenhaCorpo = false;
    			 acertos[i] = 1;
    			}
	    	 }
		    if(desenhaCorpo) {
		    	erro = erro + 1;
		    	if(erro == 6) {
		    		enforcou = true;
		    	}
		    }
	    	 for (int i=0;i<palavraJogo.length();i++) {
	    		 if(acertos[i] == 0) {
	    			 System.out.print(" _");
	    			 acertou = false;
	    		 }	 
	    		 else {
	    			 System.out.print(" "+palavraJogo.charAt(i));
	    		 }
	    		 
	    		 
    		 }
	    	 if(acertou == true) {
	    		 break;
	    	 }
	    	 if(erro>=6) {
	    		 break;
	 	    }
	    	 System.out.println();
	    	 switch (erro) {
	    		case (0):
	    			desenhaforca.base();
	    		break;
	    		case (1):
	    			desenhaforca.primeiroErro();
	    		break;
	    		case (2):
	    			desenhaforca.segundoErro();
	    		break;
	    		case (3):
	    			desenhaforca.terceiroErro();
	    		break;
	    		case (4):
	    			desenhaforca.quartoErro();
	    		break;
	    		case (5):
	    			desenhaforca.quintoErro();
	    		break;
	    		case (6):
	    			desenhaforca.sextoErro();
	    		break;
	    		
	    	}
    }
    System.out.println();
    if(erro<6) {
    	desenhaforca.limpaTela();
    	desenhaforca.venceu();
    }
    	else {
    		desenhaforca.limpaTela();
    	   	 desenhaforca.endGame();
    	   	System.out.println();
    	   	System.out.printf("A palavra era:%s\n",palavraMisteriosa[dfcl]);
    	}

	System.out.println("Fim de jogo");	
	System.out.println();
	do {
		  try {
		    System.out.println("Deseja continuar (1-sim/2-nao)?");
		    System.out.println("Digite o numero correspondente:");
		    pgt = sc.nextInt();
		    System.out.println();
		    
		    if (pgt == 1) {
		      ptgnovamente2 = false;
		      pergunta = "sim";
		    } else if (pgt == 2) {
		      ptgnovamente2 = false;
		      pergunta = "nao";
		    } else {
		      ptgnovamente2 = true;
		    }
		  } catch (InputMismatchException | NumberFormatException err) {
			  return;
		  }
		} while (ptgnovamente2);
		} while (pergunta.equalsIgnoreCase("sim"));
	sc.close();

	
}

}

Em vez de colocar um while dentro de outro, com várias variáveis para controlá-los, acho que fica mais simples se você quebrar cada parte em métodos, assim cada loop fica isolado e mais fácil de gerenciar.

Outro ponto é que se você mistura nextInt com next e podem ocorrer alguns problemas (leia aqui para entender melhor). O melhor, quando a entrada for do teclado, é ler a linha inteira sempre com nextLine e tratar o dado de acordo (por exemplo, se for um número, convertê-lo com Integer.parseInt, etc).

E no último loop eu usaria o que eu já sugeri acima (nada de criar essa variável pergunta com uma string, sendo que você pode usar o valor numérico diretamente). E eu também disse que não precisa fechar o System.in.

E procure seguir as convenções de nomenclatura da linguagem: classes devem ter NomesDesseJeito em vez de nomes_desse_jeito.

No while (!enforcou || acertou == false), por que você usou o operador ! em um e == false em outro? Use ! em ambos, ué :slight_smile: - ou seja: while (!enforcou || !acertou). Se bem que no código que eu sugeri abaixo acabei nem fazendo desta forma.

Se uma variável só é usada em algum loop interno, não precisa declará-la logo no início do programa. Prefira declarar variáveis mais próximo de onde são usadas, pois isso também ajuda a delimitar seu escopo.

Na classe que desenha a forca, em vez de ter os métodos primeiroErro, segundoErro, etc, poderia ter um único método que desenha, recebendo a quantidade de erros como parâmetro. Aí lá dentro você decide como isso deve ser desenhado.

Enfim, uma sugestão seria:

// em vez de métodos primeiroErro, segundoErro, etc, faça um genérico que recebe a quantidade de erros
public class DesenhoForca { // mudei o nome para começar com maiúscula
    public void desenha(int erros) {
        // aqui você decide como desenhar, de acordo com a quantidade de erros
        // pode até ser o switch, que chama os métodos primeiroErro, segundoErro, etc
        // enfim, se tem uma classe que sabe desenhar a forca, eu prefiro deixar toda essa lógica dentro dela
    }
}
public class AdvForca { // mudei o nome para começar com maiúscula
    // ler um número, verificar se está entre os valores válidos
    private static int lerNumero(String prompt, Scanner scanner, int valorMinimo, int valorMaximo) {
        while (true) {
            try {
                System.out.println(prompt);
                int valor = Integer.parseInt(scanner.nextLine());
                if (valor < valorMinimo || valor > valorMaximo) {
                    System.out.printf("Valor inválido, deve estar entre %d e %d\n", valorMinimo, valorMaximo);
                } else {
                    return valor; // retorna o valor lido
                }
            } catch (NumberFormatException e) {
                System.out.println("Você não digitou um número");
            }
        }
    }

    private static int lerDificuldade(Scanner scanner) {
        String prompt = "A dificuldade definirá a complexidade da palavra misteriosa\n"
                + "1-        fácil       \n"
                + "2-        médio       \n"
                + "3-       difícil      \n"
                + "Informe o número da dificuldade escolhida:";
        return lerNumero(prompt, scanner, 1, 3);
    }

    private static char lerChute(Scanner sc, Set<String> chutesDados) {
        while (true) {
            System.out.println("Vc já chutou as letras:" + String.join(" ", chutesDados));
            System.out.println("\nDigite a letra que deseja chutar:");
            // lê o chute e converte para minúscula
            String chute = sc.nextLine().substring(0, 1).toLowerCase();
            if (chutesDados.contains(chute)) {
                System.out.println("Você já chutou esta letra antes, escolha outra\n");
            } else {
                chutesDados.add(chute);
                return chute.charAt(0);
            }
        }
    }

    // loop do jogo propriamente dito (onde o usuário vai tentar adivinhar a palavra)
    private static void gameLoop(Scanner sc, String palavraJogo, DesenhoForca desenhaForca) {
        int erros = 0;
        char acertos[] = new char[palavraJogo.length()];
        for (int i = 0; i < palavraJogo.length(); i++) {
            acertos[i] = '_'; // o próprio array de acertos possuirá os caracteres que foram adivinhados
        }
        // guarde os chutes em um java.util.Set, que não permite duplicados e é rápido para verificar se um elemento já existe
        Set<String> chutesDados = new TreeSet<>(); // java.util.TreeSet guarda os valores em ordem alfabética, ótimo para mostrar na tela
        while (true) { // jogo
            System.out.print("A palavra misteriosa é: ");
            for (char c : acertos) {
                System.out.printf(" %c", c);
            }
            System.out.println();

            char chute = lerChute(sc, chutesDados);
            // verifica se o chute está certo
            boolean acertou = false;
            for (int i = 0; i < palavraJogo.length(); i++) {
                if (chute == palavraJogo.charAt(i)) {
                    acertos[i] = chute; // atualiza o array de acertos
                    acertou = true;
                }
            }

            // limpaTela era chamado no if e no else, então ele sempre é chamado
            desenhaForca.limpaTela();
            if (acertou) {
                // verifica se venceu
                String palavraAcertos = new String(acertos);
                if (palavraJogo.equals(palavraAcertos)) {
                    desenhaForca.venceu();
                    return; // se venceu, sai do jogo
                }
            } else {
                erros++;
                desenhaForca.desenha(erros);
                if (erros == 6) { // se já errou bastante, acabou o jogo
                    desenhaForca.endGame();
                    System.out.printf("\nA palavra era:%s\n", palavraJogo);
                    return; // sai do jogo
                }
            }
        }
    }

    private static int lerOpcaoSair(Scanner scanner) {
        return lerNumero("Deseja continuar (1-sim/2-nao)?\n\nDigite o numero correspondente:", scanner, 1, 2);
    }

    public static void main(String[] args) {
        DesenhoForca desenhaForca = new DesenhoForca();
        Scanner sc = new Scanner(System.in);
        String palavraMisteriosa[] = {"banana", "melancia", "granadilha"};
        while (true) {
            int dificuldade = lerDificuldade(sc);
            String palavraJogo = palavraMisteriosa[dificuldade - 1];

            gameLoop(sc, palavraJogo, desenhaForca);
            System.out.println("Fim de jogo\n");

            // se escolheu a opção de sair, então sai do while
            if (lerOpcaoSair(sc) == 2) {
                break;
            }
        }
    }
}

Criei um método genérico para ler um número inteiro, e reaproveitei ele para ler a dificuldade e a opção de sair. Para ler os chutes eu usei um java.util.Set que guarda as letras já chutadas (melhor que ficar concatenando strings, pois em um Set você pode verificar facilmente as letras que já foram tentadas antes). E usando um TreeSet eu garanto que elas estarão em ordem alfabética, o que acho que fica mais organizado na hora de imprimir. Tem mais comentários no próprio código, explicando os detalhes que eu mudei.

Veja como o main fica mais simples e organizado, usando os métodos criados. Dentro de cada método você trata a complexidade particular de cada um.

Assim fica melhor do que ter vários loops, um dentro de outro, e muitas variáveis para controlar cada um. Tendo métodos separados, cada um deles individualmente fica mais simples, e a integração entre eles também fica mais fácil de fazer e entender.


Se quiser, dá pra deixar o main um pouco mais enxuto, eliminando algumas variáveis:

public static void main(String[] args) {
    DesenhoForca desenhaForca = new DesenhoForca();
    Scanner sc = new Scanner(System.in);
    String palavraMisteriosa[] = {"banana", "melancia", "granadilha"};
    while (true) {
        gameLoop(sc, palavraMisteriosa[lerDificuldade(sc) - 1], desenhaForca);
        System.out.println("Fim de jogo\n");

        // se escolheu a opção de sair, então sai do while
        if (lerOpcaoSair(sc) == 2) {
            break;
        }
    }
}

Mas aí você tem que avaliar se isso prejudica a legibilidade (nesse caso eu diria que nem tanto). Código menor não é necessariamente melhor. Mas criar variáveis desnecessárias também não é uma boa. Encontrar o ponto de equilíbrio nem sempre é fácil e costuma ser algo bem opinativo.