Nesta aula vamos continuar com a parte II do desenvolvimento do Jogo Campo Minado em Portugol. Durante a criação do jogo campo minado iremos praticar e aplicar tudo que estudamos até aqui em nosso curso de Algoritmos e Lógica de Programação. Exercícios como este são uma ótima escolha para fixar e aprimorar seu aprendizado de algoritmos e lógica de programação.
Veja aqui a Parte I
Dando continuidade ao nosso Campo Minado, vamos agora desenvolver uma função para ler uma única coordenada. Esta função deve retornar o valor lido apenas quando ele for válido, ou seja, estiver entre 0 e tamanho – 1.
/* * função que lê uma única coordenada * retorna a coordenada lida quando for válida */ funcao inteiro leCoordenada(){ inteiro x leia(x) enquanto(x < 0 ou x >= tamanho){ escreva("\n\tÍndice Inválido. Digite um valor entre 0 e ", tamanho -1, ".\n") leia(x) } retorne x }
Com a função anterior, ao chama-la duas vezes, conseguimos ler os índices de linha e coluna. Agora, precisamos também garantir que essa posição seja uma posição ainda fechada. Para fazer essa verificação vamos elaborar outra função que, dada uma coordenada, diz se a posição já foi aberta ou não.
/* * função que verifica se uma posição já foi aberta * 1 - aberta * 0 fechada */ funcao inteiro estaAberta(inteiro l, inteiro c){ se(Texto.posicao_texto("A", tabuleiro[l][c], 0) != -1) retorne 1 // está aberta retorne 0 // está fechada }
Vamos agora elaborar um procedimento para ler o índice da linha, o índice da coluna e verificar se a coordenada digitada está fechada. A leitura deve ser repetida até que uma coordenada válida seja inserida.
/* * procedimento que faz a leitura das coordenadas de linha e coluna */ funcao leCoordenadas(){ faca{ escreva("\n\tDigite a coordenada de linha:\n") linha = leCoordenada() escreva("\n\tDigite a coordenada de coluna:\n") coluna = leCoordenada() se(estaAberta(linha, coluna) == 1) escreva("\n\tEsta posição já foi aberta. Digite outra posição!\n") }enquanto(estaAberta(linha, coluna) == 1) }
O procedimento a seguir é primordial para o bom funcionamento do nosso jogo campo minado. Para cada coordenada digitada é necessário abrir essa posição, trocando o caracter F por A. Ao abrir uma posição, a quantidade de bombas em sua vizinhança deve ser verificada, caso seja zero, então seus vizinhos também devem ser abertos. Perceba que nesse pontos temos chamadas recursivas.
Como as chamadas recursivas são feitas com somas e subtrações nos índices digitados pelo jogador (para acessar os vizinhos), pode ser que sejam gerados índices inválidos, como quando o usuário digitar a posição 0 0 por exemplo, não existe a posição -1 -1. Por isso verificamos se a posição l c recebida é válida e está fechada logo no início.
/* * procedimento para abrir posição jogada * se tiver 0 bombas vizinhas, seus vizinhos devem ser abertos também */ funcao abrirPosicao(inteiro l, inteiro c){ se(eValida(l, c) == 1 e estaAberta(l, c) == 0){ tabuleiro[l][c] = Texto.substituir(tabuleiro[l][c], "F", "A") se(Texto.posicao_texto("0", tabuleiro[l][c], 2) != -1){ abrirPosicao(l - 1, c) // acima abrirPosicao(l + 1, c) // abaixo abrirPosicao(l, c + 1) // direita abrirPosicao(l, c - 1) // esquerda } } }
Também precisaremos de uma função que diz se o jogador perdeu (abriu uma bomba).
/* * função que verifica se o jogador perdeu * 1 - perdeu * 0 - não perdei ainda */ funcao inteiro Perdeu(inteiro l, inteiro c){ se(eBomba(l, c) == 1) retorne 1 // perdeu retorne 0 // ainda não perdeu }
E uma função que verifica quantas posições fechadas que não são bombas ainda existe.
/* * função que conta e retorna a quantidade de posições fechadas que não são bombas */ funcao inteiro totalFechadas(){ inteiro quantidade = 0 para(linha = 0; linha < tamanho; linha++){ para(coluna = 0; coluna < tamanho; coluna++){ se(eBomba(linha, coluna) == 0 e estaAberta(linha, coluna) == 0) quantidade++ } } //escreva("\n==== quantidade: ", quantidade, "====\n") retorne quantidade }
Com a função anterior, conseguimos determinar agora se o jogador venceu. Caso o número de posições fechadas que não são bombas for exatamente zero significa que o jogador venceu.
/* * função para verificar se o jogador ganhou * 1 - ganhou * 0 - não ganhou ainda */ funcao inteiro Ganhou(inteiro l, inteiro c){ se(totalFechadas() == 0) retorne 1 // jogador ganhou retorne 0 // jogador não ganhou ainda }
Na aula seguinte finalizaremos nosso jogo campo minado, elaborando o loop, a repetição que irá ler as coordenadas até que o jogador vença ou perca e finalizaremos o procedimento de impressão.
Código parcial para o jogo Campo Minado em Portugol
programa{ inclua biblioteca Util inclua biblioteca Texto /* * Código escrito por Wagner Gaspar * Abril de 2021 * * O tabuleiro é uma matriz de caracteres * índice 0 da cadeia indica posição aberta ou fechada: * A = aberta * F = fechada * índice 1 da cadeia indica se é bomba ou não: * * = é uma bomba * + = não é uma bomba * índice 2 da cadeia significa a quantidade de bombas vizinhas * 0 = zero bombas na vizinhança * 1 = uma bomba na vizinhança * 2 = duas bombas na vizinhança * 3 = três bombas na vizinhança * 4 = quatro bombas na vizinhança * OBSERVAÇÃO: são consideradas vizinhas de uma posição l c apenas 4 posições: * - imediatamente acima * - imediatamente abaixo * - imediatamente à direita * - imediatamente à esquerda * EXEMPLOS: * F+1 -> ainda fechada, não é bomba e possui 1 bomba vizinha * A+2 -> aberta, não é bomba e possui duas bombas vizinhas * F*0 -> ainda fechada, é uma bomba */ // variáveis globais cadeia tabuleiro[10][10] inteiro tamanho = 10, linha, coluna /* * Este procedimento inicializa nosso matriz * F significa fechado * A - aberto * * - bomba * + - não é bomba * 0 - zero bombas vizinhas */ funcao inicializarTabuleiro(){ para(linha = 0; linha < tamanho; linha++){ para(coluna = 0; coluna < tamanho; coluna++){ tabuleiro[linha][coluna] = "F+0" } } } // procedimento para imprimir nosso campo minado funcao imprimirTabuleiro(){ para(linha = 0; linha < tamanho; linha++){ para(coluna = 0; coluna < tamanho; coluna++){ escreva(tabuleiro[linha][coluna], "\n") escreva("\n") } } /* * procedimento que sorteia bombas * recebe como parâmetro a quantidade de bombas que deve ser gerado */ funcao sortearBombas(inteiro quantidade){ inteiro l, c para(linha = 1; linha <= quantidade; linha++){ l = Util.sorteia(0, tamanho - 1) // 0 até 9 c = Util.sorteia(0, tamanho - 1) // F+0 trocar o + por um * tabuleiro[l][c] = Texto.substituir(tabuleiro[l][c], "+", "*") } } /* * função que verifica se uma coordenada é válida * 1 - é válida * 0 - não é válida */ funcao inteiro eValida(inteiro l, inteiro c){ se(l >= 0 e l < tamanho e c >= 0 e c < tamanho) retorne 1 // é válido retorne 0 // não é válido } /* * Função que verifica se uma posição é bomba ou não * 1 - é bomba * 0 - não é bomba */ funcao inteiro eBomba(inteiro l, inteiro c){ se(eValida(l, c) == 1){ se(Texto.posicao_texto("*", tabuleiro[l][c], 1) != -1) retorne 1 // é uma bomba senao retorne 0 // não é uma bomba } retorne 0 // não é bomba ou é inválida } /* * Procedimento para contar a quantidade de bombas na vizinhança para cada posição */ funcao contarBombasVizinhas(){ inteiro quantidade = 0 para(linha = 0; linha < tamanho; linha++){ para(coluna = 0; coluna < tamanho; coluna++){ quantidade += eBomba(linha - 1, coluna) // acima quantidade += eBomba(linha + 1, coluna) // abaixo quantidade += eBomba(linha, coluna + 1) // à direita quantidade += eBomba(linha, coluna - 1) // à esquerda tabuleiro[linha][coluna] = Texto.substituir(tabuleiro[linha][coluna], "0", ""+quantidade) quantidade = 0 } } } /* * função que lê uma única coordenada * retorna a coordenada lida quando for válida */ funcao inteiro leCoordenada(){ inteiro x leia(x) enquanto(x < 0 ou x >= tamanho){ escreva("\n\tÍndice Inválido. Digite um valor entre 0 e ", tamanho -1, ".\n") leia(x) } retorne x } /* * função que verifica se uma posição já foi aberta * 1 - aberta * 0 fechada */ funcao inteiro estaAberta(inteiro l, inteiro c){ se(Texto.posicao_texto("A", tabuleiro[l][c], 0) != -1) retorne 1 // está aberta retorne 0 // está fechada } /* * procedimento que faz a leitura das coordenadas de linha e coluna */ funcao leCoordenadas(){ faca{ escreva("\n\tDigite a coordenada de linha:\n") linha = leCoordenada() escreva("\n\tDigite a coordenada de coluna:\n") coluna = leCoordenada() se(estaAberta(linha, coluna) == 1) escreva("\n\tEsta posição já foi aberta. Digite outra posição!\n") }enquanto(estaAberta(linha, coluna) == 1) } /* * procedimento para abrir posição jogada * se tiver 0 bombas vizinhas, seus vizinhos devem ser abertos também */ funcao abrirPosicao(inteiro l, inteiro c){ se(eValida(l, c) == 1 e estaAberta(l, c) == 0){ tabuleiro[l][c] = Texto.substituir(tabuleiro[l][c], "F", "A") se(Texto.posicao_texto("0", tabuleiro[l][c], 2) != -1){ abrirPosicao(l - 1, c) // acima abrirPosicao(l + 1, c) // abaixo abrirPosicao(l, c + 1) // direita abrirPosicao(l, c - 1) // esquerda } } } /* * função que verifica se o jogador perdeu * 1 - perdeu * 0 - não perdei ainda */ funcao inteiro Perdeu(inteiro l, inteiro c){ se(eBomba(l, c) == 1) retorne 1 // perdeu retorne 0 // ainda não perdeu } /* * função que conta e retorna a quantidade de posições fechadas que não são bombas */ funcao inteiro totalFechadas(){ inteiro quantidade = 0 para(linha = 0; linha < tamanho; linha++){ para(coluna = 0; coluna < tamanho; coluna++){ se(eBomba(linha, coluna) == 0 e estaAberta(linha, coluna) == 0) quantidade++ } } //escreva("\n==== quantidade: ", quantidade, "====\n") retorne quantidade } /* * função para verificar se o jogador ganhou * 1 - ganhou * 0 - não ganhou ainda */ funcao inteiro Ganhou(inteiro l, inteiro c){ se(totalFechadas() == 0) retorne 1 // jogador ganhou retorne 0 // jogador não ganhou ainda } funcao inicio(){ inicializarTabuleiro() sortearBombas(10) contarBombasVizinhas() imprimirTabuleiro() } }