Ajuda com loop while

Olá pessoal, estou fazendo um exercício de criação de um jogo da velha. Segue o código:

public static void main(String[] args) {
	
	Scanner scan = new Scanner(System.in);
	
	jogoDaVelha tabuleiro = new jogoDaVelha();

	System.out.println("Iniciando jogo");
	
	tabuleiro.nomeJogador1();
	tabuleiro.nomeJogador2();
	
	System.out.println(tabuleiro.nomeJogador1 + " jogará com o símbolo: 'X'");
	System.out.println(tabuleiro.nomeJogador2 + " jogará com o símbolo: 'O'");
	
	char simbolo = 'X'; 
	boolean jogadaValida = false;
	while(!jogadaValida) { 
		if(tabuleiro.jogada % 2 == 1) {
			System.out.println("Vez de " + tabuleiro.nomeJogador1 + ". Escolha linha e coluna 1-3.");
			simbolo = 'X';
		}else {
			System.out.println("Vez de " + tabuleiro.nomeJogador2 + ". Escolha linha e coluna 1-3");
			simbolo = 'O';
		} 
	}
	
	boolean linhaValida = false;
	int linha = 0, coluna = 0;
	while(!linhaValida) {
		System.out.println("Entre com a linha 1, 2 ou 3:");
		linha = scan.nextInt();
		if(linha >= 1 && linha <= tabuleiro.tamanhoTab1) {
			linhaValida = true;
		}else {
			System.out.println("Linha inexistente, tente novamente");
		}
	}
	
	boolean colunaValida = false;
	while(!colunaValida) {
		System.out.println("Entre com a coluna 1, 2 ou 3:");
		coluna = scan.nextInt();
		if(coluna >= 1 && coluna <= tabuleiro.tamanhoTab2) {
			colunaValida = true; 
		}else {
			System.out.println("Coluna inexistente, tente novamente");
		}
	}
	
	linha--;
	coluna--;
	
	if(tabuleiro.tabuleiro[linha][coluna] == 'X' || tabuleiro.tabuleiro[linha][coluna] == 'O') {
		System.out.println("Jogada inválida, tente novamente.");
	}else {
		tabuleiro.tabuleiro[linha][coluna] = simbolo;
		tabuleiro.jogada++;
	}
	
	for(int i = 0; i < tabuleiro.tabuleiro.length; i++) {
		for(int j = 0; j < tabuleiro.tabuleiro[i].length; j++) {
			System.out.print(tabuleiro.tabuleiro[i][j] + " | ");
		}
		System.out.println();
	}
	
	if((tabuleiro.tabuleiro[0][0] == 'X' && tabuleiro.tabuleiro[0][1] == 'X' && tabuleiro.tabuleiro[0][2] == 'X') || //Horizontal 1
		(tabuleiro.tabuleiro[1][0] == 'X' && tabuleiro.tabuleiro[1][1] == 'X' && tabuleiro.tabuleiro[1][2] == 'X') || //Horizontal 2
		(tabuleiro.tabuleiro[2][0] == 'X' && tabuleiro.tabuleiro[2][1] == 'X' && tabuleiro.tabuleiro[2][2] == 'X') || //Horizontal 3
		(tabuleiro.tabuleiro[0][0] == 'X' && tabuleiro.tabuleiro[1][0] == 'X' && tabuleiro.tabuleiro[2][0] == 'X') || //Vertical 1
		(tabuleiro.tabuleiro[0][1] == 'X' && tabuleiro.tabuleiro[1][1] == 'X' && tabuleiro.tabuleiro[2][1] == 'X') || //Vertical 2
		(tabuleiro.tabuleiro[0][2] == 'X' && tabuleiro.tabuleiro[1][2] == 'X' && tabuleiro.tabuleiro[2][2] == 'X') || //Vertical 3
		(tabuleiro.tabuleiro[0][0] == 'X' && tabuleiro.tabuleiro[1][1] == 'X' && tabuleiro.tabuleiro[2][2] == 'X') || //Diagonal
		(tabuleiro.tabuleiro[0][2] == 'X' && tabuleiro.tabuleiro[1][1] == 'X' && tabuleiro.tabuleiro[2][0] == 'X')) {//Diagonal inversa
		jogadaValida = true;
		System.out.println("Parabéns " + tabuleiro.nomeJogador1 + ", você ganhou!");
		}else if((tabuleiro.tabuleiro[0][0] == 'O' && tabuleiro.tabuleiro[0][1] == 'O' && tabuleiro.tabuleiro[0][2] == 'O') || //Horizontal 1
				(tabuleiro.tabuleiro[1][0] == 'O' && tabuleiro.tabuleiro[1][1] == 'O' && tabuleiro.tabuleiro[1][2] == 'O') || //Horizontal 2
				(tabuleiro.tabuleiro[2][0] == 'O' && tabuleiro.tabuleiro[2][1] == 'O' && tabuleiro.tabuleiro[2][2] == 'O') || //Horizontal 3
				(tabuleiro.tabuleiro[0][0] == 'O' && tabuleiro.tabuleiro[1][0] == 'O' && tabuleiro.tabuleiro[2][0] == 'O') || //Vertical 1
				(tabuleiro.tabuleiro[0][1] == 'O' && tabuleiro.tabuleiro[1][1] == 'O' && tabuleiro.tabuleiro[2][1] == 'O') || //Vertical 2
				(tabuleiro.tabuleiro[0][2] == 'O' && tabuleiro.tabuleiro[1][2] == 'O' && tabuleiro.tabuleiro[2][2] == 'O') || //Vertical 3
				(tabuleiro.tabuleiro[0][0] == 'O' && tabuleiro.tabuleiro[1][1] == 'O' && tabuleiro.tabuleiro[2][2] == 'O') || //Diagonal
				(tabuleiro.tabuleiro[0][2] == 'O' && tabuleiro.tabuleiro[1][1] == 'O' && tabuleiro.tabuleiro[2][0] == 'O')) {//Diagonal inversa
				jogadaValida = true;
				System.out.println("Parabéns " + tabuleiro.nomeJogador2 + ", você ganhou!");
		}else if(tabuleiro.jogada > 9) {
			jogadaValida = true;
			System.out.println(tabuleiro.nomeJogador1 + " e " + tabuleiro.nomeJogador2 + ", vocês empataram!");
		}


}

}

O primeiro while, não permite que ele prossiga o resto do código, fica num loop infinito do IF, visto que a variável “jogada” só vai mudar se o código continuar.

Tenho o mesmo código, feito depois, mas sem usar outra classe, o código todo numa classe só, e funciona essa parte do while normalmente. Segue o outro código:

public static void main(String[] args) {
	
    Scanner scan = new Scanner(System.in);
    
    char[][] jogoVelha = new char[3][3];
    
    System.out.println("Jogador 1 = X");
    System.out.println("Jogador 2 = O");
    
    boolean ganhou = false;
    int jogada = 1;
    char sinal;
    int linha = 0, coluna = 0;
    
    while (!ganhou){
        
        if (jogada % 2 == 1){ //jogador 1
            
            System.out.println("Vez do jogador 1. Escolha linha e coluna (1-3).");
            sinal = 'X';
        } else {
            
            System.out.println("Vez do jogador 2. Escolha linha e coluna (1-3).");
            sinal = 'O';
        }

Alguém poderia me dizer porque o segundo código ele continua para o próximo while e no primeiro não?
Sei que poderia usar outras formas de fazer esse laço prosseguir, mas como estou estudando, seria interessante entender.

Provavelmente porque no teu segundo while tens outro código abaixo desse if /else (que nao colocaste aqui), ainda dentro do while, onde a variavel ganhou é alterada. E no primeiro while nunca alteras a variavel jogadaValida dentro do mesmo logo o seu valor será sempre false e nunca vai sair do while.
E parece-me que estás a confundir uma jogada válida com um jogo terminado, uma vez que a única vez que atualizas a variável é quando o jogo deveria terminar.

Esse código faz basicamente o seguinte:

  • jogadaValida é inicializado com false
  • while(!jogadaValida) - o operador ! inverte o valor da expressão seguinte, ou seja, como jogadaValida é false então !jogadaValida é true
    • Isso quer dizer que o loop vai executar enquanto o valor de jogadaValida for false
  • Mas em nenhum momento dentro do while o valor de jogadaValida é alterado, por isso que fica em loop infinito

Ah, mas eu mudo o valor lá embaixo”. Não importa, como vc está em loop infinito, nunca vai chegar na linha que muda o valor de jogadaValida. Para que o loop seja interrompido, a alteração tem que ser feita dentro do próprio loop.

O seu segundo código, como já dito acima, funciona porque em algum momento vc muda a variável ganhou e isso é feito dentro do while, por isso funciona.

Não tem segredo, se vc está dentro de um loop, só consegue sair dele se mudar a condição sendo verificada (ou se em algum ponto tiver um break, por exemplo). Se tem algo que é feito depois, fora do loop, não faz diferença, porque só vai chegar nesse ponto depois que o loop terminar.


Dito isso, acho que fica mais fácil se vc quebrar cada parte do jogo em um método específico. Sugestão:

public class TesteJogoDaVelha {
    public static void main(String[] args) throws Exception {
        Scanner scan = new Scanner(System.in);

        char[][] tabuleiro = new char[3][3];
        System.out.println("Iniciando jogo\nDigite o nome do jogador 1");
        String nomeJogador1 = scan.nextLine();
        System.out.println("Digite o nome do jogador 2");
        String nomeJogador2 = scan.nextLine();
        System.out.println(nomeJogador1 + " jogará com o símbolo: 'X'");
        System.out.println(nomeJogador2 + " jogará com o símbolo: 'O'");

        char jogada = 'X';
        while (true) {
            exibir(tabuleiro);
            jogar(jogada == 'X' ? nomeJogador1 : nomeJogador2, tabuleiro, jogada, scan);
            // trocar jogador
            if (jogada == 'X') {
                jogada = 'O';
            } else {
                jogada = 'X';
            }
            if (jogoTerminou(tabuleiro)) { // verifica se terminou
                exibir(tabuleiro);
                break; // sair do while
            }
        }
    }

    static void jogar(String jogador, char[][] tabuleiro, char jogada, Scanner scan) {
        while (true) {
            // pede linha e coluna e verifica se já está ocupada (se estiver, pede para digitar novamente)
            int linha = lerInteiro(jogador, "linha", jogada, scan);
            int coluna = lerInteiro(jogador, "coluna", jogada, scan);
            if (tabuleiro[linha][coluna] != 0) {
                System.out.println("posição já está ocupada, tente outra");
            } else {
                tabuleiro[linha][coluna] = jogada;
                break; // posição ok, sai do while
            }
        }
    }

    static int lerInteiro(String jogador, String linCol, char jogada, Scanner scan) {
        while (true) {
            System.out.printf("Vez de %s (%c), escolha %s entre 1 e 3\n", jogador, jogada, linCol);
            try {
                int n = Integer.parseInt(scan.nextLine());
                if (n < 1 || n > 3) {
                    System.out.println("Valor deve estar entre 1 e 3");
                } else {
                    // os índices de um array começam em zero, então tem que subtrair 1 pra setar a posição correta do tabuleiro
                    return n - 1;
                }
            } catch (NumberFormatException e) {
                System.out.println("Valor digitado não é um número válido");
            }
        }
    }

    static void exibir(char[][] tabuleiro) {
        for (int i = 0; i < tabuleiro.length; i++) {
            char[] linha = tabuleiro[i];
            for (char c : linha) {
                if (c == 0) { // posição vazia
                    c = ' ';
                }
                System.out.printf("| %c ", c);
            }
            System.out.println("|");
            if (i < tabuleiro.length - 1) {
                System.out.println("----+---+----");
            }
        }
    }

    static boolean jogoTerminou(char[][] tabuleiro) {
        int[][][] posicoesVencedoras = {
            // horizontais
            {{0, 0}, {0, 1}, {0, 2}}, {{1, 0}, {1, 1}, {1, 2}}, {{2, 0}, {2, 1}, {2, 2}},
            // verticais
            {{0, 0}, {1, 0}, {2, 0}}, {{0, 1}, {1, 1}, {2, 1}}, {{0, 2}, {1, 2}, {2, 2}},
            // diagonais
            {{0, 0}, {1, 1}, {2, 2}}, {{0, 2}, {1, 1}, {2, 0}}
        };
        // verifica se alguma das posições vencedoras possui o mesmo símbolo
        for (int[][] posicoes : posicoesVencedoras) {
            int p1[] = posicoes[0], p2[] = posicoes[1], p3[] = posicoes[2];
            if (tabuleiro[p1[0]][p1[1]] != 0 && tabuleiro[p1[0]][p1[1]] == tabuleiro[p2[0]][p2[1]] && tabuleiro[p1[0]][p1[1]] == tabuleiro[p3[0]][p3[1]]) {
                System.out.println("Jogo terminou, vencedor: " + tabuleiro[p1[0]][p1[1]]);
                return true; // se achou um vencedor, não precisa verificar o resto
            }
        }
        // não tem vencedor, verifica se todas as posições já foram preenchidas
        for (char[] linha : tabuleiro) {
            for (char c : linha) {
                if (c == 0) {
                    // existe posição não preenchida, continua o jogo
                    return false;
                }
            }
        }
        // se chegou aqui, é porque não tem vencedor e não tem mais posições vazias
        System.out.println("Deu velha, ninguém ganhou");
        return true;
    }
}
public static void main(String[] args) {
	
    Scanner scan = new Scanner(System.in);
    
    char[][] jogoVelha = new char[3][3];
    
    System.out.println("Jogador 1 = X");
    System.out.println("Jogador 2 = O");
    
    boolean ganhou = false;
    int jogada = 1;
    char sinal;
    int linha = 0, coluna = 0;
    
    while (!ganhou){
        
        if (jogada % 2 == 1){ //jogador 1
            
            System.out.println("Vez do jogador 1. Escolha linha e coluna (1-3).");
            sinal = 'X';
        } else {
            
            System.out.println("Vez do jogador 2. Escolha linha e coluna (1-3).");
            sinal = 'O';
        }
        
        boolean linhaValida = false;
        while (!linhaValida){
            System.out.println("Entre com a linha (1, 2 ou 3)");
            linha = scan.nextInt();
            if (linha >=1 && linha<=3){
                linhaValida = true;
            } else {
                System.out.println("Entrada inválida, tente novamente");
            }
        }
        
        boolean colunaValida = false;
        while (!colunaValida){
            System.out.println("Entre com a coluna (1, 2 ou 3)");
            coluna = scan.nextInt();
            if (coluna >=1 && coluna<=3){
                colunaValida = true;
            } else {
                System.out.println("Entrada inválida, tente novamente");
            }
        }
        
        linha--;
        coluna--;
        if (jogoVelha[linha][coluna] == 'X' || jogoVelha[linha][coluna] == 'O'){
            System.out.println("Posição já usada, tente novamente");
        } else { //jogada válida
            jogoVelha[linha][coluna] = sinal;
            jogada++;
        }
        
        //imprimir tabuleiro
        for (int i=0; i<jogoVelha.length; i++){
            for (int j=0;j<jogoVelha[i].length; j++){
                System.out.print(jogoVelha[i][j] + " | ");
            }
            System.out.println();
        }
        
        //verifica se tem ganhador
        if ((jogoVelha[0][0] == 'X' && jogoVelha[0][1] == 'X' && jogoVelha[0][2] == 'X') ||     //linha 1
                (jogoVelha[1][0] == 'X' && jogoVelha[1][1] == 'X' && jogoVelha[1][2] == 'X') || //linha 2
                (jogoVelha[2][0] == 'X' && jogoVelha[2][1] == 'X' && jogoVelha[2][2] == 'X') || //linha 3
                (jogoVelha[0][0] == 'X' && jogoVelha[1][0] == 'X' && jogoVelha[2][0] == 'X') || //coluna 1
                (jogoVelha[0][1] == 'X' && jogoVelha[1][1] == 'X' && jogoVelha[2][1] == 'X') || //coluna 2
                (jogoVelha[0][2] == 'X' && jogoVelha[1][2] == 'X' && jogoVelha[2][2] == 'X') || //coluna 3
                (jogoVelha[0][0] == 'X' && jogoVelha[1][1] == 'X' && jogoVelha[2][2] == 'X') || //diagonal
                (jogoVelha[0][2] == 'X' && jogoVelha[1][1] == 'X' && jogoVelha[2][0] == 'X')){  //diagonal inversa
            ganhou = true;
            System.out.println("Parabéns, jogador 1 ganhou!");
        } else if ((jogoVelha[0][0] == 'O' && jogoVelha[0][1] == 'O' && jogoVelha[0][2] == 'O') ||     //linha 1
                (jogoVelha[1][0] == 'O' && jogoVelha[1][1] == 'O' && jogoVelha[1][2] == 'O') || //linha 2
                (jogoVelha[2][0] == 'O' && jogoVelha[2][1] == 'O' && jogoVelha[2][2] == 'O') || //linha 3
                (jogoVelha[0][0] == 'O' && jogoVelha[1][0] == 'O' && jogoVelha[2][0] == 'O') || //coluna 1
                (jogoVelha[0][1] == 'O' && jogoVelha[1][1] == 'O' && jogoVelha[2][1] == 'O') || //coluna 2
                (jogoVelha[0][2] == 'O' && jogoVelha[1][2] == 'O' && jogoVelha[2][2] == 'O') || //coluna 3
                (jogoVelha[0][0] == 'O' && jogoVelha[1][1] == 'O' && jogoVelha[2][2] == 'O') || //diagonal
                (jogoVelha[0][2] == 'O' && jogoVelha[1][1] == 'O' && jogoVelha[2][0] == 'O')){  //diagonal inversa
            ganhou = true;
            System.out.println("Parabéns, jogador 2 ganhou!");
        } else if (jogada > 9){
            ganhou = true;
            System.out.println("Ninguém ganhou essa partida.");
        }
    }
}

Esse é o segundo código completo

Muito show. Lendo os códigos eu entendi muito bem a finalidade de cada coisa, mas boa parte não estudei ainda. Com certeza seu código seria mais eficiente, mas eu queria entender no fluxo que eu estou estudando entende? Pra ter uma base sólida e não ter que ficar voltando pra estar coisas básicas depois.

Mas de qualquer forma, entendi sua explicação. Muito obrigado.

Veja que o valor de ganhou é alterado dentro do while, por isso que chega uma hora que ele se encerra (ao contrário do que acontece no primeiro caso, no qual o valor da variável nunca é alterado e por isso a condição do while sempre é verdadeira, causando o loop infinito).

Eu estava quebrando a cabeça justamente porque não tinha nada que alterasse o boolean no código e funcionava, e no outro não funcionava. Mas só depois que enviei ele completo aqui no tópico que percebi que funciona porque todo o código está dentro do while.

Já tinha debugado e tudo, mas não percebi essa diferença.

Obrigado pela atenção de vocês.